@upshiftfinance/sdk 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,13 +33,18 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.Sui = void 0;
36
+ exports.SuiAdapter = exports.Sui = void 0;
37
37
  const SuiActions = __importStar(require("./actions"));
38
38
  const SuiClient = __importStar(require("./client"));
39
39
  const SuiConstants = __importStar(require("./constants"));
40
40
  const SuiGetters = __importStar(require("./getters"));
41
41
  const SuiTargets = __importStar(require("./targets"));
42
42
  const SuiUtils = __importStar(require("./utils"));
43
+ /**
44
+ * Sui helpers by area: chain identity (`utils`), constants, the gRPC client
45
+ * with failover (`client`), move-call targets (`targets`), vault reads
46
+ * (`getters`) and deposit / instant-redeem builders (`actions`).
47
+ */
43
48
  exports.Sui = {
44
49
  utils: SuiUtils,
45
50
  constants: SuiConstants,
@@ -108,7 +113,8 @@ class SuiAdapter {
108
113
  * RPC cost: one dry run (plus the coin lookup it needs).
109
114
  *
110
115
  * @param params - The vault, `amount` in asset base units, the `sender`, and
111
- * an optional `minSharesOut` (defaults to the dry run's exact preview).
116
+ * optionally `minSharesOut` or `slippageBps` (the minimum defaults to the
117
+ * dry run's preview less 0.1%).
112
118
  * @returns The unsigned transaction for the sender's wallet, the shares the
113
119
  * dry run minted, and the minimum it enforces.
114
120
  * @throws {@link UpshiftValidationError} for malformed input, unknown
@@ -124,21 +130,25 @@ class SuiAdapter {
124
130
  * amount: 1_000_000n,
125
131
  * sender: account.address,
126
132
  * });
127
- * await dAppKit.signAndExecuteTransaction({ transaction });
133
+ * const digest = await sdk.sui.executeTransaction(
134
+ * await dAppKit.signTransaction({ transaction }),
135
+ * );
128
136
  * ```
129
137
  */
130
138
  async vaultDeposit(params) {
131
- const { amount, sender, minSharesOut, ...vault } = params;
139
+ const { amount, sender, minSharesOut, slippageBps, ...vault } = params;
132
140
  return SuiActions.buildSuiDepositTransaction({
133
141
  vault: { ...vault, network: this.network },
134
142
  amount,
135
143
  sender,
136
144
  minSharesOut,
145
+ slippageBps,
137
146
  }, this._config);
138
147
  }
139
148
  /**
140
- * Build an unsigned instant redemption on the configured network: redeem
141
- * shares with a minimum net asset amount, send the assets to the sender.
149
+ * Build an unsigned instant redemption on the configured network: charge
150
+ * the management fee, redeem shares with a minimum net asset amount, send
151
+ * the assets to the sender.
142
152
  * Dry-runs it first; a vault with a withdrawal queue attached fails with
143
153
  * `queue_attached`.
144
154
  *
@@ -148,8 +158,8 @@ class SuiAdapter {
148
158
  * RPC cost: one dry run (plus the coin lookup it needs).
149
159
  *
150
160
  * @param params - The vault, `shares` in share base units, the `sender`, and
151
- * an optional `minAssetsOut` net of the instant fee (defaults to the dry
152
- * run's exact preview).
161
+ * optionally `minAssetsOut` (net of the instant fee) or `slippageBps` (the
162
+ * minimum defaults to the dry run's preview less 0.1%).
153
163
  * @returns The unsigned transaction, the dry run's gross and net assets, and
154
164
  * the minimum it enforces.
155
165
  * @throws {@link UpshiftValidationError} for malformed input, unknown
@@ -158,14 +168,160 @@ class SuiAdapter {
158
168
  * @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
159
169
  */
160
170
  async vaultRedeem(params) {
161
- const { shares, sender, minAssetsOut, ...vault } = params;
171
+ const { shares, sender, minAssetsOut, slippageBps, ...vault } = params;
162
172
  return SuiActions.buildSuiRedeemTransaction({
163
173
  vault: { ...vault, network: this.network },
164
174
  shares,
165
175
  sender,
166
176
  minAssetsOut,
177
+ slippageBps,
178
+ }, this._config);
179
+ }
180
+ /**
181
+ * Build an unsigned withdrawal request on the configured network, for a
182
+ * vault with a withdrawal queue attached (where instant redemption is off).
183
+ * Escrows the shares in a new request; dry-runs it first, so a vault that
184
+ * would reject it fails here.
185
+ *
186
+ * The request is priced when it is claimed. Once the vault's cooldown has
187
+ * run, claim it with {@link SuiAdapter.claimWithdrawal}; cancel it at any
188
+ * time with {@link SuiAdapter.vaultCancelRedeemRequest}. List a wallet's
189
+ * requests with {@link SuiAdapter.getWithdrawalRequests}.
190
+ *
191
+ * RPC cost: one object read for a wallet with up to 23 earlier requests on
192
+ * the vault (one more per further 25), then one dry run.
193
+ *
194
+ * @param params - The vault, `shares` in share base units, the `sender`, and
195
+ * optionally `payout` (defaults to the sender) and `finalizer`: who besides
196
+ * the owner may claim during the first day the request is claimable
197
+ * (anyone may after). Defaults to the owner alone; pass a keeper address,
198
+ * or `null` to let anyone claim as soon as the cooldown ends.
199
+ * @returns The unsigned transaction, the request ID, the request's object ID
200
+ * (`request`) for claiming or cancelling, and `expectedAssets` at today's
201
+ * price.
202
+ * @throws {@link UpshiftValidationError} for malformed input, unknown
203
+ * contract IDs, or a transaction that cannot be built.
204
+ * @throws {@link UpshiftVaultAbortError} when the vault would reject it,
205
+ * e.g. `queue_not_attached` (redeem instantly instead) or `paused`.
206
+ * @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
207
+ * @example
208
+ * ```ts
209
+ * const { transaction, request } = await sdk.sui.vaultRequestRedeem({
210
+ * vaultId: vault.address,
211
+ * assetCoinType: vault.depositAssets[0].address,
212
+ * shareCoinType: vault.receipt.address,
213
+ * shares: 1_000_000n,
214
+ * sender: account.address,
215
+ * });
216
+ * await sdk.sui.executeTransaction(await dAppKit.signTransaction({ transaction }));
217
+ * ```
218
+ */
219
+ async vaultRequestRedeem(params) {
220
+ const { shares, sender, payout, finalizer, ...vault } = params;
221
+ return SuiActions.buildSuiRequestRedeemTransaction({
222
+ vault: { ...vault, network: this.network },
223
+ shares,
224
+ sender,
225
+ payout,
226
+ finalizer,
227
+ }, this._config);
228
+ }
229
+ /**
230
+ * Build an unsigned cancellation of a pending withdrawal request on the
231
+ * configured network; the escrowed shares go back to the sender. Allowed at
232
+ * any time while the request exists, also while the vault is paused. Only
233
+ * the wallet that made the request may cancel it.
234
+ *
235
+ * RPC cost: one dry run.
236
+ *
237
+ * @param params - The vault, the `request` object ID and the `sender`.
238
+ * @returns The unsigned transaction and the shares it returns.
239
+ * @throws {@link UpshiftValidationError} for malformed input, unknown
240
+ * contract IDs, or a request that no longer exists.
241
+ * @throws {@link UpshiftVaultAbortError} when the vault would reject it,
242
+ * e.g. `not_request_owner`.
243
+ * @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
244
+ * @example
245
+ * ```ts
246
+ * const { transaction } = await sdk.sui.vaultCancelRedeemRequest({
247
+ * vaultId, assetCoinType, shareCoinType, request, sender: account.address,
248
+ * });
249
+ * ```
250
+ */
251
+ async vaultCancelRedeemRequest(params) {
252
+ const { request, sender, ...vault } = params;
253
+ return SuiActions.buildSuiCancelRedeemRequestTransaction({ vault: { ...vault, network: this.network }, request, sender }, this._config);
254
+ }
255
+ /**
256
+ * Build an unsigned claim of a withdrawal request on the configured
257
+ * network: burns the escrowed shares and pays their current value, with no
258
+ * instant fee, to the request's payout.
259
+ *
260
+ * The payout lands in the payout address's address balance, not as a coin
261
+ * object. A request can be claimed from `eligibleAtMs` until `expiresAtMs`
262
+ * by its owner or finalizer (anyone when it names none), and by anyone from
263
+ * one day after it became eligible.
264
+ *
265
+ * RPC cost: one dry run.
266
+ *
267
+ * @param params - The vault, the `request` object ID, the `sender`, and
268
+ * optionally `minAssetsOut` or `slippageBps` (the minimum defaults to the
269
+ * dry run's payout less 0.1%).
270
+ * @returns The unsigned transaction, the shares it burns, the dry run's
271
+ * payout, and the minimum it enforces.
272
+ * @throws {@link UpshiftValidationError} for malformed input, unknown
273
+ * contract IDs, or a request that no longer exists.
274
+ * @throws {@link UpshiftVaultAbortError} when the vault would reject it,
275
+ * e.g. `not_eligible_yet`, `request_expired` or `insufficient_liquidity`.
276
+ * @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
277
+ * @example
278
+ * ```ts
279
+ * const [pending] = await sdk.sui.getWithdrawalRequests({ ...vault, owner: account.address });
280
+ * const { transaction } = await sdk.sui.claimWithdrawal({
281
+ * ...vault, request: pending.request, sender: account.address,
282
+ * });
283
+ * ```
284
+ */
285
+ async claimWithdrawal(params) {
286
+ const { request, sender, minAssetsOut, slippageBps, ...vault } = params;
287
+ return SuiActions.buildSuiClaimWithdrawalTransaction({
288
+ vault: { ...vault, network: this.network },
289
+ request,
290
+ sender,
291
+ minAssetsOut,
292
+ slippageBps,
167
293
  }, this._config);
168
294
  }
295
+ /**
296
+ * A wallet's pending withdrawal requests on a vault, on the configured
297
+ * network, oldest first. Finds every request made through the SDK however
298
+ * old; claimed and cancelled requests are left out.
299
+ *
300
+ * RPC cost: one object read for a wallet that has made up to 23 requests on
301
+ * the vault (most wallets), one more per further 25.
302
+ *
303
+ * @param params - The vault and the `owner` wallet.
304
+ * @returns The pending requests, with shares, payout, and the times they
305
+ * become claimable and expire; empty when there are none.
306
+ * @throws {@link UpshiftValidationError} for malformed input, unknown
307
+ * contract IDs, or a vault ID that is not a vault of the configured
308
+ * `sui.originalPackageId`.
309
+ * @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
310
+ * @example
311
+ * ```ts
312
+ * const pending = await sdk.sui.getWithdrawalRequests({
313
+ * vaultId, assetCoinType, shareCoinType, owner: account.address,
314
+ * });
315
+ * const now = Date.now();
316
+ * const claimable = pending.filter(
317
+ * (r) => r.eligibleAtMs <= now && (r.expiresAtMs === null || now < r.expiresAtMs),
318
+ * );
319
+ * ```
320
+ */
321
+ async getWithdrawalRequests(params) {
322
+ const { owner, ...vault } = params;
323
+ return SuiGetters.getSuiWithdrawalRequests({ ...vault, network: this.network }, owner, this._config);
324
+ }
169
325
  /**
170
326
  * Execute a wallet-signed transaction on the configured network and wait
171
327
  * until it is indexed. Not needed when the wallet executes the transaction
@@ -186,5 +342,6 @@ class SuiAdapter {
186
342
  return SuiActions.submitSuiTransaction(signed, this.network, this._config);
187
343
  }
188
344
  }
345
+ exports.SuiAdapter = SuiAdapter;
189
346
  exports.default = SuiAdapter;
190
347
  //# sourceMappingURL=index.js.map
@@ -29,7 +29,8 @@ export interface ISuiVaultTargetSpec {
29
29
  * Every `upshift_vault` function the SDK calls, keyed by a stable name.
30
30
  *
31
31
  * Vault functions take the `<Asset, Share>` type arguments built by
32
- * {@link suiVaultTypeArguments}; `withdrawal::share` takes `<Share>` only.
32
+ * {@link suiVaultTypeArguments}; `withdrawal::share` and `withdrawal::shares`
33
+ * take `<Share>` only.
33
34
  */
34
35
  export declare const SUI_VAULT_TARGETS: {
35
36
  readonly deposit: {
@@ -67,6 +68,11 @@ export declare const SUI_VAULT_TARGETS: {
67
68
  readonly function: "share";
68
69
  readonly params: readonly ["WithdrawalRequest<Share>"];
69
70
  };
71
+ readonly withdrawalRequestShares: {
72
+ readonly module: "withdrawal";
73
+ readonly function: "shares";
74
+ readonly params: readonly ["&WithdrawalRequest<Share>"];
75
+ };
70
76
  readonly finalizeWithdrawal: {
71
77
  readonly module: "vault";
72
78
  readonly function: "finalize_withdrawal";
@@ -12,7 +12,8 @@ const constants_1 = require("./constants");
12
12
  * Every `upshift_vault` function the SDK calls, keyed by a stable name.
13
13
  *
14
14
  * Vault functions take the `<Asset, Share>` type arguments built by
15
- * {@link suiVaultTypeArguments}; `withdrawal::share` takes `<Share>` only.
15
+ * {@link suiVaultTypeArguments}; `withdrawal::share` and `withdrawal::shares`
16
+ * take `<Share>` only.
16
17
  */
17
18
  exports.SUI_VAULT_TARGETS = {
18
19
  deposit: {
@@ -86,6 +87,11 @@ exports.SUI_VAULT_TARGETS = {
86
87
  function: 'share',
87
88
  params: ['WithdrawalRequest<Share>'],
88
89
  },
90
+ withdrawalRequestShares: {
91
+ module: 'withdrawal',
92
+ function: 'shares',
93
+ params: ['&WithdrawalRequest<Share>'],
94
+ },
89
95
  finalizeWithdrawal: {
90
96
  module: 'vault',
91
97
  function: 'finalize_withdrawal',
@@ -65,6 +65,8 @@ exports.METHOD_CATEGORIES = {
65
65
  getVaultPendingRedemptions: 'read.redemption',
66
66
  getVaultWithdrawals: 'read.redemption',
67
67
  getWithdrawalRequestsWithStatus: 'read.redemption',
68
+ // Sui withdrawal queue: a wallet's pending requests on a vault.
69
+ getWithdrawalRequests: 'read.redemption',
68
70
  previewRedemption: 'read.redemption',
69
71
  previewRwaRedemption: 'read.redemption',
70
72
  getBurnableAmountByReceiver: 'read.redemption',
@@ -3,4 +3,4 @@
3
3
  * Generated at build time from package.json version
4
4
  * This file is gitignored and regenerated by every build
5
5
  */
6
- export declare const SDK_VERSION = "0.4.0";
6
+ export declare const SDK_VERSION = "0.5.0";
@@ -6,5 +6,5 @@ exports.SDK_VERSION = void 0;
6
6
  * Generated at build time from package.json version
7
7
  * This file is gitignored and regenerated by every build
8
8
  */
9
- exports.SDK_VERSION = '0.4.0';
9
+ exports.SDK_VERSION = '0.5.0';
10
10
  //# sourceMappingURL=version.js.map
@@ -210,10 +210,23 @@ export declare class UpshiftTransactionError extends UpshiftSDKError {
210
210
  * withdrawal queue.
211
211
  * - `slippage` — the output is below the minimum the transaction set.
212
212
  * - `zero_output` — the amount is too small to mint shares or pay assets.
213
- * - `version_retired` — the vault package version is retired; upgrade the SDK.
213
+ * - `version_retired` — the transaction targets a retired package version;
214
+ * point it at the current `upshift_vault` package (`sui.packageId`).
215
+ * - `queue_not_attached` — withdrawal requests are off; the vault has no
216
+ * withdrawal queue (redeem instantly instead).
217
+ * - `invalid_payout` — the request's payout address could never receive the
218
+ * assets (the zero, vault, Registry or request address).
219
+ * - `request_id_in_use` — the wallet already used this request ID on the
220
+ * vault; build the request again to pick the next one.
221
+ * - `not_request_owner` — only the wallet that made the request may cancel it.
222
+ * - `not_finalizer` — this wallet may not claim the request yet: only its
223
+ * owner or named finalizer may, until one day after it became eligible.
224
+ * - `not_eligible_yet` — the request's cooldown has not run out.
225
+ * - `request_expired` — the request's fulfillment window has passed; cancel it
226
+ * to get the shares back.
214
227
  * - `other` — any other abort; see `module` and `constant`.
215
228
  */
216
- export type UpshiftVaultAbortReason = 'paused' | 'below_min_deposit' | 'above_max_deposit' | 'deposit_cap_exceeded' | 'above_max_withdrawal' | 'insufficient_liquidity' | 'queue_attached' | 'slippage' | 'zero_output' | 'version_retired' | 'other';
229
+ export type UpshiftVaultAbortReason = 'paused' | 'below_min_deposit' | 'above_max_deposit' | 'deposit_cap_exceeded' | 'above_max_withdrawal' | 'insufficient_liquidity' | 'queue_attached' | 'slippage' | 'zero_output' | 'version_retired' | 'queue_not_attached' | 'invalid_payout' | 'request_id_in_use' | 'not_request_owner' | 'not_finalizer' | 'not_eligible_yet' | 'request_expired' | 'other';
217
230
  /**
218
231
  * A vault contract rejected a transaction, in a dry run before signing or on
219
232
  * chain. `reason` is stable to branch on; `module` and `constant` name the
@@ -8,39 +8,6 @@
8
8
  *
9
9
  * @module evm/methods/crossChainVault
10
10
  */
11
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
- if (k2 === undefined) k2 = k;
13
- var desc = Object.getOwnPropertyDescriptor(m, k);
14
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
- desc = { enumerable: true, get: function() { return m[k]; } };
16
- }
17
- Object.defineProperty(o, k2, desc);
18
- }) : (function(o, m, k, k2) {
19
- if (k2 === undefined) k2 = k;
20
- o[k2] = m[k];
21
- }));
22
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
- Object.defineProperty(o, "default", { enumerable: true, value: v });
24
- }) : function(o, v) {
25
- o["default"] = v;
26
- });
27
- var __importStar = (this && this.__importStar) || (function () {
28
- var ownKeys = function(o) {
29
- ownKeys = Object.getOwnPropertyNames || function (o) {
30
- var ar = [];
31
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
- return ar;
33
- };
34
- return ownKeys(o);
35
- };
36
- return function (mod) {
37
- if (mod && mod.__esModule) return mod;
38
- var result = {};
39
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
- __setModuleDefault(result, mod);
41
- return result;
42
- };
43
- })();
44
11
  Object.defineProperty(exports, "__esModule", { value: true });
45
12
  exports.LAYERZERO_ENDPOINT_IDS = void 0;
46
13
  exports.buildCrossChainVaultTx = buildCrossChainVaultTx;
@@ -59,6 +26,7 @@ exports.getAvailableChains = getAvailableChains;
59
26
  exports.formatCrossChainError = formatCrossChainError;
60
27
  exports.getLayerZeroScanUrl = getLayerZeroScanUrl;
61
28
  const attribution_1 = require("../../core/attribution");
29
+ const ethers_1 = require("ethers");
62
30
  const core_1 = require("../../core");
63
31
  const crossChain_1 = require("../types/crossChain");
64
32
  const OFT_1 = require("../../abis/OFT");
@@ -369,19 +337,33 @@ async function buildCrossChainVaultTx(props) {
369
337
  const hubRpc = hubChain?.rpcUrls;
370
338
  const hubRpcUrl = hubRpc?.default?.http?.[0];
371
339
  let hubPublicClient;
372
- try {
373
- // Dynamic import to keep viem optional at the module level
374
- const viem = await Promise.resolve().then(() => __importStar(require('viem')));
375
- hubPublicClient = viem.createPublicClient({
376
- chain: hubChain,
377
- transport: viem.http(hubRpcUrl),
378
- });
340
+ if (hubRpcUrl) {
341
+ // Hub reads (multi-asset previews) go through ethers, which is a hard
342
+ // dependency. This used to dynamically import the optional `viem` peer, which tsc
343
+ // compiles to a literal CJS require that bundlers resolve at build time
344
+ // and fail on when `viem` is not installed.
345
+ // `createProvider` is cached per (url, chainId) and uses a static
346
+ // network, so each readContract costs exactly one `eth_call`.
347
+ const hubChainId = hubChain?.id;
348
+ const provider = (0, core_1.createProvider)(hubRpcUrl, hubChainId);
349
+ hubPublicClient = {
350
+ readContract: async (args) => {
351
+ const { address, abi, functionName, args: callArgs = [], } = args;
352
+ const result = await new ethers_1.Contract(address, abi, provider)
353
+ .getFunction(functionName)
354
+ .staticCallResult(...callArgs);
355
+ // Match viem's readContract shape: a single output is returned
356
+ // bare; multiple outputs as a positional array.
357
+ return result.length === 1 ? result[0] : result.toArray();
358
+ },
359
+ };
379
360
  }
380
- catch {
381
- // If viem is not available, provide a stub that will fail on multi-asset only
361
+ else {
362
+ // No hub RPC on the chain object: only multi-asset paths read the hub,
363
+ // so fail those (redeem previews fall back to 1:1, as before).
382
364
  hubPublicClient = {
383
365
  readContract: async () => {
384
- throw new Error('viem is required for multi-asset vault operations. Install viem as a peer dependency.');
366
+ throw new core_1.UpshiftValidationError('INVALID_INPUT', 'hubChain.rpcUrls.default.http[0] is required for multi-asset vault operations.');
385
367
  },
386
368
  };
387
369
  }
package/lib/index.d.ts CHANGED
@@ -22,6 +22,10 @@ export * from './adapters/solana';
22
22
  * Stellar export
23
23
  */
24
24
  export * from './adapters/stellar';
25
+ /**
26
+ * Sui export
27
+ */
28
+ export * from './adapters/sui';
25
29
  export declare const VAULT_ADDRESSES: Record<"musd" | "predlp" | "sentusd" | "pregrid" | "repouscc" | "earnxrp" | "tivkbtc" | "tivusdt0" | "supermon" | "earnmon" | "k3europ" | "wmon/ausd" | "wbtc/ausd" | "earnausd_monad" | "sausd" | "farmbold" | "sentuscc" | "sentbtc" | "svusdc" | "apusdc" | "testwethtsa" | "tacusr" | "upinjusdt" | "tac-teth" | "upausd" | "taccbbtc" | "xupusdc" | "uptbtc" | "gteth" | "ageth" | "upusdc" | "hgeth" | "upssylva" | "hbbtc" | "hbhype" | "upazt" | "susdt" | "upedge" | "coreusdc" | "upcusdo" | "shifteth" | "upsusde" | "uplbtc" | "upavax" | "tacrseth" | "earnausd" | "alpineusdcflagship" | "alpinecoinshiftusdc" | "upbtc" | "upstrbtc" | "maxiusr" | "upgammausdc" | "xhype" | "wildusd" | "alpinebtc" | "prenusd" | "upyzusd" | "upnusd" | "hlpe" | "nemo eth yield", `0x${string}`>;
26
30
  /**
27
31
  * Export the SDK class as default
package/lib/index.js CHANGED
@@ -39,6 +39,10 @@ __exportStar(require("./adapters/solana"), exports);
39
39
  * Stellar export
40
40
  */
41
41
  __exportStar(require("./adapters/stellar"), exports);
42
+ /**
43
+ * Sui export
44
+ */
45
+ __exportStar(require("./adapters/sui"), exports);
42
46
  /**
43
47
  * Upshift Vaults
44
48
  */
package/lib/main.d.ts CHANGED
@@ -266,8 +266,10 @@ export declare class UpshiftSDK extends UpshiftBase {
266
266
  solanaWallet?: string;
267
267
  stellarWallet?: string;
268
268
  /**
269
- * Sui wallet address (`0x` + 64 hex chars). Its share balance is read for
270
- * each Sui vault; Sui positions have no redemptions in v1.
269
+ * Sui wallet address (`0x` + 64 hex chars). Its share balance and its
270
+ * pending withdrawal requests (as redemptions) are read for each Sui vault:
271
+ * one balance read plus, when the SDK has the contract IDs, one object read
272
+ * (one more per further 25 requests the wallet has made on the vault).
271
273
  */
272
274
  suiWallet?: string;
273
275
  options?: IVaultBaseOptions;
@@ -185,7 +185,15 @@ async function getVault({ vault, loans = false, allocations = false, options, lo
185
185
  // caller sees the same throw either way, so it is recorded as a breadcrumb
186
186
  // rather than a billed Sentry issue. Everything else stays at error.
187
187
  (0, core_1.logChainError)('getVault', err, (0, core_1.isRetryableRpcError)(err) || (0, core_1.isEmptyViewResponse)(err), { vault });
188
- throw new Error(`#getVault::${vault}: ${err?.message}`);
188
+ // Typed SDK errors (rate limit, server, validation, …) pass through
189
+ // unchanged so callers can branch on `instanceof` / `code` and Sentry
190
+ // groups them by class. Anything else is wrapped in a typed error with the
191
+ // original kept as `cause`; the `#getVault::` message is unchanged.
192
+ if (err instanceof errors_1.UpshiftSDKError)
193
+ throw err;
194
+ throw new errors_1.UpshiftSDKError('UNKNOWN', `#getVault::${vault}: ${err?.message}`, {
195
+ cause: err,
196
+ });
189
197
  }
190
198
  // Loans enrichment stays EVM-only — there is no non-EVM loan book to read.
191
199
  // Allocations additionally covers Stellar (Untangled) and Solana (Octav, per
@@ -1606,6 +1614,10 @@ async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, s
1606
1614
  const shareCoinType = v.sui_vault_metadata?.share_coin_type;
1607
1615
  const network = v.sui_vault_metadata?.network_name;
1608
1616
  let balance = (0, core_1.toNormalizedBn)(0, decimals ?? 0);
1617
+ let redemptions = {
1618
+ availableRedemptions: [],
1619
+ pendingRedemptions: [],
1620
+ };
1609
1621
  // No metadata at all: a legacy Ember/Gamma vault, nothing to read.
1610
1622
  if (suiWallet && (0, chain_address_1.isSuiAddress)(suiWallet) && v.sui_vault_metadata) {
1611
1623
  if (decimals == null || !shareCoinType || !network) {
@@ -1613,15 +1625,31 @@ async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, s
1613
1625
  // read or no way to scale it: fail the row, don't show zero.
1614
1626
  throw new errors_1.UpshiftValidationError('INVALID_INPUT', 'getVaultPositions: Sui vault is missing sui_vault_metadata share type, decimals or network', { context: { vault: v.address } });
1615
1627
  }
1616
- const shares = await SuiGetters.getSuiShareBalance(suiWallet, shareCoinType, network, options.sui);
1628
+ const ref = SuiGetters.suiVaultRefFromRow(v);
1629
+ const [shares, queued] = await Promise.all([
1630
+ SuiGetters.getSuiShareBalance(suiWallet, shareCoinType, network, options.sui),
1631
+ // Shares escrowed in withdrawal requests have left the wallet.
1632
+ // A failed request read keeps the balance, as on Solana.
1633
+ ref
1634
+ ? SuiGetters.getSuiPositionRedemptions(ref, suiWallet, decimals, options.sui).catch((error) => {
1635
+ core_1.Logger.log.warn('getVaultPositions', 'Sui withdrawal requests unread — position shows the share balance only', { vault: v.address, error });
1636
+ return redemptions;
1637
+ })
1638
+ : redemptions,
1639
+ ]);
1617
1640
  balance = (0, core_1.toNormalizedBn)(shares, decimals);
1641
+ redemptions = queued;
1618
1642
  }
1619
- // Sui vaults have no redemption reads in v1.
1620
1643
  return {
1621
1644
  vault: v.address,
1622
- status: renderStatus([], balance),
1623
- availableRedemptions: [],
1624
- pendingRedemptions: [],
1645
+ // Any queued request, waiting or claimable, marks the position
1646
+ // REDEEM, as on Solana.
1647
+ status: renderStatus([
1648
+ ...redemptions.availableRedemptions,
1649
+ ...redemptions.pendingRedemptions,
1650
+ ], balance),
1651
+ availableRedemptions: redemptions.availableRedemptions,
1652
+ pendingRedemptions: redemptions.pendingRedemptions,
1625
1653
  redeemable: (0, core_1.toNormalizedBn)(0),
1626
1654
  walletBalance: balance,
1627
1655
  };
@@ -242,8 +242,10 @@ export declare class UpshiftVaults extends UpshiftBase {
242
242
  solanaWallet?: string;
243
243
  stellarWallet?: string;
244
244
  /**
245
- * Sui wallet address (`0x` + 64 hex chars). Its share balance is read for
246
- * each Sui vault; Sui positions have no redemptions in v1.
245
+ * Sui wallet address (`0x` + 64 hex chars). Its share balance and its
246
+ * pending withdrawal requests (as redemptions) are read for each Sui vault:
247
+ * one balance read plus, when the SDK has the contract IDs, one object read
248
+ * (one more per further 25 requests the wallet has made on the vault).
247
249
  */
248
250
  suiWallet?: string;
249
251
  options?: IVaultBaseOptions;
@@ -738,7 +738,9 @@ class UpshiftVaults extends core_1.UpshiftBase {
738
738
  }
739
739
  const allVaults = await (0, core_1.fetchTokenizedVaults)(undefined, this.headers, false, false);
740
740
  if (chainId) {
741
- const vaultsPerChain = allVaults.filter((v) => v.chain === chainId);
741
+ // Sui rows without sui_vault_metadata are legacy Ember/Gamma vaults.
742
+ const vaultsPerChain = allVaults.filter((v) => v.chain === chainId &&
743
+ !(v.chain_type === 'sui' && !v.sui_vault_metadata));
742
744
  // Batch balanceOf/lagDuration via Multicall3 before the per-vault
743
745
  // fan-out; un-batched vaults fall back to their own reads.
744
746
  const prefetchedReads = await (0, prefetch_1.prefetchVaultPositionReads)({