@tbookdev/vault-sdk 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,4297 @@
1
+ import { AnchorProvider, Program } from '@coral-xyz/anchor';
2
+ import { PublicKey, SystemProgram, Connection, Keypair } from '@solana/web3.js';
3
+ import { createHash } from 'crypto';
4
+ import bs58 from 'bs58';
5
+ import BN from 'bn.js';
6
+ import { getAssociatedTokenAddress, TOKEN_PROGRAM_ID } from '@solana/spl-token';
7
+
8
+ // src/client.ts
9
+ var USDC_DECIMALS = 6;
10
+ var SHARE_DECIMALS = 6;
11
+ var ONE_USDC = 1e6;
12
+ var ONE_SHARE = 1e6;
13
+ var DEFAULT_CANCEL_FEE_BPS = 10;
14
+ var DEFAULT_RPC_TIMEOUT_MS = 3e4;
15
+ var DEFAULT_RPC = {
16
+ devnet: "https://api.devnet.solana.com",
17
+ "mainnet-beta": "https://api.mainnet-beta.solana.com"
18
+ };
19
+ function getDefaultRpcUrl(network) {
20
+ return DEFAULT_RPC[network];
21
+ }
22
+ var SHARE_PRICE_API_URL = "https://d5a6giwxrgpzqpxktgpgufpvtq0ltxgk.lambda-url.ap-southeast-1.on.aws/share-price";
23
+ var EXPLORER_BASE = "https://explorer.solana.com";
24
+ function explorerUrl(type, value, network) {
25
+ const cluster = network === "mainnet-beta" ? "" : `?cluster=${network}`;
26
+ return `${EXPLORER_BASE}/${type}/${value}${cluster}`;
27
+ }
28
+ var BUILT_IN_VAULTS = {
29
+ "devnet:rcUSDP": {
30
+ id: "rcUSDP",
31
+ programId: new PublicKey("BdeE87CsE9kvxJJcxaJqxNsC2nC6hFft3UBzcgAm5X1i"),
32
+ // Circle devnet USDC — must match the mint the on-chain vault was initialized with
33
+ usdcMint: new PublicKey("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"),
34
+ network: "devnet"
35
+ }
36
+ // mainnet config will be added when contract is deployed
37
+ // "mainnet-beta:rcUSDP": { ... },
38
+ };
39
+ function getVaultConfig(vaultId, network) {
40
+ return BUILT_IN_VAULTS[`${network}:${vaultId}`];
41
+ }
42
+ function listBuiltInVaults(network) {
43
+ return Object.values(BUILT_IN_VAULTS).filter((v) => v.network === network).map((v) => ({
44
+ id: v.id,
45
+ name: `${v.id} Vault`,
46
+ description: "RWA yield vault powered by TBook",
47
+ programId: v.programId.toBase58(),
48
+ network: v.network,
49
+ usdcMint: v.usdcMint.toBase58(),
50
+ shareToken: { symbol: v.id, decimals: SHARE_DECIMALS },
51
+ status: "active"
52
+ }));
53
+ }
54
+ var DEFAULT_VAULT_ID = "rcUSDP";
55
+
56
+ // src/errors.ts
57
+ var VaultSdkError = class extends Error {
58
+ /** Machine-readable error code. */
59
+ code;
60
+ /** Non-technical message suitable for displaying to end users. */
61
+ userFriendlyMessage;
62
+ constructor(code, message, userFriendlyMessage) {
63
+ super(message);
64
+ this.name = "VaultSdkError";
65
+ this.code = code;
66
+ this.userFriendlyMessage = userFriendlyMessage ?? "Something went wrong. Please try again.";
67
+ }
68
+ };
69
+ var VaultPausedError = class extends VaultSdkError {
70
+ constructor() {
71
+ super(
72
+ "VAULT_PAUSED",
73
+ "The vault is currently paused. Only claim operations are allowed.",
74
+ "The service is temporarily unavailable. Please try again later."
75
+ );
76
+ this.name = "VaultPausedError";
77
+ }
78
+ };
79
+ var MinDepositError = class extends VaultSdkError {
80
+ /** The minimum required deposit in USDC. */
81
+ minDeposit;
82
+ constructor(minDeposit) {
83
+ super(
84
+ "MIN_DEPOSIT",
85
+ `Minimum deposit is ${minDeposit} USDC.`,
86
+ `The deposit amount is too small. Minimum is ${minDeposit} USDC.`
87
+ );
88
+ this.name = "MinDepositError";
89
+ this.minDeposit = minDeposit;
90
+ }
91
+ };
92
+ var MaxEpochDepositError = class extends VaultSdkError {
93
+ maxEpochDeposit;
94
+ constructor(maxEpochDeposit) {
95
+ super(
96
+ "MAX_EPOCH_DEPOSIT",
97
+ `This deposit would exceed the epoch limit of ${maxEpochDeposit} USDC.`,
98
+ `The deposit amount exceeds the current limit of ${maxEpochDeposit} USDC.`
99
+ );
100
+ this.name = "MaxEpochDepositError";
101
+ this.maxEpochDeposit = maxEpochDeposit;
102
+ }
103
+ };
104
+ var EpochNotOpenError = class extends VaultSdkError {
105
+ constructor(epochType, status) {
106
+ super(
107
+ "EPOCH_NOT_OPEN",
108
+ `The ${epochType} epoch is currently "${status}", not "Open".`,
109
+ "This operation is not available right now. Please try again later."
110
+ );
111
+ this.name = "EpochNotOpenError";
112
+ }
113
+ };
114
+ var VaultNotFoundError = class extends VaultSdkError {
115
+ constructor() {
116
+ super(
117
+ "VAULT_NOT_FOUND",
118
+ "VaultMirror account not found on-chain. Is the program deployed?",
119
+ "The vault service is currently unreachable. Please try again later."
120
+ );
121
+ this.name = "VaultNotFoundError";
122
+ }
123
+ };
124
+ var UserAccountNotFoundError = class extends VaultSdkError {
125
+ constructor(pubkey) {
126
+ super(
127
+ "USER_NOT_FOUND",
128
+ `No vault user account found for ${pubkey}.`,
129
+ "No account found. Please make a deposit first to create your account."
130
+ );
131
+ this.name = "UserAccountNotFoundError";
132
+ }
133
+ };
134
+ var UnknownVaultError = class extends VaultSdkError {
135
+ constructor(vaultId) {
136
+ super(
137
+ "UNKNOWN_VAULT",
138
+ `Vault "${vaultId}" is not registered. Use listVaults() to see available vaults.`,
139
+ "The requested vault is not available."
140
+ );
141
+ this.name = "UnknownVaultError";
142
+ }
143
+ };
144
+ var SharePriceUnavailableError = class extends VaultSdkError {
145
+ constructor(reason) {
146
+ super(
147
+ "PRICE_UNAVAILABLE",
148
+ `Share price data is unavailable${reason ? `: ${reason}` : "."}`,
149
+ "Price information is temporarily unavailable. Please try again in a few minutes."
150
+ );
151
+ this.name = "SharePriceUnavailableError";
152
+ }
153
+ };
154
+ var NoPendingDepositError = class extends VaultSdkError {
155
+ constructor() {
156
+ super(
157
+ "NO_PENDING_DEPOSIT",
158
+ "No pending deposit to cancel. The user has no active deposit in the current epoch.",
159
+ "You don't have a pending deposit to cancel."
160
+ );
161
+ this.name = "NoPendingDepositError";
162
+ }
163
+ };
164
+ var RpcError = class extends VaultSdkError {
165
+ /** The underlying error from the RPC library. */
166
+ cause;
167
+ constructor(operation, cause) {
168
+ const msg = cause instanceof Error ? cause.message : String(cause);
169
+ super(
170
+ "RPC_ERROR",
171
+ `RPC call failed during ${operation}: ${msg}`,
172
+ "A network error occurred. Please check your connection and try again."
173
+ );
174
+ this.name = "RpcError";
175
+ this.cause = cause;
176
+ }
177
+ };
178
+ var RpcTimeoutError = class extends VaultSdkError {
179
+ /** The timeout duration in milliseconds. */
180
+ timeoutMs;
181
+ constructor(operation, timeoutMs) {
182
+ super(
183
+ "RPC_TIMEOUT",
184
+ `RPC call timed out after ${timeoutMs}ms during ${operation}`,
185
+ "The request timed out. Please try again."
186
+ );
187
+ this.name = "RpcTimeoutError";
188
+ this.timeoutMs = timeoutMs;
189
+ }
190
+ };
191
+ var TransactionExpiredError = class extends VaultSdkError {
192
+ /** The transaction signature. */
193
+ signature;
194
+ constructor(signature) {
195
+ super(
196
+ "TX_EXPIRED",
197
+ `Transaction ${signature} expired (blockhash no longer valid)`,
198
+ "The transaction expired. Please try again."
199
+ );
200
+ this.name = "TransactionExpiredError";
201
+ this.signature = signature;
202
+ }
203
+ };
204
+ var TransactionFailedError = class extends VaultSdkError {
205
+ /** The transaction signature. */
206
+ signature;
207
+ /** The on-chain error object. */
208
+ txError;
209
+ constructor(signature, txError) {
210
+ super(
211
+ "TX_FAILED",
212
+ `Transaction ${signature} failed on-chain: ${JSON.stringify(txError)}`,
213
+ "The transaction failed. Please try again or contact support."
214
+ );
215
+ this.name = "TransactionFailedError";
216
+ this.signature = signature;
217
+ this.txError = txError;
218
+ }
219
+ };
220
+ function getVaultMirrorPda(programId) {
221
+ return PublicKey.findProgramAddressSync(
222
+ [Buffer.from("vault_mirror")],
223
+ programId
224
+ );
225
+ }
226
+ function getUsdcVaultPda(vaultMirror, programId) {
227
+ return PublicKey.findProgramAddressSync(
228
+ [Buffer.from("usdc_vault"), vaultMirror.toBuffer()],
229
+ programId
230
+ );
231
+ }
232
+ function getUserPda(vaultMirror, user, programId) {
233
+ return PublicKey.findProgramAddressSync(
234
+ [Buffer.from("user"), vaultMirror.toBuffer(), user.toBuffer()],
235
+ programId
236
+ );
237
+ }
238
+ function getDepositEpochPda(vaultMirror, epoch, programId) {
239
+ return PublicKey.findProgramAddressSync(
240
+ [
241
+ Buffer.from("deposit_epoch"),
242
+ vaultMirror.toBuffer(),
243
+ epochToLeBytes(epoch)
244
+ ],
245
+ programId
246
+ );
247
+ }
248
+ function getRedeemEpochPda(vaultMirror, epoch, programId) {
249
+ return PublicKey.findProgramAddressSync(
250
+ [
251
+ Buffer.from("redeem_epoch"),
252
+ vaultMirror.toBuffer(),
253
+ epochToLeBytes(epoch)
254
+ ],
255
+ programId
256
+ );
257
+ }
258
+ function epochToLeBytes(epoch) {
259
+ return Buffer.from(
260
+ new Uint8Array(new BigUint64Array([BigInt(epoch)]).buffer)
261
+ );
262
+ }
263
+
264
+ // src/types.ts
265
+ var STATUS_MAP = {
266
+ 0: "Open",
267
+ 1: "Frozen",
268
+ 2: "Bridging",
269
+ 3: "Settled",
270
+ 4: "RolledBack"
271
+ };
272
+
273
+ // src/queries.ts
274
+ async function fetchVaultInfo(connection, programId, program) {
275
+ const [vaultMirrorPda] = getVaultMirrorPda(programId);
276
+ if (program) {
277
+ try {
278
+ return await fetchVaultInfoAnchor(connection, programId, program, vaultMirrorPda);
279
+ } catch {
280
+ }
281
+ }
282
+ return fetchVaultInfoRaw(connection, programId, vaultMirrorPda);
283
+ }
284
+ async function fetchVaultInfoAnchor(connection, programId, program, vaultMirrorPda) {
285
+ const v = await program.account.vaultMirror.fetch(vaultMirrorPda);
286
+ const [usdcVaultPda] = getUsdcVaultPda(vaultMirrorPda, programId);
287
+ const vaultUsdcBalance = await safeTokenBalance(connection, usdcVaultPda);
288
+ const currentDepositEpoch = v.currentDepositEpoch.toNumber();
289
+ const currentRedeemEpoch = v.currentRedeemEpoch.toNumber();
290
+ const vault = {
291
+ admin: v.authority.toString(),
292
+ curator: v.curator.toString(),
293
+ operator: v.operator.toString(),
294
+ totalShares: v.totalShares.toNumber() / 10 ** SHARE_DECIMALS,
295
+ currentDepositEpoch,
296
+ currentRedeemEpoch,
297
+ maxEpochDeposit: v.maxEpochDeposit.toNumber() / 10 ** USDC_DECIMALS,
298
+ minDeposit: v.minDeposit.toNumber() / 10 ** USDC_DECIMALS,
299
+ paused: v.paused,
300
+ usdcMint: v.usdcMint,
301
+ vaultUsdcBalance,
302
+ tvlEstimate: vaultUsdcBalance,
303
+ cancelFeeBps: DEFAULT_CANCEL_FEE_BPS
304
+ };
305
+ const depositEpoch = await fetchDepositEpochAnchor(program, programId, vaultMirrorPda, currentDepositEpoch);
306
+ const redeemEpoch = await fetchRedeemEpochAnchor(program, programId, vaultMirrorPda, currentRedeemEpoch);
307
+ return { vault, depositEpoch, redeemEpoch };
308
+ }
309
+ async function fetchVaultInfoRaw(connection, programId, vaultMirrorPda) {
310
+ let accountInfo;
311
+ try {
312
+ accountInfo = await connection.getAccountInfo(vaultMirrorPda);
313
+ } catch (err) {
314
+ throw new RpcError("fetchVaultInfo", err);
315
+ }
316
+ if (!accountInfo) {
317
+ return { vault: null, depositEpoch: null, redeemEpoch: null };
318
+ }
319
+ const data = accountInfo.data;
320
+ const o = 8;
321
+ const admin = new PublicKey(data.subarray(o, o + 32));
322
+ const curator = new PublicKey(data.subarray(o + 32, o + 64));
323
+ const operator = new PublicKey(data.subarray(o + 64, o + 96));
324
+ const usdcMintKey = new PublicKey(data.subarray(o + 96, o + 128));
325
+ const usdcVaultKey = new PublicKey(data.subarray(o + 128, o + 160));
326
+ const totalShares = Number(data.readBigUInt64LE(o + 160)) / 10 ** SHARE_DECIMALS;
327
+ const currentDepositEpoch = Number(data.readBigUInt64LE(o + 168));
328
+ const currentRedeemEpoch = Number(data.readBigUInt64LE(o + 176));
329
+ const maxEpochDeposit = Number(data.readBigUInt64LE(o + 184)) / 10 ** USDC_DECIMALS;
330
+ const minDeposit = Number(data.readBigUInt64LE(o + 192)) / 10 ** USDC_DECIMALS;
331
+ const paused = data[o + 200] !== 0;
332
+ const vaultUsdcBalance = await safeTokenBalance(connection, usdcVaultKey);
333
+ const vault = {
334
+ admin: admin.toString(),
335
+ curator: curator.toString(),
336
+ operator: operator.toString(),
337
+ totalShares,
338
+ currentDepositEpoch,
339
+ currentRedeemEpoch,
340
+ maxEpochDeposit,
341
+ minDeposit,
342
+ paused,
343
+ usdcMint: usdcMintKey,
344
+ vaultUsdcBalance,
345
+ tvlEstimate: vaultUsdcBalance,
346
+ cancelFeeBps: DEFAULT_CANCEL_FEE_BPS
347
+ };
348
+ const depositEpoch = await fetchDepositEpochRaw(connection, programId, getVaultMirrorPda(programId)[0], currentDepositEpoch);
349
+ const redeemEpoch = await fetchRedeemEpochRaw(connection, programId, getVaultMirrorPda(programId)[0], currentRedeemEpoch);
350
+ return { vault, depositEpoch, redeemEpoch };
351
+ }
352
+ async function fetchDepositEpochAnchor(program, programId, vaultMirrorPda, epoch) {
353
+ try {
354
+ const [pda] = getDepositEpochPda(vaultMirrorPda, epoch, programId);
355
+ const e = await program.account.depositEpoch.fetch(pda);
356
+ const statusKey = Object.keys(e.status)[0];
357
+ return {
358
+ epoch: e.epoch.toNumber(),
359
+ status: statusKey.charAt(0).toUpperCase() + statusKey.slice(1),
360
+ totalDepositUsdc: e.totalDepositUsdc.toNumber() / 10 ** USDC_DECIMALS,
361
+ depositUserCount: e.depositUserCount,
362
+ settledSharesMinted: e.settledSharesMinted.toNumber() / 10 ** SHARE_DECIMALS
363
+ };
364
+ } catch (err) {
365
+ if (err instanceof Error && (err.message.includes("Account does not exist") || err.message.includes("could not find account"))) {
366
+ return null;
367
+ }
368
+ throw err;
369
+ }
370
+ }
371
+ async function fetchRedeemEpochAnchor(program, programId, vaultMirrorPda, epoch) {
372
+ try {
373
+ const [pda] = getRedeemEpochPda(vaultMirrorPda, epoch, programId);
374
+ const e = await program.account.redeemEpoch.fetch(pda);
375
+ const statusKey = Object.keys(e.status)[0];
376
+ return {
377
+ epoch: e.epoch.toNumber(),
378
+ status: statusKey.charAt(0).toUpperCase() + statusKey.slice(1),
379
+ totalRedeemShares: e.totalRedeemShares.toNumber() / 10 ** SHARE_DECIMALS,
380
+ redeemUserCount: e.redeemUserCount,
381
+ settledUsdcForRedeems: e.settledUsdcForRedeems.toNumber() / 10 ** USDC_DECIMALS
382
+ };
383
+ } catch (err) {
384
+ if (err instanceof Error && (err.message.includes("Account does not exist") || err.message.includes("could not find account"))) {
385
+ return null;
386
+ }
387
+ throw err;
388
+ }
389
+ }
390
+ async function fetchDepositEpochRaw(connection, programId, vaultMirrorPda, epoch) {
391
+ try {
392
+ const [pda] = getDepositEpochPda(vaultMirrorPda, epoch, programId);
393
+ const info = await connection.getAccountInfo(pda);
394
+ if (!info) return null;
395
+ const d = info.data;
396
+ const eo = 8;
397
+ return {
398
+ epoch: Number(d.readBigUInt64LE(eo)),
399
+ status: STATUS_MAP[d[eo + 8]] || "Open",
400
+ totalDepositUsdc: Number(d.readBigUInt64LE(eo + 9)) / 10 ** USDC_DECIMALS,
401
+ depositUserCount: d.readUInt32LE(eo + 17),
402
+ settledSharesMinted: Number(d.readBigUInt64LE(eo + 21)) / 10 ** SHARE_DECIMALS
403
+ };
404
+ } catch (err) {
405
+ if (err instanceof Error && (err.message.includes("Account does not exist") || err.message.includes("could not find account"))) {
406
+ return null;
407
+ }
408
+ throw err;
409
+ }
410
+ }
411
+ async function fetchRedeemEpochRaw(connection, programId, vaultMirrorPda, epoch) {
412
+ try {
413
+ const [pda] = getRedeemEpochPda(vaultMirrorPda, epoch, programId);
414
+ const info = await connection.getAccountInfo(pda);
415
+ if (!info) return null;
416
+ const d = info.data;
417
+ const eo = 8;
418
+ return {
419
+ epoch: Number(d.readBigUInt64LE(eo)),
420
+ status: STATUS_MAP[d[eo + 8]] || "Open",
421
+ totalRedeemShares: Number(d.readBigUInt64LE(eo + 9)) / 10 ** SHARE_DECIMALS,
422
+ redeemUserCount: d.readUInt32LE(eo + 17),
423
+ settledUsdcForRedeems: Number(d.readBigUInt64LE(eo + 21)) / 10 ** USDC_DECIMALS
424
+ };
425
+ } catch (err) {
426
+ if (err instanceof Error && (err.message.includes("Account does not exist") || err.message.includes("could not find account"))) {
427
+ return null;
428
+ }
429
+ throw err;
430
+ }
431
+ }
432
+ async function fetchUserAccount(connection, programId, user, program) {
433
+ const [vaultMirrorPda] = getVaultMirrorPda(programId);
434
+ const [userPda] = getUserPda(vaultMirrorPda, user, programId);
435
+ if (!program) {
436
+ return null;
437
+ }
438
+ let userAccount;
439
+ try {
440
+ userAccount = await program.account.userAccount.fetch(userPda);
441
+ } catch (err) {
442
+ if (err instanceof Error && (err.message.includes("Account does not exist") || err.message.includes("could not find account"))) {
443
+ return null;
444
+ }
445
+ throw err;
446
+ }
447
+ const shares = userAccount.shares.toNumber() / 10 ** SHARE_DECIMALS;
448
+ const pendingDepositUsdc = userAccount.pendingDepositUsdc.toNumber() / 10 ** USDC_DECIMALS;
449
+ const pendingDepositEpoch = userAccount.pendingDepositEpoch.toNumber();
450
+ const pendingRedeemShares = userAccount.pendingRedeemShares.toNumber() / 10 ** SHARE_DECIMALS;
451
+ const pendingRedeemEpoch = userAccount.pendingRedeemEpoch.toNumber();
452
+ const claimableUsdc = userAccount.claimableUsdc.toNumber() / 10 ** USDC_DECIMALS;
453
+ const effective = await computeEffectiveBalance(
454
+ connection,
455
+ programId,
456
+ program,
457
+ vaultMirrorPda,
458
+ {
459
+ shares: BigInt(userAccount.shares.toString()),
460
+ claimableUsdc: BigInt(userAccount.claimableUsdc.toString()),
461
+ pendingDepositUsdc: BigInt(userAccount.pendingDepositUsdc.toString()),
462
+ pendingDepositEpoch,
463
+ pendingRedeemShares: BigInt(userAccount.pendingRedeemShares.toString()),
464
+ pendingRedeemEpoch
465
+ }
466
+ );
467
+ return {
468
+ shares,
469
+ pendingDepositUsdc,
470
+ pendingDepositEpoch,
471
+ pendingRedeemShares,
472
+ pendingRedeemEpoch,
473
+ claimableUsdc,
474
+ effectiveShares: Number(effective.shares) / 10 ** SHARE_DECIMALS,
475
+ effectiveClaimableUsdc: Number(effective.claimableUsdc) / 10 ** USDC_DECIMALS
476
+ };
477
+ }
478
+ async function computeEffectiveBalance(connection, programId, program, vaultMirrorPda, raw) {
479
+ let shares = raw.shares;
480
+ let claimableUsdc = raw.claimableUsdc;
481
+ let vaultState;
482
+ try {
483
+ vaultState = await program.account.vaultMirror.fetch(vaultMirrorPda);
484
+ } catch (err) {
485
+ if (err instanceof Error && (err.message.includes("Account does not exist") || err.message.includes("could not find account"))) {
486
+ return { shares, claimableUsdc };
487
+ }
488
+ throw err;
489
+ }
490
+ const currentDepositEpoch = vaultState.currentDepositEpoch.toNumber();
491
+ const currentRedeemEpoch = vaultState.currentRedeemEpoch.toNumber();
492
+ if (raw.pendingDepositUsdc > 0n && raw.pendingDepositEpoch !== currentDepositEpoch) {
493
+ try {
494
+ const [pda] = getDepositEpochPda(vaultMirrorPda, raw.pendingDepositEpoch, programId);
495
+ const epoch = await program.account.depositEpoch.fetch(pda);
496
+ const status = epoch.status;
497
+ if ("settled" in status) {
498
+ const totalDeposit = BigInt(epoch.totalDepositUsdc.toString());
499
+ const sharesMinted = BigInt(epoch.settledSharesMinted.toString());
500
+ if (totalDeposit > 0n) {
501
+ shares += raw.pendingDepositUsdc * sharesMinted / totalDeposit;
502
+ }
503
+ } else if ("rolledBack" in status) {
504
+ claimableUsdc += raw.pendingDepositUsdc;
505
+ }
506
+ } catch (err) {
507
+ if (err instanceof Error && (err.message.includes("Account does not exist") || err.message.includes("could not find account"))) ; else {
508
+ throw err;
509
+ }
510
+ }
511
+ }
512
+ if (raw.pendingRedeemShares > 0n && raw.pendingRedeemEpoch !== currentRedeemEpoch) {
513
+ try {
514
+ const [pda] = getRedeemEpochPda(vaultMirrorPda, raw.pendingRedeemEpoch, programId);
515
+ const epoch = await program.account.redeemEpoch.fetch(pda);
516
+ const status = epoch.status;
517
+ if ("settled" in status) {
518
+ const totalRedeem = BigInt(epoch.totalRedeemShares.toString());
519
+ const usdcForRedeems = BigInt(epoch.settledUsdcForRedeems.toString());
520
+ if (totalRedeem > 0n) {
521
+ claimableUsdc += raw.pendingRedeemShares * usdcForRedeems / totalRedeem;
522
+ }
523
+ } else if ("rolledBack" in status) {
524
+ shares += raw.pendingRedeemShares;
525
+ }
526
+ } catch (err) {
527
+ if (err instanceof Error && (err.message.includes("Account does not exist") || err.message.includes("could not find account"))) ; else {
528
+ throw err;
529
+ }
530
+ }
531
+ }
532
+ return { shares, claimableUsdc };
533
+ }
534
+ async function getLazySettleRemainingAccounts(program, programId, vaultMirrorPda, userPda) {
535
+ try {
536
+ const userAccount = await program.account.userAccount.fetch(userPda);
537
+ const pendingDepositEpoch = userAccount.pendingDepositEpoch.toNumber();
538
+ const pendingRedeemEpoch = userAccount.pendingRedeemEpoch.toNumber();
539
+ const hasPendingDeposit = userAccount.pendingDepositUsdc.toNumber() > 0;
540
+ const hasPendingRedeem = userAccount.pendingRedeemShares.toNumber() > 0;
541
+ if (!hasPendingDeposit && !hasPendingRedeem) return [];
542
+ const accounts = [];
543
+ if (hasPendingDeposit) {
544
+ const [pda] = getDepositEpochPda(vaultMirrorPda, pendingDepositEpoch, programId);
545
+ accounts.push({ pubkey: pda, isWritable: false, isSigner: false });
546
+ }
547
+ if (hasPendingRedeem) {
548
+ const [pda] = getRedeemEpochPda(vaultMirrorPda, pendingRedeemEpoch, programId);
549
+ accounts.push({ pubkey: pda, isWritable: false, isSigner: false });
550
+ }
551
+ return accounts;
552
+ } catch (err) {
553
+ if (err instanceof Error && (err.message.includes("Account does not exist") || err.message.includes("could not find account"))) {
554
+ return [];
555
+ }
556
+ throw err;
557
+ }
558
+ }
559
+ async function safeTokenBalance(connection, tokenAccount) {
560
+ try {
561
+ const info = await connection.getTokenAccountBalance(tokenAccount);
562
+ return info.value.uiAmount ?? 0;
563
+ } catch {
564
+ return 0;
565
+ }
566
+ }
567
+
568
+ // src/share-price-cache.ts
569
+ var DEFAULT_MAX_STALE_MS = 6e5;
570
+ var DEFAULT_FAILURE_THRESHOLD = 3;
571
+ var DEFAULT_CIRCUIT_RESET_MS = 6e4;
572
+ var SharePriceCache = class {
573
+ /** Maximum age (in ms) before cached data is considered stale. */
574
+ maxStaleMs;
575
+ data = null;
576
+ storedAt = 0;
577
+ // Circuit breaker state
578
+ failureCount = 0;
579
+ failureThreshold;
580
+ circuitOpenedAt = 0;
581
+ circuitResetMs;
582
+ constructor(opts) {
583
+ this.maxStaleMs = opts?.maxStaleMs ?? DEFAULT_MAX_STALE_MS;
584
+ this.failureThreshold = opts?.failureThreshold ?? DEFAULT_FAILURE_THRESHOLD;
585
+ this.circuitResetMs = opts?.circuitResetMs ?? DEFAULT_CIRCUIT_RESET_MS;
586
+ }
587
+ /**
588
+ * Return cached data if it is fresher than `maxAge` milliseconds.
589
+ * Returns `null` if the cache is empty or the data is too old.
590
+ *
591
+ * @param maxAge - Override the default freshness window (pass `Infinity` for stale-ok)
592
+ */
593
+ get(maxAge) {
594
+ if (!this.data) return null;
595
+ const age = Date.now() - this.storedAt;
596
+ const threshold = maxAge ?? this.maxStaleMs;
597
+ if (age > threshold) return null;
598
+ return this.data;
599
+ }
600
+ /** Store fresh share price data. */
601
+ set(data) {
602
+ this.data = data;
603
+ this.storedAt = Date.now();
604
+ }
605
+ /** Whether the circuit breaker is currently open (API calls should be skipped). */
606
+ isCircuitOpen() {
607
+ if (this.failureCount < this.failureThreshold) return false;
608
+ if (Date.now() - this.circuitOpenedAt >= this.circuitResetMs) {
609
+ return false;
610
+ }
611
+ return true;
612
+ }
613
+ /** Record a failed API call. Opens the circuit after the threshold is reached. */
614
+ recordFailure() {
615
+ this.failureCount += 1;
616
+ if (this.failureCount >= this.failureThreshold) {
617
+ this.circuitOpenedAt = Date.now();
618
+ }
619
+ }
620
+ /** Record a successful API call. Resets the circuit breaker. */
621
+ recordSuccess() {
622
+ this.failureCount = 0;
623
+ this.circuitOpenedAt = 0;
624
+ }
625
+ };
626
+ var sharePriceCache = new SharePriceCache();
627
+
628
+ // src/share-price.ts
629
+ var API_FETCH_TIMEOUT_MS = 5e3;
630
+ async function fetchSharePrice(options) {
631
+ const opts = typeof options === "string" ? { apiUrl: options } : options ?? {};
632
+ const apiUrl = opts.apiUrl ?? SHARE_PRICE_API_URL;
633
+ const cached = sharePriceCache.get();
634
+ if (cached) {
635
+ return { ...cached, source: "cache", stale: false };
636
+ }
637
+ if (!sharePriceCache.isCircuitOpen()) {
638
+ try {
639
+ const data = await fetchFromApi(apiUrl);
640
+ sharePriceCache.set(data);
641
+ sharePriceCache.recordSuccess();
642
+ return { ...data, source: "api" };
643
+ } catch {
644
+ sharePriceCache.recordFailure();
645
+ }
646
+ }
647
+ const stale = sharePriceCache.get(Infinity);
648
+ if (stale) {
649
+ return { ...stale, source: "cache", stale: true };
650
+ }
651
+ throw new SharePriceUnavailableError("All price sources exhausted");
652
+ }
653
+ async function fetchFromApi(apiUrl) {
654
+ const controller = new AbortController();
655
+ const timer = setTimeout(() => controller.abort(), API_FETCH_TIMEOUT_MS);
656
+ let res;
657
+ try {
658
+ res = await fetch(apiUrl, { signal: controller.signal });
659
+ } catch (err) {
660
+ throw new SharePriceUnavailableError(
661
+ err instanceof Error ? err.message : "Network error"
662
+ );
663
+ } finally {
664
+ clearTimeout(timer);
665
+ }
666
+ if (!res.ok) {
667
+ throw new SharePriceUnavailableError(`API returned HTTP ${res.status}`);
668
+ }
669
+ let data;
670
+ try {
671
+ data = await res.json();
672
+ } catch {
673
+ throw new SharePriceUnavailableError("Invalid JSON response");
674
+ }
675
+ if (!data.success || !data.sharePrice) {
676
+ throw new SharePriceUnavailableError("Missing sharePrice in response");
677
+ }
678
+ const priceNum = parseFloat(data.sharePrice);
679
+ if (!Number.isFinite(priceNum) || priceNum <= 0) {
680
+ throw new SharePriceUnavailableError(
681
+ `Invalid sharePrice value: ${data.sharePrice}`
682
+ );
683
+ }
684
+ return {
685
+ price: data.sharePrice,
686
+ priceNum,
687
+ apy: data.apy || "0",
688
+ date: data.date || "",
689
+ fetchedAt: data.fetchedAt || (/* @__PURE__ */ new Date()).toISOString()
690
+ };
691
+ }
692
+ function anchorDiscriminator(name) {
693
+ return createHash("sha256").update(`global:${name}`).digest().subarray(0, 8);
694
+ }
695
+ var DISCRIMINATORS = {
696
+ deposit: {
697
+ disc: anchorDiscriminator("deposit"),
698
+ type: "deposit",
699
+ label: "Deposit"
700
+ },
701
+ request_redeem: {
702
+ disc: anchorDiscriminator("request_redeem"),
703
+ type: "redeem",
704
+ label: "Request Redeem"
705
+ },
706
+ claim: {
707
+ disc: anchorDiscriminator("claim"),
708
+ type: "claim",
709
+ label: "Claim"
710
+ },
711
+ cancel_deposit: {
712
+ disc: anchorDiscriminator("cancel_deposit"),
713
+ type: "cancel_deposit",
714
+ label: "Cancel Deposit"
715
+ }
716
+ };
717
+ var DISCRIMINATOR_ENTRIES = Object.values(DISCRIMINATORS);
718
+ function classifyInstruction(ixData) {
719
+ if (ixData.length < 8) {
720
+ return { type: "unknown", label: "Unknown" };
721
+ }
722
+ const prefix = Buffer.from(ixData.subarray(0, 8));
723
+ for (const entry of DISCRIMINATOR_ENTRIES) {
724
+ if (prefix.equals(entry.disc)) {
725
+ return { type: entry.type, label: entry.label };
726
+ }
727
+ }
728
+ return { type: "unknown", label: "Unknown" };
729
+ }
730
+ async function fetchTransactionHistory(connection, userPda, programId, options) {
731
+ const limit = options?.limit ?? 20;
732
+ const signatures = await connection.getSignaturesForAddress(userPda, {
733
+ limit,
734
+ before: options?.before
735
+ });
736
+ if (signatures.length === 0) {
737
+ return [];
738
+ }
739
+ const sigStrings = signatures.map((s) => s.signature);
740
+ const parsedTxs = await connection.getParsedTransactions(sigStrings, {
741
+ maxSupportedTransactionVersion: 0
742
+ });
743
+ return signatures.map((sig, index) => {
744
+ const parsed = parsedTxs[index];
745
+ let type = "unknown";
746
+ let description = "Vault transaction";
747
+ if (parsed?.transaction?.message?.instructions) {
748
+ for (const ix of parsed.transaction.message.instructions) {
749
+ if ("data" in ix && ix.programId.equals(programId)) {
750
+ try {
751
+ const data = Buffer.from(bs58.decode(ix.data));
752
+ const classified = classifyInstruction(data);
753
+ type = classified.type;
754
+ description = classified.label;
755
+ } catch {
756
+ }
757
+ break;
758
+ }
759
+ }
760
+ if (type === "unknown" && parsed.meta?.innerInstructions) {
761
+ for (const inner of parsed.meta.innerInstructions) {
762
+ for (const ix of inner.instructions) {
763
+ if ("data" in ix && ix.programId.equals(programId)) {
764
+ try {
765
+ const data = Buffer.from(bs58.decode(ix.data));
766
+ const classified = classifyInstruction(data);
767
+ type = classified.type;
768
+ description = classified.label;
769
+ } catch {
770
+ }
771
+ if (type !== "unknown") break;
772
+ }
773
+ }
774
+ if (type !== "unknown") break;
775
+ }
776
+ }
777
+ }
778
+ if (sig.err) {
779
+ description = `Failed: ${description}`;
780
+ }
781
+ return {
782
+ signature: sig.signature,
783
+ type,
784
+ timestamp: sig.blockTime ?? 0,
785
+ slot: sig.slot,
786
+ success: sig.err === null,
787
+ description
788
+ };
789
+ });
790
+ }
791
+
792
+ // src/rpc-utils.ts
793
+ function sleep(ms) {
794
+ return new Promise((resolve) => setTimeout(resolve, ms));
795
+ }
796
+ async function withTimeout(promise, ms, label) {
797
+ if (ms <= 0 || !Number.isFinite(ms)) return promise;
798
+ const controller = new AbortController();
799
+ const timer = setTimeout(() => controller.abort(), ms);
800
+ try {
801
+ const result = await Promise.race([
802
+ promise,
803
+ new Promise((_, reject) => {
804
+ controller.signal.addEventListener(
805
+ "abort",
806
+ () => reject(new RpcTimeoutError(label, ms))
807
+ );
808
+ })
809
+ ]);
810
+ return result;
811
+ } finally {
812
+ clearTimeout(timer);
813
+ }
814
+ }
815
+ async function confirmTransactionWithRetry(opts) {
816
+ const {
817
+ connection,
818
+ signature,
819
+ blockhash: _blockhash,
820
+ lastValidBlockHeight,
821
+ commitment = "confirmed",
822
+ timeoutMs = 6e4,
823
+ initialIntervalMs = 2e3
824
+ } = opts;
825
+ const COMMITMENT_LEVELS = ["processed", "confirmed", "finalized"];
826
+ const desiredLevel = COMMITMENT_LEVELS.indexOf(commitment);
827
+ const start = Date.now();
828
+ let interval = initialIntervalMs;
829
+ const MAX_INTERVAL = 5e3;
830
+ while (Date.now() - start < timeoutMs) {
831
+ try {
832
+ const blockHeight = await connection.getBlockHeight();
833
+ if (blockHeight > lastValidBlockHeight) {
834
+ throw new TransactionExpiredError(signature);
835
+ }
836
+ } catch (err) {
837
+ if (err instanceof TransactionExpiredError) throw err;
838
+ }
839
+ try {
840
+ const { value } = await connection.getSignatureStatuses([signature]);
841
+ const status = value?.[0];
842
+ if (status?.err) {
843
+ throw new TransactionFailedError(signature, status.err);
844
+ }
845
+ if (status?.confirmationStatus) {
846
+ const actualLevel = COMMITMENT_LEVELS.indexOf(
847
+ status.confirmationStatus
848
+ );
849
+ if (actualLevel >= desiredLevel) {
850
+ return;
851
+ }
852
+ }
853
+ } catch (err) {
854
+ if (err instanceof TransactionFailedError) throw err;
855
+ }
856
+ await sleep(interval);
857
+ interval = Math.min(interval * 1.5, MAX_INTERVAL);
858
+ }
859
+ throw new RpcTimeoutError("confirmTransaction", timeoutMs);
860
+ }
861
+
862
+ // src/transactions.ts
863
+ async function finalizeTx(tx, program, feePayer, timeoutMs = DEFAULT_RPC_TIMEOUT_MS) {
864
+ const connection = program.provider.connection;
865
+ const { blockhash, lastValidBlockHeight } = await withTimeout(
866
+ connection.getLatestBlockhash(),
867
+ timeoutMs,
868
+ "getLatestBlockhash"
869
+ );
870
+ tx.recentBlockhash = blockhash;
871
+ tx.lastValidBlockHeight = lastValidBlockHeight;
872
+ tx.feePayer = feePayer;
873
+ return {
874
+ transaction: tx,
875
+ serialized: bs58.encode(tx.serialize({ requireAllSignatures: false })),
876
+ blockhash,
877
+ lastValidBlockHeight
878
+ };
879
+ }
880
+ function usdcToRaw(amount) {
881
+ const str = amount.toFixed(USDC_DECIMALS);
882
+ const [whole, frac = ""] = str.split(".");
883
+ const raw = whole + frac.padEnd(USDC_DECIMALS, "0").slice(0, USDC_DECIMALS);
884
+ return new BN(raw);
885
+ }
886
+ function sharesToRaw(amount) {
887
+ const str = amount.toFixed(SHARE_DECIMALS);
888
+ const [whole, frac = ""] = str.split(".");
889
+ const raw = whole + frac.padEnd(SHARE_DECIMALS, "0").slice(0, SHARE_DECIMALS);
890
+ return new BN(raw);
891
+ }
892
+ async function buildDepositTx(program, params, options) {
893
+ const { user, amountUsdc } = params;
894
+ const programId = program.programId;
895
+ const [vaultMirrorPda] = getVaultMirrorPda(programId);
896
+ const [usdcVaultPda] = getUsdcVaultPda(vaultMirrorPda, programId);
897
+ const [userPda] = getUserPda(vaultMirrorPda, user, programId);
898
+ let vaultMirrorAccount;
899
+ try {
900
+ vaultMirrorAccount = await program.account.vaultMirror.fetch(vaultMirrorPda);
901
+ } catch (err) {
902
+ throw new RpcError("buildDepositTx:fetchVaultMirror", err);
903
+ }
904
+ if (vaultMirrorAccount.paused) {
905
+ throw new VaultPausedError();
906
+ }
907
+ const minDeposit = vaultMirrorAccount.minDeposit.toNumber() / 10 ** USDC_DECIMALS;
908
+ if (amountUsdc < minDeposit) {
909
+ throw new MinDepositError(minDeposit);
910
+ }
911
+ const currentDepositEpoch = vaultMirrorAccount.currentDepositEpoch.toNumber();
912
+ vaultMirrorAccount.currentRedeemEpoch.toNumber();
913
+ const [depositEpochPda] = getDepositEpochPda(vaultMirrorPda, currentDepositEpoch, programId);
914
+ try {
915
+ const epochAccount = await program.account.depositEpoch.fetch(depositEpochPda);
916
+ if (!("open" in epochAccount.status)) {
917
+ const statusKey = Object.keys(epochAccount.status)[0] || "unknown";
918
+ throw new EpochNotOpenError("deposit", statusKey);
919
+ }
920
+ } catch (err) {
921
+ if (err instanceof EpochNotOpenError) throw err;
922
+ }
923
+ const usdcMint = vaultMirrorAccount.usdcMint;
924
+ const userUsdc = await getAssociatedTokenAddress(usdcMint, user, true);
925
+ const amountBn = usdcToRaw(amountUsdc);
926
+ const remainingAccounts = await getLazySettleRemainingAccounts(
927
+ program,
928
+ programId,
929
+ vaultMirrorPda,
930
+ userPda
931
+ );
932
+ const tx = await program.methods.deposit(amountBn).accounts({
933
+ user,
934
+ vaultMirror: vaultMirrorPda,
935
+ userAccount: userPda,
936
+ depositEpoch: depositEpochPda,
937
+ userUsdc,
938
+ usdcVault: usdcVaultPda,
939
+ usdcMint,
940
+ tokenProgram: TOKEN_PROGRAM_ID,
941
+ systemProgram: SystemProgram.programId
942
+ }).remainingAccounts(remainingAccounts).transaction();
943
+ const finalized = await finalizeTx(tx, program, user, options?.timeoutMs);
944
+ return {
945
+ ...finalized,
946
+ message: `Deposit ${amountUsdc} USDC into TBook Vault (Epoch #${currentDepositEpoch})`
947
+ };
948
+ }
949
+ async function buildRedeemTx(program, params, options) {
950
+ const { user, shares } = params;
951
+ const programId = program.programId;
952
+ const [vaultMirrorPda] = getVaultMirrorPda(programId);
953
+ const [userPda] = getUserPda(vaultMirrorPda, user, programId);
954
+ let vaultMirrorAccount;
955
+ try {
956
+ vaultMirrorAccount = await program.account.vaultMirror.fetch(vaultMirrorPda);
957
+ } catch (err) {
958
+ throw new RpcError("buildRedeemTx:fetchVaultMirror", err);
959
+ }
960
+ if (vaultMirrorAccount.paused) {
961
+ throw new VaultPausedError();
962
+ }
963
+ vaultMirrorAccount.currentDepositEpoch.toNumber();
964
+ const currentRedeemEpoch = vaultMirrorAccount.currentRedeemEpoch.toNumber();
965
+ const [redeemEpochPda] = getRedeemEpochPda(vaultMirrorPda, currentRedeemEpoch, programId);
966
+ try {
967
+ const epochAccount = await program.account.redeemEpoch.fetch(redeemEpochPda);
968
+ if (!("open" in epochAccount.status)) {
969
+ const statusKey = Object.keys(epochAccount.status)[0] || "unknown";
970
+ throw new EpochNotOpenError("redeem", statusKey);
971
+ }
972
+ } catch (err) {
973
+ if (err instanceof EpochNotOpenError) throw err;
974
+ }
975
+ const sharesBn = sharesToRaw(shares);
976
+ const remainingAccounts = await getLazySettleRemainingAccounts(
977
+ program,
978
+ programId,
979
+ vaultMirrorPda,
980
+ userPda
981
+ );
982
+ const tx = await program.methods.requestRedeem(sharesBn).accounts({
983
+ user,
984
+ vaultMirror: vaultMirrorPda,
985
+ userAccount: userPda,
986
+ redeemEpoch: redeemEpochPda,
987
+ systemProgram: SystemProgram.programId
988
+ }).remainingAccounts(remainingAccounts).transaction();
989
+ const finalized = await finalizeTx(tx, program, user, options?.timeoutMs);
990
+ return {
991
+ ...finalized,
992
+ message: `Request redeem of ${shares} vault shares (Epoch #${currentRedeemEpoch})`
993
+ };
994
+ }
995
+ async function buildClaimTx(program, params, options) {
996
+ const { user } = params;
997
+ const programId = program.programId;
998
+ const [vaultMirrorPda] = getVaultMirrorPda(programId);
999
+ const [usdcVaultPda] = getUsdcVaultPda(vaultMirrorPda, programId);
1000
+ const [userPda] = getUserPda(vaultMirrorPda, user, programId);
1001
+ let vaultMirrorAccount;
1002
+ try {
1003
+ vaultMirrorAccount = await program.account.vaultMirror.fetch(vaultMirrorPda);
1004
+ } catch (err) {
1005
+ throw new RpcError("buildClaimTx:fetchVaultMirror", err);
1006
+ }
1007
+ const usdcMint = vaultMirrorAccount.usdcMint;
1008
+ const userUsdc = await getAssociatedTokenAddress(usdcMint, user, true);
1009
+ const remainingAccounts = await getLazySettleRemainingAccounts(
1010
+ program,
1011
+ programId,
1012
+ vaultMirrorPda,
1013
+ userPda
1014
+ );
1015
+ const tx = await program.methods.claim().accounts({
1016
+ user,
1017
+ vaultMirror: vaultMirrorPda,
1018
+ userAccount: userPda,
1019
+ userUsdc,
1020
+ usdcVault: usdcVaultPda,
1021
+ tokenProgram: TOKEN_PROGRAM_ID
1022
+ }).remainingAccounts(remainingAccounts).transaction();
1023
+ const finalized = await finalizeTx(tx, program, user, options?.timeoutMs);
1024
+ return {
1025
+ ...finalized,
1026
+ message: "Claim USDC from TBook Vault"
1027
+ };
1028
+ }
1029
+ async function buildCancelDepositTx(program, params, options) {
1030
+ const { user } = params;
1031
+ const programId = program.programId;
1032
+ const [vaultMirrorPda] = getVaultMirrorPda(programId);
1033
+ const [usdcVaultPda] = getUsdcVaultPda(vaultMirrorPda, programId);
1034
+ const [userPda] = getUserPda(vaultMirrorPda, user, programId);
1035
+ let vaultMirrorAccount;
1036
+ try {
1037
+ vaultMirrorAccount = await program.account.vaultMirror.fetch(vaultMirrorPda);
1038
+ } catch (err) {
1039
+ throw new RpcError("buildCancelDepositTx:fetchVaultMirror", err);
1040
+ }
1041
+ const usdcMint = vaultMirrorAccount.usdcMint;
1042
+ const userUsdc = await getAssociatedTokenAddress(usdcMint, user, true);
1043
+ let userAccount;
1044
+ try {
1045
+ userAccount = await program.account.userAccount.fetchNullable(userPda);
1046
+ } catch (err) {
1047
+ throw new RpcError("buildCancelDepositTx:fetchUserAccount", err);
1048
+ }
1049
+ if (!userAccount) {
1050
+ throw new NoPendingDepositError();
1051
+ }
1052
+ if (userAccount.pendingDepositUsdc && userAccount.pendingDepositUsdc.toNumber() === 0) {
1053
+ throw new NoPendingDepositError();
1054
+ }
1055
+ const depositEpochNum = userAccount.pendingDepositEpoch.toNumber();
1056
+ const [depositEpochPda] = getDepositEpochPda(vaultMirrorPda, depositEpochNum, programId);
1057
+ const remainingAccounts = await getLazySettleRemainingAccounts(
1058
+ program,
1059
+ programId,
1060
+ vaultMirrorPda,
1061
+ userPda
1062
+ );
1063
+ const tx = await program.methods.cancelDeposit().accounts({
1064
+ user,
1065
+ vaultMirror: vaultMirrorPda,
1066
+ userAccount: userPda,
1067
+ depositEpoch: depositEpochPda,
1068
+ userUsdc,
1069
+ usdcVault: usdcVaultPda,
1070
+ tokenProgram: TOKEN_PROGRAM_ID
1071
+ }).remainingAccounts(remainingAccounts).transaction();
1072
+ const finalized = await finalizeTx(tx, program, user, options?.timeoutMs);
1073
+ return {
1074
+ ...finalized,
1075
+ message: "Cancel pending deposit (0.1% fee applies)"
1076
+ };
1077
+ }
1078
+
1079
+ // src/idl/vault-gateway-devnet.json
1080
+ var vault_gateway_devnet_default = {
1081
+ address: "BdeE87CsE9kvxJJcxaJqxNsC2nC6hFft3UBzcgAm5X1i",
1082
+ metadata: {
1083
+ name: "vault_gateway",
1084
+ version: "0.1.0",
1085
+ spec: "0.1.0",
1086
+ description: "Solana Vault Gateway - Cross-chain entry point for TBook Vault investment"
1087
+ },
1088
+ instructions: [
1089
+ {
1090
+ name: "cancel_deposit",
1091
+ docs: [
1092
+ "Cancel a pending deposit in the current open deposit epoch (with fee)."
1093
+ ],
1094
+ discriminator: [
1095
+ 207,
1096
+ 37,
1097
+ 219,
1098
+ 229,
1099
+ 183,
1100
+ 50,
1101
+ 54,
1102
+ 245
1103
+ ],
1104
+ accounts: [
1105
+ {
1106
+ name: "user",
1107
+ writable: true,
1108
+ signer: true
1109
+ },
1110
+ {
1111
+ name: "vault_mirror",
1112
+ pda: {
1113
+ seeds: [
1114
+ {
1115
+ kind: "const",
1116
+ value: [
1117
+ 118,
1118
+ 97,
1119
+ 117,
1120
+ 108,
1121
+ 116,
1122
+ 95,
1123
+ 109,
1124
+ 105,
1125
+ 114,
1126
+ 114,
1127
+ 111,
1128
+ 114
1129
+ ]
1130
+ }
1131
+ ]
1132
+ }
1133
+ },
1134
+ {
1135
+ name: "user_account",
1136
+ writable: true,
1137
+ pda: {
1138
+ seeds: [
1139
+ {
1140
+ kind: "const",
1141
+ value: [
1142
+ 117,
1143
+ 115,
1144
+ 101,
1145
+ 114
1146
+ ]
1147
+ },
1148
+ {
1149
+ kind: "account",
1150
+ path: "vault_mirror"
1151
+ },
1152
+ {
1153
+ kind: "account",
1154
+ path: "user"
1155
+ }
1156
+ ]
1157
+ }
1158
+ },
1159
+ {
1160
+ name: "deposit_epoch",
1161
+ docs: [
1162
+ "Current open deposit epoch"
1163
+ ],
1164
+ writable: true,
1165
+ pda: {
1166
+ seeds: [
1167
+ {
1168
+ kind: "const",
1169
+ value: [
1170
+ 100,
1171
+ 101,
1172
+ 112,
1173
+ 111,
1174
+ 115,
1175
+ 105,
1176
+ 116,
1177
+ 95,
1178
+ 101,
1179
+ 112,
1180
+ 111,
1181
+ 99,
1182
+ 104
1183
+ ]
1184
+ },
1185
+ {
1186
+ kind: "account",
1187
+ path: "vault_mirror"
1188
+ },
1189
+ {
1190
+ kind: "account",
1191
+ path: "vault_mirror.current_deposit_epoch",
1192
+ account: "VaultMirror"
1193
+ }
1194
+ ]
1195
+ }
1196
+ },
1197
+ {
1198
+ name: "user_usdc",
1199
+ writable: true
1200
+ },
1201
+ {
1202
+ name: "usdc_vault",
1203
+ writable: true
1204
+ },
1205
+ {
1206
+ name: "token_program",
1207
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
1208
+ }
1209
+ ],
1210
+ args: []
1211
+ },
1212
+ {
1213
+ name: "claim",
1214
+ docs: [
1215
+ "Claim available USDC (works even when paused)."
1216
+ ],
1217
+ discriminator: [
1218
+ 62,
1219
+ 198,
1220
+ 214,
1221
+ 193,
1222
+ 213,
1223
+ 159,
1224
+ 108,
1225
+ 210
1226
+ ],
1227
+ accounts: [
1228
+ {
1229
+ name: "user",
1230
+ writable: true,
1231
+ signer: true
1232
+ },
1233
+ {
1234
+ name: "vault_mirror",
1235
+ pda: {
1236
+ seeds: [
1237
+ {
1238
+ kind: "const",
1239
+ value: [
1240
+ 118,
1241
+ 97,
1242
+ 117,
1243
+ 108,
1244
+ 116,
1245
+ 95,
1246
+ 109,
1247
+ 105,
1248
+ 114,
1249
+ 114,
1250
+ 111,
1251
+ 114
1252
+ ]
1253
+ }
1254
+ ]
1255
+ }
1256
+ },
1257
+ {
1258
+ name: "user_account",
1259
+ writable: true,
1260
+ pda: {
1261
+ seeds: [
1262
+ {
1263
+ kind: "const",
1264
+ value: [
1265
+ 117,
1266
+ 115,
1267
+ 101,
1268
+ 114
1269
+ ]
1270
+ },
1271
+ {
1272
+ kind: "account",
1273
+ path: "vault_mirror"
1274
+ },
1275
+ {
1276
+ kind: "account",
1277
+ path: "user"
1278
+ }
1279
+ ]
1280
+ }
1281
+ },
1282
+ {
1283
+ name: "user_usdc",
1284
+ writable: true
1285
+ },
1286
+ {
1287
+ name: "usdc_vault",
1288
+ writable: true
1289
+ },
1290
+ {
1291
+ name: "token_program",
1292
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
1293
+ }
1294
+ ],
1295
+ args: []
1296
+ },
1297
+ {
1298
+ name: "deposit",
1299
+ docs: [
1300
+ "Deposit USDC into the current open deposit epoch.",
1301
+ "Pass previous DepositEpoch/RedeemEpoch PDAs as remaining_accounts for lazy settlement."
1302
+ ],
1303
+ discriminator: [
1304
+ 242,
1305
+ 35,
1306
+ 198,
1307
+ 137,
1308
+ 82,
1309
+ 225,
1310
+ 242,
1311
+ 182
1312
+ ],
1313
+ accounts: [
1314
+ {
1315
+ name: "user",
1316
+ writable: true,
1317
+ signer: true
1318
+ },
1319
+ {
1320
+ name: "vault_mirror",
1321
+ pda: {
1322
+ seeds: [
1323
+ {
1324
+ kind: "const",
1325
+ value: [
1326
+ 118,
1327
+ 97,
1328
+ 117,
1329
+ 108,
1330
+ 116,
1331
+ 95,
1332
+ 109,
1333
+ 105,
1334
+ 114,
1335
+ 114,
1336
+ 111,
1337
+ 114
1338
+ ]
1339
+ }
1340
+ ]
1341
+ }
1342
+ },
1343
+ {
1344
+ name: "user_account",
1345
+ writable: true,
1346
+ pda: {
1347
+ seeds: [
1348
+ {
1349
+ kind: "const",
1350
+ value: [
1351
+ 117,
1352
+ 115,
1353
+ 101,
1354
+ 114
1355
+ ]
1356
+ },
1357
+ {
1358
+ kind: "account",
1359
+ path: "vault_mirror"
1360
+ },
1361
+ {
1362
+ kind: "account",
1363
+ path: "user"
1364
+ }
1365
+ ]
1366
+ }
1367
+ },
1368
+ {
1369
+ name: "deposit_epoch",
1370
+ docs: [
1371
+ "Current open deposit epoch"
1372
+ ],
1373
+ writable: true,
1374
+ pda: {
1375
+ seeds: [
1376
+ {
1377
+ kind: "const",
1378
+ value: [
1379
+ 100,
1380
+ 101,
1381
+ 112,
1382
+ 111,
1383
+ 115,
1384
+ 105,
1385
+ 116,
1386
+ 95,
1387
+ 101,
1388
+ 112,
1389
+ 111,
1390
+ 99,
1391
+ 104
1392
+ ]
1393
+ },
1394
+ {
1395
+ kind: "account",
1396
+ path: "vault_mirror"
1397
+ },
1398
+ {
1399
+ kind: "account",
1400
+ path: "vault_mirror.current_deposit_epoch",
1401
+ account: "VaultMirror"
1402
+ }
1403
+ ]
1404
+ }
1405
+ },
1406
+ {
1407
+ name: "user_usdc",
1408
+ docs: [
1409
+ "User's USDC token account"
1410
+ ],
1411
+ writable: true
1412
+ },
1413
+ {
1414
+ name: "usdc_vault",
1415
+ docs: [
1416
+ "Vault USDC token account"
1417
+ ],
1418
+ writable: true
1419
+ },
1420
+ {
1421
+ name: "token_program",
1422
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
1423
+ },
1424
+ {
1425
+ name: "system_program",
1426
+ address: "11111111111111111111111111111111"
1427
+ }
1428
+ ],
1429
+ args: [
1430
+ {
1431
+ name: "amount",
1432
+ type: "u64"
1433
+ }
1434
+ ]
1435
+ },
1436
+ {
1437
+ name: "deposit_to_vault",
1438
+ docs: [
1439
+ "Deposit USDC back into the PDA vault (curator \u2192 vault).",
1440
+ "Used after bridging redeemed USDC back from Sui to make it claimable."
1441
+ ],
1442
+ discriminator: [
1443
+ 18,
1444
+ 62,
1445
+ 110,
1446
+ 8,
1447
+ 26,
1448
+ 106,
1449
+ 248,
1450
+ 151
1451
+ ],
1452
+ accounts: [
1453
+ {
1454
+ name: "curator",
1455
+ writable: true,
1456
+ signer: true
1457
+ },
1458
+ {
1459
+ name: "vault_mirror",
1460
+ pda: {
1461
+ seeds: [
1462
+ {
1463
+ kind: "const",
1464
+ value: [
1465
+ 118,
1466
+ 97,
1467
+ 117,
1468
+ 108,
1469
+ 116,
1470
+ 95,
1471
+ 109,
1472
+ 105,
1473
+ 114,
1474
+ 114,
1475
+ 111,
1476
+ 114
1477
+ ]
1478
+ }
1479
+ ]
1480
+ }
1481
+ },
1482
+ {
1483
+ name: "usdc_vault",
1484
+ writable: true
1485
+ },
1486
+ {
1487
+ name: "curator_usdc",
1488
+ writable: true
1489
+ },
1490
+ {
1491
+ name: "token_program",
1492
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
1493
+ }
1494
+ ],
1495
+ args: [
1496
+ {
1497
+ name: "amount",
1498
+ type: "u64"
1499
+ }
1500
+ ]
1501
+ },
1502
+ {
1503
+ name: "emergency_withdraw",
1504
+ docs: [
1505
+ "Emergency withdraw: Authority can withdraw USDC from the vault."
1506
+ ],
1507
+ discriminator: [
1508
+ 239,
1509
+ 45,
1510
+ 203,
1511
+ 64,
1512
+ 150,
1513
+ 73,
1514
+ 218,
1515
+ 92
1516
+ ],
1517
+ accounts: [
1518
+ {
1519
+ name: "authority",
1520
+ writable: true,
1521
+ signer: true
1522
+ },
1523
+ {
1524
+ name: "vault_mirror",
1525
+ pda: {
1526
+ seeds: [
1527
+ {
1528
+ kind: "const",
1529
+ value: [
1530
+ 118,
1531
+ 97,
1532
+ 117,
1533
+ 108,
1534
+ 116,
1535
+ 95,
1536
+ 109,
1537
+ 105,
1538
+ 114,
1539
+ 114,
1540
+ 111,
1541
+ 114
1542
+ ]
1543
+ }
1544
+ ]
1545
+ }
1546
+ },
1547
+ {
1548
+ name: "usdc_vault",
1549
+ writable: true
1550
+ },
1551
+ {
1552
+ name: "authority_usdc",
1553
+ writable: true
1554
+ },
1555
+ {
1556
+ name: "token_program",
1557
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
1558
+ }
1559
+ ],
1560
+ args: [
1561
+ {
1562
+ name: "amount",
1563
+ type: "u64"
1564
+ }
1565
+ ]
1566
+ },
1567
+ {
1568
+ name: "freeze_deposit_epoch",
1569
+ docs: [
1570
+ "Freeze the current open deposit epoch and create a new one.",
1571
+ "Rejects if epoch has no deposits.",
1572
+ "Serial constraint: previous deposit epoch must be Settled or RolledBack.",
1573
+ "Routine operation \u2014 requires operator (1-of-3)."
1574
+ ],
1575
+ discriminator: [
1576
+ 120,
1577
+ 11,
1578
+ 158,
1579
+ 152,
1580
+ 237,
1581
+ 62,
1582
+ 188,
1583
+ 235
1584
+ ],
1585
+ accounts: [
1586
+ {
1587
+ name: "operator",
1588
+ writable: true,
1589
+ signer: true
1590
+ },
1591
+ {
1592
+ name: "vault_mirror",
1593
+ writable: true,
1594
+ pda: {
1595
+ seeds: [
1596
+ {
1597
+ kind: "const",
1598
+ value: [
1599
+ 118,
1600
+ 97,
1601
+ 117,
1602
+ 108,
1603
+ 116,
1604
+ 95,
1605
+ 109,
1606
+ 105,
1607
+ 114,
1608
+ 114,
1609
+ 111,
1610
+ 114
1611
+ ]
1612
+ }
1613
+ ]
1614
+ }
1615
+ },
1616
+ {
1617
+ name: "current_deposit_epoch",
1618
+ docs: [
1619
+ "Current deposit epoch to freeze"
1620
+ ],
1621
+ writable: true,
1622
+ pda: {
1623
+ seeds: [
1624
+ {
1625
+ kind: "const",
1626
+ value: [
1627
+ 100,
1628
+ 101,
1629
+ 112,
1630
+ 111,
1631
+ 115,
1632
+ 105,
1633
+ 116,
1634
+ 95,
1635
+ 101,
1636
+ 112,
1637
+ 111,
1638
+ 99,
1639
+ 104
1640
+ ]
1641
+ },
1642
+ {
1643
+ kind: "account",
1644
+ path: "vault_mirror"
1645
+ },
1646
+ {
1647
+ kind: "account",
1648
+ path: "vault_mirror.current_deposit_epoch",
1649
+ account: "VaultMirror"
1650
+ }
1651
+ ]
1652
+ }
1653
+ },
1654
+ {
1655
+ name: "previous_deposit_epoch",
1656
+ docs: [
1657
+ "Previous deposit epoch (optional \u2014 only needed if current > 0 for serial constraint)"
1658
+ ],
1659
+ optional: true
1660
+ },
1661
+ {
1662
+ name: "next_deposit_epoch",
1663
+ docs: [
1664
+ "Next deposit epoch to create"
1665
+ ],
1666
+ writable: true,
1667
+ pda: {
1668
+ seeds: [
1669
+ {
1670
+ kind: "const",
1671
+ value: [
1672
+ 100,
1673
+ 101,
1674
+ 112,
1675
+ 111,
1676
+ 115,
1677
+ 105,
1678
+ 116,
1679
+ 95,
1680
+ 101,
1681
+ 112,
1682
+ 111,
1683
+ 99,
1684
+ 104
1685
+ ]
1686
+ },
1687
+ {
1688
+ kind: "account",
1689
+ path: "vault_mirror"
1690
+ },
1691
+ {
1692
+ kind: "account",
1693
+ path: "vault_mirror.current_deposit_epoch.checked_add(1)",
1694
+ account: "VaultMirror"
1695
+ }
1696
+ ]
1697
+ }
1698
+ },
1699
+ {
1700
+ name: "system_program",
1701
+ address: "11111111111111111111111111111111"
1702
+ }
1703
+ ],
1704
+ args: []
1705
+ },
1706
+ {
1707
+ name: "freeze_redeem_epoch",
1708
+ docs: [
1709
+ "Freeze the current open redeem epoch and create a new one.",
1710
+ "Rejects if epoch has no redeems.",
1711
+ "Serial constraint: previous redeem epoch must be Settled or RolledBack.",
1712
+ "Routine operation \u2014 requires operator (1-of-3)."
1713
+ ],
1714
+ discriminator: [
1715
+ 233,
1716
+ 155,
1717
+ 202,
1718
+ 214,
1719
+ 205,
1720
+ 1,
1721
+ 50,
1722
+ 15
1723
+ ],
1724
+ accounts: [
1725
+ {
1726
+ name: "operator",
1727
+ writable: true,
1728
+ signer: true
1729
+ },
1730
+ {
1731
+ name: "vault_mirror",
1732
+ writable: true,
1733
+ pda: {
1734
+ seeds: [
1735
+ {
1736
+ kind: "const",
1737
+ value: [
1738
+ 118,
1739
+ 97,
1740
+ 117,
1741
+ 108,
1742
+ 116,
1743
+ 95,
1744
+ 109,
1745
+ 105,
1746
+ 114,
1747
+ 114,
1748
+ 111,
1749
+ 114
1750
+ ]
1751
+ }
1752
+ ]
1753
+ }
1754
+ },
1755
+ {
1756
+ name: "current_redeem_epoch",
1757
+ docs: [
1758
+ "Current redeem epoch to freeze"
1759
+ ],
1760
+ writable: true,
1761
+ pda: {
1762
+ seeds: [
1763
+ {
1764
+ kind: "const",
1765
+ value: [
1766
+ 114,
1767
+ 101,
1768
+ 100,
1769
+ 101,
1770
+ 101,
1771
+ 109,
1772
+ 95,
1773
+ 101,
1774
+ 112,
1775
+ 111,
1776
+ 99,
1777
+ 104
1778
+ ]
1779
+ },
1780
+ {
1781
+ kind: "account",
1782
+ path: "vault_mirror"
1783
+ },
1784
+ {
1785
+ kind: "account",
1786
+ path: "vault_mirror.current_redeem_epoch",
1787
+ account: "VaultMirror"
1788
+ }
1789
+ ]
1790
+ }
1791
+ },
1792
+ {
1793
+ name: "previous_redeem_epoch",
1794
+ docs: [
1795
+ "Previous redeem epoch (optional \u2014 only needed if current > 0 for serial constraint)"
1796
+ ],
1797
+ optional: true
1798
+ },
1799
+ {
1800
+ name: "next_redeem_epoch",
1801
+ docs: [
1802
+ "Next redeem epoch to create"
1803
+ ],
1804
+ writable: true,
1805
+ pda: {
1806
+ seeds: [
1807
+ {
1808
+ kind: "const",
1809
+ value: [
1810
+ 114,
1811
+ 101,
1812
+ 100,
1813
+ 101,
1814
+ 101,
1815
+ 109,
1816
+ 95,
1817
+ 101,
1818
+ 112,
1819
+ 111,
1820
+ 99,
1821
+ 104
1822
+ ]
1823
+ },
1824
+ {
1825
+ kind: "account",
1826
+ path: "vault_mirror"
1827
+ },
1828
+ {
1829
+ kind: "account",
1830
+ path: "vault_mirror.current_redeem_epoch.checked_add(1)",
1831
+ account: "VaultMirror"
1832
+ }
1833
+ ]
1834
+ }
1835
+ },
1836
+ {
1837
+ name: "system_program",
1838
+ address: "11111111111111111111111111111111"
1839
+ }
1840
+ ],
1841
+ args: []
1842
+ },
1843
+ {
1844
+ name: "initialize",
1845
+ docs: [
1846
+ "Initialize the vault mirror, first deposit epoch (0, Open), and first redeem epoch (0, Open)"
1847
+ ],
1848
+ discriminator: [
1849
+ 175,
1850
+ 175,
1851
+ 109,
1852
+ 31,
1853
+ 13,
1854
+ 152,
1855
+ 155,
1856
+ 237
1857
+ ],
1858
+ accounts: [
1859
+ {
1860
+ name: "authority",
1861
+ writable: true,
1862
+ signer: true
1863
+ },
1864
+ {
1865
+ name: "vault_mirror",
1866
+ writable: true,
1867
+ pda: {
1868
+ seeds: [
1869
+ {
1870
+ kind: "const",
1871
+ value: [
1872
+ 118,
1873
+ 97,
1874
+ 117,
1875
+ 108,
1876
+ 116,
1877
+ 95,
1878
+ 109,
1879
+ 105,
1880
+ 114,
1881
+ 114,
1882
+ 111,
1883
+ 114
1884
+ ]
1885
+ }
1886
+ ]
1887
+ }
1888
+ },
1889
+ {
1890
+ name: "usdc_vault",
1891
+ docs: [
1892
+ "USDC token account owned by VaultMirror PDA"
1893
+ ],
1894
+ writable: true,
1895
+ pda: {
1896
+ seeds: [
1897
+ {
1898
+ kind: "const",
1899
+ value: [
1900
+ 117,
1901
+ 115,
1902
+ 100,
1903
+ 99,
1904
+ 95,
1905
+ 118,
1906
+ 97,
1907
+ 117,
1908
+ 108,
1909
+ 116
1910
+ ]
1911
+ },
1912
+ {
1913
+ kind: "account",
1914
+ path: "vault_mirror"
1915
+ }
1916
+ ]
1917
+ }
1918
+ },
1919
+ {
1920
+ name: "usdc_mint"
1921
+ },
1922
+ {
1923
+ name: "deposit_epoch",
1924
+ docs: [
1925
+ "First deposit epoch (epoch 0)"
1926
+ ],
1927
+ writable: true,
1928
+ pda: {
1929
+ seeds: [
1930
+ {
1931
+ kind: "const",
1932
+ value: [
1933
+ 100,
1934
+ 101,
1935
+ 112,
1936
+ 111,
1937
+ 115,
1938
+ 105,
1939
+ 116,
1940
+ 95,
1941
+ 101,
1942
+ 112,
1943
+ 111,
1944
+ 99,
1945
+ 104
1946
+ ]
1947
+ },
1948
+ {
1949
+ kind: "account",
1950
+ path: "vault_mirror"
1951
+ },
1952
+ {
1953
+ kind: "const",
1954
+ value: [
1955
+ 0,
1956
+ 0,
1957
+ 0,
1958
+ 0,
1959
+ 0,
1960
+ 0,
1961
+ 0,
1962
+ 0
1963
+ ]
1964
+ }
1965
+ ]
1966
+ }
1967
+ },
1968
+ {
1969
+ name: "redeem_epoch",
1970
+ docs: [
1971
+ "First redeem epoch (epoch 0)"
1972
+ ],
1973
+ writable: true,
1974
+ pda: {
1975
+ seeds: [
1976
+ {
1977
+ kind: "const",
1978
+ value: [
1979
+ 114,
1980
+ 101,
1981
+ 100,
1982
+ 101,
1983
+ 101,
1984
+ 109,
1985
+ 95,
1986
+ 101,
1987
+ 112,
1988
+ 111,
1989
+ 99,
1990
+ 104
1991
+ ]
1992
+ },
1993
+ {
1994
+ kind: "account",
1995
+ path: "vault_mirror"
1996
+ },
1997
+ {
1998
+ kind: "const",
1999
+ value: [
2000
+ 0,
2001
+ 0,
2002
+ 0,
2003
+ 0,
2004
+ 0,
2005
+ 0,
2006
+ 0,
2007
+ 0
2008
+ ]
2009
+ }
2010
+ ]
2011
+ }
2012
+ },
2013
+ {
2014
+ name: "token_program",
2015
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
2016
+ },
2017
+ {
2018
+ name: "system_program",
2019
+ address: "11111111111111111111111111111111"
2020
+ },
2021
+ {
2022
+ name: "rent",
2023
+ address: "SysvarRent111111111111111111111111111111111"
2024
+ }
2025
+ ],
2026
+ args: [
2027
+ {
2028
+ name: "curator",
2029
+ type: "pubkey"
2030
+ },
2031
+ {
2032
+ name: "operator",
2033
+ type: "pubkey"
2034
+ },
2035
+ {
2036
+ name: "max_epoch_deposit",
2037
+ type: "u64"
2038
+ }
2039
+ ]
2040
+ },
2041
+ {
2042
+ name: "mark_bridging",
2043
+ docs: [
2044
+ "Mark a frozen deposit epoch as bridging and extract USDC to curator's wallet for CCTP.",
2045
+ "Sensitive operation \u2014 requires curator (2-of-3)."
2046
+ ],
2047
+ discriminator: [
2048
+ 161,
2049
+ 92,
2050
+ 108,
2051
+ 165,
2052
+ 34,
2053
+ 123,
2054
+ 227,
2055
+ 110
2056
+ ],
2057
+ accounts: [
2058
+ {
2059
+ name: "curator",
2060
+ writable: true,
2061
+ signer: true
2062
+ },
2063
+ {
2064
+ name: "vault_mirror",
2065
+ writable: true,
2066
+ pda: {
2067
+ seeds: [
2068
+ {
2069
+ kind: "const",
2070
+ value: [
2071
+ 118,
2072
+ 97,
2073
+ 117,
2074
+ 108,
2075
+ 116,
2076
+ 95,
2077
+ 109,
2078
+ 105,
2079
+ 114,
2080
+ 114,
2081
+ 111,
2082
+ 114
2083
+ ]
2084
+ }
2085
+ ]
2086
+ }
2087
+ },
2088
+ {
2089
+ name: "deposit_epoch",
2090
+ writable: true
2091
+ },
2092
+ {
2093
+ name: "usdc_vault",
2094
+ docs: [
2095
+ "Vault USDC token account (PDA-owned, source of funds)"
2096
+ ],
2097
+ writable: true
2098
+ },
2099
+ {
2100
+ name: "curator_usdc",
2101
+ docs: [
2102
+ "Curator's USDC token account (destination for CCTP bridge)"
2103
+ ],
2104
+ writable: true
2105
+ },
2106
+ {
2107
+ name: "token_program",
2108
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
2109
+ }
2110
+ ],
2111
+ args: [
2112
+ {
2113
+ name: "nonce",
2114
+ type: "u64"
2115
+ }
2116
+ ]
2117
+ },
2118
+ {
2119
+ name: "request_redeem",
2120
+ docs: [
2121
+ "Request redemption of shares in the current open redeem epoch."
2122
+ ],
2123
+ discriminator: [
2124
+ 105,
2125
+ 49,
2126
+ 44,
2127
+ 38,
2128
+ 207,
2129
+ 241,
2130
+ 33,
2131
+ 173
2132
+ ],
2133
+ accounts: [
2134
+ {
2135
+ name: "user",
2136
+ writable: true,
2137
+ signer: true
2138
+ },
2139
+ {
2140
+ name: "vault_mirror",
2141
+ pda: {
2142
+ seeds: [
2143
+ {
2144
+ kind: "const",
2145
+ value: [
2146
+ 118,
2147
+ 97,
2148
+ 117,
2149
+ 108,
2150
+ 116,
2151
+ 95,
2152
+ 109,
2153
+ 105,
2154
+ 114,
2155
+ 114,
2156
+ 111,
2157
+ 114
2158
+ ]
2159
+ }
2160
+ ]
2161
+ }
2162
+ },
2163
+ {
2164
+ name: "user_account",
2165
+ writable: true,
2166
+ pda: {
2167
+ seeds: [
2168
+ {
2169
+ kind: "const",
2170
+ value: [
2171
+ 117,
2172
+ 115,
2173
+ 101,
2174
+ 114
2175
+ ]
2176
+ },
2177
+ {
2178
+ kind: "account",
2179
+ path: "vault_mirror"
2180
+ },
2181
+ {
2182
+ kind: "account",
2183
+ path: "user"
2184
+ }
2185
+ ]
2186
+ }
2187
+ },
2188
+ {
2189
+ name: "redeem_epoch",
2190
+ docs: [
2191
+ "Current open redeem epoch"
2192
+ ],
2193
+ writable: true,
2194
+ pda: {
2195
+ seeds: [
2196
+ {
2197
+ kind: "const",
2198
+ value: [
2199
+ 114,
2200
+ 101,
2201
+ 100,
2202
+ 101,
2203
+ 101,
2204
+ 109,
2205
+ 95,
2206
+ 101,
2207
+ 112,
2208
+ 111,
2209
+ 99,
2210
+ 104
2211
+ ]
2212
+ },
2213
+ {
2214
+ kind: "account",
2215
+ path: "vault_mirror"
2216
+ },
2217
+ {
2218
+ kind: "account",
2219
+ path: "vault_mirror.current_redeem_epoch",
2220
+ account: "VaultMirror"
2221
+ }
2222
+ ]
2223
+ }
2224
+ }
2225
+ ],
2226
+ args: [
2227
+ {
2228
+ name: "shares",
2229
+ type: "u64"
2230
+ }
2231
+ ]
2232
+ },
2233
+ {
2234
+ name: "rollback_deposit_epoch",
2235
+ docs: [
2236
+ "Rollback a deposit epoch (Frozen or Bridging \u2192 RolledBack).",
2237
+ "Users auto-rollback via lazy settlement: deposits become claimable USDC.",
2238
+ "Sensitive operation \u2014 requires curator (2-of-3)."
2239
+ ],
2240
+ discriminator: [
2241
+ 119,
2242
+ 99,
2243
+ 137,
2244
+ 62,
2245
+ 80,
2246
+ 139,
2247
+ 61,
2248
+ 181
2249
+ ],
2250
+ accounts: [
2251
+ {
2252
+ name: "curator",
2253
+ signer: true
2254
+ },
2255
+ {
2256
+ name: "vault_mirror",
2257
+ writable: true,
2258
+ pda: {
2259
+ seeds: [
2260
+ {
2261
+ kind: "const",
2262
+ value: [
2263
+ 118,
2264
+ 97,
2265
+ 117,
2266
+ 108,
2267
+ 116,
2268
+ 95,
2269
+ 109,
2270
+ 105,
2271
+ 114,
2272
+ 114,
2273
+ 111,
2274
+ 114
2275
+ ]
2276
+ }
2277
+ ]
2278
+ }
2279
+ },
2280
+ {
2281
+ name: "deposit_epoch",
2282
+ writable: true
2283
+ }
2284
+ ],
2285
+ args: []
2286
+ },
2287
+ {
2288
+ name: "rollback_redeem_epoch",
2289
+ docs: [
2290
+ "Rollback a redeem epoch (Frozen \u2192 RolledBack).",
2291
+ "Users auto-rollback via lazy settlement: locked shares are restored.",
2292
+ "Sensitive operation \u2014 requires curator (2-of-3)."
2293
+ ],
2294
+ discriminator: [
2295
+ 172,
2296
+ 217,
2297
+ 168,
2298
+ 112,
2299
+ 217,
2300
+ 37,
2301
+ 157,
2302
+ 218
2303
+ ],
2304
+ accounts: [
2305
+ {
2306
+ name: "curator",
2307
+ signer: true
2308
+ },
2309
+ {
2310
+ name: "vault_mirror",
2311
+ writable: true,
2312
+ pda: {
2313
+ seeds: [
2314
+ {
2315
+ kind: "const",
2316
+ value: [
2317
+ 118,
2318
+ 97,
2319
+ 117,
2320
+ 108,
2321
+ 116,
2322
+ 95,
2323
+ 109,
2324
+ 105,
2325
+ 114,
2326
+ 114,
2327
+ 111,
2328
+ 114
2329
+ ]
2330
+ }
2331
+ ]
2332
+ }
2333
+ },
2334
+ {
2335
+ name: "redeem_epoch",
2336
+ writable: true
2337
+ }
2338
+ ],
2339
+ args: []
2340
+ },
2341
+ {
2342
+ name: "set_authority",
2343
+ docs: [
2344
+ "Transfer authority to a new address"
2345
+ ],
2346
+ discriminator: [
2347
+ 133,
2348
+ 250,
2349
+ 37,
2350
+ 21,
2351
+ 110,
2352
+ 163,
2353
+ 26,
2354
+ 121
2355
+ ],
2356
+ accounts: [
2357
+ {
2358
+ name: "authority",
2359
+ signer: true
2360
+ },
2361
+ {
2362
+ name: "vault_mirror",
2363
+ writable: true,
2364
+ pda: {
2365
+ seeds: [
2366
+ {
2367
+ kind: "const",
2368
+ value: [
2369
+ 118,
2370
+ 97,
2371
+ 117,
2372
+ 108,
2373
+ 116,
2374
+ 95,
2375
+ 109,
2376
+ 105,
2377
+ 114,
2378
+ 114,
2379
+ 111,
2380
+ 114
2381
+ ]
2382
+ }
2383
+ ]
2384
+ }
2385
+ }
2386
+ ],
2387
+ args: [
2388
+ {
2389
+ name: "new_authority",
2390
+ type: "pubkey"
2391
+ }
2392
+ ]
2393
+ },
2394
+ {
2395
+ name: "set_curator",
2396
+ docs: [
2397
+ "Change the curator address"
2398
+ ],
2399
+ discriminator: [
2400
+ 115,
2401
+ 127,
2402
+ 196,
2403
+ 219,
2404
+ 235,
2405
+ 198,
2406
+ 96,
2407
+ 253
2408
+ ],
2409
+ accounts: [
2410
+ {
2411
+ name: "authority",
2412
+ signer: true
2413
+ },
2414
+ {
2415
+ name: "vault_mirror",
2416
+ writable: true,
2417
+ pda: {
2418
+ seeds: [
2419
+ {
2420
+ kind: "const",
2421
+ value: [
2422
+ 118,
2423
+ 97,
2424
+ 117,
2425
+ 108,
2426
+ 116,
2427
+ 95,
2428
+ 109,
2429
+ 105,
2430
+ 114,
2431
+ 114,
2432
+ 111,
2433
+ 114
2434
+ ]
2435
+ }
2436
+ ]
2437
+ }
2438
+ }
2439
+ ],
2440
+ args: [
2441
+ {
2442
+ name: "new_curator",
2443
+ type: "pubkey"
2444
+ }
2445
+ ]
2446
+ },
2447
+ {
2448
+ name: "set_max_epoch_deposit",
2449
+ docs: [
2450
+ "Update the maximum epoch deposit cap"
2451
+ ],
2452
+ discriminator: [
2453
+ 205,
2454
+ 115,
2455
+ 249,
2456
+ 24,
2457
+ 108,
2458
+ 6,
2459
+ 169,
2460
+ 224
2461
+ ],
2462
+ accounts: [
2463
+ {
2464
+ name: "authority",
2465
+ signer: true
2466
+ },
2467
+ {
2468
+ name: "vault_mirror",
2469
+ writable: true,
2470
+ pda: {
2471
+ seeds: [
2472
+ {
2473
+ kind: "const",
2474
+ value: [
2475
+ 118,
2476
+ 97,
2477
+ 117,
2478
+ 108,
2479
+ 116,
2480
+ 95,
2481
+ 109,
2482
+ 105,
2483
+ 114,
2484
+ 114,
2485
+ 111,
2486
+ 114
2487
+ ]
2488
+ }
2489
+ ]
2490
+ }
2491
+ }
2492
+ ],
2493
+ args: [
2494
+ {
2495
+ name: "new_max",
2496
+ type: "u64"
2497
+ }
2498
+ ]
2499
+ },
2500
+ {
2501
+ name: "set_min_deposit",
2502
+ docs: [
2503
+ "Update the minimum deposit amount."
2504
+ ],
2505
+ discriminator: [
2506
+ 15,
2507
+ 182,
2508
+ 74,
2509
+ 248,
2510
+ 251,
2511
+ 148,
2512
+ 186,
2513
+ 171
2514
+ ],
2515
+ accounts: [
2516
+ {
2517
+ name: "authority",
2518
+ signer: true
2519
+ },
2520
+ {
2521
+ name: "vault_mirror",
2522
+ writable: true,
2523
+ pda: {
2524
+ seeds: [
2525
+ {
2526
+ kind: "const",
2527
+ value: [
2528
+ 118,
2529
+ 97,
2530
+ 117,
2531
+ 108,
2532
+ 116,
2533
+ 95,
2534
+ 109,
2535
+ 105,
2536
+ 114,
2537
+ 114,
2538
+ 111,
2539
+ 114
2540
+ ]
2541
+ }
2542
+ ]
2543
+ }
2544
+ }
2545
+ ],
2546
+ args: [
2547
+ {
2548
+ name: "new_min",
2549
+ type: "u64"
2550
+ }
2551
+ ]
2552
+ },
2553
+ {
2554
+ name: "set_operator",
2555
+ docs: [
2556
+ "Change the operator address"
2557
+ ],
2558
+ discriminator: [
2559
+ 238,
2560
+ 153,
2561
+ 101,
2562
+ 169,
2563
+ 243,
2564
+ 131,
2565
+ 36,
2566
+ 1
2567
+ ],
2568
+ accounts: [
2569
+ {
2570
+ name: "authority",
2571
+ signer: true
2572
+ },
2573
+ {
2574
+ name: "vault_mirror",
2575
+ writable: true,
2576
+ pda: {
2577
+ seeds: [
2578
+ {
2579
+ kind: "const",
2580
+ value: [
2581
+ 118,
2582
+ 97,
2583
+ 117,
2584
+ 108,
2585
+ 116,
2586
+ 95,
2587
+ 109,
2588
+ 105,
2589
+ 114,
2590
+ 114,
2591
+ 111,
2592
+ 114
2593
+ ]
2594
+ }
2595
+ ]
2596
+ }
2597
+ }
2598
+ ],
2599
+ args: [
2600
+ {
2601
+ name: "new_operator",
2602
+ type: "pubkey"
2603
+ }
2604
+ ]
2605
+ },
2606
+ {
2607
+ name: "set_pause",
2608
+ docs: [
2609
+ "Toggle pause state. When paused, deposit and redeem are blocked, but claim still works."
2610
+ ],
2611
+ discriminator: [
2612
+ 63,
2613
+ 32,
2614
+ 154,
2615
+ 2,
2616
+ 56,
2617
+ 103,
2618
+ 79,
2619
+ 45
2620
+ ],
2621
+ accounts: [
2622
+ {
2623
+ name: "authority",
2624
+ signer: true
2625
+ },
2626
+ {
2627
+ name: "vault_mirror",
2628
+ writable: true,
2629
+ pda: {
2630
+ seeds: [
2631
+ {
2632
+ kind: "const",
2633
+ value: [
2634
+ 118,
2635
+ 97,
2636
+ 117,
2637
+ 108,
2638
+ 116,
2639
+ 95,
2640
+ 109,
2641
+ 105,
2642
+ 114,
2643
+ 114,
2644
+ 111,
2645
+ 114
2646
+ ]
2647
+ }
2648
+ ]
2649
+ }
2650
+ }
2651
+ ],
2652
+ args: [
2653
+ {
2654
+ name: "paused",
2655
+ type: "bool"
2656
+ }
2657
+ ]
2658
+ },
2659
+ {
2660
+ name: "settle_deposits",
2661
+ docs: [
2662
+ "Settle a deposit epoch: lock in shares minted and mark as Settled.",
2663
+ "Updates vault_mirror.total_shares.",
2664
+ "Sensitive operation \u2014 requires curator (2-of-3)."
2665
+ ],
2666
+ discriminator: [
2667
+ 216,
2668
+ 163,
2669
+ 126,
2670
+ 212,
2671
+ 189,
2672
+ 105,
2673
+ 226,
2674
+ 91
2675
+ ],
2676
+ accounts: [
2677
+ {
2678
+ name: "curator",
2679
+ signer: true
2680
+ },
2681
+ {
2682
+ name: "vault_mirror",
2683
+ writable: true,
2684
+ pda: {
2685
+ seeds: [
2686
+ {
2687
+ kind: "const",
2688
+ value: [
2689
+ 118,
2690
+ 97,
2691
+ 117,
2692
+ 108,
2693
+ 116,
2694
+ 95,
2695
+ 109,
2696
+ 105,
2697
+ 114,
2698
+ 114,
2699
+ 111,
2700
+ 114
2701
+ ]
2702
+ }
2703
+ ]
2704
+ }
2705
+ },
2706
+ {
2707
+ name: "deposit_epoch",
2708
+ writable: true
2709
+ }
2710
+ ],
2711
+ args: [
2712
+ {
2713
+ name: "shares_minted",
2714
+ type: "u64"
2715
+ }
2716
+ ]
2717
+ },
2718
+ {
2719
+ name: "settle_redeems",
2720
+ docs: [
2721
+ "Settle a redeem epoch: lock in USDC for redeemers and mark as Settled.",
2722
+ "Updates vault_mirror.total_shares.",
2723
+ "Curator must have called deposit_to_vault first to fund the PDA.",
2724
+ "Sensitive operation \u2014 requires curator (2-of-3)."
2725
+ ],
2726
+ discriminator: [
2727
+ 41,
2728
+ 238,
2729
+ 144,
2730
+ 152,
2731
+ 241,
2732
+ 69,
2733
+ 163,
2734
+ 71
2735
+ ],
2736
+ accounts: [
2737
+ {
2738
+ name: "curator",
2739
+ signer: true
2740
+ },
2741
+ {
2742
+ name: "vault_mirror",
2743
+ writable: true,
2744
+ pda: {
2745
+ seeds: [
2746
+ {
2747
+ kind: "const",
2748
+ value: [
2749
+ 118,
2750
+ 97,
2751
+ 117,
2752
+ 108,
2753
+ 116,
2754
+ 95,
2755
+ 109,
2756
+ 105,
2757
+ 114,
2758
+ 114,
2759
+ 111,
2760
+ 114
2761
+ ]
2762
+ }
2763
+ ]
2764
+ }
2765
+ },
2766
+ {
2767
+ name: "redeem_epoch",
2768
+ writable: true
2769
+ }
2770
+ ],
2771
+ args: [
2772
+ {
2773
+ name: "usdc_for_redeems",
2774
+ type: "u64"
2775
+ }
2776
+ ]
2777
+ }
2778
+ ],
2779
+ accounts: [
2780
+ {
2781
+ name: "DepositEpoch",
2782
+ discriminator: [
2783
+ 113,
2784
+ 170,
2785
+ 28,
2786
+ 39,
2787
+ 59,
2788
+ 253,
2789
+ 178,
2790
+ 123
2791
+ ]
2792
+ },
2793
+ {
2794
+ name: "RedeemEpoch",
2795
+ discriminator: [
2796
+ 151,
2797
+ 138,
2798
+ 181,
2799
+ 158,
2800
+ 40,
2801
+ 74,
2802
+ 85,
2803
+ 44
2804
+ ]
2805
+ },
2806
+ {
2807
+ name: "UserAccount",
2808
+ discriminator: [
2809
+ 211,
2810
+ 33,
2811
+ 136,
2812
+ 16,
2813
+ 186,
2814
+ 110,
2815
+ 242,
2816
+ 127
2817
+ ]
2818
+ },
2819
+ {
2820
+ name: "VaultMirror",
2821
+ discriminator: [
2822
+ 187,
2823
+ 198,
2824
+ 62,
2825
+ 71,
2826
+ 138,
2827
+ 173,
2828
+ 117,
2829
+ 230
2830
+ ]
2831
+ }
2832
+ ],
2833
+ events: [
2834
+ {
2835
+ name: "AuthorityChanged",
2836
+ discriminator: [
2837
+ 31,
2838
+ 19,
2839
+ 174,
2840
+ 152,
2841
+ 4,
2842
+ 82,
2843
+ 215,
2844
+ 226
2845
+ ]
2846
+ },
2847
+ {
2848
+ name: "CuratorChanged",
2849
+ discriminator: [
2850
+ 87,
2851
+ 59,
2852
+ 49,
2853
+ 50,
2854
+ 194,
2855
+ 244,
2856
+ 0,
2857
+ 178
2858
+ ]
2859
+ },
2860
+ {
2861
+ name: "DepositCancelled",
2862
+ discriminator: [
2863
+ 233,
2864
+ 23,
2865
+ 42,
2866
+ 206,
2867
+ 203,
2868
+ 207,
2869
+ 147,
2870
+ 35
2871
+ ]
2872
+ },
2873
+ {
2874
+ name: "DepositEpochBridging",
2875
+ discriminator: [
2876
+ 135,
2877
+ 188,
2878
+ 190,
2879
+ 180,
2880
+ 130,
2881
+ 52,
2882
+ 8,
2883
+ 188
2884
+ ]
2885
+ },
2886
+ {
2887
+ name: "DepositEpochFrozen",
2888
+ discriminator: [
2889
+ 212,
2890
+ 27,
2891
+ 141,
2892
+ 46,
2893
+ 207,
2894
+ 146,
2895
+ 142,
2896
+ 61
2897
+ ]
2898
+ },
2899
+ {
2900
+ name: "DepositEpochRolledBack",
2901
+ discriminator: [
2902
+ 66,
2903
+ 33,
2904
+ 209,
2905
+ 58,
2906
+ 163,
2907
+ 10,
2908
+ 48,
2909
+ 130
2910
+ ]
2911
+ },
2912
+ {
2913
+ name: "DepositLazySettled",
2914
+ discriminator: [
2915
+ 200,
2916
+ 161,
2917
+ 186,
2918
+ 180,
2919
+ 2,
2920
+ 93,
2921
+ 11,
2922
+ 155
2923
+ ]
2924
+ },
2925
+ {
2926
+ name: "DepositsSettled",
2927
+ discriminator: [
2928
+ 202,
2929
+ 56,
2930
+ 60,
2931
+ 238,
2932
+ 125,
2933
+ 34,
2934
+ 71,
2935
+ 245
2936
+ ]
2937
+ },
2938
+ {
2939
+ name: "EmergencyWithdrawn",
2940
+ discriminator: [
2941
+ 116,
2942
+ 226,
2943
+ 36,
2944
+ 3,
2945
+ 37,
2946
+ 92,
2947
+ 138,
2948
+ 76
2949
+ ]
2950
+ },
2951
+ {
2952
+ name: "MaxEpochDepositChanged",
2953
+ discriminator: [
2954
+ 111,
2955
+ 5,
2956
+ 240,
2957
+ 109,
2958
+ 254,
2959
+ 93,
2960
+ 243,
2961
+ 97
2962
+ ]
2963
+ },
2964
+ {
2965
+ name: "MinDepositChanged",
2966
+ discriminator: [
2967
+ 110,
2968
+ 94,
2969
+ 102,
2970
+ 242,
2971
+ 183,
2972
+ 121,
2973
+ 89,
2974
+ 244
2975
+ ]
2976
+ },
2977
+ {
2978
+ name: "OperatorChanged",
2979
+ discriminator: [
2980
+ 231,
2981
+ 79,
2982
+ 62,
2983
+ 226,
2984
+ 190,
2985
+ 139,
2986
+ 176,
2987
+ 51
2988
+ ]
2989
+ },
2990
+ {
2991
+ name: "RedeemEpochFrozen",
2992
+ discriminator: [
2993
+ 25,
2994
+ 105,
2995
+ 245,
2996
+ 218,
2997
+ 51,
2998
+ 117,
2999
+ 30,
3000
+ 200
3001
+ ]
3002
+ },
3003
+ {
3004
+ name: "RedeemEpochRolledBack",
3005
+ discriminator: [
3006
+ 186,
3007
+ 177,
3008
+ 42,
3009
+ 37,
3010
+ 248,
3011
+ 170,
3012
+ 149,
3013
+ 196
3014
+ ]
3015
+ },
3016
+ {
3017
+ name: "RedeemLazySettled",
3018
+ discriminator: [
3019
+ 196,
3020
+ 65,
3021
+ 102,
3022
+ 125,
3023
+ 173,
3024
+ 10,
3025
+ 31,
3026
+ 23
3027
+ ]
3028
+ },
3029
+ {
3030
+ name: "RedeemRequested",
3031
+ discriminator: [
3032
+ 5,
3033
+ 130,
3034
+ 67,
3035
+ 249,
3036
+ 243,
3037
+ 168,
3038
+ 11,
3039
+ 88
3040
+ ]
3041
+ },
3042
+ {
3043
+ name: "RedeemsSettled",
3044
+ discriminator: [
3045
+ 233,
3046
+ 221,
3047
+ 150,
3048
+ 195,
3049
+ 102,
3050
+ 11,
3051
+ 229,
3052
+ 220
3053
+ ]
3054
+ },
3055
+ {
3056
+ name: "UserClaimed",
3057
+ discriminator: [
3058
+ 222,
3059
+ 173,
3060
+ 163,
3061
+ 173,
3062
+ 82,
3063
+ 10,
3064
+ 133,
3065
+ 226
3066
+ ]
3067
+ },
3068
+ {
3069
+ name: "UserDeposited",
3070
+ discriminator: [
3071
+ 83,
3072
+ 166,
3073
+ 194,
3074
+ 102,
3075
+ 87,
3076
+ 104,
3077
+ 88,
3078
+ 137
3079
+ ]
3080
+ },
3081
+ {
3082
+ name: "VaultFunded",
3083
+ discriminator: [
3084
+ 192,
3085
+ 119,
3086
+ 245,
3087
+ 193,
3088
+ 55,
3089
+ 223,
3090
+ 195,
3091
+ 50
3092
+ ]
3093
+ },
3094
+ {
3095
+ name: "VaultInitialized",
3096
+ discriminator: [
3097
+ 180,
3098
+ 43,
3099
+ 207,
3100
+ 2,
3101
+ 18,
3102
+ 71,
3103
+ 3,
3104
+ 75
3105
+ ]
3106
+ },
3107
+ {
3108
+ name: "VaultPaused",
3109
+ discriminator: [
3110
+ 198,
3111
+ 157,
3112
+ 22,
3113
+ 151,
3114
+ 68,
3115
+ 100,
3116
+ 162,
3117
+ 35
3118
+ ]
3119
+ }
3120
+ ],
3121
+ errors: [
3122
+ {
3123
+ code: 6e3,
3124
+ name: "VaultPaused",
3125
+ msg: "Vault is paused"
3126
+ },
3127
+ {
3128
+ code: 6001,
3129
+ name: "DepositBelowMinimum",
3130
+ msg: "Deposit amount below minimum"
3131
+ },
3132
+ {
3133
+ code: 6002,
3134
+ name: "EpochCapExceeded",
3135
+ msg: "Epoch deposit cap exceeded"
3136
+ },
3137
+ {
3138
+ code: 6003,
3139
+ name: "InvalidEpochStatus",
3140
+ msg: "Invalid epoch status for this operation"
3141
+ },
3142
+ {
3143
+ code: 6004,
3144
+ name: "EmptyDepositEpoch",
3145
+ msg: "Deposit epoch has no deposits"
3146
+ },
3147
+ {
3148
+ code: 6005,
3149
+ name: "EmptyRedeemEpoch",
3150
+ msg: "Redeem epoch has no redeems"
3151
+ },
3152
+ {
3153
+ code: 6006,
3154
+ name: "PreviousDepositEpochUnsettled",
3155
+ msg: "Previous deposit epoch not yet settled or rolled back"
3156
+ },
3157
+ {
3158
+ code: 6007,
3159
+ name: "PreviousRedeemEpochUnsettled",
3160
+ msg: "Previous redeem epoch not yet settled or rolled back"
3161
+ },
3162
+ {
3163
+ code: 6008,
3164
+ name: "NoPendingDeposit",
3165
+ msg: "No pending deposit to cancel"
3166
+ },
3167
+ {
3168
+ code: 6009,
3169
+ name: "InsufficientShares",
3170
+ msg: "Insufficient shares for redemption"
3171
+ },
3172
+ {
3173
+ code: 6010,
3174
+ name: "NothingToClaim",
3175
+ msg: "No claimable USDC"
3176
+ },
3177
+ {
3178
+ code: 6011,
3179
+ name: "NotCurator",
3180
+ msg: "Unauthorized: not curator"
3181
+ },
3182
+ {
3183
+ code: 6012,
3184
+ name: "NotOperator",
3185
+ msg: "Unauthorized: not operator"
3186
+ },
3187
+ {
3188
+ code: 6013,
3189
+ name: "NotAuthority",
3190
+ msg: "Unauthorized: not authority"
3191
+ },
3192
+ {
3193
+ code: 6014,
3194
+ name: "MathOverflow",
3195
+ msg: "Math overflow"
3196
+ },
3197
+ {
3198
+ code: 6015,
3199
+ name: "InvalidDepositEpoch",
3200
+ msg: "Invalid deposit epoch PDA passed for lazy settlement"
3201
+ },
3202
+ {
3203
+ code: 6016,
3204
+ name: "InvalidRedeemEpoch",
3205
+ msg: "Invalid redeem epoch PDA passed for lazy settlement"
3206
+ },
3207
+ {
3208
+ code: 6017,
3209
+ name: "CannotRollbackSettled",
3210
+ msg: "Cannot rollback a settled epoch"
3211
+ },
3212
+ {
3213
+ code: 6018,
3214
+ name: "PendingDepositUnsettled",
3215
+ msg: "User has unsettled pending deposit from a different epoch"
3216
+ },
3217
+ {
3218
+ code: 6019,
3219
+ name: "PendingRedeemUnsettled",
3220
+ msg: "User has unsettled pending redeem from a different epoch"
3221
+ },
3222
+ {
3223
+ code: 6020,
3224
+ name: "ZeroAmount",
3225
+ msg: "Amount must be greater than zero"
3226
+ },
3227
+ {
3228
+ code: 6021,
3229
+ name: "MinDepositTooLow",
3230
+ msg: "Minimum deposit must be at least 1 token (smallest unit)"
3231
+ },
3232
+ {
3233
+ code: 6022,
3234
+ name: "NoDepositActivity",
3235
+ msg: "Deposit epoch has no activity to settle"
3236
+ },
3237
+ {
3238
+ code: 6023,
3239
+ name: "NoRedeemActivity",
3240
+ msg: "Redeem epoch has no activity to settle"
3241
+ }
3242
+ ],
3243
+ types: [
3244
+ {
3245
+ name: "AuthorityChanged",
3246
+ type: {
3247
+ kind: "struct",
3248
+ fields: [
3249
+ {
3250
+ name: "old_authority",
3251
+ type: "pubkey"
3252
+ },
3253
+ {
3254
+ name: "new_authority",
3255
+ type: "pubkey"
3256
+ }
3257
+ ]
3258
+ }
3259
+ },
3260
+ {
3261
+ name: "CuratorChanged",
3262
+ type: {
3263
+ kind: "struct",
3264
+ fields: [
3265
+ {
3266
+ name: "old_curator",
3267
+ type: "pubkey"
3268
+ },
3269
+ {
3270
+ name: "new_curator",
3271
+ type: "pubkey"
3272
+ }
3273
+ ]
3274
+ }
3275
+ },
3276
+ {
3277
+ name: "DepositCancelled",
3278
+ type: {
3279
+ kind: "struct",
3280
+ fields: [
3281
+ {
3282
+ name: "user",
3283
+ type: "pubkey"
3284
+ },
3285
+ {
3286
+ name: "amount_returned",
3287
+ type: "u64"
3288
+ },
3289
+ {
3290
+ name: "fee",
3291
+ type: "u64"
3292
+ },
3293
+ {
3294
+ name: "deposit_epoch",
3295
+ type: "u64"
3296
+ }
3297
+ ]
3298
+ }
3299
+ },
3300
+ {
3301
+ name: "DepositEpoch",
3302
+ docs: [
3303
+ "Deposit epoch \u2014 tracks a batch of user deposits (Solana \u2192 Sui path)",
3304
+ 'Seeds: [b"deposit_epoch", vault_mirror.key(), epoch.to_le_bytes()]',
3305
+ "",
3306
+ "Lifecycle: Open \u2192 Frozen \u2192 Bridging \u2192 Settled (or RolledBack from Frozen/Bridging)"
3307
+ ],
3308
+ type: {
3309
+ kind: "struct",
3310
+ fields: [
3311
+ {
3312
+ name: "epoch",
3313
+ docs: [
3314
+ "Deposit epoch number"
3315
+ ],
3316
+ type: "u64"
3317
+ },
3318
+ {
3319
+ name: "status",
3320
+ docs: [
3321
+ "Current status"
3322
+ ],
3323
+ type: {
3324
+ defined: {
3325
+ name: "EpochStatus"
3326
+ }
3327
+ }
3328
+ },
3329
+ {
3330
+ name: "total_deposit_usdc",
3331
+ docs: [
3332
+ "Total USDC deposited by users in this epoch"
3333
+ ],
3334
+ type: "u64"
3335
+ },
3336
+ {
3337
+ name: "deposit_user_count",
3338
+ docs: [
3339
+ "Number of users who deposited"
3340
+ ],
3341
+ type: "u32"
3342
+ },
3343
+ {
3344
+ name: "settled_shares_minted",
3345
+ docs: [
3346
+ "Total new shares minted for depositors (set by settle_deposits)"
3347
+ ],
3348
+ type: "u64"
3349
+ },
3350
+ {
3351
+ name: "frozen_at",
3352
+ docs: [
3353
+ "Timestamp when epoch was frozen"
3354
+ ],
3355
+ type: "i64"
3356
+ },
3357
+ {
3358
+ name: "bridge_nonce",
3359
+ docs: [
3360
+ "CCTP bridge nonce (set by mark_bridging)"
3361
+ ],
3362
+ type: "u64"
3363
+ },
3364
+ {
3365
+ name: "bump",
3366
+ docs: [
3367
+ "Bump for this PDA"
3368
+ ],
3369
+ type: "u8"
3370
+ }
3371
+ ]
3372
+ }
3373
+ },
3374
+ {
3375
+ name: "DepositEpochBridging",
3376
+ type: {
3377
+ kind: "struct",
3378
+ fields: [
3379
+ {
3380
+ name: "epoch",
3381
+ type: "u64"
3382
+ },
3383
+ {
3384
+ name: "nonce",
3385
+ type: "u64"
3386
+ }
3387
+ ]
3388
+ }
3389
+ },
3390
+ {
3391
+ name: "DepositEpochFrozen",
3392
+ type: {
3393
+ kind: "struct",
3394
+ fields: [
3395
+ {
3396
+ name: "epoch",
3397
+ type: "u64"
3398
+ },
3399
+ {
3400
+ name: "total_deposit_usdc",
3401
+ type: "u64"
3402
+ },
3403
+ {
3404
+ name: "deposit_user_count",
3405
+ type: "u32"
3406
+ }
3407
+ ]
3408
+ }
3409
+ },
3410
+ {
3411
+ name: "DepositEpochRolledBack",
3412
+ type: {
3413
+ kind: "struct",
3414
+ fields: [
3415
+ {
3416
+ name: "epoch",
3417
+ type: "u64"
3418
+ },
3419
+ {
3420
+ name: "deposits_returned",
3421
+ type: "u64"
3422
+ }
3423
+ ]
3424
+ }
3425
+ },
3426
+ {
3427
+ name: "DepositLazySettled",
3428
+ docs: [
3429
+ "Emitted when a user's pending deposit is lazily settled"
3430
+ ],
3431
+ type: {
3432
+ kind: "struct",
3433
+ fields: [
3434
+ {
3435
+ name: "user",
3436
+ type: "pubkey"
3437
+ },
3438
+ {
3439
+ name: "deposit_epoch",
3440
+ type: "u64"
3441
+ },
3442
+ {
3443
+ name: "shares_received",
3444
+ type: "u64"
3445
+ },
3446
+ {
3447
+ name: "is_rollback",
3448
+ type: "bool"
3449
+ }
3450
+ ]
3451
+ }
3452
+ },
3453
+ {
3454
+ name: "DepositsSettled",
3455
+ type: {
3456
+ kind: "struct",
3457
+ fields: [
3458
+ {
3459
+ name: "epoch",
3460
+ type: "u64"
3461
+ },
3462
+ {
3463
+ name: "shares_minted",
3464
+ type: "u64"
3465
+ }
3466
+ ]
3467
+ }
3468
+ },
3469
+ {
3470
+ name: "EmergencyWithdrawn",
3471
+ type: {
3472
+ kind: "struct",
3473
+ fields: [
3474
+ {
3475
+ name: "authority",
3476
+ type: "pubkey"
3477
+ },
3478
+ {
3479
+ name: "amount",
3480
+ type: "u64"
3481
+ }
3482
+ ]
3483
+ }
3484
+ },
3485
+ {
3486
+ name: "EpochStatus",
3487
+ docs: [
3488
+ "Epoch lifecycle for deposits: Open \u2192 Frozen \u2192 Bridging \u2192 Settled (or RolledBack)",
3489
+ "Epoch lifecycle for redeems: Open \u2192 Frozen \u2192 Settled (or RolledBack)"
3490
+ ],
3491
+ type: {
3492
+ kind: "enum",
3493
+ variants: [
3494
+ {
3495
+ name: "Open"
3496
+ },
3497
+ {
3498
+ name: "Frozen"
3499
+ },
3500
+ {
3501
+ name: "Bridging"
3502
+ },
3503
+ {
3504
+ name: "Settled"
3505
+ },
3506
+ {
3507
+ name: "RolledBack"
3508
+ }
3509
+ ]
3510
+ }
3511
+ },
3512
+ {
3513
+ name: "MaxEpochDepositChanged",
3514
+ type: {
3515
+ kind: "struct",
3516
+ fields: [
3517
+ {
3518
+ name: "old_value",
3519
+ type: "u64"
3520
+ },
3521
+ {
3522
+ name: "new_value",
3523
+ type: "u64"
3524
+ }
3525
+ ]
3526
+ }
3527
+ },
3528
+ {
3529
+ name: "MinDepositChanged",
3530
+ type: {
3531
+ kind: "struct",
3532
+ fields: [
3533
+ {
3534
+ name: "old_value",
3535
+ type: "u64"
3536
+ },
3537
+ {
3538
+ name: "new_value",
3539
+ type: "u64"
3540
+ }
3541
+ ]
3542
+ }
3543
+ },
3544
+ {
3545
+ name: "OperatorChanged",
3546
+ type: {
3547
+ kind: "struct",
3548
+ fields: [
3549
+ {
3550
+ name: "old_operator",
3551
+ type: "pubkey"
3552
+ },
3553
+ {
3554
+ name: "new_operator",
3555
+ type: "pubkey"
3556
+ }
3557
+ ]
3558
+ }
3559
+ },
3560
+ {
3561
+ name: "RedeemEpoch",
3562
+ docs: [
3563
+ "Redeem epoch \u2014 tracks a batch of user redeems (Sui \u2192 Solana path)",
3564
+ 'Seeds: [b"redeem_epoch", vault_mirror.key(), epoch.to_le_bytes()]',
3565
+ "",
3566
+ "Lifecycle: Open \u2192 Frozen \u2192 Settled (or RolledBack from Frozen)",
3567
+ "No Bridging state \u2014 the Sui\u2192Solana bridge is handled off-chain by the curator."
3568
+ ],
3569
+ type: {
3570
+ kind: "struct",
3571
+ fields: [
3572
+ {
3573
+ name: "epoch",
3574
+ docs: [
3575
+ "Redeem epoch number"
3576
+ ],
3577
+ type: "u64"
3578
+ },
3579
+ {
3580
+ name: "status",
3581
+ docs: [
3582
+ "Current status"
3583
+ ],
3584
+ type: {
3585
+ defined: {
3586
+ name: "EpochStatus"
3587
+ }
3588
+ }
3589
+ },
3590
+ {
3591
+ name: "total_redeem_shares",
3592
+ docs: [
3593
+ "Total shares queued for redemption in this epoch"
3594
+ ],
3595
+ type: "u64"
3596
+ },
3597
+ {
3598
+ name: "redeem_user_count",
3599
+ docs: [
3600
+ "Number of users who redeemed"
3601
+ ],
3602
+ type: "u32"
3603
+ },
3604
+ {
3605
+ name: "settled_usdc_for_redeems",
3606
+ docs: [
3607
+ "Total USDC allocated for redeemers (set by settle_redeems)"
3608
+ ],
3609
+ type: "u64"
3610
+ },
3611
+ {
3612
+ name: "frozen_at",
3613
+ docs: [
3614
+ "Timestamp when epoch was frozen"
3615
+ ],
3616
+ type: "i64"
3617
+ },
3618
+ {
3619
+ name: "bump",
3620
+ docs: [
3621
+ "Bump for this PDA"
3622
+ ],
3623
+ type: "u8"
3624
+ }
3625
+ ]
3626
+ }
3627
+ },
3628
+ {
3629
+ name: "RedeemEpochFrozen",
3630
+ type: {
3631
+ kind: "struct",
3632
+ fields: [
3633
+ {
3634
+ name: "epoch",
3635
+ type: "u64"
3636
+ },
3637
+ {
3638
+ name: "total_redeem_shares",
3639
+ type: "u64"
3640
+ },
3641
+ {
3642
+ name: "redeem_user_count",
3643
+ type: "u32"
3644
+ }
3645
+ ]
3646
+ }
3647
+ },
3648
+ {
3649
+ name: "RedeemEpochRolledBack",
3650
+ type: {
3651
+ kind: "struct",
3652
+ fields: [
3653
+ {
3654
+ name: "epoch",
3655
+ type: "u64"
3656
+ },
3657
+ {
3658
+ name: "shares_restored",
3659
+ type: "u64"
3660
+ }
3661
+ ]
3662
+ }
3663
+ },
3664
+ {
3665
+ name: "RedeemLazySettled",
3666
+ docs: [
3667
+ "Emitted when a user's pending redeem is lazily settled"
3668
+ ],
3669
+ type: {
3670
+ kind: "struct",
3671
+ fields: [
3672
+ {
3673
+ name: "user",
3674
+ type: "pubkey"
3675
+ },
3676
+ {
3677
+ name: "redeem_epoch",
3678
+ type: "u64"
3679
+ },
3680
+ {
3681
+ name: "usdc_received",
3682
+ type: "u64"
3683
+ },
3684
+ {
3685
+ name: "is_rollback",
3686
+ type: "bool"
3687
+ }
3688
+ ]
3689
+ }
3690
+ },
3691
+ {
3692
+ name: "RedeemRequested",
3693
+ type: {
3694
+ kind: "struct",
3695
+ fields: [
3696
+ {
3697
+ name: "user",
3698
+ type: "pubkey"
3699
+ },
3700
+ {
3701
+ name: "shares",
3702
+ type: "u64"
3703
+ },
3704
+ {
3705
+ name: "redeem_epoch",
3706
+ type: "u64"
3707
+ }
3708
+ ]
3709
+ }
3710
+ },
3711
+ {
3712
+ name: "RedeemsSettled",
3713
+ type: {
3714
+ kind: "struct",
3715
+ fields: [
3716
+ {
3717
+ name: "epoch",
3718
+ type: "u64"
3719
+ },
3720
+ {
3721
+ name: "usdc_for_redeems",
3722
+ type: "u64"
3723
+ }
3724
+ ]
3725
+ }
3726
+ },
3727
+ {
3728
+ name: "UserAccount",
3729
+ docs: [
3730
+ "Per-user account tracking shares and pending operations",
3731
+ 'Seeds: [b"user", vault_mirror.key(), user_pubkey]',
3732
+ "",
3733
+ "pending_deposit_epoch refers to a DepositEpoch number.",
3734
+ "pending_redeem_epoch refers to a RedeemEpoch number.",
3735
+ "The two are completely independent."
3736
+ ],
3737
+ type: {
3738
+ kind: "struct",
3739
+ fields: [
3740
+ {
3741
+ name: "owner",
3742
+ docs: [
3743
+ "User wallet pubkey"
3744
+ ],
3745
+ type: "pubkey"
3746
+ },
3747
+ {
3748
+ name: "shares",
3749
+ docs: [
3750
+ "Settled (confirmed) shares"
3751
+ ],
3752
+ type: "u64"
3753
+ },
3754
+ {
3755
+ name: "pending_deposit_usdc",
3756
+ docs: [
3757
+ "USDC amount pending deposit in current open deposit epoch"
3758
+ ],
3759
+ type: "u64"
3760
+ },
3761
+ {
3762
+ name: "pending_deposit_epoch",
3763
+ docs: [
3764
+ "DepositEpoch number of pending deposit"
3765
+ ],
3766
+ type: "u64"
3767
+ },
3768
+ {
3769
+ name: "pending_redeem_shares",
3770
+ docs: [
3771
+ "Shares pending redemption in current open redeem epoch"
3772
+ ],
3773
+ type: "u64"
3774
+ },
3775
+ {
3776
+ name: "pending_redeem_epoch",
3777
+ docs: [
3778
+ "RedeemEpoch number of pending redeem"
3779
+ ],
3780
+ type: "u64"
3781
+ },
3782
+ {
3783
+ name: "claimable_usdc",
3784
+ docs: [
3785
+ "USDC available for user to claim"
3786
+ ],
3787
+ type: "u64"
3788
+ },
3789
+ {
3790
+ name: "bump",
3791
+ docs: [
3792
+ "Bump for this PDA"
3793
+ ],
3794
+ type: "u8"
3795
+ }
3796
+ ]
3797
+ }
3798
+ },
3799
+ {
3800
+ name: "UserClaimed",
3801
+ type: {
3802
+ kind: "struct",
3803
+ fields: [
3804
+ {
3805
+ name: "user",
3806
+ type: "pubkey"
3807
+ },
3808
+ {
3809
+ name: "amount",
3810
+ type: "u64"
3811
+ }
3812
+ ]
3813
+ }
3814
+ },
3815
+ {
3816
+ name: "UserDeposited",
3817
+ type: {
3818
+ kind: "struct",
3819
+ fields: [
3820
+ {
3821
+ name: "user",
3822
+ type: "pubkey"
3823
+ },
3824
+ {
3825
+ name: "amount",
3826
+ type: "u64"
3827
+ },
3828
+ {
3829
+ name: "deposit_epoch",
3830
+ type: "u64"
3831
+ },
3832
+ {
3833
+ name: "total_pending",
3834
+ type: "u64"
3835
+ }
3836
+ ]
3837
+ }
3838
+ },
3839
+ {
3840
+ name: "VaultFunded",
3841
+ type: {
3842
+ kind: "struct",
3843
+ fields: [
3844
+ {
3845
+ name: "curator",
3846
+ type: "pubkey"
3847
+ },
3848
+ {
3849
+ name: "amount",
3850
+ type: "u64"
3851
+ }
3852
+ ]
3853
+ }
3854
+ },
3855
+ {
3856
+ name: "VaultInitialized",
3857
+ type: {
3858
+ kind: "struct",
3859
+ fields: [
3860
+ {
3861
+ name: "authority",
3862
+ type: "pubkey"
3863
+ },
3864
+ {
3865
+ name: "curator",
3866
+ type: "pubkey"
3867
+ },
3868
+ {
3869
+ name: "usdc_mint",
3870
+ type: "pubkey"
3871
+ },
3872
+ {
3873
+ name: "max_epoch_deposit",
3874
+ type: "u64"
3875
+ }
3876
+ ]
3877
+ }
3878
+ },
3879
+ {
3880
+ name: "VaultMirror",
3881
+ docs: [
3882
+ "Global vault mirror \u2014 single PDA holding all state",
3883
+ 'Seeds: [b"vault_mirror"]'
3884
+ ],
3885
+ type: {
3886
+ kind: "struct",
3887
+ fields: [
3888
+ {
3889
+ name: "authority",
3890
+ docs: [
3891
+ "Admin authority (can pause, set curator/operator, change config)"
3892
+ ],
3893
+ type: "pubkey"
3894
+ },
3895
+ {
3896
+ name: "curator",
3897
+ docs: [
3898
+ "Curator multisig (2-of-3) \u2014 sensitive ops: mark_bridging, settle, rollback"
3899
+ ],
3900
+ type: "pubkey"
3901
+ },
3902
+ {
3903
+ name: "operator",
3904
+ docs: [
3905
+ "Operator multisig (1-of-3) \u2014 routine ops: freeze epochs"
3906
+ ],
3907
+ type: "pubkey"
3908
+ },
3909
+ {
3910
+ name: "usdc_mint",
3911
+ docs: [
3912
+ "USDC SPL token mint"
3913
+ ],
3914
+ type: "pubkey"
3915
+ },
3916
+ {
3917
+ name: "usdc_vault",
3918
+ docs: [
3919
+ "PDA token account holding all USDC deposits"
3920
+ ],
3921
+ type: "pubkey"
3922
+ },
3923
+ {
3924
+ name: "total_shares",
3925
+ docs: [
3926
+ "Total shares across all users"
3927
+ ],
3928
+ type: "u64"
3929
+ },
3930
+ {
3931
+ name: "current_deposit_epoch",
3932
+ docs: [
3933
+ "Current deposit epoch number (independent of redeem epochs)"
3934
+ ],
3935
+ type: "u64"
3936
+ },
3937
+ {
3938
+ name: "current_redeem_epoch",
3939
+ docs: [
3940
+ "Current redeem epoch number (independent of deposit epochs)"
3941
+ ],
3942
+ type: "u64"
3943
+ },
3944
+ {
3945
+ name: "max_epoch_deposit",
3946
+ docs: [
3947
+ "Global cap on deposits per epoch"
3948
+ ],
3949
+ type: "u64"
3950
+ },
3951
+ {
3952
+ name: "min_deposit",
3953
+ docs: [
3954
+ "Minimum deposit amount (e.g. 1 USDC = 1_000_000)"
3955
+ ],
3956
+ type: "u64"
3957
+ },
3958
+ {
3959
+ name: "paused",
3960
+ docs: [
3961
+ "Whether deposits and redeems are paused (claim always works)"
3962
+ ],
3963
+ type: "bool"
3964
+ },
3965
+ {
3966
+ name: "bump",
3967
+ docs: [
3968
+ "Bump for VaultMirror PDA"
3969
+ ],
3970
+ type: "u8"
3971
+ },
3972
+ {
3973
+ name: "vault_bump",
3974
+ docs: [
3975
+ "Bump for usdc_vault token account PDA"
3976
+ ],
3977
+ type: "u8"
3978
+ }
3979
+ ]
3980
+ }
3981
+ },
3982
+ {
3983
+ name: "VaultPaused",
3984
+ type: {
3985
+ kind: "struct",
3986
+ fields: [
3987
+ {
3988
+ name: "paused",
3989
+ type: "bool"
3990
+ }
3991
+ ]
3992
+ }
3993
+ }
3994
+ ]
3995
+ };
3996
+
3997
+ // src/client.ts
3998
+ var TBookVault = class _TBookVault {
3999
+ /** Solana RPC connection. */
4000
+ connection;
4001
+ /** Active network. */
4002
+ network;
4003
+ /** Default timeout for RPC calls in milliseconds. */
4004
+ timeoutMs;
4005
+ apiKey;
4006
+ /** Cache of Anchor programs, keyed by vault ID. */
4007
+ programs = /* @__PURE__ */ new Map();
4008
+ /** Timestamps of when each cached program was created. */
4009
+ programCreatedAt = /* @__PURE__ */ new Map();
4010
+ /** Maximum age for cached programs (5 minutes). */
4011
+ static PROGRAM_CACHE_TTL_MS = 5 * 60 * 1e3;
4012
+ constructor(options) {
4013
+ this.network = options.network;
4014
+ this.apiKey = options.apiKey;
4015
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_RPC_TIMEOUT_MS;
4016
+ this.connection = new Connection(
4017
+ options.rpcUrl ?? getDefaultRpcUrl(options.network),
4018
+ {
4019
+ commitment: "confirmed",
4020
+ confirmTransactionInitialTimeout: this.timeoutMs
4021
+ }
4022
+ );
4023
+ }
4024
+ // ── Vault Registry ──
4025
+ /**
4026
+ * List all available vaults on the current network.
4027
+ *
4028
+ * Currently returns built-in vaults only. In the future, this will
4029
+ * also query the TBook API for dynamically registered vaults.
4030
+ */
4031
+ async listVaults() {
4032
+ return listBuiltInVaults(this.network);
4033
+ }
4034
+ // ── Read Operations ──
4035
+ /**
4036
+ * Fetch global vault state and current epoch info.
4037
+ *
4038
+ * @param vaultId - Vault identifier (default: "rcUSDP")
4039
+ */
4040
+ async getVaultInfo(vaultId) {
4041
+ const config = this.resolveVault(vaultId);
4042
+ const program = this.getProgram(config);
4043
+ return fetchVaultInfo(this.connection, config.programId, program);
4044
+ }
4045
+ /**
4046
+ * Fetch a user's vault account with effective balances.
4047
+ *
4048
+ * Returns `null` if the user has never interacted with the vault.
4049
+ *
4050
+ * @param user - User's Solana public key (string or PublicKey)
4051
+ * @param vaultId - Vault identifier (default: "rcUSDP")
4052
+ */
4053
+ async getUserAccount(user, vaultId) {
4054
+ const config = this.resolveVault(vaultId);
4055
+ const program = this.getProgram(config);
4056
+ const pubkey = typeof user === "string" ? new PublicKey(user) : user;
4057
+ return fetchUserAccount(this.connection, config.programId, pubkey, program);
4058
+ }
4059
+ /**
4060
+ * Fetch current share price from the NAV oracle.
4061
+ *
4062
+ * Uses a 3-layer fallback: fresh cache -> API fetch -> stale cache.
4063
+ * The API endpoint (NAV oracle) is the authoritative price source.
4064
+ *
4065
+ * @param vaultId - Vault identifier. Currently all registered vaults share
4066
+ * one NAV source; per-vault price URLs land with the multi-asset registry
4067
+ * (VaultConfig.sharePriceUrl) in 0.2.0.
4068
+ */
4069
+ async getSharePrice(vaultId) {
4070
+ if (vaultId) this.resolveVault(vaultId);
4071
+ return fetchSharePrice();
4072
+ }
4073
+ // ── Write Operations (unsigned TX builders) ──
4074
+ /**
4075
+ * Build an unsigned deposit transaction.
4076
+ *
4077
+ * @param params - Deposit parameters (user + amount)
4078
+ * @param vaultId - Vault identifier (default: "rcUSDP")
4079
+ * @returns Unsigned transaction + base58 serialization + description
4080
+ */
4081
+ async buildDeposit(params, vaultId) {
4082
+ const config = this.resolveVault(vaultId);
4083
+ const program = this.getProgram(config);
4084
+ return buildDepositTx(program, params, { timeoutMs: this.timeoutMs });
4085
+ }
4086
+ /**
4087
+ * Build an unsigned redeem transaction.
4088
+ *
4089
+ * @param params - Redeem parameters (user + shares)
4090
+ * @param vaultId - Vault identifier (default: "rcUSDP")
4091
+ */
4092
+ async buildRedeem(params, vaultId) {
4093
+ const config = this.resolveVault(vaultId);
4094
+ const program = this.getProgram(config);
4095
+ return buildRedeemTx(program, params, { timeoutMs: this.timeoutMs });
4096
+ }
4097
+ /**
4098
+ * Build an unsigned claim transaction.
4099
+ * Claim always works even when the vault is paused.
4100
+ *
4101
+ * @param params - User parameters
4102
+ * @param vaultId - Vault identifier (default: "rcUSDP")
4103
+ */
4104
+ async buildClaim(params, vaultId) {
4105
+ const config = this.resolveVault(vaultId);
4106
+ const program = this.getProgram(config);
4107
+ return buildClaimTx(program, params, { timeoutMs: this.timeoutMs });
4108
+ }
4109
+ /**
4110
+ * Build an unsigned cancel-deposit transaction.
4111
+ * Returns pending USDC minus 0.1% fee.
4112
+ *
4113
+ * @param params - User parameters
4114
+ * @param vaultId - Vault identifier (default: "rcUSDP")
4115
+ */
4116
+ async buildCancelDeposit(params, vaultId) {
4117
+ const config = this.resolveVault(vaultId);
4118
+ const program = this.getProgram(config);
4119
+ return buildCancelDepositTx(program, params, { timeoutMs: this.timeoutMs });
4120
+ }
4121
+ // ── History ──
4122
+ /**
4123
+ * Fetch vault transaction history for a user.
4124
+ *
4125
+ * Returns classified transactions (deposit, redeem, claim, cancel_deposit)
4126
+ * by querying the user's PDA account for signatures and parsing instruction data.
4127
+ *
4128
+ * @param userAddress - User's Solana wallet address (string or PublicKey)
4129
+ * @param options - Pagination options (limit, before cursor)
4130
+ * @param vaultId - Vault identifier (default: "rcUSDP")
4131
+ * @returns Array of classified vault transactions, newest first
4132
+ */
4133
+ async getTransactionHistory(userAddress, options, vaultId) {
4134
+ const config = this.resolveVault(vaultId);
4135
+ const pubkey = typeof userAddress === "string" ? new PublicKey(userAddress) : userAddress;
4136
+ const [vaultMirror] = getVaultMirrorPda(config.programId);
4137
+ const [userPda] = getUserPda(vaultMirror, pubkey, config.programId);
4138
+ return fetchTransactionHistory(
4139
+ this.connection,
4140
+ userPda,
4141
+ config.programId,
4142
+ options
4143
+ );
4144
+ }
4145
+ // ── Utility Methods ──
4146
+ /**
4147
+ * Estimate the number of shares that a USDC deposit would yield
4148
+ * at the current share price.
4149
+ *
4150
+ * @param amountUsdc - USDC amount (human-readable)
4151
+ * @returns Estimated shares as a string, or null if price unavailable
4152
+ */
4153
+ async estimateShares(amountUsdc) {
4154
+ try {
4155
+ const price = await this.getSharePrice();
4156
+ if (price.priceNum <= 0) return null;
4157
+ const shares = amountUsdc / price.priceNum;
4158
+ return shares.toFixed(6);
4159
+ } catch {
4160
+ return null;
4161
+ }
4162
+ }
4163
+ /**
4164
+ * Estimate the USDC value of a given number of shares
4165
+ * at the current share price.
4166
+ *
4167
+ * @param shares - Number of shares (human-readable)
4168
+ * @returns Estimated USDC value as a string, or null if price unavailable
4169
+ */
4170
+ async estimateUsdc(shares) {
4171
+ try {
4172
+ const price = await this.getSharePrice();
4173
+ const usdc = shares * price.priceNum;
4174
+ return usdc.toFixed(6);
4175
+ } catch {
4176
+ return null;
4177
+ }
4178
+ }
4179
+ // ── Internal ──
4180
+ /**
4181
+ * Resolve a vault config by ID, falling back to the default vault.
4182
+ * @throws {UnknownVaultError} If the vault ID is not registered
4183
+ */
4184
+ resolveVault(vaultId) {
4185
+ const id = vaultId ?? DEFAULT_VAULT_ID;
4186
+ const config = getVaultConfig(id, this.network);
4187
+ if (!config) {
4188
+ throw new UnknownVaultError(id);
4189
+ }
4190
+ return config;
4191
+ }
4192
+ /**
4193
+ * Get or create a cached Anchor Program instance for a vault.
4194
+ *
4195
+ * Uses a read-only provider (no signer) since the SDK only builds
4196
+ * transactions — it never signs or submits them.
4197
+ */
4198
+ getProgram(config) {
4199
+ const key = `${config.network}:${config.id}`;
4200
+ let program = this.programs.get(key);
4201
+ const createdAt = this.programCreatedAt.get(key);
4202
+ if (program && createdAt && Date.now() - createdAt > _TBookVault.PROGRAM_CACHE_TTL_MS) {
4203
+ this.programs.delete(key);
4204
+ this.programCreatedAt.delete(key);
4205
+ program = void 0;
4206
+ }
4207
+ if (!program) {
4208
+ const dummyWallet = {
4209
+ publicKey: Keypair.generate().publicKey,
4210
+ signTransaction: async (tx) => tx,
4211
+ signAllTransactions: async (txs) => txs
4212
+ };
4213
+ const provider = new AnchorProvider(this.connection, dummyWallet, {
4214
+ commitment: "confirmed"
4215
+ });
4216
+ const idl = this.resolveIdl(config);
4217
+ program = new Program(idl, provider);
4218
+ this.programs.set(key, program);
4219
+ this.programCreatedAt.set(key, Date.now());
4220
+ }
4221
+ return program;
4222
+ }
4223
+ /**
4224
+ * Resolve the Anchor IDL for a given vault configuration.
4225
+ * Currently only the built-in rcUSDP devnet IDL is supported.
4226
+ */
4227
+ resolveIdl(config) {
4228
+ return vault_gateway_devnet_default;
4229
+ }
4230
+ };
4231
+
4232
+ // src/utils.ts
4233
+ function truncateNumber(value, decimals) {
4234
+ const factor = Math.pow(10, decimals);
4235
+ return Math.trunc(value * factor) / factor;
4236
+ }
4237
+ function formatTruncated(value, decimals) {
4238
+ return truncateNumber(value, decimals).toFixed(decimals);
4239
+ }
4240
+ function formatNumber(value, decimals = 2) {
4241
+ const truncated = truncateNumber(value, decimals);
4242
+ return new Intl.NumberFormat("en-US", {
4243
+ minimumFractionDigits: decimals,
4244
+ maximumFractionDigits: decimals
4245
+ }).format(truncated);
4246
+ }
4247
+ function formatCurrency(value, currency = "USD") {
4248
+ const truncated = truncateNumber(value, 2);
4249
+ return new Intl.NumberFormat("en-US", {
4250
+ style: "currency",
4251
+ currency,
4252
+ minimumFractionDigits: 2,
4253
+ maximumFractionDigits: 2
4254
+ }).format(truncated);
4255
+ }
4256
+ function formatPercentage(value, decimals = 2) {
4257
+ return `${formatNumber(value, decimals)}%`;
4258
+ }
4259
+ function shortenAddress(address, chars = 4) {
4260
+ if (address.length <= chars * 2 + 3) return address;
4261
+ return `${address.slice(0, chars)}...${address.slice(-chars)}`;
4262
+ }
4263
+ function formatUsdc(raw) {
4264
+ return formatNumber(Number(raw) / 10 ** USDC_DECIMALS, USDC_DECIMALS);
4265
+ }
4266
+ function formatShares(raw) {
4267
+ return formatNumber(Number(raw) / 10 ** SHARE_DECIMALS, SHARE_DECIMALS);
4268
+ }
4269
+ function rawToUi(raw, decimals) {
4270
+ return Number(raw) / Math.pow(10, decimals);
4271
+ }
4272
+ function uiToRaw(ui, decimals) {
4273
+ return Math.floor(ui * Math.pow(10, decimals));
4274
+ }
4275
+ function validateAmount(amount, balance, action, token, min, max) {
4276
+ const numericAmount = Number(amount);
4277
+ if (isNaN(numericAmount) || numericAmount <= 0) {
4278
+ return { valid: false, error: "Please enter a valid amount greater than 0" };
4279
+ }
4280
+ if (numericAmount > balance) {
4281
+ return { valid: false, error: `You only have ${balance} ${token}.` };
4282
+ }
4283
+ if (min !== void 0 && numericAmount < min) {
4284
+ return { valid: false, error: `You need to ${action} at least ${min} ${token}.` };
4285
+ }
4286
+ if (max !== void 0 && numericAmount > max) {
4287
+ return { valid: false, error: `You can only ${action} up to ${max} ${token}.` };
4288
+ }
4289
+ return { valid: true };
4290
+ }
4291
+ function isValidAmountInput(value) {
4292
+ return value === "" || /^\d*\.?\d*$/.test(value);
4293
+ }
4294
+
4295
+ export { DEFAULT_CANCEL_FEE_BPS, DEFAULT_VAULT_ID, EpochNotOpenError, MaxEpochDepositError, MinDepositError, NoPendingDepositError, ONE_SHARE, ONE_USDC, RpcError, RpcTimeoutError, SHARE_DECIMALS, STATUS_MAP, SharePriceCache, SharePriceUnavailableError, TBookVault, TransactionExpiredError, TransactionFailedError, USDC_DECIMALS, UnknownVaultError, UserAccountNotFoundError, VaultNotFoundError, VaultPausedError, VaultSdkError, buildCancelDepositTx, buildClaimTx, buildDepositTx, buildRedeemTx, confirmTransactionWithRetry, explorerUrl, fetchSharePrice, fetchTransactionHistory, fetchUserAccount, fetchVaultInfo, formatCurrency, formatNumber, formatPercentage, formatShares, formatTruncated, formatUsdc, getDefaultRpcUrl, getDepositEpochPda, getRedeemEpochPda, getUsdcVaultPda, getUserPda, getVaultConfig, getVaultMirrorPda, isValidAmountInput, listBuiltInVaults, rawToUi, sharePriceCache, shortenAddress, sleep, truncateNumber, uiToRaw, validateAmount, withTimeout };
4296
+ //# sourceMappingURL=index.js.map
4297
+ //# sourceMappingURL=index.js.map