@0xmonaco/core 0.8.10 → 0.8.14

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
@@ -62,26 +62,17 @@ npm install viem@^2.31.7
62
62
 
63
63
  ## Network Support
64
64
 
65
- The SDK supports the following preset networks. Configure the network by providing the `network` and `seiRpcUrl` parameters:
65
+ The SDK supports the following documented preset networks. Configure the network by providing the `network` and `seiRpcUrl` parameters:
66
66
 
67
67
  **Preset Networks:**
68
- - `"development"` - Development environment (https://develop.apimonaco.xyz)
69
68
  - `"staging"` - Staging environment (https://staging.apimonaco.xyz)
70
69
  - `"mainnet"` - Production environment (https://api.monaco.xyz)
71
- - `"local"` - Local development (http://localhost:8080)
72
70
 
73
71
  WebSocket URLs are automatically resolved per network.
74
72
 
75
73
  ```typescript
76
74
  import { MonacoSDK } from "@0xmonaco/core";
77
75
 
78
- // Development configuration
79
- const devSdk = new MonacoSDK({
80
- walletClient,
81
- network: "development",
82
- seiRpcUrl: "https://evm-rpc-testnet.sei-apis.com",
83
- });
84
-
85
76
  // Staging configuration
86
77
  const stagingSdk = new MonacoSDK({
87
78
  walletClient,
@@ -114,7 +105,7 @@ const walletClient = createWalletClient({
114
105
 
115
106
  const monaco = new MonacoSDK({
116
107
  walletClient,
117
- network: "development", // or "staging", "mainnet", "local"
108
+ network: "staging", // or "mainnet"
118
109
  seiRpcUrl: "https://evm-rpc-testnet.sei-apis.com", // or https://evm-rpc.sei-apis.com for mainnet
119
110
  });
120
111
 
@@ -251,7 +242,7 @@ interface SDKConfig {
251
242
  /** Wallet client for signing operations (optional - can be set later via setWalletClient) */
252
243
  walletClient?: WalletClient;
253
244
 
254
- /** Network preset: "local", "development", "staging", or "mainnet" */
245
+ /** Use "staging" for public testnet or "mainnet" for production. */
255
246
  network: Network;
256
247
 
257
248
  /** RPC URL for Sei blockchain interactions */
@@ -1,13 +1,18 @@
1
- import type { GetAvailableCollateralParams, GetAvailableCollateralResponse, GetMarginAccountMovementsParams, GetMarginAccountMovementsResponse, GetMarginAccountSummaryParams, ListMarginAccountsParams, ListMarginAccountsResponse, MarginAccountSummary, MarginAccountsAPI, SimulateAutoMarginOrderRiskRequest, SimulateOrderRiskRequest, SimulateOrderRiskResponse, TransferCollateralRequest, TransferCollateralResponse, TransferCollateralToAutoMarginAccountRequest } from "@0xmonaco/types";
1
+ import type { GetAvailableCollateralParams, GetAvailableCollateralResponse, GetMarginAccountMovementsParams, GetMarginAccountMovementsResponse, GetMarginAccountSummaryParams, ListMarginAccountsParams, ListMarginAccountsResponse, MarginAccountSummary, MarginAccountsAPI, SimulateOrderRiskRequest, SimulateOrderRiskResponse, SimulateRiskBucketOrderRiskRequest, TransferCollateralFromParentMarginAccountRequest, TransferCollateralRequest, TransferCollateralResponse, TransferCollateralToParentMarginAccountRequest, TransferCollateralToRiskBucketRequest } from "@0xmonaco/types";
2
2
  import { BaseAPI } from "../base";
3
3
  export declare class MarginAccountsAPIImpl extends BaseAPI implements MarginAccountsAPI {
4
4
  listMarginAccounts(params?: ListMarginAccountsParams): Promise<ListMarginAccountsResponse>;
5
5
  getMarginAccountSummary(marginAccountId: string, params?: GetMarginAccountSummaryParams): Promise<MarginAccountSummary>;
6
+ getParentMarginAccountSummary(params?: GetMarginAccountSummaryParams): Promise<MarginAccountSummary>;
6
7
  getAvailableCollateral(params?: GetAvailableCollateralParams): Promise<GetAvailableCollateralResponse>;
7
8
  transferCollateralToMarginAccount(marginAccountId: string, request: TransferCollateralRequest): Promise<TransferCollateralResponse>;
8
- transferCollateralToAutoMarginAccount(request: TransferCollateralToAutoMarginAccountRequest): Promise<TransferCollateralResponse>;
9
+ transferCollateralToParentMarginAccount(request: TransferCollateralToParentMarginAccountRequest): Promise<TransferCollateralResponse>;
10
+ transferCollateralToRiskBucket(request: TransferCollateralToRiskBucketRequest): Promise<TransferCollateralResponse>;
9
11
  transferCollateralFromMarginAccount(marginAccountId: string, request: TransferCollateralRequest): Promise<TransferCollateralResponse>;
12
+ transferCollateralFromParentMarginAccount(request: TransferCollateralFromParentMarginAccountRequest): Promise<TransferCollateralResponse>;
10
13
  getMarginAccountMovements(marginAccountId: string, params?: GetMarginAccountMovementsParams): Promise<GetMarginAccountMovementsResponse>;
14
+ getParentMarginAccountMovements(params?: GetMarginAccountMovementsParams): Promise<GetMarginAccountMovementsResponse>;
11
15
  simulateOrderRisk(marginAccountId: string, request: SimulateOrderRiskRequest): Promise<SimulateOrderRiskResponse>;
12
- simulateAutoMarginOrderRisk(request: SimulateAutoMarginOrderRiskRequest): Promise<SimulateOrderRiskResponse>;
16
+ simulateParentMarginOrderRisk(request: SimulateOrderRiskRequest): Promise<SimulateOrderRiskResponse>;
17
+ simulateRiskBucketOrderRisk(request: SimulateRiskBucketOrderRiskRequest): Promise<SimulateOrderRiskResponse>;
13
18
  }
@@ -1,4 +1,4 @@
1
- import { GetAvailableCollateralSchema, GetMarginAccountMovementsSchema, GetMarginAccountSummarySchema, ListMarginAccountsSchema, SimulateAutoMarginOrderRiskSchema, SimulateOrderRiskSchema, TransferCollateralSchema, TransferCollateralToAutoMarginAccountSchema, validate, } from "@0xmonaco/types";
1
+ import { GetAvailableCollateralSchema, GetMarginAccountMovementsSchema, GetMarginAccountSummarySchema, GetParentMarginAccountMovementsSchema, GetParentMarginAccountSummarySchema, ListMarginAccountsSchema, SimulateOrderRiskSchema, SimulateParentMarginOrderRiskSchema, SimulateRiskBucketOrderRiskSchema, TransferCollateralFromParentMarginAccountSchema, TransferCollateralSchema, TransferCollateralToParentMarginAccountSchema, TransferCollateralToRiskBucketSchema, validate, } from "@0xmonaco/types";
2
2
  import { BaseAPI } from "../base";
3
3
  import { perpRoutes } from "../perp";
4
4
  export class MarginAccountsAPIImpl extends BaseAPI {
@@ -19,6 +19,10 @@ export class MarginAccountsAPIImpl extends BaseAPI {
19
19
  validate(GetMarginAccountSummarySchema, { marginAccountId, ...params });
20
20
  return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.summary(marginAccountId, { trading_pair_id: params?.tradingPairId }));
21
21
  }
22
+ async getParentMarginAccountSummary(params) {
23
+ validate(GetParentMarginAccountSummarySchema, params);
24
+ return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.parentSummary({ trading_pair_id: params?.tradingPairId }));
25
+ }
22
26
  async getAvailableCollateral(params) {
23
27
  validate(GetAvailableCollateralSchema, params);
24
28
  return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.availableCollateral(params));
@@ -35,9 +39,19 @@ export class MarginAccountsAPIImpl extends BaseAPI {
35
39
  }),
36
40
  });
37
41
  }
38
- async transferCollateralToAutoMarginAccount(request) {
39
- validate(TransferCollateralToAutoMarginAccountSchema, { request });
40
- return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.transferInAuto(), {
42
+ async transferCollateralToParentMarginAccount(request) {
43
+ validate(TransferCollateralToParentMarginAccountSchema, { request });
44
+ return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.transferInParent(), {
45
+ method: "POST",
46
+ body: JSON.stringify({
47
+ asset: request.asset,
48
+ amount: request.amount,
49
+ }),
50
+ });
51
+ }
52
+ async transferCollateralToRiskBucket(request) {
53
+ validate(TransferCollateralToRiskBucketSchema, { request });
54
+ return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.transferInRiskBucket(), {
41
55
  method: "POST",
42
56
  body: JSON.stringify({
43
57
  asset: request.asset,
@@ -59,10 +73,24 @@ export class MarginAccountsAPIImpl extends BaseAPI {
59
73
  }),
60
74
  });
61
75
  }
76
+ async transferCollateralFromParentMarginAccount(request) {
77
+ validate(TransferCollateralFromParentMarginAccountSchema, { request });
78
+ return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.transferOutParent(), {
79
+ method: "POST",
80
+ body: JSON.stringify({
81
+ asset: request.asset,
82
+ amount: request.amount,
83
+ }),
84
+ });
85
+ }
62
86
  async getMarginAccountMovements(marginAccountId, params) {
63
87
  validate(GetMarginAccountMovementsSchema, { marginAccountId, ...params });
64
88
  return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.movements(marginAccountId, params));
65
89
  }
90
+ async getParentMarginAccountMovements(params) {
91
+ validate(GetParentMarginAccountMovementsSchema, params);
92
+ return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.parentMovements(params));
93
+ }
66
94
  async simulateOrderRisk(marginAccountId, request) {
67
95
  validate(SimulateOrderRiskSchema, { marginAccountId, request });
68
96
  return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.simulateOrderRisk(marginAccountId), {
@@ -79,9 +107,25 @@ export class MarginAccountsAPIImpl extends BaseAPI {
79
107
  }),
80
108
  });
81
109
  }
82
- async simulateAutoMarginOrderRisk(request) {
83
- validate(SimulateAutoMarginOrderRiskSchema, { request });
84
- return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.simulateOrderRiskAuto(), {
110
+ async simulateParentMarginOrderRisk(request) {
111
+ validate(SimulateParentMarginOrderRiskSchema, { request });
112
+ return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.simulateParentMarginOrderRisk(), {
113
+ method: "POST",
114
+ body: JSON.stringify({
115
+ trading_pair_id: request.tradingPairId,
116
+ side: request.side,
117
+ position_side: request.positionSide,
118
+ order_type: request.orderType,
119
+ price: request.price,
120
+ quantity: request.quantity,
121
+ leverage: request.leverage,
122
+ reduce_only: request.reduceOnly,
123
+ }),
124
+ });
125
+ }
126
+ async simulateRiskBucketOrderRisk(request) {
127
+ validate(SimulateRiskBucketOrderRiskSchema, { request });
128
+ return await this.makeAuthenticatedRequest(perpRoutes.marginAccounts.simulateRiskBucketOrderRisk(), {
85
129
  method: "POST",
86
130
  body: JSON.stringify({
87
131
  trading_pair_id: request.tradingPairId,
@@ -1,4 +1,4 @@
1
- import type { Candlestick, FundingState, GetCandlesticksParams, GetScreenerParams, GetScreenerResponse, GetTradingPairsParams, GetTradingPairsResponse, IndexPrice, Interval, ListFundingHistoryParams, ListFundingHistoryResponse, MarketAPI, MarketMetadata, MarkPrice, OpenInterest, PerpMarketConfig, PerpMarketSummary, TradingPair } from "@0xmonaco/types";
1
+ import type { Candlestick, FundingState, GetCandlesticksParams, GetScreenerParams, GetScreenerResponse, GetTradingPairsParams, GetTradingPairsResponse, IndexPrice, Interval, ListFundingHistoryParams, ListFundingHistoryResponse, MarketAPI, MarketMetadata, MarketStats, MarkPrice, OpenInterest, PerpMarketConfig, PerpMarketSummary, TradingMode, TradingPair } from "@0xmonaco/types";
2
2
  import { BaseAPI } from "../base";
3
3
  /**
4
4
  * Market API Implementation
@@ -8,7 +8,7 @@ import { BaseAPI } from "../base";
8
8
  export declare class MarketAPIImpl extends BaseAPI implements MarketAPI {
9
9
  getPaginatedTradingPairs(params?: GetTradingPairsParams): Promise<GetTradingPairsResponse>;
10
10
  getTradingPair(tradingPairId: string): Promise<TradingPair>;
11
- getTradingPairBySymbol(symbol: string): Promise<TradingPair | undefined>;
11
+ getTradingPairBySymbol(symbol: string, marketType?: TradingMode): Promise<TradingPair | undefined>;
12
12
  getCandlesticks(tradingPairId: string, interval: Interval, params?: GetCandlesticksParams): Promise<Candlestick[]>;
13
13
  getMarketMetadata(tradingPairId: string): Promise<MarketMetadata>;
14
14
  getPerpMarketConfig(tradingPairId: string): Promise<PerpMarketConfig>;
@@ -19,4 +19,5 @@ export declare class MarketAPIImpl extends BaseAPI implements MarketAPI {
19
19
  listFundingHistory(tradingPairId: string, params?: ListFundingHistoryParams): Promise<ListFundingHistoryResponse>;
20
20
  getOpenInterest(tradingPairId: string): Promise<OpenInterest>;
21
21
  getScreener(params?: GetScreenerParams): Promise<GetScreenerResponse>;
22
+ getMarketStats(): Promise<MarketStats>;
22
23
  }
@@ -27,6 +27,9 @@ export class MarketAPIImpl extends BaseAPI {
27
27
  if (params?.is_active !== undefined) {
28
28
  searchParams.append("is_active", params.is_active.toString());
29
29
  }
30
+ if (params?.category) {
31
+ searchParams.append("category", params.category);
32
+ }
30
33
  const queryString = searchParams.toString();
31
34
  const url = queryString ? `/api/v1/market/pairs?${queryString}` : "/api/v1/market/pairs";
32
35
  return await this.makePublicRequest(url);
@@ -36,9 +39,11 @@ export class MarketAPIImpl extends BaseAPI {
36
39
  const response = await this.makePublicRequest(perpRoutes.market.getTradingPair(tradingPairId));
37
40
  return response.trading_pair;
38
41
  }
39
- async getTradingPairBySymbol(symbol) {
40
- // Backend endpoint expects UUID, not symbol, so we fetch all pairs and filter
41
- const response = await this.getPaginatedTradingPairs({ page_size: 100 });
42
+ async getTradingPairBySymbol(symbol, marketType) {
43
+ // Backend endpoint expects UUID, not symbol, so we fetch all pairs and filter.
44
+ // An optional market_type narrows the listing so a symbol present in both
45
+ // SPOT and MARGIN resolves unambiguously instead of returning the first match.
46
+ const response = await this.getPaginatedTradingPairs({ page_size: 100, market_type: marketType });
42
47
  return response.trading_pairs.find((pair) => pair.symbol === symbol);
43
48
  }
44
49
  async getCandlesticks(tradingPairId, interval, params) {
@@ -102,4 +107,7 @@ export class MarketAPIImpl extends BaseAPI {
102
107
  async getScreener(params) {
103
108
  return await this.makePublicRequest(perpRoutes.market.getScreener(params));
104
109
  }
110
+ async getMarketStats() {
111
+ return await this.makePublicRequest(perpRoutes.market.getMarketStats());
112
+ }
105
113
  }
@@ -69,7 +69,9 @@ export declare const perpRoutes: {
69
69
  page_size?: number;
70
70
  market_type?: string;
71
71
  is_active?: boolean;
72
+ category?: string;
72
73
  }) => string;
74
+ readonly getMarketStats: () => string;
73
75
  };
74
76
  readonly orderbook: {
75
77
  readonly get: (tradingPairId: string, params?: {
@@ -149,6 +151,7 @@ export declare const perpRoutes: {
149
151
  }) => string;
150
152
  readonly get: (positionId: string) => string;
151
153
  readonly close: (positionId: string) => string;
154
+ readonly batchCloseAll: () => string;
152
155
  readonly risk: (positionId: string) => string;
153
156
  readonly addMargin: (positionId: string) => string;
154
157
  readonly reduceMargin: (positionId: string) => string;
@@ -168,6 +171,9 @@ export declare const perpRoutes: {
168
171
  state?: string;
169
172
  trading_pair_id?: string;
170
173
  }) => string;
174
+ readonly parentSummary: (params?: {
175
+ trading_pair_id?: string;
176
+ }) => string;
171
177
  readonly summary: (marginAccountId: string, params?: {
172
178
  trading_pair_id?: string;
173
179
  }) => string;
@@ -175,15 +181,23 @@ export declare const perpRoutes: {
175
181
  asset?: string;
176
182
  }) => string;
177
183
  readonly transferIn: (marginAccountId: string) => string;
178
- readonly transferInAuto: () => string;
184
+ readonly transferInParent: () => string;
185
+ readonly transferInRiskBucket: () => string;
179
186
  readonly transferOut: (marginAccountId: string) => string;
187
+ readonly transferOutParent: () => string;
180
188
  readonly movements: (marginAccountId: string, params?: {
181
189
  movement_type?: string;
182
190
  page?: number;
183
191
  page_size?: number;
184
192
  }) => string;
193
+ readonly parentMovements: (params?: {
194
+ movement_type?: string;
195
+ page?: number;
196
+ page_size?: number;
197
+ }) => string;
198
+ readonly simulateParentMarginOrderRisk: () => string;
185
199
  readonly simulateOrderRisk: (marginAccountId: string) => string;
186
- readonly simulateOrderRiskAuto: () => string;
200
+ readonly simulateRiskBucketOrderRisk: () => string;
187
201
  };
188
202
  readonly streams: {
189
203
  readonly orderbook: () => string;
@@ -52,6 +52,7 @@ export const perpRoutes = {
52
52
  listFundingHistory: (tradingPairId, params) => withQuery(`${API_V1}/market/pairs/${encodeSegment(tradingPairId)}/funding/history`, params),
53
53
  getOpenInterest: (tradingPairId) => `${API_V1}/market/pairs/${encodeSegment(tradingPairId)}/open-interest`,
54
54
  getScreener: (params) => withQuery(`${API_V1}/market/screener`, params),
55
+ getMarketStats: () => `${API_V1}/market/stats`,
55
56
  },
56
57
  orderbook: {
57
58
  get: (tradingPairId, params) => withQuery(`${API_V1}/orderbook/${encodeSegment(tradingPairId)}`, params),
@@ -84,6 +85,7 @@ export const perpRoutes = {
84
85
  list: (params) => withQuery(`${API_V1}/positions`, params),
85
86
  get: (positionId) => `${API_V1}/positions/${encodeSegment(positionId)}`,
86
87
  close: (positionId) => `${API_V1}/positions/${encodeSegment(positionId)}/close`,
88
+ batchCloseAll: () => `${API_V1}/positions/batch-close-all`,
87
89
  risk: (positionId) => `${API_V1}/positions/${encodeSegment(positionId)}/risk`,
88
90
  addMargin: (positionId) => `${API_V1}/positions/${encodeSegment(positionId)}/margin/add`,
89
91
  reduceMargin: (positionId) => `${API_V1}/positions/${encodeSegment(positionId)}/margin/reduce`,
@@ -92,14 +94,19 @@ export const perpRoutes = {
92
94
  },
93
95
  marginAccounts: {
94
96
  list: (params) => withQuery(`${API_V1}/margin/accounts`, params),
97
+ parentSummary: (params) => withQuery(`${API_V1}/margin/parent-margin-account`, params),
95
98
  summary: (marginAccountId, params) => withQuery(`${API_V1}/margin/accounts/${encodeSegment(marginAccountId)}`, params),
96
99
  availableCollateral: (params) => withQuery(`${API_V1}/margin/collateral/available`, params),
97
100
  transferIn: (marginAccountId) => `${API_V1}/margin/accounts/${encodeSegment(marginAccountId)}/collateral/transfer-in`,
98
- transferInAuto: () => `${API_V1}/margin/collateral/transfer-in`,
101
+ transferInParent: () => `${API_V1}/margin/parent-margin-account/collateral/transfer-in`,
102
+ transferInRiskBucket: () => `${API_V1}/margin/risk-buckets/collateral/transfer-in`,
99
103
  transferOut: (marginAccountId) => `${API_V1}/margin/accounts/${encodeSegment(marginAccountId)}/collateral/transfer-out`,
104
+ transferOutParent: () => `${API_V1}/margin/parent-margin-account/collateral/transfer-out`,
100
105
  movements: (marginAccountId, params) => withQuery(`${API_V1}/margin/accounts/${encodeSegment(marginAccountId)}/movements`, params),
106
+ parentMovements: (params) => withQuery(`${API_V1}/margin/parent-margin-account/movements`, params),
107
+ simulateParentMarginOrderRisk: () => `${API_V1}/margin/parent-margin-account/simulate-order-risk`,
101
108
  simulateOrderRisk: (marginAccountId) => `${API_V1}/margin/accounts/${encodeSegment(marginAccountId)}/simulate-order-risk`,
102
- simulateOrderRiskAuto: () => `${API_V1}/margin/simulate-order-risk`,
109
+ simulateRiskBucketOrderRisk: () => `${API_V1}/margin/risk-buckets/simulate-order-risk`,
103
110
  },
104
111
  streams: {
105
112
  orderbook: () => `${API_V1}/streaming/orderbook`,
@@ -1,9 +1,10 @@
1
- import type { AddPositionMarginRequest, AttachPositionTpSlRequest, AttachPositionTpSlResponse, ClosePositionRequest, ClosePositionResponse, GetPositionResponse, ListPositionHistoryParams, ListPositionHistoryResponse, ListPositionsParams, ListPositionsResponse, PositionMarginResponse, PositionRisk, PositionsAPI, ReducePositionMarginRequest } from "@0xmonaco/types";
1
+ import type { AddPositionMarginRequest, AttachPositionTpSlRequest, AttachPositionTpSlResponse, BatchCloseAllRequest, BatchCloseAllResponse, ClosePositionRequest, ClosePositionResponse, GetPositionResponse, ListPositionHistoryParams, ListPositionHistoryResponse, ListPositionsParams, ListPositionsResponse, PositionMarginResponse, PositionRisk, PositionsAPI, ReducePositionMarginRequest } from "@0xmonaco/types";
2
2
  import { BaseAPI } from "../base";
3
3
  export declare class PositionsAPIImpl extends BaseAPI implements PositionsAPI {
4
4
  listPositions(params?: ListPositionsParams): Promise<ListPositionsResponse>;
5
5
  getPosition(positionId: string): Promise<GetPositionResponse>;
6
6
  closePosition(positionId: string, request: ClosePositionRequest): Promise<ClosePositionResponse>;
7
+ batchCloseAllPositions(request?: BatchCloseAllRequest): Promise<BatchCloseAllResponse>;
7
8
  getPositionRisk(positionId: string): Promise<PositionRisk>;
8
9
  addPositionMargin(positionId: string, request: AddPositionMarginRequest): Promise<PositionMarginResponse>;
9
10
  reducePositionMargin(positionId: string, request: ReducePositionMarginRequest): Promise<PositionMarginResponse>;
@@ -1,4 +1,4 @@
1
- import { AddPositionMarginSchema, AttachPositionTpSlSchema, ClosePositionSchema, GetPositionRiskSchema, GetPositionSchema, ListPositionHistorySchema, ListPositionsSchema, ReducePositionMarginSchema, validate, } from "@0xmonaco/types";
1
+ import { AddPositionMarginSchema, AttachPositionTpSlSchema, BatchCloseAllSchema, ClosePositionSchema, GetPositionRiskSchema, GetPositionSchema, ListPositionHistorySchema, ListPositionsSchema, ReducePositionMarginSchema, validate, } from "@0xmonaco/types";
2
2
  import { BaseAPI } from "../base";
3
3
  import { perpRoutes } from "../perp";
4
4
  export class PositionsAPIImpl extends BaseAPI {
@@ -24,6 +24,18 @@ export class PositionsAPIImpl extends BaseAPI {
24
24
  }),
25
25
  });
26
26
  }
27
+ async batchCloseAllPositions(request) {
28
+ if (request) {
29
+ validate(BatchCloseAllSchema, request);
30
+ }
31
+ return await this.makeAuthenticatedRequest(perpRoutes.positions.batchCloseAll(), {
32
+ method: "POST",
33
+ body: JSON.stringify({
34
+ trading_pair_id: request?.tradingPairId,
35
+ slippage_tolerance_bps: request?.slippageToleranceBps,
36
+ }),
37
+ });
38
+ }
27
39
  async getPositionRisk(positionId) {
28
40
  validate(GetPositionRiskSchema, { positionId });
29
41
  return await this.makeAuthenticatedRequest(perpRoutes.positions.risk(positionId));
@@ -80,8 +80,8 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
80
80
  expirationDate?: string;
81
81
  timeInForce?: TimeInForce;
82
82
  marginAccountId?: string;
83
- marginBucketId?: string;
84
- marginBucketCollateral?: string;
83
+ riskBucketId?: string;
84
+ riskBucketCollateral?: string;
85
85
  strategyKey?: string;
86
86
  positionSide?: PositionSide;
87
87
  leverage?: string;
@@ -134,8 +134,8 @@ export declare class TradingAPIImpl extends BaseAPI implements TradingAPI {
134
134
  tradingMode?: TradingMode;
135
135
  slippageTolerance?: number;
136
136
  marginAccountId?: string;
137
- marginBucketId?: string;
138
- marginBucketCollateral?: string;
137
+ riskBucketId?: string;
138
+ riskBucketCollateral?: string;
139
139
  strategyKey?: string;
140
140
  positionSide?: PositionSide;
141
141
  leverage?: string;
@@ -107,8 +107,8 @@ export class TradingAPIImpl extends BaseAPI {
107
107
  expiration_date: options?.expirationDate,
108
108
  time_in_force: options?.timeInForce,
109
109
  margin_account_id: options?.marginAccountId,
110
- margin_bucket_id: options?.marginBucketId,
111
- margin_bucket_collateral: options?.marginBucketCollateral,
110
+ risk_bucket_id: options?.riskBucketId,
111
+ risk_bucket_collateral: options?.riskBucketCollateral,
112
112
  strategy_key: options?.strategyKey,
113
113
  position_side: options?.positionSide,
114
114
  leverage: options?.leverage,
@@ -179,8 +179,8 @@ export class TradingAPIImpl extends BaseAPI {
179
179
  quantity,
180
180
  trading_mode: options?.tradingMode || "SPOT",
181
181
  margin_account_id: options?.marginAccountId,
182
- margin_bucket_id: options?.marginBucketId,
183
- margin_bucket_collateral: options?.marginBucketCollateral,
182
+ risk_bucket_id: options?.riskBucketId,
183
+ risk_bucket_collateral: options?.riskBucketCollateral,
184
184
  strategy_key: options?.strategyKey,
185
185
  position_side: options?.positionSide,
186
186
  leverage: options?.leverage,
@@ -344,8 +344,8 @@ export class TradingAPIImpl extends BaseAPI {
344
344
  expiration_date: order.expirationDate,
345
345
  time_in_force: order.timeInForce,
346
346
  margin_account_id: order.marginAccountId,
347
- margin_bucket_id: order.marginBucketId,
348
- margin_bucket_collateral: order.marginBucketCollateral,
347
+ risk_bucket_id: order.riskBucketId,
348
+ risk_bucket_collateral: order.riskBucketCollateral,
349
349
  strategy_key: order.strategyKey,
350
350
  position_side: order.positionSide,
351
351
  leverage: order.leverage,
@@ -23,9 +23,21 @@
23
23
  * console.log(`Deposit transaction: ${result.hash}`);
24
24
  * ```
25
25
  */
26
- import type { ApplicationsAPI, Balance, ProfileAPI, TransactionResult, VaultAPI, WithdrawResult } from "@0xmonaco/types";
26
+ import type { ApplicationsAPI, Balance, DepositTarget, ProfileAPI, TransactionResult, VaultAPI, WithdrawalRetryOptions, WithdrawalSource, WithdrawResult } from "@0xmonaco/types";
27
27
  import { type Address, type Chain, type PublicClient, type WalletClient } from "viem";
28
28
  import { BaseAPI } from "../base";
29
+ /**
30
+ * Encode the on-chain `applicationData` for a deposit.
31
+ *
32
+ * For `"spot"` (the default) this stays the bare `clientId` string — byte-for-byte
33
+ * identical to the legacy encoding, so existing deposits are unchanged. For
34
+ * `"margin"` it emits a small JSON routing hint the indexer decodes to credit the
35
+ * deposit to margin collateral.
36
+ *
37
+ * MUST stay in sync with the indexer decoder in
38
+ * `indexer/src/listeners/deposit_routing.rs`.
39
+ */
40
+ export declare function encodeDepositApplicationData(clientId: string, target: DepositTarget): string;
29
41
  export declare class VaultAPIImpl extends BaseAPI implements VaultAPI {
30
42
  private readonly publicClient;
31
43
  private readonly chain;
@@ -121,6 +133,10 @@ export declare class VaultAPIImpl extends BaseAPI implements VaultAPI {
121
133
  * @param assetId - The asset identifier (UUID) to deposit
122
134
  * @param amount - The amount of tokens to deposit (as bigint)
123
135
  * @param autoWait - Whether to automatically wait for transaction confirmation (defaults to true)
136
+ * @param target - Destination ledger: `"spot"` (default) credits the spot
137
+ * wallet; `"margin"` routes the deposit into the parent margin account's
138
+ * collateral (auto-creating the account if needed). Margin deposits that
139
+ * cannot be routed safely fall back to spot.
124
140
  * @returns Promise resolving to TransactionResult with transaction details
125
141
  * @throws {ContractError} When deposit fails or approval is insufficient
126
142
  * @throws {APIError} When the asset is not found or the assetId is invalid
@@ -136,6 +152,14 @@ export declare class VaultAPIImpl extends BaseAPI implements VaultAPI {
136
152
  * console.log(`Deposit transaction: ${result.hash}`);
137
153
  * console.log(`Status: ${result.status}`); // "confirmed" if successful
138
154
  *
155
+ * // Deposit straight into margin collateral
156
+ * const marginDeposit = await vaultAPI.deposit(
157
+ * "123e4567-e89b-12d3-a456-426614174000",
158
+ * parseUnits("100", 6),
159
+ * true,
160
+ * "margin"
161
+ * );
162
+ *
139
163
  * // Or skip auto-waiting
140
164
  * const result = await vaultAPI.deposit(
141
165
  * "123e4567-e89b-12d3-a456-426614174000",
@@ -146,23 +170,54 @@ export declare class VaultAPIImpl extends BaseAPI implements VaultAPI {
146
170
  * const receipt = await sdk.waitForTransaction(result.hash);
147
171
  * ```
148
172
  */
149
- deposit(assetId: string, amount: bigint, autoWait?: boolean): Promise<TransactionResult>;
173
+ deposit(assetId: string, amount: bigint, autoWait?: boolean, target?: DepositTarget): Promise<TransactionResult>;
150
174
  /**
151
- * Initiates a withdrawal through the API Gateway and submits the resulting
152
- * pre-signed calldata on-chain via the connected wallet.
175
+ * Polls `GET /api/v1/withdrawals/{index}` until the gateway can serve the
176
+ * `executeWithdrawal` calldata, then returns it with the vault address.
177
+ *
178
+ * The calldata only exists once the withdrawal's root is confirmed on-chain
179
+ * and its merkle proof is persisted. Until then the gateway returns 404 (the
180
+ * row is written asynchronously by the persistor after `initiate`) or 409
181
+ * (the proof is not confirmed yet); both are transient for a freshly-allocated
182
+ * index, so we retry on each until ready or the timeout elapses.
153
183
  *
154
- * The gateway allocates a `withdrawalIndex` via the matching engine and returns
155
- * ABI-encoded calldata for `executeSignedWithdrawal(...)` signed by the
156
- * server-side `WITHDRAWAL_SIGNER`. The user's wallet pays gas to submit it,
157
- * but the contract authenticates against the embedded signature not the
158
- * `msg.sender`. The connected wallet's address is sent as the on-chain
159
- * `destination`.
184
+ * Returns `null` when the timeout elapses before the proof is available — the
185
+ * index is still valid and the balance already debited, so callers surface
186
+ * this as `awaiting_proof` (with the index intact) rather than as a thrown
187
+ * error that would strand the index. Only non-transient errors (anything
188
+ * other than 404/409) throw.
189
+ *
190
+ * @throws {APIError} On non-transient errors while polling.
191
+ */
192
+ private pollWithdrawalCalldata;
193
+ /**
194
+ * Initiates a withdrawal through the API Gateway, waits for its on-chain
195
+ * confirmation, then submits the resulting `executeWithdrawal` calldata via
196
+ * the connected wallet.
197
+ *
198
+ * The gateway allocates a `withdrawalIndex` via the matching engine and debits
199
+ * the balance immediately, but the executable calldata needs the withdrawal's
200
+ * merkle proof, which only exists once its root is confirmed on-chain. This
201
+ * method polls until the calldata is available (see `retry`), then submits it.
202
+ * The connected wallet pays gas; the contract authorises the withdrawal
203
+ * against the merkle proof embedded in the calldata. The wallet's address is
204
+ * sent as the on-chain `destination`.
205
+ *
206
+ * If the proof does not become available within the poll window, this method
207
+ * does NOT throw — it returns `{ withdrawalIndex, status: "awaiting_proof" }`
208
+ * with nothing submitted on-chain. The balance is already debited, so losing
209
+ * the index would strand the funds; the caller keeps it and finishes the
210
+ * withdrawal later with `retryWithdrawal(withdrawalIndex)`.
160
211
  *
161
212
  * @param assetId - The asset identifier (UUID) to withdraw
162
213
  * @param amount - The raw token amount to withdraw (as bigint)
163
214
  * @param autoWait - Whether to automatically wait for transaction confirmation (defaults to true)
164
- * @returns Promise resolving to `{ withdrawalIndex, transaction }`
165
- * @throws {APIError} When the asset is not found or the request is rejected
215
+ * @param sourceOrRetry - Source ledger (`"spot"` or `"margin"`) or legacy retry options
216
+ * @param retry - Polling cadence/timeout while waiting for the proof when a source is supplied
217
+ * @returns Promise resolving to a [`WithdrawResult`]: on success the submitted
218
+ * transaction plus `withdrawalIndex`; on poll-timeout `{ withdrawalIndex,
219
+ * status: "awaiting_proof" }`.
220
+ * @throws {APIError} When the asset is not found or the initiate request is rejected
166
221
  * @throws {ContractError} When the on-chain submission fails
167
222
  * @throws {InvalidConfigError} When wallet account is not available
168
223
  *
@@ -174,38 +229,50 @@ export declare class VaultAPIImpl extends BaseAPI implements VaultAPI {
174
229
  * parseUnits("50", 6),
175
230
  * );
176
231
  * console.log(`Withdrawal index: ${result.withdrawalIndex}`);
177
- * console.log(`Tx hash: ${result.hash}, status: ${result.status}`);
232
+ * if (result.status === "awaiting_proof") {
233
+ * // Proof not ready yet — balance already debited. Finish later:
234
+ * await vaultAPI.retryWithdrawal(result.withdrawalIndex);
235
+ * } else {
236
+ * console.log(`Tx hash: ${result.hash}, status: ${result.status}`);
237
+ * }
178
238
  *
179
- * // Or skip auto-waiting and finalise later
239
+ * // Custom polling (check every 10s, wait up to an hour)
180
240
  * const result = await vaultAPI.withdraw(
181
241
  * "123e4567-e89b-12d3-a456-426614174000",
182
242
  * parseUnits("50", 6),
183
- * false,
243
+ * true,
244
+ * { pollIntervalMs: 10_000, timeoutMs: 3_600_000 },
184
245
  * );
185
- * const receipt = await sdk.waitForTransaction(result.hash);
186
246
  * ```
187
247
  */
188
- withdraw(assetId: string, amount: bigint, autoWait?: boolean): Promise<WithdrawResult>;
248
+ withdraw(assetId: string, amount: bigint, autoWait?: boolean, sourceOrRetry?: WithdrawalSource | WithdrawalRetryOptions, retry?: WithdrawalRetryOptions): Promise<WithdrawResult>;
189
249
  /**
190
- * Retries a previously-initiated withdrawal whose on-chain submission never
191
- * landed — e.g. the wallet rejected the tx, the page reloaded before the
192
- * receipt came back, or a stuck mempool entry needs resending.
250
+ * Submits (or resubmits) a previously-initiated withdrawal on-chain.
251
+ *
252
+ * Use this when the original `withdraw()` could not complete — e.g. the proof
253
+ * was not yet confirmed within its polling window, the wallet rejected the tx,
254
+ * the page reloaded before the receipt came back, or a stuck mempool entry
255
+ * needs resending. Polls `GET /withdrawals/{index}` for the
256
+ * `executeWithdrawal` calldata (retrying while it is not yet confirmed), then
257
+ * submits it through the connected wallet. Does NOT initiate a new withdrawal
258
+ * — the matching engine already debited the balance and allocated the index.
259
+ * The contract is idempotent against double-submission of a settled
260
+ * withdrawal: it reverts once the index is consumed on-chain.
193
261
  *
194
- * Re-fetches the same `executeSignedWithdrawal` calldata the gateway
195
- * generated when the withdrawal was initiated, then submits it through the
196
- * connected wallet. Does NOT initiate a new withdrawal — the matching engine
197
- * already debited the balance and allocated the index. The contract is
198
- * idempotent against double-submission of a settled withdrawal: it will
199
- * revert once the index is consumed on-chain.
262
+ * Like `withdraw()`, if the proof is still unavailable within the poll window
263
+ * this returns `{ withdrawalIndex, status: "awaiting_proof" }` instead of
264
+ * throwing, so the caller can simply retry again later.
200
265
  *
201
266
  * @param withdrawalIndex - The index returned by the original `withdraw()` call
202
267
  * @param autoWait - Whether to await on-chain confirmation (defaults to true)
203
- * @returns Promise resolving to `{ withdrawalIndex, ...transaction }`
204
- * @throws {APIError} When the index doesn't exist
268
+ * @param retry - Polling cadence/timeout while waiting for the proof
269
+ * @returns Promise resolving to a [`WithdrawResult`]: the submitted transaction
270
+ * plus `withdrawalIndex`, or `{ withdrawalIndex, status: "awaiting_proof" }`
271
+ * if the proof did not arrive within the poll window.
205
272
  * @throws {ContractError} When the on-chain submission fails
206
273
  * @throws {InvalidConfigError} When wallet account is not available
207
274
  */
208
- retryWithdrawal(withdrawalIndex: number, autoWait?: boolean): Promise<WithdrawResult>;
275
+ retryWithdrawal(withdrawalIndex: number, autoWait?: boolean, retry?: WithdrawalRetryOptions): Promise<WithdrawResult>;
209
276
  /**
210
277
  * Retrieves the user's token balance in the vault.
211
278
  *
@@ -28,6 +28,31 @@ import { ApproveTokenSchema, DepositSchema, validate, WithdrawSchema } from "@0x
28
28
  import { erc20Abi, zeroAddress } from "viem";
29
29
  import { APIError, ContractError, InvalidConfigError } from "../../errors";
30
30
  import { BaseAPI } from "../base";
31
+ /** Default delay between polls while waiting for a withdrawal's proof. */
32
+ const DEFAULT_WITHDRAWAL_POLL_INTERVAL_MS = 5_000;
33
+ /**
34
+ * Default total wait for a withdrawal's proof (5 minutes). A withdrawal's merkle
35
+ * proof only lands after its root is confirmed on-chain, which on a healthy
36
+ * pipeline takes ~20s but can lag behind a brief proposer hiccup; the previous
37
+ * 60s tripped too eagerly. On timeout the SDK returns `awaiting_proof` rather
38
+ * than throwing, so the caller never loses the index — it just retries later.
39
+ */
40
+ const DEFAULT_WITHDRAWAL_TIMEOUT_MS = 300_000;
41
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
42
+ /**
43
+ * Encode the on-chain `applicationData` for a deposit.
44
+ *
45
+ * For `"spot"` (the default) this stays the bare `clientId` string — byte-for-byte
46
+ * identical to the legacy encoding, so existing deposits are unchanged. For
47
+ * `"margin"` it emits a small JSON routing hint the indexer decodes to credit the
48
+ * deposit to margin collateral.
49
+ *
50
+ * MUST stay in sync with the indexer decoder in
51
+ * `indexer/src/listeners/deposit_routing.rs`.
52
+ */
53
+ export function encodeDepositApplicationData(clientId, target) {
54
+ return target === "margin" ? JSON.stringify({ clientId, depositTarget: "MARGIN" }) : clientId;
55
+ }
31
56
  export class VaultAPIImpl extends BaseAPI {
32
57
  publicClient;
33
58
  chain;
@@ -196,6 +221,10 @@ export class VaultAPIImpl extends BaseAPI {
196
221
  * @param assetId - The asset identifier (UUID) to deposit
197
222
  * @param amount - The amount of tokens to deposit (as bigint)
198
223
  * @param autoWait - Whether to automatically wait for transaction confirmation (defaults to true)
224
+ * @param target - Destination ledger: `"spot"` (default) credits the spot
225
+ * wallet; `"margin"` routes the deposit into the parent margin account's
226
+ * collateral (auto-creating the account if needed). Margin deposits that
227
+ * cannot be routed safely fall back to spot.
199
228
  * @returns Promise resolving to TransactionResult with transaction details
200
229
  * @throws {ContractError} When deposit fails or approval is insufficient
201
230
  * @throws {APIError} When the asset is not found or the assetId is invalid
@@ -211,6 +240,14 @@ export class VaultAPIImpl extends BaseAPI {
211
240
  * console.log(`Deposit transaction: ${result.hash}`);
212
241
  * console.log(`Status: ${result.status}`); // "confirmed" if successful
213
242
  *
243
+ * // Deposit straight into margin collateral
244
+ * const marginDeposit = await vaultAPI.deposit(
245
+ * "123e4567-e89b-12d3-a456-426614174000",
246
+ * parseUnits("100", 6),
247
+ * true,
248
+ * "margin"
249
+ * );
250
+ *
214
251
  * // Or skip auto-waiting
215
252
  * const result = await vaultAPI.deposit(
216
253
  * "123e4567-e89b-12d3-a456-426614174000",
@@ -221,14 +258,17 @@ export class VaultAPIImpl extends BaseAPI {
221
258
  * const receipt = await sdk.waitForTransaction(result.hash);
222
259
  * ```
223
260
  */
224
- async deposit(assetId, amount, autoWait = true) {
261
+ async deposit(assetId, amount, autoWait = true, target = "spot") {
225
262
  if (!this.walletClient) {
226
263
  throw new InvalidConfigError("Wallet client not set. Connect a wallet first.", "walletClient");
227
264
  }
228
265
  // Validate inputs
229
- validate(DepositSchema, { assetId, amount, autoWait });
266
+ validate(DepositSchema, { assetId, amount, autoWait, target });
230
267
  const vaultAddress = await this.getVaultAddress();
231
268
  const clientId = await this.getClientId();
269
+ // The on-chain applicationData carries the client id plus, for margin
270
+ // deposits, the routing hint the indexer decodes.
271
+ const applicationData = encodeDepositApplicationData(clientId, target);
232
272
  const { tokenAddress, isNativeToken } = await this.resolveAsset(assetId);
233
273
  if (!isNativeToken) {
234
274
  // Check if approval is needed before proceeding
@@ -250,7 +290,7 @@ export class VaultAPIImpl extends BaseAPI {
250
290
  address: vaultAddress,
251
291
  abi: CONTRACT_ABIS.vault,
252
292
  functionName: "depositNative",
253
- args: [walletAccount.address, clientId],
293
+ args: [walletAccount.address, applicationData],
254
294
  account: walletAccount,
255
295
  chain: this.chain,
256
296
  value: amount,
@@ -261,7 +301,7 @@ export class VaultAPIImpl extends BaseAPI {
261
301
  address: vaultAddress,
262
302
  abi: CONTRACT_ABIS.vault,
263
303
  functionName: "depositERC20",
264
- args: [walletAccount.address, clientId, tokenAddress, amount],
304
+ args: [walletAccount.address, applicationData, tokenAddress, amount],
265
305
  account: walletAccount,
266
306
  chain: this.chain,
267
307
  });
@@ -275,21 +315,80 @@ export class VaultAPIImpl extends BaseAPI {
275
315
  return await this.waitForTransaction(txResult, autoWait);
276
316
  }
277
317
  /**
278
- * Initiates a withdrawal through the API Gateway and submits the resulting
279
- * pre-signed calldata on-chain via the connected wallet.
318
+ * Polls `GET /api/v1/withdrawals/{index}` until the gateway can serve the
319
+ * `executeWithdrawal` calldata, then returns it with the vault address.
320
+ *
321
+ * The calldata only exists once the withdrawal's root is confirmed on-chain
322
+ * and its merkle proof is persisted. Until then the gateway returns 404 (the
323
+ * row is written asynchronously by the persistor after `initiate`) or 409
324
+ * (the proof is not confirmed yet); both are transient for a freshly-allocated
325
+ * index, so we retry on each until ready or the timeout elapses.
280
326
  *
281
- * The gateway allocates a `withdrawalIndex` via the matching engine and returns
282
- * ABI-encoded calldata for `executeSignedWithdrawal(...)` signed by the
283
- * server-side `WITHDRAWAL_SIGNER`. The user's wallet pays gas to submit it,
284
- * but the contract authenticates against the embedded signature not the
285
- * `msg.sender`. The connected wallet's address is sent as the on-chain
286
- * `destination`.
327
+ * Returns `null` when the timeout elapses before the proof is available — the
328
+ * index is still valid and the balance already debited, so callers surface
329
+ * this as `awaiting_proof` (with the index intact) rather than as a thrown
330
+ * error that would strand the index. Only non-transient errors (anything
331
+ * other than 404/409) throw.
332
+ *
333
+ * @throws {APIError} On non-transient errors while polling.
334
+ */
335
+ async pollWithdrawalCalldata(withdrawalIndex, retry) {
336
+ const pollIntervalMs = retry?.pollIntervalMs ?? DEFAULT_WITHDRAWAL_POLL_INTERVAL_MS;
337
+ const timeoutMs = retry?.timeoutMs ?? DEFAULT_WITHDRAWAL_TIMEOUT_MS;
338
+ const deadline = Date.now() + timeoutMs;
339
+ for (;;) {
340
+ try {
341
+ const { vault_address: vaultAddress, calldata } = await this.makePublicRequest(`/api/v1/withdrawals/${withdrawalIndex}`);
342
+ // Ready: non-empty calldata. (Defensively treat an empty/`0x` body as
343
+ // not-ready too, though the gateway returns an error in that case.)
344
+ if (calldata && calldata !== "0x") {
345
+ return { vaultAddress, calldata };
346
+ }
347
+ }
348
+ catch (error) {
349
+ // 404 (row not persisted yet) and 409 (proof not confirmed yet) are
350
+ // transient for a valid index — keep polling. Anything else is fatal.
351
+ const isTransient = error instanceof APIError && (error.statusCode === 404 || error.statusCode === 409);
352
+ if (!isTransient) {
353
+ throw error;
354
+ }
355
+ }
356
+ if (Date.now() + pollIntervalMs > deadline) {
357
+ // Timed out waiting for the proof. Signal not-ready; the caller keeps the
358
+ // index and retries later rather than losing it inside a thrown error.
359
+ return null;
360
+ }
361
+ await sleep(pollIntervalMs);
362
+ }
363
+ }
364
+ /**
365
+ * Initiates a withdrawal through the API Gateway, waits for its on-chain
366
+ * confirmation, then submits the resulting `executeWithdrawal` calldata via
367
+ * the connected wallet.
368
+ *
369
+ * The gateway allocates a `withdrawalIndex` via the matching engine and debits
370
+ * the balance immediately, but the executable calldata needs the withdrawal's
371
+ * merkle proof, which only exists once its root is confirmed on-chain. This
372
+ * method polls until the calldata is available (see `retry`), then submits it.
373
+ * The connected wallet pays gas; the contract authorises the withdrawal
374
+ * against the merkle proof embedded in the calldata. The wallet's address is
375
+ * sent as the on-chain `destination`.
376
+ *
377
+ * If the proof does not become available within the poll window, this method
378
+ * does NOT throw — it returns `{ withdrawalIndex, status: "awaiting_proof" }`
379
+ * with nothing submitted on-chain. The balance is already debited, so losing
380
+ * the index would strand the funds; the caller keeps it and finishes the
381
+ * withdrawal later with `retryWithdrawal(withdrawalIndex)`.
287
382
  *
288
383
  * @param assetId - The asset identifier (UUID) to withdraw
289
384
  * @param amount - The raw token amount to withdraw (as bigint)
290
385
  * @param autoWait - Whether to automatically wait for transaction confirmation (defaults to true)
291
- * @returns Promise resolving to `{ withdrawalIndex, transaction }`
292
- * @throws {APIError} When the asset is not found or the request is rejected
386
+ * @param sourceOrRetry - Source ledger (`"spot"` or `"margin"`) or legacy retry options
387
+ * @param retry - Polling cadence/timeout while waiting for the proof when a source is supplied
388
+ * @returns Promise resolving to a [`WithdrawResult`]: on success the submitted
389
+ * transaction plus `withdrawalIndex`; on poll-timeout `{ withdrawalIndex,
390
+ * status: "awaiting_proof" }`.
391
+ * @throws {APIError} When the asset is not found or the initiate request is rejected
293
392
  * @throws {ContractError} When the on-chain submission fails
294
393
  * @throws {InvalidConfigError} When wallet account is not available
295
394
  *
@@ -301,18 +400,23 @@ export class VaultAPIImpl extends BaseAPI {
301
400
  * parseUnits("50", 6),
302
401
  * );
303
402
  * console.log(`Withdrawal index: ${result.withdrawalIndex}`);
304
- * console.log(`Tx hash: ${result.hash}, status: ${result.status}`);
403
+ * if (result.status === "awaiting_proof") {
404
+ * // Proof not ready yet — balance already debited. Finish later:
405
+ * await vaultAPI.retryWithdrawal(result.withdrawalIndex);
406
+ * } else {
407
+ * console.log(`Tx hash: ${result.hash}, status: ${result.status}`);
408
+ * }
305
409
  *
306
- * // Or skip auto-waiting and finalise later
410
+ * // Custom polling (check every 10s, wait up to an hour)
307
411
  * const result = await vaultAPI.withdraw(
308
412
  * "123e4567-e89b-12d3-a456-426614174000",
309
413
  * parseUnits("50", 6),
310
- * false,
414
+ * true,
415
+ * { pollIntervalMs: 10_000, timeoutMs: 3_600_000 },
311
416
  * );
312
- * const receipt = await sdk.waitForTransaction(result.hash);
313
417
  * ```
314
418
  */
315
- async withdraw(assetId, amount, autoWait = true) {
419
+ async withdraw(assetId, amount, autoWait = true, sourceOrRetry, retry) {
316
420
  if (!this.walletClient) {
317
421
  throw new InvalidConfigError("Wallet client not set. Connect a wallet first.", "walletClient");
318
422
  }
@@ -321,21 +425,33 @@ export class VaultAPIImpl extends BaseAPI {
321
425
  throw new InvalidConfigError("No account available in wallet client", "account");
322
426
  }
323
427
  const destination = walletAccount.address.toLowerCase();
324
- validate(WithdrawSchema, { assetId, amount, destination, autoWait });
325
- // 1. Gateway allocates withdrawal_index + returns pre-signed
326
- // executeSignedWithdrawal calldata along with the vault address to send
327
- // it to. Sourcing the vault address from this response (instead of
328
- // GET /applications/config) keeps the calldata + target contract in
329
- // lockstep and avoids an extra round-trip.
330
- const { withdrawal_index: withdrawalIndex, vault_address: vaultAddress, calldata, } = await this.makeAuthenticatedRequest("/api/v1/withdrawals", {
428
+ const source = typeof sourceOrRetry === "string" ? sourceOrRetry : "spot";
429
+ const retryOptions = typeof sourceOrRetry === "string" ? retry : sourceOrRetry;
430
+ validate(WithdrawSchema, { assetId, amount, destination, autoWait, source });
431
+ // 1. Gateway allocates the withdrawal_index and debits the balance. No
432
+ // executable calldata yet — it needs the merkle proof, available only once
433
+ // the withdrawal root is confirmed on-chain.
434
+ const { withdrawal_index: withdrawalIndex } = await this.makeAuthenticatedRequest("/api/v1/withdrawals", {
331
435
  method: "POST",
332
436
  body: JSON.stringify({
333
437
  asset_id: assetId,
334
438
  amount: amount.toString(),
335
439
  destination,
440
+ source,
336
441
  }),
337
442
  });
338
- // 2. Submit the calldata on-chain through the connected wallet.
443
+ // 2. Poll for the executeWithdrawal calldata until the proof is confirmed.
444
+ // The vault address comes back with it, keeping calldata + target contract
445
+ // in lockstep.
446
+ const poll = await this.pollWithdrawalCalldata(withdrawalIndex, retryOptions);
447
+ if (!poll) {
448
+ // Proof not available within the poll window. The balance is already
449
+ // debited and the index is valid; return it (without submitting anything
450
+ // on-chain) so the caller can finish later via retryWithdrawal(index).
451
+ return { withdrawalIndex, status: "awaiting_proof" };
452
+ }
453
+ const { vaultAddress, calldata } = poll;
454
+ // 3. Submit the calldata on-chain through the connected wallet.
339
455
  let hash;
340
456
  try {
341
457
  hash = await this.walletClient.sendTransaction({
@@ -346,7 +462,7 @@ export class VaultAPIImpl extends BaseAPI {
346
462
  });
347
463
  }
348
464
  catch (error) {
349
- throw new ContractError(`Failed to submit executeSignedWithdrawal for withdrawal ${withdrawalIndex}: ${error instanceof Error ? error.message : "unknown error"}`, { cause: error instanceof Error ? error : undefined });
465
+ throw new ContractError(`Failed to submit executeWithdrawal for withdrawal ${withdrawalIndex}: ${error instanceof Error ? error.message : "unknown error"}`, { cause: error instanceof Error ? error : undefined });
350
466
  }
351
467
  const nonce = walletAccount.getNonce ? await walletAccount.getNonce() : 0n;
352
468
  const txResult = {
@@ -358,25 +474,32 @@ export class VaultAPIImpl extends BaseAPI {
358
474
  return { ...settled, withdrawalIndex };
359
475
  }
360
476
  /**
361
- * Retries a previously-initiated withdrawal whose on-chain submission never
362
- * landed — e.g. the wallet rejected the tx, the page reloaded before the
363
- * receipt came back, or a stuck mempool entry needs resending.
477
+ * Submits (or resubmits) a previously-initiated withdrawal on-chain.
478
+ *
479
+ * Use this when the original `withdraw()` could not complete — e.g. the proof
480
+ * was not yet confirmed within its polling window, the wallet rejected the tx,
481
+ * the page reloaded before the receipt came back, or a stuck mempool entry
482
+ * needs resending. Polls `GET /withdrawals/{index}` for the
483
+ * `executeWithdrawal` calldata (retrying while it is not yet confirmed), then
484
+ * submits it through the connected wallet. Does NOT initiate a new withdrawal
485
+ * — the matching engine already debited the balance and allocated the index.
486
+ * The contract is idempotent against double-submission of a settled
487
+ * withdrawal: it reverts once the index is consumed on-chain.
364
488
  *
365
- * Re-fetches the same `executeSignedWithdrawal` calldata the gateway
366
- * generated when the withdrawal was initiated, then submits it through the
367
- * connected wallet. Does NOT initiate a new withdrawal — the matching engine
368
- * already debited the balance and allocated the index. The contract is
369
- * idempotent against double-submission of a settled withdrawal: it will
370
- * revert once the index is consumed on-chain.
489
+ * Like `withdraw()`, if the proof is still unavailable within the poll window
490
+ * this returns `{ withdrawalIndex, status: "awaiting_proof" }` instead of
491
+ * throwing, so the caller can simply retry again later.
371
492
  *
372
493
  * @param withdrawalIndex - The index returned by the original `withdraw()` call
373
494
  * @param autoWait - Whether to await on-chain confirmation (defaults to true)
374
- * @returns Promise resolving to `{ withdrawalIndex, ...transaction }`
375
- * @throws {APIError} When the index doesn't exist
495
+ * @param retry - Polling cadence/timeout while waiting for the proof
496
+ * @returns Promise resolving to a [`WithdrawResult`]: the submitted transaction
497
+ * plus `withdrawalIndex`, or `{ withdrawalIndex, status: "awaiting_proof" }`
498
+ * if the proof did not arrive within the poll window.
376
499
  * @throws {ContractError} When the on-chain submission fails
377
500
  * @throws {InvalidConfigError} When wallet account is not available
378
501
  */
379
- async retryWithdrawal(withdrawalIndex, autoWait = true) {
502
+ async retryWithdrawal(withdrawalIndex, autoWait = true, retry) {
380
503
  if (!this.walletClient) {
381
504
  throw new InvalidConfigError("Wallet client not set. Connect a wallet first.", "walletClient");
382
505
  }
@@ -384,7 +507,13 @@ export class VaultAPIImpl extends BaseAPI {
384
507
  if (!walletAccount) {
385
508
  throw new InvalidConfigError("No account available in wallet client", "account");
386
509
  }
387
- const { vault_address: vaultAddress, calldata } = await this.makePublicRequest(`/api/v1/withdrawals/${withdrawalIndex}`);
510
+ const poll = await this.pollWithdrawalCalldata(withdrawalIndex, retry);
511
+ if (!poll) {
512
+ // Still not confirmed on-chain within the poll window — return the index
513
+ // so the caller can retry again later, rather than throwing.
514
+ return { withdrawalIndex, status: "awaiting_proof" };
515
+ }
516
+ const { vaultAddress, calldata } = poll;
388
517
  let hash;
389
518
  try {
390
519
  hash = await this.walletClient.sendTransaction({
@@ -395,7 +524,7 @@ export class VaultAPIImpl extends BaseAPI {
395
524
  });
396
525
  }
397
526
  catch (error) {
398
- throw new ContractError(`Failed to resubmit executeSignedWithdrawal for withdrawal ${withdrawalIndex}: ${error instanceof Error ? error.message : "unknown error"}`, { cause: error instanceof Error ? error : undefined });
527
+ throw new ContractError(`Failed to resubmit executeWithdrawal for withdrawal ${withdrawalIndex}: ${error instanceof Error ? error.message : "unknown error"}`, { cause: error instanceof Error ? error : undefined });
399
528
  }
400
529
  const nonce = walletAccount.getNonce ? await walletAccount.getNonce() : 0n;
401
530
  const txResult = {
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * Vault API Module
3
3
  */
4
- export { VaultAPIImpl } from "./api";
4
+ export { encodeDepositApplicationData, VaultAPIImpl } from "./api";
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * Vault API Module
3
3
  */
4
- export { VaultAPIImpl } from "./api";
4
+ export { encodeDepositApplicationData, VaultAPIImpl } from "./api";
@@ -4,10 +4,12 @@ import { BaseAPI } from "../base";
4
4
  * Low-level withdrawals client.
5
5
  *
6
6
  * `initiateWithdrawal` debits the caller's balance via the matching engine and
7
- * returns the target vault address plus pre-signed `executeSignedWithdrawal`
8
- * calldata; `getWithdrawal` re-fetches that calldata for a previously-initiated
9
- * index. Neither submits on-chain use the high-level vault API for the flow
10
- * that also broadcasts the transaction.
7
+ * allocates a `withdrawal_index` (its `calldata` is empty — the proof is not
8
+ * available until the withdrawal root is confirmed on-chain). `getWithdrawal`
9
+ * returns the `executeWithdrawal` calldata once that proof is ready, and throws
10
+ * an `APIError` (404 not-persisted-yet / 409 not-confirmed-yet) while it is not.
11
+ * Neither submits on-chain — use the high-level vault API, which polls for the
12
+ * calldata and broadcasts the transaction.
11
13
  */
12
14
  export declare class WithdrawalsAPIImpl extends BaseAPI implements WithdrawalsAPI {
13
15
  initiateWithdrawal(request: InitiateWithdrawalRequest): Promise<WithdrawalResponse>;
@@ -4,10 +4,12 @@ import { perpRoutes } from "../perp";
4
4
  * Low-level withdrawals client.
5
5
  *
6
6
  * `initiateWithdrawal` debits the caller's balance via the matching engine and
7
- * returns the target vault address plus pre-signed `executeSignedWithdrawal`
8
- * calldata; `getWithdrawal` re-fetches that calldata for a previously-initiated
9
- * index. Neither submits on-chain use the high-level vault API for the flow
10
- * that also broadcasts the transaction.
7
+ * allocates a `withdrawal_index` (its `calldata` is empty — the proof is not
8
+ * available until the withdrawal root is confirmed on-chain). `getWithdrawal`
9
+ * returns the `executeWithdrawal` calldata once that proof is ready, and throws
10
+ * an `APIError` (404 not-persisted-yet / 409 not-confirmed-yet) while it is not.
11
+ * Neither submits on-chain — use the high-level vault API, which polls for the
12
+ * calldata and broadcasts the transaction.
11
13
  */
12
14
  export class WithdrawalsAPIImpl extends BaseAPI {
13
15
  async initiateWithdrawal(request) {
@@ -17,6 +19,7 @@ export class WithdrawalsAPIImpl extends BaseAPI {
17
19
  asset_id: request.assetId,
18
20
  amount: request.amount,
19
21
  destination: request.destination,
22
+ source: request.source ?? "spot",
20
23
  }),
21
24
  });
22
25
  }
@@ -5,6 +5,7 @@ export declare const COVERED: {
5
5
  batch_cancel_all: string;
6
6
  batch_cancel_all_by_pair: string;
7
7
  batch_cancel_orders: string;
8
+ batch_close_all_positions: string;
8
9
  batch_create_orders: string;
9
10
  batch_replace_orders: string;
10
11
  cancel_conditional_order: string;
@@ -25,6 +26,7 @@ export declare const COVERED: {
25
26
  get_margin_account_summary: string;
26
27
  get_mark_price: string;
27
28
  get_market_metadata: string;
29
+ get_market_stats: string;
28
30
  get_open_interest: string;
29
31
  get_order_by_id: string;
30
32
  get_orderbook_snapshot: string;
@@ -35,6 +37,8 @@ export declare const COVERED: {
35
37
  get_portfolio_stats: string;
36
38
  get_position: string;
37
39
  get_position_risk: string;
40
+ get_parent_margin_account_movements: string;
41
+ get_parent_margin_account_summary: string;
38
42
  get_screener: string;
39
43
  get_sub_account_limits: string;
40
44
  get_trade_by_id: string;
@@ -67,13 +71,16 @@ export declare const COVERED: {
67
71
  replace_order: string;
68
72
  revoke_delegated_agent: string;
69
73
  revoke_session: string;
70
- simulate_auto_margin_order_risk: string;
74
+ simulate_risk_bucket_order_risk: string;
71
75
  simulate_fees: string;
72
76
  simulate_order_risk: string;
77
+ simulate_parent_margin_order_risk: string;
73
78
  submit_whitelist: string;
79
+ transfer_collateral_from_parent_margin_account: string;
74
80
  transfer_collateral_from_margin_account: string;
75
- transfer_collateral_to_auto_margin_account: string;
76
81
  transfer_collateral_to_margin_account: string;
82
+ transfer_collateral_to_parent_margin_account: string;
83
+ transfer_collateral_to_risk_bucket: string;
77
84
  update_sub_account_limit: string;
78
85
  upsert_delegated_agent: string;
79
86
  verify_signature: string;
package/dist/coverage.js CHANGED
@@ -5,6 +5,7 @@ export const COVERED = {
5
5
  batch_cancel_all: "batchCancelAll",
6
6
  batch_cancel_all_by_pair: "batchCancelAll (with tradingPairId)",
7
7
  batch_cancel_orders: "batchCancel",
8
+ batch_close_all_positions: "batchCloseAllPositions",
8
9
  batch_create_orders: "batchCreate",
9
10
  batch_replace_orders: "batchReplace",
10
11
  cancel_conditional_order: "cancelConditionalOrder",
@@ -25,6 +26,7 @@ export const COVERED = {
25
26
  get_margin_account_summary: "getMarginAccountSummary",
26
27
  get_mark_price: "getMarkPrice",
27
28
  get_market_metadata: "getMarketMetadata",
29
+ get_market_stats: "getMarketStats",
28
30
  get_open_interest: "getOpenInterest",
29
31
  get_order_by_id: "getOrder",
30
32
  get_orderbook_snapshot: "getOrderbook",
@@ -35,6 +37,8 @@ export const COVERED = {
35
37
  get_portfolio_stats: "getPortfolioStats",
36
38
  get_position: "getPosition",
37
39
  get_position_risk: "getPositionRisk",
40
+ get_parent_margin_account_movements: "getParentMarginAccountMovements",
41
+ get_parent_margin_account_summary: "getParentMarginAccountSummary",
38
42
  get_screener: "getScreener",
39
43
  get_sub_account_limits: "subAccounts.getLimits",
40
44
  get_trade_by_id: "getTradeById",
@@ -67,13 +71,16 @@ export const COVERED = {
67
71
  replace_order: "replaceOrder",
68
72
  revoke_delegated_agent: "revokeDelegatedAgent",
69
73
  revoke_session: "revokeSession",
70
- simulate_auto_margin_order_risk: "simulateAutoMarginOrderRisk",
74
+ simulate_risk_bucket_order_risk: "simulateRiskBucketOrderRisk",
71
75
  simulate_fees: "simulateFees",
72
76
  simulate_order_risk: "simulateOrderRisk",
77
+ simulate_parent_margin_order_risk: "simulateParentMarginOrderRisk",
73
78
  submit_whitelist: "whitelist.submit",
79
+ transfer_collateral_from_parent_margin_account: "transferCollateralFromParentMarginAccount",
74
80
  transfer_collateral_from_margin_account: "transferCollateralFromMarginAccount",
75
- transfer_collateral_to_auto_margin_account: "transferCollateralToAutoMarginAccount",
76
81
  transfer_collateral_to_margin_account: "transferCollateralToMarginAccount",
82
+ transfer_collateral_to_parent_margin_account: "transferCollateralToParentMarginAccount",
83
+ transfer_collateral_to_risk_bucket: "transferCollateralToRiskBucket",
77
84
  update_sub_account_limit: "subAccounts.updateLimit",
78
85
  upsert_delegated_agent: "upsertDelegatedAgent",
79
86
  verify_signature: "verifySignature",
package/dist/sdk.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ApplicationsAPI, AuthAPI, AuthState, DelegatedAgentsAPI, FaucetAPI, FeesAPI, MarginAccountsAPI, MarketAPI, MonacoSDK, Network, PositionsAPI, ProfileAPI, SDKConfig, SubAccountsAPI, TradingAPI, VaultAPI, WhitelistAPI, WithdrawalsAPI } from "@0xmonaco/types";
2
- import { type PublicClient, type TransactionReceipt, type WalletClient } from "viem";
2
+ import { type Address, type PublicClient, type TransactionReceipt, type WalletClient } from "viem";
3
3
  import { type MonacoWebSocket, OrderbookAPIImpl, TradesAPIImpl } from "./api";
4
4
  export declare class MonacoSDKImpl implements MonacoSDK {
5
5
  auth: AuthAPI;
@@ -147,7 +147,7 @@ export declare class MonacoSDKImpl implements MonacoSDK {
147
147
  /**
148
148
  * Get the current account address
149
149
  */
150
- getAccountAddress(): string;
150
+ getAccountAddress(): Address;
151
151
  /**
152
152
  * Get the current network
153
153
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xmonaco/core",
3
- "version": "0.8.10",
3
+ "version": "0.8.14",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,8 +23,8 @@
23
23
  "viem": "^2.45.2"
24
24
  },
25
25
  "dependencies": {
26
- "@0xmonaco/contracts": "0.8.10",
27
- "@0xmonaco/types": "0.8.10",
26
+ "@0xmonaco/contracts": "0.8.14",
27
+ "@0xmonaco/types": "0.8.14",
28
28
  "@noble/curves": "^1.9.1",
29
29
  "@noble/hashes": "^1.8.0",
30
30
  "http-status-codes": "^2.3.0"