@0xmonaco/core 0.8.7-develop.5d0e403 → 0.8.7-develop.a107b34

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.
@@ -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,41 @@ 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>;
174
+ /**
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.
183
+ *
184
+ * @throws {APIError} On non-transient errors, or when the timeout elapses
185
+ * before the proof is available.
186
+ */
187
+ private pollWithdrawalCalldata;
150
188
  /**
151
- * Initiates a withdrawal through the API Gateway and submits the resulting
152
- * pre-signed calldata on-chain via the connected wallet.
189
+ * Initiates a withdrawal through the API Gateway, waits for its on-chain
190
+ * confirmation, then submits the resulting `executeWithdrawal` calldata via
191
+ * the connected wallet.
153
192
  *
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`.
193
+ * The gateway allocates a `withdrawalIndex` via the matching engine and debits
194
+ * the balance immediately, but the executable calldata needs the withdrawal's
195
+ * merkle proof, which only exists once its root is confirmed on-chain. This
196
+ * method polls until the calldata is available (see `retry`), then submits it.
197
+ * The connected wallet pays gas; the contract authorises the withdrawal
198
+ * against the merkle proof embedded in the calldata. The wallet's address is
199
+ * sent as the on-chain `destination`.
160
200
  *
161
201
  * @param assetId - The asset identifier (UUID) to withdraw
162
202
  * @param amount - The raw token amount to withdraw (as bigint)
163
203
  * @param autoWait - Whether to automatically wait for transaction confirmation (defaults to true)
204
+ * @param sourceOrRetry - Source ledger (`"spot"` or `"margin"`) or legacy retry options
205
+ * @param retry - Polling cadence/timeout while waiting for the proof when a source is supplied
164
206
  * @returns Promise resolving to `{ withdrawalIndex, transaction }`
165
- * @throws {APIError} When the asset is not found or the request is rejected
207
+ * @throws {APIError} When the asset is not found, the request is rejected, or the proof never becomes available within the timeout
166
208
  * @throws {ContractError} When the on-chain submission fails
167
209
  * @throws {InvalidConfigError} When wallet account is not available
168
210
  *
@@ -176,36 +218,38 @@ export declare class VaultAPIImpl extends BaseAPI implements VaultAPI {
176
218
  * console.log(`Withdrawal index: ${result.withdrawalIndex}`);
177
219
  * console.log(`Tx hash: ${result.hash}, status: ${result.status}`);
178
220
  *
179
- * // Or skip auto-waiting and finalise later
221
+ * // Custom polling (check every 10s, wait up to an hour)
180
222
  * const result = await vaultAPI.withdraw(
181
223
  * "123e4567-e89b-12d3-a456-426614174000",
182
224
  * parseUnits("50", 6),
183
- * false,
225
+ * true,
226
+ * { pollIntervalMs: 10_000, timeoutMs: 3_600_000 },
184
227
  * );
185
- * const receipt = await sdk.waitForTransaction(result.hash);
186
228
  * ```
187
229
  */
188
- withdraw(assetId: string, amount: bigint, autoWait?: boolean): Promise<WithdrawResult>;
230
+ withdraw(assetId: string, amount: bigint, autoWait?: boolean, sourceOrRetry?: WithdrawalSource | WithdrawalRetryOptions, retry?: WithdrawalRetryOptions): Promise<WithdrawResult>;
189
231
  /**
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.
232
+ * Submits (or resubmits) a previously-initiated withdrawal on-chain.
193
233
  *
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.
234
+ * Use this when the original `withdraw()` could not complete — e.g. the proof
235
+ * was not yet confirmed within its polling window, the wallet rejected the tx,
236
+ * the page reloaded before the receipt came back, or a stuck mempool entry
237
+ * needs resending. Polls `GET /withdrawals/{index}` for the
238
+ * `executeWithdrawal` calldata (retrying while it is not yet confirmed), then
239
+ * submits it through the connected wallet. Does NOT initiate a new withdrawal
240
+ * — the matching engine already debited the balance and allocated the index.
241
+ * The contract is idempotent against double-submission of a settled
242
+ * withdrawal: it reverts once the index is consumed on-chain.
200
243
  *
201
244
  * @param withdrawalIndex - The index returned by the original `withdraw()` call
202
245
  * @param autoWait - Whether to await on-chain confirmation (defaults to true)
246
+ * @param retry - Polling cadence/timeout while waiting for the proof
203
247
  * @returns Promise resolving to `{ withdrawalIndex, ...transaction }`
204
- * @throws {APIError} When the index doesn't exist
248
+ * @throws {APIError} When the index doesn't exist or the proof never becomes available within the timeout
205
249
  * @throws {ContractError} When the on-chain submission fails
206
250
  * @throws {InvalidConfigError} When wallet account is not available
207
251
  */
208
- retryWithdrawal(withdrawalIndex: number, autoWait?: boolean): Promise<WithdrawResult>;
252
+ retryWithdrawal(withdrawalIndex: number, autoWait?: boolean, retry?: WithdrawalRetryOptions): Promise<WithdrawResult>;
209
253
  /**
210
254
  * Retrieves the user's token balance in the vault.
211
255
  *
@@ -28,6 +28,25 @@ 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
+ /** Default total wait for a withdrawal's proof (1 minute). */
34
+ const DEFAULT_WITHDRAWAL_TIMEOUT_MS = 60_000;
35
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
36
+ /**
37
+ * Encode the on-chain `applicationData` for a deposit.
38
+ *
39
+ * For `"spot"` (the default) this stays the bare `clientId` string — byte-for-byte
40
+ * identical to the legacy encoding, so existing deposits are unchanged. For
41
+ * `"margin"` it emits a small JSON routing hint the indexer decodes to credit the
42
+ * deposit to margin collateral.
43
+ *
44
+ * MUST stay in sync with the indexer decoder in
45
+ * `indexer/src/listeners/deposit_routing.rs`.
46
+ */
47
+ export function encodeDepositApplicationData(clientId, target) {
48
+ return target === "margin" ? JSON.stringify({ clientId, depositTarget: "MARGIN" }) : clientId;
49
+ }
31
50
  export class VaultAPIImpl extends BaseAPI {
32
51
  publicClient;
33
52
  chain;
@@ -196,6 +215,10 @@ export class VaultAPIImpl extends BaseAPI {
196
215
  * @param assetId - The asset identifier (UUID) to deposit
197
216
  * @param amount - The amount of tokens to deposit (as bigint)
198
217
  * @param autoWait - Whether to automatically wait for transaction confirmation (defaults to true)
218
+ * @param target - Destination ledger: `"spot"` (default) credits the spot
219
+ * wallet; `"margin"` routes the deposit into the parent margin account's
220
+ * collateral (auto-creating the account if needed). Margin deposits that
221
+ * cannot be routed safely fall back to spot.
199
222
  * @returns Promise resolving to TransactionResult with transaction details
200
223
  * @throws {ContractError} When deposit fails or approval is insufficient
201
224
  * @throws {APIError} When the asset is not found or the assetId is invalid
@@ -211,6 +234,14 @@ export class VaultAPIImpl extends BaseAPI {
211
234
  * console.log(`Deposit transaction: ${result.hash}`);
212
235
  * console.log(`Status: ${result.status}`); // "confirmed" if successful
213
236
  *
237
+ * // Deposit straight into margin collateral
238
+ * const marginDeposit = await vaultAPI.deposit(
239
+ * "123e4567-e89b-12d3-a456-426614174000",
240
+ * parseUnits("100", 6),
241
+ * true,
242
+ * "margin"
243
+ * );
244
+ *
214
245
  * // Or skip auto-waiting
215
246
  * const result = await vaultAPI.deposit(
216
247
  * "123e4567-e89b-12d3-a456-426614174000",
@@ -221,14 +252,17 @@ export class VaultAPIImpl extends BaseAPI {
221
252
  * const receipt = await sdk.waitForTransaction(result.hash);
222
253
  * ```
223
254
  */
224
- async deposit(assetId, amount, autoWait = true) {
255
+ async deposit(assetId, amount, autoWait = true, target = "spot") {
225
256
  if (!this.walletClient) {
226
257
  throw new InvalidConfigError("Wallet client not set. Connect a wallet first.", "walletClient");
227
258
  }
228
259
  // Validate inputs
229
- validate(DepositSchema, { assetId, amount, autoWait });
260
+ validate(DepositSchema, { assetId, amount, autoWait, target });
230
261
  const vaultAddress = await this.getVaultAddress();
231
262
  const clientId = await this.getClientId();
263
+ // The on-chain applicationData carries the client id plus, for margin
264
+ // deposits, the routing hint the indexer decodes.
265
+ const applicationData = encodeDepositApplicationData(clientId, target);
232
266
  const { tokenAddress, isNativeToken } = await this.resolveAsset(assetId);
233
267
  if (!isNativeToken) {
234
268
  // Check if approval is needed before proceeding
@@ -250,7 +284,7 @@ export class VaultAPIImpl extends BaseAPI {
250
284
  address: vaultAddress,
251
285
  abi: CONTRACT_ABIS.vault,
252
286
  functionName: "depositNative",
253
- args: [walletAccount.address, clientId],
287
+ args: [walletAccount.address, applicationData],
254
288
  account: walletAccount,
255
289
  chain: this.chain,
256
290
  value: amount,
@@ -261,7 +295,7 @@ export class VaultAPIImpl extends BaseAPI {
261
295
  address: vaultAddress,
262
296
  abi: CONTRACT_ABIS.vault,
263
297
  functionName: "depositERC20",
264
- args: [walletAccount.address, clientId, tokenAddress, amount],
298
+ args: [walletAccount.address, applicationData, tokenAddress, amount],
265
299
  account: walletAccount,
266
300
  chain: this.chain,
267
301
  });
@@ -275,21 +309,65 @@ export class VaultAPIImpl extends BaseAPI {
275
309
  return await this.waitForTransaction(txResult, autoWait);
276
310
  }
277
311
  /**
278
- * Initiates a withdrawal through the API Gateway and submits the resulting
279
- * pre-signed calldata on-chain via the connected wallet.
312
+ * Polls `GET /api/v1/withdrawals/{index}` until the gateway can serve the
313
+ * `executeWithdrawal` calldata, then returns it with the vault address.
280
314
  *
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`.
315
+ * The calldata only exists once the withdrawal's root is confirmed on-chain
316
+ * and its merkle proof is persisted. Until then the gateway returns 404 (the
317
+ * row is written asynchronously by the persistor after `initiate`) or 409
318
+ * (the proof is not confirmed yet); both are transient for a freshly-allocated
319
+ * index, so we retry on each until ready or the timeout elapses.
320
+ *
321
+ * @throws {APIError} On non-transient errors, or when the timeout elapses
322
+ * before the proof is available.
323
+ */
324
+ async pollWithdrawalCalldata(withdrawalIndex, retry) {
325
+ const pollIntervalMs = retry?.pollIntervalMs ?? DEFAULT_WITHDRAWAL_POLL_INTERVAL_MS;
326
+ const timeoutMs = retry?.timeoutMs ?? DEFAULT_WITHDRAWAL_TIMEOUT_MS;
327
+ const deadline = Date.now() + timeoutMs;
328
+ for (;;) {
329
+ try {
330
+ const { vault_address: vaultAddress, calldata } = await this.makePublicRequest(`/api/v1/withdrawals/${withdrawalIndex}`);
331
+ // Ready: non-empty calldata. (Defensively treat an empty/`0x` body as
332
+ // not-ready too, though the gateway returns an error in that case.)
333
+ if (calldata && calldata !== "0x") {
334
+ return { vaultAddress, calldata };
335
+ }
336
+ }
337
+ catch (error) {
338
+ // 404 (row not persisted yet) and 409 (proof not confirmed yet) are
339
+ // transient for a valid index — keep polling. Anything else is fatal.
340
+ const isTransient = error instanceof APIError && (error.statusCode === 404 || error.statusCode === 409);
341
+ if (!isTransient) {
342
+ throw error;
343
+ }
344
+ }
345
+ if (Date.now() + pollIntervalMs > deadline) {
346
+ throw new APIError(`Withdrawal ${withdrawalIndex} proof not available after ${timeoutMs}ms; it may not be confirmed on-chain yet — retry later via retryWithdrawal(${withdrawalIndex})`, { statusCode: 409 });
347
+ }
348
+ await sleep(pollIntervalMs);
349
+ }
350
+ }
351
+ /**
352
+ * Initiates a withdrawal through the API Gateway, waits for its on-chain
353
+ * confirmation, then submits the resulting `executeWithdrawal` calldata via
354
+ * the connected wallet.
355
+ *
356
+ * The gateway allocates a `withdrawalIndex` via the matching engine and debits
357
+ * the balance immediately, but the executable calldata needs the withdrawal's
358
+ * merkle proof, which only exists once its root is confirmed on-chain. This
359
+ * method polls until the calldata is available (see `retry`), then submits it.
360
+ * The connected wallet pays gas; the contract authorises the withdrawal
361
+ * against the merkle proof embedded in the calldata. The wallet's address is
362
+ * sent as the on-chain `destination`.
287
363
  *
288
364
  * @param assetId - The asset identifier (UUID) to withdraw
289
365
  * @param amount - The raw token amount to withdraw (as bigint)
290
366
  * @param autoWait - Whether to automatically wait for transaction confirmation (defaults to true)
367
+ * @param sourceOrRetry - Source ledger (`"spot"` or `"margin"`) or legacy retry options
368
+ * @param retry - Polling cadence/timeout while waiting for the proof when a source is supplied
291
369
  * @returns Promise resolving to `{ withdrawalIndex, transaction }`
292
- * @throws {APIError} When the asset is not found or the request is rejected
370
+ * @throws {APIError} When the asset is not found, the request is rejected, or the proof never becomes available within the timeout
293
371
  * @throws {ContractError} When the on-chain submission fails
294
372
  * @throws {InvalidConfigError} When wallet account is not available
295
373
  *
@@ -303,16 +381,16 @@ export class VaultAPIImpl extends BaseAPI {
303
381
  * console.log(`Withdrawal index: ${result.withdrawalIndex}`);
304
382
  * console.log(`Tx hash: ${result.hash}, status: ${result.status}`);
305
383
  *
306
- * // Or skip auto-waiting and finalise later
384
+ * // Custom polling (check every 10s, wait up to an hour)
307
385
  * const result = await vaultAPI.withdraw(
308
386
  * "123e4567-e89b-12d3-a456-426614174000",
309
387
  * parseUnits("50", 6),
310
- * false,
388
+ * true,
389
+ * { pollIntervalMs: 10_000, timeoutMs: 3_600_000 },
311
390
  * );
312
- * const receipt = await sdk.waitForTransaction(result.hash);
313
391
  * ```
314
392
  */
315
- async withdraw(assetId, amount, autoWait = true) {
393
+ async withdraw(assetId, amount, autoWait = true, sourceOrRetry, retry) {
316
394
  if (!this.walletClient) {
317
395
  throw new InvalidConfigError("Wallet client not set. Connect a wallet first.", "walletClient");
318
396
  }
@@ -321,21 +399,26 @@ export class VaultAPIImpl extends BaseAPI {
321
399
  throw new InvalidConfigError("No account available in wallet client", "account");
322
400
  }
323
401
  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", {
402
+ const source = typeof sourceOrRetry === "string" ? sourceOrRetry : "spot";
403
+ const retryOptions = typeof sourceOrRetry === "string" ? retry : sourceOrRetry;
404
+ validate(WithdrawSchema, { assetId, amount, destination, autoWait, source });
405
+ // 1. Gateway allocates the withdrawal_index and debits the balance. No
406
+ // executable calldata yet — it needs the merkle proof, available only once
407
+ // the withdrawal root is confirmed on-chain.
408
+ const { withdrawal_index: withdrawalIndex } = await this.makeAuthenticatedRequest("/api/v1/withdrawals", {
331
409
  method: "POST",
332
410
  body: JSON.stringify({
333
411
  asset_id: assetId,
334
412
  amount: amount.toString(),
335
413
  destination,
414
+ source,
336
415
  }),
337
416
  });
338
- // 2. Submit the calldata on-chain through the connected wallet.
417
+ // 2. Poll for the executeWithdrawal calldata until the proof is confirmed.
418
+ // The vault address comes back with it, keeping calldata + target contract
419
+ // in lockstep.
420
+ const { vaultAddress, calldata } = await this.pollWithdrawalCalldata(withdrawalIndex, retryOptions);
421
+ // 3. Submit the calldata on-chain through the connected wallet.
339
422
  let hash;
340
423
  try {
341
424
  hash = await this.walletClient.sendTransaction({
@@ -346,7 +429,7 @@ export class VaultAPIImpl extends BaseAPI {
346
429
  });
347
430
  }
348
431
  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 });
432
+ throw new ContractError(`Failed to submit executeWithdrawal for withdrawal ${withdrawalIndex}: ${error instanceof Error ? error.message : "unknown error"}`, { cause: error instanceof Error ? error : undefined });
350
433
  }
351
434
  const nonce = walletAccount.getNonce ? await walletAccount.getNonce() : 0n;
352
435
  const txResult = {
@@ -358,25 +441,27 @@ export class VaultAPIImpl extends BaseAPI {
358
441
  return { ...settled, withdrawalIndex };
359
442
  }
360
443
  /**
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.
444
+ * Submits (or resubmits) a previously-initiated withdrawal on-chain.
364
445
  *
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.
446
+ * Use this when the original `withdraw()` could not complete — e.g. the proof
447
+ * was not yet confirmed within its polling window, the wallet rejected the tx,
448
+ * the page reloaded before the receipt came back, or a stuck mempool entry
449
+ * needs resending. Polls `GET /withdrawals/{index}` for the
450
+ * `executeWithdrawal` calldata (retrying while it is not yet confirmed), then
451
+ * submits it through the connected wallet. Does NOT initiate a new withdrawal
452
+ * — the matching engine already debited the balance and allocated the index.
453
+ * The contract is idempotent against double-submission of a settled
454
+ * withdrawal: it reverts once the index is consumed on-chain.
371
455
  *
372
456
  * @param withdrawalIndex - The index returned by the original `withdraw()` call
373
457
  * @param autoWait - Whether to await on-chain confirmation (defaults to true)
458
+ * @param retry - Polling cadence/timeout while waiting for the proof
374
459
  * @returns Promise resolving to `{ withdrawalIndex, ...transaction }`
375
- * @throws {APIError} When the index doesn't exist
460
+ * @throws {APIError} When the index doesn't exist or the proof never becomes available within the timeout
376
461
  * @throws {ContractError} When the on-chain submission fails
377
462
  * @throws {InvalidConfigError} When wallet account is not available
378
463
  */
379
- async retryWithdrawal(withdrawalIndex, autoWait = true) {
464
+ async retryWithdrawal(withdrawalIndex, autoWait = true, retry) {
380
465
  if (!this.walletClient) {
381
466
  throw new InvalidConfigError("Wallet client not set. Connect a wallet first.", "walletClient");
382
467
  }
@@ -384,7 +469,7 @@ export class VaultAPIImpl extends BaseAPI {
384
469
  if (!walletAccount) {
385
470
  throw new InvalidConfigError("No account available in wallet client", "account");
386
471
  }
387
- const { vault_address: vaultAddress, calldata } = await this.makePublicRequest(`/api/v1/withdrawals/${withdrawalIndex}`);
472
+ const { vaultAddress, calldata } = await this.pollWithdrawalCalldata(withdrawalIndex, retry);
388
473
  let hash;
389
474
  try {
390
475
  hash = await this.walletClient.sendTransaction({
@@ -395,7 +480,7 @@ export class VaultAPIImpl extends BaseAPI {
395
480
  });
396
481
  }
397
482
  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 });
483
+ throw new ContractError(`Failed to resubmit executeWithdrawal for withdrawal ${withdrawalIndex}: ${error instanceof Error ? error.message : "unknown error"}`, { cause: error instanceof Error ? error : undefined });
399
484
  }
400
485
  const nonce = walletAccount.getNonce ? await walletAccount.getNonce() : 0n;
401
486
  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
  }
@@ -1,7 +1,92 @@
1
+ /** operationId → the @0xmonaco/core client method that covers it. */
2
+ export declare const COVERED: {
3
+ add_position_margin: string;
4
+ attach_position_tp_sl: string;
5
+ batch_cancel_all: string;
6
+ batch_cancel_all_by_pair: string;
7
+ batch_cancel_orders: string;
8
+ batch_close_all_positions: string;
9
+ batch_create_orders: string;
10
+ batch_replace_orders: string;
11
+ cancel_conditional_order: string;
12
+ cancel_order: string;
13
+ close_position: string;
14
+ create_challenge: string;
15
+ create_delegated_session: string;
16
+ create_order: string;
17
+ create_sub_account_limit: string;
18
+ delete_sub_account_limit: string;
19
+ get_application_config: string;
20
+ get_application_stats: string;
21
+ get_available_collateral: string;
22
+ get_candles: string;
23
+ get_funding_state: string;
24
+ get_index_price: string;
25
+ get_margin_account_movements: string;
26
+ get_margin_account_summary: string;
27
+ get_mark_price: string;
28
+ get_market_metadata: string;
29
+ get_market_stats: string;
30
+ get_open_interest: string;
31
+ get_order_by_id: string;
32
+ get_orderbook_snapshot: string;
33
+ get_orders: string;
34
+ get_perp_market_config: string;
35
+ get_perp_market_summary: string;
36
+ get_portfolio_chart: string;
37
+ get_portfolio_stats: string;
38
+ get_position: string;
39
+ get_position_risk: string;
40
+ get_parent_margin_account_movements: string;
41
+ get_parent_margin_account_summary: string;
42
+ get_screener: string;
43
+ get_sub_account_limits: string;
44
+ get_trade_by_id: string;
45
+ get_trades: string;
46
+ get_trading_pair_by_id: string;
47
+ get_user_balance_by_asset: string;
48
+ get_user_balances: string;
49
+ get_user_movements: string;
50
+ get_user_profile: string;
51
+ get_user_trades: string;
52
+ get_withdrawal: string;
53
+ initiate_withdrawal: string;
54
+ list_application_balances: string;
55
+ list_application_movements: string;
56
+ list_application_orders: string;
57
+ list_application_users: string;
58
+ list_conditional_orders: string;
59
+ list_delegated_agent_owners: string;
60
+ list_delegated_agents: string;
61
+ list_funding_history: string;
62
+ list_funding_payments: string;
63
+ list_margin_accounts: string;
64
+ list_position_history: string;
65
+ list_positions: string;
66
+ list_sub_accounts_with_balances: string;
67
+ list_trading_pairs: string;
68
+ mint_tokens: string;
69
+ reduce_position_margin: string;
70
+ refresh_session: string;
71
+ replace_order: string;
72
+ revoke_delegated_agent: string;
73
+ revoke_session: string;
74
+ simulate_risk_bucket_order_risk: string;
75
+ simulate_fees: string;
76
+ simulate_order_risk: string;
77
+ simulate_parent_margin_order_risk: string;
78
+ submit_whitelist: string;
79
+ transfer_collateral_from_parent_margin_account: string;
80
+ transfer_collateral_from_margin_account: string;
81
+ transfer_collateral_to_margin_account: string;
82
+ transfer_collateral_to_parent_margin_account: string;
83
+ transfer_collateral_to_risk_bucket: string;
84
+ update_sub_account_limit: string;
85
+ upsert_delegated_agent: string;
86
+ verify_signature: string;
87
+ };
1
88
  /** operationId → reason it is intentionally not covered by @0xmonaco/core. */
2
89
  export declare const INTENTIONALLY_EXCLUDED: {
3
90
  authenticate_backend: string;
4
91
  health_check: string;
5
- list_funding_payments: string;
6
- list_user_trades: string;
7
92
  };