@upshiftfinance/sdk 0.3.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.
Files changed (61) hide show
  1. package/README.md +6 -2
  2. package/esm/index.d.mts +15 -0
  3. package/esm/index.mjs +18 -0
  4. package/lib/abis/KycRegistry.d.ts +109 -0
  5. package/lib/abis/KycRegistry.js +73 -0
  6. package/lib/abis/index.d.ts +1 -0
  7. package/lib/abis/index.js +1 -0
  8. package/lib/adapters/sui/actions.d.ts +420 -0
  9. package/lib/adapters/sui/actions.js +701 -0
  10. package/lib/adapters/sui/client.d.ts +3 -3
  11. package/lib/adapters/sui/client.js +27 -9
  12. package/lib/adapters/sui/getters.d.ts +268 -0
  13. package/lib/adapters/sui/getters.js +669 -0
  14. package/lib/adapters/sui/index.d.ts +243 -3
  15. package/lib/adapters/sui/index.js +250 -3
  16. package/lib/adapters/sui/targets.d.ts +42 -1
  17. package/lib/adapters/sui/targets.js +42 -1
  18. package/lib/core/analytics/method-taxonomy.d.ts +1 -1
  19. package/lib/core/analytics/method-taxonomy.js +9 -0
  20. package/lib/core/analytics/version.d.ts +1 -1
  21. package/lib/core/analytics/version.js +1 -1
  22. package/lib/core/constants/core.d.ts +5 -0
  23. package/lib/core/constants/core.js +5 -0
  24. package/lib/core/constants/kyc.d.ts +9 -0
  25. package/lib/core/constants/kyc.js +12 -0
  26. package/lib/core/constants/web3.d.ts +2 -2
  27. package/lib/core/constants/web3.js +9 -2
  28. package/lib/core/errors/index.d.ts +90 -1
  29. package/lib/core/errors/index.js +94 -1
  30. package/lib/core/fetcher.d.ts +5 -0
  31. package/lib/core/fetcher.js +1 -1
  32. package/lib/core/helpers/chain-support.d.ts +2 -2
  33. package/lib/core/helpers/chain-support.js +3 -3
  34. package/lib/core/helpers/kyc.d.ts +47 -0
  35. package/lib/core/helpers/kyc.js +111 -0
  36. package/lib/core/helpers/revert-decode.js +3 -2
  37. package/lib/core/index.d.ts +2 -0
  38. package/lib/core/index.js +2 -0
  39. package/lib/evm/methods/crossChainVault.js +25 -43
  40. package/lib/index.d.ts +4 -0
  41. package/lib/index.js +4 -0
  42. package/lib/main.d.ts +33 -3
  43. package/lib/main.js +32 -0
  44. package/lib/modules/api/kyc.d.ts +4 -0
  45. package/lib/modules/api/kyc.js +148 -0
  46. package/lib/modules/api/main.d.ts +63 -1
  47. package/lib/modules/api/main.js +64 -0
  48. package/lib/modules/vaults/getters.d.ts +3 -1
  49. package/lib/modules/vaults/getters.js +82 -10
  50. package/lib/modules/vaults/main.d.ts +41 -3
  51. package/lib/modules/vaults/main.js +82 -6
  52. package/lib/modules/vaults/types.d.ts +7 -1
  53. package/lib/modules/vaults/utils.js +4 -1
  54. package/lib/sdk.d.ts +16363 -14615
  55. package/lib/types/index.d.ts +1 -0
  56. package/lib/types/index.js +1 -0
  57. package/lib/types/kyc.d.ts +61 -0
  58. package/lib/types/kyc.js +3 -0
  59. package/lib/types/vaults.d.ts +2 -0
  60. package/lib/types/webserver.d.ts +5 -0
  61. package/package.json +21 -2
@@ -0,0 +1,669 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.decodeSuiUint = decodeSuiUint;
37
+ exports.decodeSuiBool = decodeSuiBool;
38
+ exports.parseInstantRedemptionFeeBps = parseInstantRedemptionFeeBps;
39
+ exports.suiConfigForNetwork = suiConfigForNetwork;
40
+ exports.readSuiVaultState = readSuiVaultState;
41
+ exports.previewSuiDeposit = previewSuiDeposit;
42
+ exports.previewSuiRedemption = previewSuiRedemption;
43
+ exports.getSuiShareBalance = getSuiShareBalance;
44
+ exports.getSuiWithdrawalRequests = getSuiWithdrawalRequests;
45
+ exports.suiPositionRedemptions = suiPositionRedemptions;
46
+ exports.getSuiPositionRedemptions = getSuiPositionRedemptions;
47
+ exports.nextSuiWithdrawalRequestId = nextSuiWithdrawalRequestId;
48
+ exports.deriveSuiWithdrawalRequestId = deriveSuiWithdrawalRequestId;
49
+ exports.suiVaultRefFromRow = suiVaultRefFromRow;
50
+ exports.buildSuiVault = buildSuiVault;
51
+ exports.getSuiVault = getSuiVault;
52
+ const types_1 = require("../../types");
53
+ const core_1 = require("../../core");
54
+ const errors_1 = require("../../core/errors");
55
+ const chain_address_1 = require("../../core/helpers/chain-address");
56
+ const utils_1 = require("../../modules/vaults/utils");
57
+ const client_1 = require("./client");
58
+ const constants_1 = require("./constants");
59
+ const targets_1 = require("./targets");
60
+ /**
61
+ * Re-throw a failed read as a typed SDK error: SDK errors pass through, and
62
+ * anything else (an object that does not exist, a malformed coin type, a
63
+ * package that is not the vault package) becomes `INVALID_INPUT`.
64
+ */
65
+ function asReadError(err, network) {
66
+ if (err instanceof core_1.UpshiftSDKError)
67
+ throw err;
68
+ throw new core_1.UpshiftValidationError('INVALID_INPUT', `Sui read failed — check the vault ID, its coin types and the contract IDs for this network: ${err instanceof Error ? err.message : String(err)}`, { cause: err, context: { network } });
69
+ }
70
+ /** Simulations need a sender; no signature or gas is involved. */
71
+ const SIMULATION_SENDER = `0x${'0'.repeat(64)}`;
72
+ /** The contract's "no limit" value for `u64` caps. */
73
+ const U64_MAX = 2n ** 64n - 1n;
74
+ /** Read-only targets {@link readSuiVaultState} calls, in command order. */
75
+ const STATE_READS = [
76
+ 'totalAssets',
77
+ 'totalSupply',
78
+ 'sharePrice',
79
+ 'decimals',
80
+ 'paused',
81
+ 'queueAttached',
82
+ 'minDeposit',
83
+ 'depositCap',
84
+ 'maxDepositAmount',
85
+ 'maxWithdrawalAmount',
86
+ ];
87
+ /** Targets that also take the Registry after the vault. */
88
+ const NEEDS_REGISTRY = new Set(['totalAssets', 'sharePrice']);
89
+ /**
90
+ * Decode a little-endian BCS unsigned integer of `size` bytes.
91
+ * @internal
92
+ */
93
+ function decodeSuiUint(bytes, size) {
94
+ if (bytes.length !== size) {
95
+ throw new core_1.UpshiftValidationError('INVALID_INPUT', `Sui read: expected a ${size}-byte integer, got ${bytes.length} bytes.`);
96
+ }
97
+ let value = 0n;
98
+ for (let i = size - 1; i >= 0; i -= 1) {
99
+ value = (value << 8n) | BigInt(bytes[i]);
100
+ }
101
+ return value;
102
+ }
103
+ /**
104
+ * Decode a BCS `bool`.
105
+ * @internal
106
+ */
107
+ function decodeSuiBool(bytes) {
108
+ if (bytes.length !== 1 || bytes[0] > 1) {
109
+ throw new core_1.UpshiftValidationError('INVALID_INPUT', 'Sui read: expected a BCS bool.');
110
+ }
111
+ return bytes[0] === 1;
112
+ }
113
+ /**
114
+ * Read the instant-redemption fee from the vault object's JSON fields: the fee
115
+ * sits in a nested struct that a transaction cannot return.
116
+ * @internal
117
+ */
118
+ function parseInstantRedemptionFeeBps(json) {
119
+ const raw = json
120
+ ?.fee_state?.instant_redemption_fee_bps;
121
+ const text = typeof raw === 'number' ? String(raw) : raw;
122
+ if (typeof text !== 'string' || !/^\d+$/.test(text)) {
123
+ throw new core_1.UpshiftValidationError('INVALID_INPUT', 'Sui read: vault object has no fee_state.instant_redemption_fee_bps.');
124
+ }
125
+ return Number(text);
126
+ }
127
+ /**
128
+ * The `sui` config to use for a vault on `network`: the consumer's config when
129
+ * it is for that network, else the network alone (built-in endpoint and
130
+ * contract IDs). A testnet override must never serve a mainnet vault.
131
+ * @internal
132
+ */
133
+ function suiConfigForNetwork(network, sui) {
134
+ return (sui?.network ?? 'mainnet') === network
135
+ ? { ...sui, network }
136
+ : { network };
137
+ }
138
+ function assertVaultRef(ref, method) {
139
+ if (!(0, chain_address_1.isSuiAddress)(ref.vaultId)) {
140
+ throw new core_1.UpshiftValidationError('INVALID_ADDRESS', `${method}: vaultId must be a Sui object ID (0x + 64 hex chars).`);
141
+ }
142
+ }
143
+ /**
144
+ * Simulate read-only vault calls in one transaction and return each call's
145
+ * return values (BCS bytes), in order.
146
+ */
147
+ async function simulateVaultReads(ref, sui, calls) {
148
+ const config = suiConfigForNetwork(ref.network, sui);
149
+ const contracts = (0, targets_1.resolveSuiVaultContracts)(config);
150
+ const { Transaction: SuiTransaction } = await Promise.resolve().then(() => __importStar(require('@mysten/sui/transactions')));
151
+ const typeArguments = (0, targets_1.suiVaultTypeArguments)(ref.assetCoinType, ref.shareCoinType);
152
+ return (0, client_1.withSuiClient)((0, client_1.resolveSuiNetworkConfig)(config), async (client) => {
153
+ // Built per attempt: building resolves object versions against this client.
154
+ const tx = new SuiTransaction();
155
+ tx.setSender(SIMULATION_SENDER);
156
+ // Charge the management fee accrued since the last charge first, so every
157
+ // read sees the current NAV. A simulation persists nothing.
158
+ tx.moveCall({
159
+ target: (0, targets_1.suiMoveCallTarget)(contracts, 'chargeManagementFee'),
160
+ typeArguments,
161
+ arguments: [
162
+ tx.object(ref.vaultId),
163
+ tx.object(contracts.registryId),
164
+ tx.object(constants_1.SUI_CLOCK_ID),
165
+ ],
166
+ });
167
+ for (const call of calls) {
168
+ tx.moveCall({
169
+ target: (0, targets_1.suiMoveCallTarget)(contracts, call.target),
170
+ typeArguments,
171
+ arguments: [
172
+ tx.object(ref.vaultId),
173
+ ...(call.registry ? [tx.object(contracts.registryId)] : []),
174
+ ...(call.pure?.(tx) ?? []),
175
+ ],
176
+ });
177
+ }
178
+ const result = await client.core.simulateTransaction({
179
+ transaction: tx,
180
+ include: { commandResults: true, effects: true },
181
+ });
182
+ if (result.$kind !== 'Transaction') {
183
+ throw new core_1.UpshiftValidationError('INVALID_INPUT', 'Sui read: vault simulation failed — check the vault ID, its coin types and the contract IDs for this network.', {
184
+ context: {
185
+ network: ref.network,
186
+ status: JSON.stringify(result.FailedTransaction?.effects?.status),
187
+ },
188
+ });
189
+ }
190
+ // Drop the fee charge's (empty) result so indexes match `calls`.
191
+ return result.commandResults
192
+ .slice(1)
193
+ .map((c) => c.returnValues.map((v) => v.bcs));
194
+ }).catch((err) => asReadError(err, ref.network));
195
+ }
196
+ /**
197
+ * Read a vault's on-chain state.
198
+ *
199
+ * RPC cost: two requests, run in parallel: one transaction simulation and one
200
+ * object read. `getVaults` makes these two per Sui vault.
201
+ *
202
+ * @param ref - The vault and its coin types and network.
203
+ * @param sui - The SDK's `sui` config; applied only when its network matches
204
+ * `ref.network`.
205
+ * @returns The vault's state; see {@link ISuiVaultState} for units.
206
+ * @throws {@link UpshiftValidationError} (`INVALID_ADDRESS`) for a malformed
207
+ * vault ID, (`INVALID_INPUT`) when the contract IDs for the network are not
208
+ * known or the simulation aborts (wrong vault, coin types or package).
209
+ * @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
210
+ * @example
211
+ * ```ts
212
+ * const state = await readSuiVaultState(
213
+ * { vaultId, assetCoinType, shareCoinType, network: 'testnet' },
214
+ * { network: 'testnet', packageId, originalPackageId, registryId },
215
+ * );
216
+ * ```
217
+ */
218
+ async function readSuiVaultState(ref, sui) {
219
+ assertVaultRef(ref, 'readSuiVaultState');
220
+ const config = suiConfigForNetwork(ref.network, sui);
221
+ const [values, fee] = await Promise.all([
222
+ simulateVaultReads(ref, sui, STATE_READS.map((target) => ({
223
+ target,
224
+ registry: NEEDS_REGISTRY.has(target),
225
+ }))),
226
+ (0, client_1.withSuiClient)((0, client_1.resolveSuiNetworkConfig)(config), (client) => client.core.getObject({ objectId: ref.vaultId, include: { json: true } })).catch((err) => asReadError(err, ref.network)),
227
+ ]);
228
+ const at = (i) => {
229
+ const bytes = values[i]?.[0];
230
+ if (!bytes) {
231
+ throw new core_1.UpshiftValidationError('INVALID_INPUT', `Sui read: ${STATE_READS[i]} returned no value.`);
232
+ }
233
+ return bytes;
234
+ };
235
+ return {
236
+ totalAssets: decodeSuiUint(at(0), 16),
237
+ totalSupply: decodeSuiUint(at(1), 8),
238
+ sharePrice: decodeSuiUint(at(2), 16),
239
+ decimals: Number(decodeSuiUint(at(3), 1)),
240
+ paused: decodeSuiBool(at(4)),
241
+ queueAttached: decodeSuiBool(at(5)),
242
+ minDeposit: decodeSuiUint(at(6), 8),
243
+ depositCap: decodeSuiUint(at(7), 8),
244
+ maxDepositAmount: decodeSuiUint(at(8), 8),
245
+ maxWithdrawalAmount: decodeSuiUint(at(9), 8),
246
+ instantRedemptionFeeBps: parseInstantRedemptionFeeBps(fee.object.json),
247
+ };
248
+ }
249
+ /**
250
+ * Shares a deposit would mint at the current price. Limits are not checked.
251
+ *
252
+ * RPC cost: one transaction simulation.
253
+ *
254
+ * @param ref - The vault and its coin types and network.
255
+ * @param assets - Deposit amount in asset base units.
256
+ * @param sui - The SDK's `sui` config; applied only for `ref.network`.
257
+ * @returns Shares in share base units.
258
+ * @throws {@link UpshiftValidationError} for a malformed vault ID, a negative
259
+ * or out-of-range amount, unknown contract IDs or an aborted simulation.
260
+ * @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
261
+ */
262
+ async function previewSuiDeposit(ref, assets, sui) {
263
+ assertVaultRef(ref, 'previewSuiDeposit');
264
+ assertU64(assets, 'previewSuiDeposit', 'assets');
265
+ const [returned] = await simulateVaultReads(ref, sui, [
266
+ {
267
+ target: 'previewDeposit',
268
+ registry: true,
269
+ pure: (tx) => [tx.pure.u64(assets)],
270
+ },
271
+ ]);
272
+ return decodeSuiUint(returned[0], 8);
273
+ }
274
+ /**
275
+ * Assets a redemption of `shares` would pay at the current price.
276
+ *
277
+ * RPC cost: one transaction simulation.
278
+ *
279
+ * @param ref - The vault and its coin types and network.
280
+ * @param shares - Shares to redeem, in share base units.
281
+ * @param isInstant - `true` for instant `redeem` (charges the instant fee),
282
+ * `false` for a queued withdrawal (no fee).
283
+ * @param sui - The SDK's `sui` config; applied only for `ref.network`.
284
+ * @returns `gross` and `net` (after the instant fee) in asset base units.
285
+ * @throws {@link UpshiftValidationError} for a malformed vault ID, a negative
286
+ * or out-of-range amount, unknown contract IDs or an aborted simulation.
287
+ * @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
288
+ */
289
+ async function previewSuiRedemption(ref, shares, isInstant, sui) {
290
+ assertVaultRef(ref, 'previewSuiRedemption');
291
+ assertU64(shares, 'previewSuiRedemption', 'shares');
292
+ const [returned] = await simulateVaultReads(ref, sui, [
293
+ {
294
+ target: 'previewRedemption',
295
+ registry: true,
296
+ pure: (tx) => [tx.pure.u64(shares), tx.pure.bool(isInstant)],
297
+ },
298
+ ]);
299
+ return {
300
+ gross: decodeSuiUint(returned[0], 8),
301
+ net: decodeSuiUint(returned[1], 8),
302
+ };
303
+ }
304
+ function assertU64(value, method, name) {
305
+ if (typeof value !== 'bigint' || value < 0n || value > U64_MAX) {
306
+ throw new core_1.UpshiftValidationError('INVALID_INPUT', `${method}: ${name} must be a bigint between 0 and 2^64 - 1.`);
307
+ }
308
+ }
309
+ /**
310
+ * A wallet's balance of a share coin: coin objects plus the address balance.
311
+ *
312
+ * RPC cost: one balance read.
313
+ *
314
+ * @param owner - Wallet address (`0x` + 64 hex chars).
315
+ * @param shareCoinType - Move type of the vault's share coin.
316
+ * @param network - Network to read.
317
+ * @param sui - The SDK's `sui` config; applied only for `network`.
318
+ * @returns Shares in share base units.
319
+ * @throws {@link UpshiftValidationError} (`INVALID_ADDRESS`) for a malformed
320
+ * owner.
321
+ * @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
322
+ */
323
+ async function getSuiShareBalance(owner, shareCoinType, network, sui) {
324
+ if (!(0, chain_address_1.isSuiAddress)(owner)) {
325
+ throw new core_1.UpshiftValidationError('INVALID_ADDRESS', 'getSuiShareBalance: owner must be a Sui address (0x + 64 hex chars).');
326
+ }
327
+ const config = suiConfigForNetwork(network, sui);
328
+ const { balance } = await (0, client_1.withSuiClient)((0, client_1.resolveSuiNetworkConfig)(config), (client) => client.core.getBalance({ owner, coinType: shareCoinType })).catch((err) => asReadError(err, network));
329
+ return BigInt(balance.balance);
330
+ }
331
+ /**
332
+ * IDs checked per object read: each needs its claim marker and its request,
333
+ * and the gRPC client sends at most 50 objects per request. The first page
334
+ * also carries the vault object, so it checks one ID fewer.
335
+ */
336
+ const REQUEST_SCAN_PAGE = 25;
337
+ /**
338
+ * Walk a wallet's request IDs on a vault, `0, 1, 2, …`, and read each one.
339
+ *
340
+ * The contract derives a request's object ID from (vault, owner, request ID)
341
+ * and leaves a permanent claim marker on the vault for every ID ever used,
342
+ * also after the request is claimed or cancelled. The SDK numbers requests
343
+ * sequentially, so the first ID without a marker is the next one to use, and
344
+ * every request below it that still exists is pending. This needs neither an
345
+ * event index (public fullnodes keep about two weeks of events) nor an
346
+ * indexer.
347
+ *
348
+ * Requests made outside the SDK with other IDs are not found.
349
+ *
350
+ * The first read also checks that `ref.vaultId` is a vault of the configured
351
+ * original package, so a wrong vault ID or `sui.originalPackageId` (which
352
+ * names the request IDs) fails loudly instead of finding nothing.
353
+ *
354
+ * RPC cost: one object read for up to 23 used IDs, one more per further 25.
355
+ */
356
+ async function scanSuiWithdrawalRequests(ref, owner, sui) {
357
+ const config = suiConfigForNetwork(ref.network, sui);
358
+ const contracts = (0, targets_1.resolveSuiVaultContracts)(config);
359
+ const [{ bcs }, { deriveDynamicFieldID }] = await Promise.all([
360
+ Promise.resolve().then(() => __importStar(require('@mysten/sui/bcs'))),
361
+ Promise.resolve().then(() => __importStar(require('@mysten/sui/utils'))),
362
+ ]);
363
+ const WithdrawalRequest = bcs.struct('WithdrawalRequest', {
364
+ id: bcs.Address,
365
+ vaultId: bcs.Address,
366
+ owner: bcs.Address,
367
+ requestId: bcs.u64(),
368
+ shares: bcs.u64(),
369
+ payout: bcs.Address,
370
+ finalizer: bcs.option(bcs.Address),
371
+ requestedAtMs: bcs.u64(),
372
+ eligibleAtMs: bcs.u64(),
373
+ expiresAtMs: bcs.option(bcs.u64()),
374
+ });
375
+ const requests = [];
376
+ for (let start = 0n;;) {
377
+ const first = start === 0n;
378
+ const size = first ? REQUEST_SCAN_PAGE - 1 : REQUEST_SCAN_PAGE;
379
+ const ids = Array.from({ length: size }, (_, i) => start + BigInt(i));
380
+ start += BigInt(size);
381
+ const requestIds = await Promise.all(ids.map((requestId) => deriveSuiWithdrawalRequestId(ref.vaultId, owner, requestId, contracts.originalPackageId)));
382
+ // The marker is a dynamic field on the vault keyed by the request ID.
383
+ const markerIds = requestIds.map((id) => deriveDynamicFieldID(ref.vaultId, '0x2::derived_object::Claimed', bcs.Address.serialize(id).toBytes()));
384
+ const { objects } = await (0, client_1.withSuiClient)((0, client_1.resolveSuiNetworkConfig)(config), (client) => client.core.getObjects({
385
+ objectIds: [
386
+ ...markerIds,
387
+ ...requestIds,
388
+ ...(first ? [ref.vaultId] : []),
389
+ ],
390
+ include: { content: true },
391
+ })).catch((err) => asReadError(err, ref.network));
392
+ const missing = (o) => {
393
+ if (!(o instanceof Error))
394
+ return false;
395
+ const reason = o.reason;
396
+ if (reason === 'notFound' || reason === 'deleted')
397
+ return true;
398
+ // Any other per-object error means the node did not answer for it.
399
+ throw new errors_1.UpshiftNetworkError(`Sui read: withdrawal request lookup failed: ${o.message}`, { cause: o, context: { network: ref.network } });
400
+ };
401
+ if (first) {
402
+ const vault = objects[2 * size];
403
+ // A missing vault is a wrong ID; any other per-object error is the node.
404
+ assertVaultOfPackage(missing(vault) ? undefined : vault, ref, contracts);
405
+ }
406
+ for (let i = 0; i < size; i += 1) {
407
+ if (missing(objects[i])) {
408
+ return { nextRequestId: ids[i], requests };
409
+ }
410
+ const object = objects[size + i];
411
+ // Claimed but gone: the request was claimed or cancelled.
412
+ if (missing(object) || object instanceof Error)
413
+ continue;
414
+ const parsed = WithdrawalRequest.parse(object.content);
415
+ requests.push({
416
+ request: requestIds[i],
417
+ requestId: BigInt(parsed.requestId),
418
+ owner: parsed.owner,
419
+ shares: BigInt(parsed.shares),
420
+ payout: parsed.payout,
421
+ finalizer: parsed.finalizer ?? null,
422
+ requestedAtMs: Number(parsed.requestedAtMs),
423
+ eligibleAtMs: Number(parsed.eligibleAtMs),
424
+ expiresAtMs: parsed.expiresAtMs == null ? null : Number(parsed.expiresAtMs),
425
+ });
426
+ }
427
+ }
428
+ }
429
+ /**
430
+ * Throw unless `object` is the vault `ref` names, typed by the configured
431
+ * original package: request IDs are derived from that package, so a wrong one
432
+ * would make every request invisible.
433
+ */
434
+ function assertVaultOfPackage(object, ref, contracts) {
435
+ const type = object instanceof Error ? undefined : object?.type;
436
+ const expected = `${contracts.originalPackageId}::vault::Vault<`.toLowerCase();
437
+ if (!type) {
438
+ throw new core_1.UpshiftValidationError('INVALID_INPUT', 'Sui read: the vault object was not found — check the vault ID for this network.', { context: { network: ref.network } });
439
+ }
440
+ if (!type.toLowerCase().startsWith(expected)) {
441
+ throw new core_1.UpshiftValidationError('INVALID_INPUT', `Sui read: ${ref.vaultId} is not a vault of the configured original package (its type is ${type}) — check the vault ID and sui.originalPackageId.`, { context: { network: ref.network } });
442
+ }
443
+ }
444
+ function assertOwner(owner, method) {
445
+ if (!(0, chain_address_1.isSuiAddress)(owner)) {
446
+ throw new core_1.UpshiftValidationError('INVALID_ADDRESS', `${method}: owner must be a Sui address (0x + 64 hex chars).`);
447
+ }
448
+ }
449
+ /**
450
+ * A wallet's pending withdrawal requests on a vault, oldest first.
451
+ *
452
+ * Finds the requests the SDK made (see `buildSuiRequestRedeemTransaction`),
453
+ * however old: requests made outside the SDK with non-sequential IDs are not
454
+ * listed. Claimed and cancelled requests no longer exist and are left out.
455
+ *
456
+ * RPC cost: one object read for a wallet that has made up to 23 requests on
457
+ * the vault (most wallets), one more per further 25.
458
+ *
459
+ * @param ref - The vault and its coin types and network.
460
+ * @param owner - Wallet that made the requests (`0x` + 64 hex chars).
461
+ * @param sui - The SDK's `sui` config; applied only for `ref.network`.
462
+ * @returns The pending requests; empty when there are none.
463
+ * @throws {@link UpshiftValidationError} (`INVALID_ADDRESS`) for a malformed
464
+ * vault ID or owner, (`INVALID_INPUT`) for unknown contract IDs, or a vault
465
+ * ID that is not a vault of the configured `sui.originalPackageId`.
466
+ * @throws {@link UpshiftNetworkError} when every Sui endpoint fails, or the
467
+ * node cannot answer for one of the objects.
468
+ * @example
469
+ * ```ts
470
+ * const pending = await getSuiWithdrawalRequests(ref, account.address, sdkSuiConfig);
471
+ * const now = Date.now();
472
+ * const claimable = pending.filter(
473
+ * (r) => r.eligibleAtMs <= now && (r.expiresAtMs === null || now < r.expiresAtMs),
474
+ * );
475
+ * ```
476
+ */
477
+ async function getSuiWithdrawalRequests(ref, owner, sui) {
478
+ assertVaultRef(ref, 'getSuiWithdrawalRequests');
479
+ assertOwner(owner, 'getSuiWithdrawalRequests');
480
+ return (await scanSuiWithdrawalRequests(ref, owner, sui)).requests;
481
+ }
482
+ /**
483
+ * Split a wallet's withdrawal requests into position redemptions, the way
484
+ * the EVM and Solana paths do: `availableRedemptions` holds requests still in
485
+ * their cooldown or past their window (cancel-only), `pendingRedemptions` the
486
+ * ones claimable now. Each entry's `amount` is the escrowed shares; `date` is
487
+ * when it becomes claimable, `timestamp` when it was made (Unix seconds).
488
+ * @internal
489
+ */
490
+ function suiPositionRedemptions(requests, vault, decimals, nowMs) {
491
+ const availableRedemptions = [];
492
+ const pendingRedemptions = [];
493
+ for (const r of requests) {
494
+ const status = r.expiresAtMs !== null && nowMs >= r.expiresAtMs
495
+ ? types_1.VaultRedemptionStatus.EXPIRED
496
+ : nowMs >= r.eligibleAtMs
497
+ ? types_1.VaultRedemptionStatus.READY_TO_CLAIM
498
+ : types_1.VaultRedemptionStatus.AWAITING_COOLDOWN;
499
+ const date = new Date(r.eligibleAtMs);
500
+ const entry = {
501
+ id: r.request,
502
+ hash: r.request,
503
+ timestamp: Math.floor(r.requestedAtMs / 1000),
504
+ receiver: r.payout,
505
+ day: (0, core_1.toNormalizedBn)(date.getUTCDate(), 0),
506
+ month: (0, core_1.toNormalizedBn)(date.getUTCMonth() + 1, 0),
507
+ year: (0, core_1.toNormalizedBn)(date.getUTCFullYear(), 0),
508
+ amount: (0, core_1.toNormalizedBn)(r.shares, decimals),
509
+ date,
510
+ vault: vault,
511
+ status,
512
+ };
513
+ if (status === types_1.VaultRedemptionStatus.READY_TO_CLAIM) {
514
+ pendingRedemptions.push(entry);
515
+ }
516
+ else {
517
+ availableRedemptions.push(entry);
518
+ }
519
+ }
520
+ return { availableRedemptions, pendingRedemptions };
521
+ }
522
+ /**
523
+ * A wallet's withdrawal requests on a vault as position redemptions (see
524
+ * {@link suiPositionRedemptions}). Empty when the SDK has no contract IDs for
525
+ * the network: the share balance can still be read without them, so a
526
+ * position should not fail for that.
527
+ *
528
+ * RPC cost: as {@link getSuiWithdrawalRequests}; none without contract IDs.
529
+ * @internal
530
+ */
531
+ async function getSuiPositionRedemptions(ref, owner, decimals, sui) {
532
+ const config = suiConfigForNetwork(ref.network, sui);
533
+ const defaults = constants_1.SUI_VAULT_CONTRACTS[ref.network];
534
+ const configured = (config.packageId ?? defaults.packageId) &&
535
+ (config.originalPackageId ?? defaults.originalPackageId) &&
536
+ (config.registryId ?? defaults.registryId);
537
+ if (!configured) {
538
+ return suiPositionRedemptions([], ref.vaultId, decimals, Date.now());
539
+ }
540
+ const requests = await getSuiWithdrawalRequests(ref, owner, sui);
541
+ return suiPositionRedemptions(requests, ref.vaultId, decimals, Date.now());
542
+ }
543
+ /**
544
+ * The request ID a wallet's next withdrawal request on a vault takes: the
545
+ * lowest it has never used.
546
+ *
547
+ * RPC cost: as {@link getSuiWithdrawalRequests}.
548
+ * @internal
549
+ */
550
+ async function nextSuiWithdrawalRequestId(ref, owner, sui) {
551
+ assertVaultRef(ref, 'nextSuiWithdrawalRequestId');
552
+ assertOwner(owner, 'nextSuiWithdrawalRequestId');
553
+ return (await scanSuiWithdrawalRequests(ref, owner, sui)).nextRequestId;
554
+ }
555
+ /**
556
+ * The object ID of a wallet's withdrawal request: derived from the vault, the
557
+ * owner and the request ID, as the contract derives it.
558
+ *
559
+ * @param vaultId - Vault object ID.
560
+ * @param owner - Wallet that made the request.
561
+ * @param requestId - The wallet's numeric request ID on the vault.
562
+ * @param originalPackageId - The original `upshift_vault` package, which
563
+ * names the derivation key type.
564
+ * @returns The request object ID.
565
+ * @internal
566
+ */
567
+ async function deriveSuiWithdrawalRequestId(vaultId, owner, requestId, originalPackageId) {
568
+ const [{ bcs }, { deriveObjectID, normalizeSuiAddress }] = await Promise.all([
569
+ Promise.resolve().then(() => __importStar(require('@mysten/sui/bcs'))),
570
+ Promise.resolve().then(() => __importStar(require('@mysten/sui/utils'))),
571
+ ]);
572
+ const key = bcs
573
+ .struct('RequestKey', { owner: bcs.Address, requestId: bcs.u64() })
574
+ .serialize({ owner: normalizeSuiAddress(owner), requestId })
575
+ .toBytes();
576
+ return deriveObjectID(normalizeSuiAddress(vaultId), `${originalPackageId}::withdrawal::RequestKey`, key);
577
+ }
578
+ /**
579
+ * The vault reference for a backend row, or `null` when the row has no usable
580
+ * `sui_vault_metadata`.
581
+ * @internal
582
+ */
583
+ function suiVaultRefFromRow(row) {
584
+ const meta = row.sui_vault_metadata;
585
+ if (!meta?.share_coin_type || !meta.deposit_coin_type)
586
+ return null;
587
+ if (meta.network_name !== 'mainnet' && meta.network_name !== 'testnet') {
588
+ return null;
589
+ }
590
+ return {
591
+ vaultId: row.address,
592
+ assetCoinType: meta.deposit_coin_type,
593
+ shareCoinType: meta.share_coin_type,
594
+ network: meta.network_name,
595
+ };
596
+ }
597
+ /**
598
+ * Build the SDK's `IVault` for a Sui vault from its backend row and, when the
599
+ * read succeeded, its on-chain state. Without state, total assets fall back
600
+ * to the backend's `latest_reported_tvl`, supply to zero, and the vault is
601
+ * marked deposit-paused so no deposit is offered on unread state.
602
+ * @internal
603
+ */
604
+ function buildSuiVault(row, state) {
605
+ const meta = row.sui_vault_metadata;
606
+ const decimals = state?.decimals ?? meta?.deposit_token_decimals ?? 0;
607
+ const vault = (0, utils_1.buildBackendVault)(row, {
608
+ chainId: constants_1.SUI_CHAIN_ID,
609
+ version: 'sui-0',
610
+ name: row.vault_name || 'Sui Vault',
611
+ totalAssets: state
612
+ ? (0, core_1.toNormalizedBn)(state.totalAssets, decimals)
613
+ : (0, utils_1.backendTvlToNormalizedBn)(row.latest_reported_tvl, decimals),
614
+ totalSupply: (0, core_1.toNormalizedBn)(state?.totalSupply ?? 0n, decimals),
615
+ depositAssets: [
616
+ {
617
+ address: meta?.deposit_coin_type ?? '',
618
+ symbol: meta?.deposit_token_symbol ?? 'UNKNOWN',
619
+ decimals,
620
+ },
621
+ ],
622
+ receipt: {
623
+ address: meta?.share_coin_type ?? '',
624
+ symbol: row.receipt_token_symbol || 'UNKNOWN',
625
+ decimals,
626
+ },
627
+ isDepositPaused: !state || state.paused || !meta?.deposit_coin_type,
628
+ isWithdrawalPaused: state?.paused ?? false,
629
+ });
630
+ // u64::MAX is the contract's "no cap"; leave the field unset for it.
631
+ if (state && state.depositCap !== U64_MAX) {
632
+ vault.depositCap = (0, core_1.toNormalizedBn)(state.depositCap, decimals);
633
+ }
634
+ return vault;
635
+ }
636
+ /**
637
+ * Build a Sui vault's `IVault` from its backend row plus on-chain state.
638
+ *
639
+ * A failed read (unknown contract IDs, every endpoint down, an aborted
640
+ * simulation) does not fail the call: the vault is built from the backend row
641
+ * alone (see `buildSuiVault`) and a warning is logged.
642
+ *
643
+ * RPC cost: as {@link readSuiVaultState}.
644
+ *
645
+ * @param row - Backend `tokenized_vault` row with `sui_vault_metadata`.
646
+ * @param options - Vault options; `options.sui` carries the SDK's `sui` config.
647
+ * @returns The vault.
648
+ * @throws {@link UpshiftValidationError} (`INVALID_INPUT`) when the row has no
649
+ * usable `sui_vault_metadata` (e.g. a legacy Ember/Gamma Sui vault).
650
+ */
651
+ async function getSuiVault(row, options) {
652
+ const ref = suiVaultRefFromRow(row);
653
+ if (!ref) {
654
+ throw new core_1.UpshiftValidationError('INVALID_INPUT', 'getSuiVault: not an Upshift Sui vault — the backend row has no usable sui_vault_metadata.', { context: { vault: row.address } });
655
+ }
656
+ let state = null;
657
+ try {
658
+ state = await readSuiVaultState(ref, options?.sui);
659
+ }
660
+ catch (err) {
661
+ core_1.Logger.log.warn('getSuiVault', 'On-chain read failed — using backend values', {
662
+ vault: row.address,
663
+ network: ref.network,
664
+ error: err instanceof Error ? err.message : String(err),
665
+ });
666
+ }
667
+ return buildSuiVault(row, state);
668
+ }
669
+ //# sourceMappingURL=getters.js.map