@loyal-labs/private-transactions 0.2.8 → 0.2.9

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 CHANGED
@@ -1,17 +1,14 @@
1
1
  // src/LoyalPrivateTransactionsClient.ts
2
2
  import {
3
- Connection,
4
- PublicKey as PublicKey4,
5
- SystemProgram
3
+ Connection as Connection3,
4
+ PublicKey as PublicKey7,
5
+ SystemProgram as SystemProgram5,
6
+ Transaction as Transaction7
6
7
  } from "@solana/web3.js";
7
- import { AnchorProvider, BN, Program } from "@coral-xyz/anchor";
8
- import {
9
- getAssociatedTokenAddressSync,
10
- TOKEN_PROGRAM_ID,
11
- ASSOCIATED_TOKEN_PROGRAM_ID
12
- } from "@solana/spl-token";
8
+ import { AnchorProvider as AnchorProvider2, BN as BN2, Program as Program2 } from "@coral-xyz/anchor";
9
+ import { TOKEN_PROGRAM_ID as TOKEN_PROGRAM_ID4 } from "@solana/spl-token";
13
10
  import {
14
- verifyTeeRpcIntegrity,
11
+ verifyTeeIntegrity,
15
12
  getAuthToken
16
13
  } from "@magicblock-labs/ephemeral-rollups-sdk";
17
14
  // src/idl/telegram_private_transfer.json
@@ -105,6 +102,107 @@ var telegram_private_transfer_default = {
105
102
  }
106
103
  ]
107
104
  },
105
+ {
106
+ name: "close_deposit",
107
+ docs: [
108
+ "Closes an empty user deposit account and returns its rent to the deposit owner."
109
+ ],
110
+ discriminator: [
111
+ 200,
112
+ 19,
113
+ 254,
114
+ 192,
115
+ 15,
116
+ 110,
117
+ 209,
118
+ 179
119
+ ],
120
+ accounts: [
121
+ {
122
+ name: "user",
123
+ writable: true,
124
+ signer: true,
125
+ relations: [
126
+ "deposit"
127
+ ]
128
+ },
129
+ {
130
+ name: "deposit",
131
+ writable: true,
132
+ pda: {
133
+ seeds: [
134
+ {
135
+ kind: "const",
136
+ value: [
137
+ 100,
138
+ 101,
139
+ 112,
140
+ 111,
141
+ 115,
142
+ 105,
143
+ 116,
144
+ 95,
145
+ 118,
146
+ 50
147
+ ]
148
+ },
149
+ {
150
+ kind: "account",
151
+ path: "user"
152
+ },
153
+ {
154
+ kind: "account",
155
+ path: "token_mint"
156
+ }
157
+ ]
158
+ }
159
+ },
160
+ {
161
+ name: "token_mint",
162
+ relations: [
163
+ "deposit"
164
+ ]
165
+ }
166
+ ],
167
+ args: []
168
+ },
169
+ {
170
+ name: "close_username_deposit",
171
+ docs: [
172
+ "Closes an empty username deposit account after verified username ownership."
173
+ ],
174
+ discriminator: [
175
+ 238,
176
+ 181,
177
+ 185,
178
+ 209,
179
+ 149,
180
+ 161,
181
+ 124,
182
+ 79
183
+ ],
184
+ accounts: [
185
+ {
186
+ name: "authority",
187
+ writable: true,
188
+ signer: true
189
+ },
190
+ {
191
+ name: "deposit",
192
+ writable: true
193
+ },
194
+ {
195
+ name: "token_mint",
196
+ relations: [
197
+ "deposit"
198
+ ]
199
+ },
200
+ {
201
+ name: "session"
202
+ }
203
+ ],
204
+ args: []
205
+ },
108
206
  {
109
207
  name: "create_permission",
110
208
  docs: [
@@ -817,10 +915,20 @@ var telegram_private_transfer_default = {
817
915
  {
818
916
  name: "modify_balance",
819
917
  docs: [
820
- "Modifies the balance of a user's deposit account by transferring tokens in or out.",
918
+ "Modifies a user's deposit balance and the backing vault position for the given mint.",
919
+ "",
920
+ "For non-USDC mints, this is a direct vault transfer: if `args.increase` is true, `args.amount`",
921
+ "is transferred from the user's token account to the vault token account and added to",
922
+ "`deposit.amount`. If false, `args.amount` is transferred from the vault token account back to",
923
+ "the user's token account and subtracted from `deposit.amount`.",
821
924
  "",
822
- "If `args.increase` is true, tokens are transferred from the user's token account to the deposit account.",
823
- "If false, tokens are transferred from the deposit account back to the user's token account."
925
+ "For USDC, liquidity is routed through Kamino Lending instead of being left idle in the vault.",
926
+ "If `args.increase` is true, `args.amount` USDC is transferred into the vault token account,",
927
+ "supplied to the configured Kamino reserve, and `deposit.amount` is increased by the Kamino",
928
+ "reserve collateral shares (kTokens) minted to the vault. If false, `args.amount` is",
929
+ "interpreted as the Kamino share amount to redeem; the reserve returns the corresponding USDC",
930
+ "at the current exchange rate, that USDC is transferred from the vault token account to the",
931
+ "user's token account, and `deposit.amount` is decreased by the burned share amount."
824
932
  ],
825
933
  discriminator: [
826
934
  148,
@@ -1657,6 +1765,11 @@ var telegram_private_transfer_default = {
1657
1765
  code: 6013,
1658
1766
  name: "InvalidAmount",
1659
1767
  msg: "Invalid amount"
1768
+ },
1769
+ {
1770
+ code: 6014,
1771
+ name: "NonZeroDeposit",
1772
+ msg: "Deposit account must have zero amount before it can be closed"
1660
1773
  }
1661
1774
  ],
1662
1775
  types: [
@@ -1823,14 +1936,14 @@ import {
1823
1936
  LAMPORTS_PER_SOL,
1824
1937
  SYSVAR_INSTRUCTIONS_PUBKEY
1825
1938
  } from "@solana/web3.js";
1826
- var ER_VALIDATOR_DEVNET = new PublicKey("FnE6VJT5QNZdedZPnCoLsARgBwoE6DeJNjBs2H1gySXA");
1939
+ var ER_VALIDATOR_DEVNET = new PublicKey("MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo");
1827
1940
  var ER_VALIDATOR_MAINNET = new PublicKey("MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo");
1828
1941
  var ER_VALIDATOR = ER_VALIDATOR_DEVNET;
1829
1942
  function getErValidatorForSolanaEnv(env) {
1830
1943
  return env === "mainnet" ? ER_VALIDATOR_MAINNET : ER_VALIDATOR_DEVNET;
1831
1944
  }
1832
- function getErValidatorForRpcEndpoint(rpcEndpoint) {
1833
- return rpcEndpoint.includes("mainnet-tee") ? ER_VALIDATOR_MAINNET : ER_VALIDATOR_DEVNET;
1945
+ function getErValidatorForRpcEndpoint(perRpcEndpoint) {
1946
+ return perRpcEndpoint.includes("mainnet-tee") ? ER_VALIDATOR_MAINNET : ER_VALIDATOR_DEVNET;
1834
1947
  }
1835
1948
  function getKaminoModifyBalanceAccountsForTokenMint(tokenMint) {
1836
1949
  if (tokenMint.equals(USDC_MINT_MAINNET)) {
@@ -1889,6 +2002,125 @@ function lamportsToSol(lamports) {
1889
2002
  return lamports / LAMPORTS_PER_SOL;
1890
2003
  }
1891
2004
 
2005
+ // src/enumerate-deposits.ts
2006
+ import { AnchorProvider, Program } from "@coral-xyz/anchor";
2007
+ import {
2008
+ PublicKey as PublicKey2
2009
+ } from "@solana/web3.js";
2010
+ var DEPOSIT_ACCOUNT_SIZE = 80;
2011
+ var DEPOSIT_USER_OFFSET = 8;
2012
+ var DEPOSIT_MINT_OFFSET = 40;
2013
+ var DEPOSIT_AMOUNT_OFFSET = 72;
2014
+
2015
+ class ReadOnlyWallet {
2016
+ publicKey;
2017
+ constructor(publicKey) {
2018
+ this.publicKey = publicKey;
2019
+ }
2020
+ async signTransaction(_tx) {
2021
+ throw new Error("ReadOnlyWallet cannot sign transactions; construct a real client for write paths.");
2022
+ }
2023
+ async signAllTransactions(_txs) {
2024
+ throw new Error("ReadOnlyWallet cannot sign transactions; construct a real client for write paths.");
2025
+ }
2026
+ }
2027
+ function createReadOnlyDepositProgram(connection) {
2028
+ const wallet = new ReadOnlyWallet(PublicKey2.default);
2029
+ const provider = new AnchorProvider(connection, wallet, {
2030
+ commitment: connection.commitment ?? "confirmed"
2031
+ });
2032
+ return new Program(telegram_private_transfer_default, provider);
2033
+ }
2034
+ function readDepositAmount(data) {
2035
+ let value = BigInt(0);
2036
+ for (let i = 0;i < 8; i++) {
2037
+ value += BigInt(data[DEPOSIT_AMOUNT_OFFSET + i] ?? 0) << BigInt(i * 8);
2038
+ }
2039
+ return value;
2040
+ }
2041
+ async function readDelegatedDepositsOnBase(baseConnection, user) {
2042
+ const accounts = await baseConnection.getProgramAccounts(DELEGATION_PROGRAM_ID, {
2043
+ filters: [
2044
+ { dataSize: DEPOSIT_ACCOUNT_SIZE },
2045
+ {
2046
+ memcmp: {
2047
+ offset: DEPOSIT_USER_OFFSET,
2048
+ bytes: user.toBase58()
2049
+ }
2050
+ }
2051
+ ]
2052
+ });
2053
+ const result = [];
2054
+ for (const { pubkey, account } of accounts) {
2055
+ const data = account.data;
2056
+ if (data.length < DEPOSIT_ACCOUNT_SIZE)
2057
+ continue;
2058
+ const tokenMint = new PublicKey2(data.subarray(DEPOSIT_MINT_OFFSET, DEPOSIT_MINT_OFFSET + 32));
2059
+ result.push({
2060
+ user,
2061
+ tokenMint,
2062
+ amount: readDepositAmount(data),
2063
+ address: pubkey
2064
+ });
2065
+ }
2066
+ return result;
2067
+ }
2068
+ async function enumerateDepositsByUser(args) {
2069
+ const userFilter = [
2070
+ {
2071
+ memcmp: {
2072
+ offset: DEPOSIT_USER_OFFSET,
2073
+ bytes: args.user.toBase58()
2074
+ }
2075
+ }
2076
+ ];
2077
+ const baseProgram = createReadOnlyDepositProgram(args.baseConnection);
2078
+ const ephemeralProgram = args.ephemeralConnection ? createReadOnlyDepositProgram(args.ephemeralConnection) : null;
2079
+ const [baseUndelegatedResult, baseDelegatedResult, ephemeralResult] = await Promise.allSettled([
2080
+ baseProgram.account.deposit.all(userFilter),
2081
+ readDelegatedDepositsOnBase(args.baseConnection, args.user),
2082
+ ephemeralProgram ? ephemeralProgram.account.deposit.all(userFilter) : Promise.resolve([])
2083
+ ]);
2084
+ const byPda = new Map;
2085
+ const ingestAnchor = (results, preferOverwrite) => {
2086
+ for (const { publicKey, account } of results) {
2087
+ const key = publicKey.toBase58();
2088
+ if (!preferOverwrite && byPda.has(key))
2089
+ continue;
2090
+ byPda.set(key, {
2091
+ user: account.user,
2092
+ tokenMint: account.tokenMint,
2093
+ amount: BigInt(account.amount.toString()),
2094
+ address: publicKey
2095
+ });
2096
+ }
2097
+ };
2098
+ const ingestRaw = (results, preferOverwrite) => {
2099
+ for (const data of results) {
2100
+ const key = data.address.toBase58();
2101
+ if (!preferOverwrite && byPda.has(key))
2102
+ continue;
2103
+ byPda.set(key, data);
2104
+ }
2105
+ };
2106
+ if (baseUndelegatedResult.status === "fulfilled") {
2107
+ ingestAnchor(baseUndelegatedResult.value, false);
2108
+ } else {
2109
+ console.warn("[enumerateDepositsByUser] base undelegated enumeration failed", baseUndelegatedResult.reason);
2110
+ }
2111
+ if (baseDelegatedResult.status === "fulfilled") {
2112
+ ingestRaw(baseDelegatedResult.value, true);
2113
+ } else {
2114
+ console.warn("[enumerateDepositsByUser] base delegated enumeration failed", baseDelegatedResult.reason);
2115
+ }
2116
+ if (ephemeralResult.status === "fulfilled" && ephemeralProgram) {
2117
+ ingestAnchor(ephemeralResult.value, true);
2118
+ } else if (ephemeralProgram && ephemeralResult.status === "rejected") {
2119
+ console.warn("[enumerateDepositsByUser] ephemeral enumeration failed", ephemeralResult.reason);
2120
+ }
2121
+ return Array.from(byPda.values());
2122
+ }
2123
+
1892
2124
  // src/kamino.ts
1893
2125
  var KAMINO_RESERVE_DISCRIMINATOR = Buffer.from([
1894
2126
  43,
@@ -1902,14 +2134,24 @@ var KAMINO_RESERVE_DISCRIMINATOR = Buffer.from([
1902
2134
  ]);
1903
2135
  var KAMINO_FRACTION_BITS = 60n;
1904
2136
  var KAMINO_FRACTION_SCALE = 1n << KAMINO_FRACTION_BITS;
2137
+ function bytesEqual(a, b) {
2138
+ if (a.length !== b.length)
2139
+ return false;
2140
+ for (let i = 0;i < a.length; i++) {
2141
+ if (a[i] !== b[i])
2142
+ return false;
2143
+ }
2144
+ return true;
2145
+ }
2146
+ var KAMINO_DISCRIMINATOR_OFFSET = 8;
1905
2147
  var KAMINO_RESERVE_LAYOUT_OFFSETS = {
1906
- liquidityAvailableAmount: 216,
1907
- liquidityBorrowedAmountSf: 224,
1908
- liquidityMintDecimals: 264,
1909
- liquidityAccumulatedProtocolFeesSf: 336,
1910
- liquidityAccumulatedReferrerFeesSf: 352,
1911
- liquidityPendingReferrerFeesSf: 368,
1912
- collateralMintTotalSupply: 2584
2148
+ liquidityAvailableAmount: KAMINO_DISCRIMINATOR_OFFSET + 216,
2149
+ liquidityBorrowedAmountSf: KAMINO_DISCRIMINATOR_OFFSET + 224,
2150
+ liquidityMintDecimals: KAMINO_DISCRIMINATOR_OFFSET + 264,
2151
+ liquidityAccumulatedProtocolFeesSf: KAMINO_DISCRIMINATOR_OFFSET + 336,
2152
+ liquidityAccumulatedReferrerFeesSf: KAMINO_DISCRIMINATOR_OFFSET + 352,
2153
+ liquidityPendingReferrerFeesSf: KAMINO_DISCRIMINATOR_OFFSET + 368,
2154
+ collateralMintTotalSupply: KAMINO_DISCRIMINATOR_OFFSET + 2584
1913
2155
  };
1914
2156
  function readUint64LE(data, offset) {
1915
2157
  return data.readBigUInt64LE(offset);
@@ -1929,22 +2171,22 @@ function divCeil(numerator, denominator) {
1929
2171
  return (numerator + denominator - 1n) / denominator;
1930
2172
  }
1931
2173
  function parseKaminoReserveSnapshotFromAccountData(args) {
1932
- const { data, reserve, tokenMint } = args;
1933
- if (data.length < 8 || !data.subarray(0, 8).equals(KAMINO_RESERVE_DISCRIMINATOR)) {
2174
+ const { reserve, tokenMint } = args;
2175
+ const data = Buffer.isBuffer(args.data) ? args.data : Buffer.from(args.data);
2176
+ if (data.length < 8 || !bytesEqual(data.subarray(0, 8), KAMINO_RESERVE_DISCRIMINATOR)) {
1934
2177
  throw new Error(`Kamino reserve ${reserve.toBase58()} has an invalid discriminator`);
1935
2178
  }
1936
- const accountData = data.subarray(8);
1937
2179
  const requiredLength = KAMINO_RESERVE_LAYOUT_OFFSETS.collateralMintTotalSupply + 8;
1938
- if (accountData.length < requiredLength) {
2180
+ if (data.length < requiredLength) {
1939
2181
  throw new Error(`Kamino reserve ${reserve.toBase58()} is too small: expected at least ${requiredLength} bytes`);
1940
2182
  }
1941
- const liquidityAvailableAmount = readUint64LE(accountData, KAMINO_RESERVE_LAYOUT_OFFSETS.liquidityAvailableAmount);
1942
- const liquidityBorrowedAmountSf = readUint128LE(accountData, KAMINO_RESERVE_LAYOUT_OFFSETS.liquidityBorrowedAmountSf);
1943
- const liquidityAccumulatedProtocolFeesSf = readUint128LE(accountData, KAMINO_RESERVE_LAYOUT_OFFSETS.liquidityAccumulatedProtocolFeesSf);
1944
- const liquidityAccumulatedReferrerFeesSf = readUint128LE(accountData, KAMINO_RESERVE_LAYOUT_OFFSETS.liquidityAccumulatedReferrerFeesSf);
1945
- const liquidityPendingReferrerFeesSf = readUint128LE(accountData, KAMINO_RESERVE_LAYOUT_OFFSETS.liquidityPendingReferrerFeesSf);
1946
- const collateralSupplyRaw = readUint64LE(accountData, KAMINO_RESERVE_LAYOUT_OFFSETS.collateralMintTotalSupply);
1947
- const liquidityDecimals = Number(readUint64LE(accountData, KAMINO_RESERVE_LAYOUT_OFFSETS.liquidityMintDecimals));
2183
+ const liquidityAvailableAmount = readUint64LE(data, KAMINO_RESERVE_LAYOUT_OFFSETS.liquidityAvailableAmount);
2184
+ const liquidityBorrowedAmountSf = readUint128LE(data, KAMINO_RESERVE_LAYOUT_OFFSETS.liquidityBorrowedAmountSf);
2185
+ const liquidityAccumulatedProtocolFeesSf = readUint128LE(data, KAMINO_RESERVE_LAYOUT_OFFSETS.liquidityAccumulatedProtocolFeesSf);
2186
+ const liquidityAccumulatedReferrerFeesSf = readUint128LE(data, KAMINO_RESERVE_LAYOUT_OFFSETS.liquidityAccumulatedReferrerFeesSf);
2187
+ const liquidityPendingReferrerFeesSf = readUint128LE(data, KAMINO_RESERVE_LAYOUT_OFFSETS.liquidityPendingReferrerFeesSf);
2188
+ const collateralSupplyRaw = readUint64LE(data, KAMINO_RESERVE_LAYOUT_OFFSETS.collateralMintTotalSupply);
2189
+ const liquidityDecimals = Number(readUint64LE(data, KAMINO_RESERVE_LAYOUT_OFFSETS.liquidityMintDecimals));
1948
2190
  const grossLiquiditySupplyScaled = (liquidityAvailableAmount << KAMINO_FRACTION_BITS) + liquidityBorrowedAmountSf;
1949
2191
  const totalFeeAmountScaled = liquidityAccumulatedProtocolFeesSf + liquidityAccumulatedReferrerFeesSf + liquidityPendingReferrerFeesSf;
1950
2192
  return {
@@ -2019,41 +2261,98 @@ async function fetchKaminoReserveSnapshot(args) {
2019
2261
  }
2020
2262
 
2021
2263
  // src/pda.ts
2022
- import { PublicKey as PublicKey2 } from "@solana/web3.js";
2264
+ import { PublicKey as PublicKey4 } from "@solana/web3.js";
2023
2265
 
2024
2266
  // src/utils.ts
2267
+ import { PublicKey as PublicKey3 } from "@solana/web3.js";
2268
+ function prettyStringify(obj) {
2269
+ const json = JSON.stringify(obj, (_key, value) => {
2270
+ if (value instanceof PublicKey3)
2271
+ return value.toBase58();
2272
+ if (typeof value === "bigint")
2273
+ return value.toString();
2274
+ return value;
2275
+ }, 2);
2276
+ return json.replace(/\[\s+(\d[\d,\s]*\d)\s+\]/g, (_match, inner) => {
2277
+ const items = inner.split(/,\s*/).map((s) => s.trim());
2278
+ return `[${items.join(", ")}]`;
2279
+ });
2280
+ }
2281
+ function waitForAccountOwnerChange(connection, account, expectedOwner, timeoutMs = 15000, intervalMs = 1000) {
2282
+ let skipWait;
2283
+ const subId = connection.onAccountChange(account, (accountInfo) => {
2284
+ if (accountInfo.owner.equals(expectedOwner) && skipWait) {
2285
+ console.log(`waitForAccountOwnerChange: ${account.toString()} - short-circuit polling wait`);
2286
+ skipWait();
2287
+ }
2288
+ }, { commitment: "confirmed" });
2289
+ const cleanup = async () => {
2290
+ await connection.removeAccountChangeListener(subId);
2291
+ };
2292
+ const wait = async () => {
2293
+ try {
2294
+ const start = Date.now();
2295
+ while (Date.now() - start < timeoutMs) {
2296
+ const info = await connection.getAccountInfo(account, "confirmed");
2297
+ if (info && info.owner.equals(expectedOwner)) {
2298
+ console.log(`waitForAccountOwnerChange: ${account.toString()} appeared with owner ${expectedOwner.toString()} after ${Date.now() - start}ms`);
2299
+ return;
2300
+ }
2301
+ if (info) {
2302
+ console.log(`waitForAccountOwnerChange: ${account.toString()} exists but owner is ${info.owner.toString()}, expected ${expectedOwner.toString()}`);
2303
+ }
2304
+ await new Promise((r) => {
2305
+ skipWait = r;
2306
+ setTimeout(r, intervalMs);
2307
+ });
2308
+ }
2309
+ throw new Error(`waitForAccountOwnerChange: ${account.toString()} did not appear with owner ${expectedOwner.toString()} after ${timeoutMs}ms`);
2310
+ } finally {
2311
+ await cleanup();
2312
+ }
2313
+ };
2314
+ return { wait, cancel: cleanup };
2315
+ }
2025
2316
  async function sha256hash(data) {
2026
2317
  const encoded = Uint8Array.from(new TextEncoder().encode(data));
2027
2318
  const hash = await crypto.subtle.digest("SHA-256", encoded);
2028
2319
  return Array.from(new Uint8Array(hash));
2029
2320
  }
2321
+ function validateUsername(username) {
2322
+ if (!username || username.length < 5 || username.length > 32) {
2323
+ throw new Error("Username must be between 5 and 32 characters");
2324
+ }
2325
+ if (!/^[a-z0-9_]+$/.test(username)) {
2326
+ throw new Error("Username can only contain lowercase alphanumeric characters and underscores");
2327
+ }
2328
+ }
2030
2329
 
2031
2330
  // src/pda.ts
2032
2331
  function findDepositPda(user, tokenMint, programId = PROGRAM_ID) {
2033
- return PublicKey2.findProgramAddressSync([DEPOSIT_SEED_BYTES, user.toBuffer(), tokenMint.toBuffer()], programId);
2332
+ return PublicKey4.findProgramAddressSync([DEPOSIT_SEED_BYTES, user.toBuffer(), tokenMint.toBuffer()], programId);
2034
2333
  }
2035
2334
  async function findUsernameDepositPda(username, tokenMint, programId = PROGRAM_ID) {
2036
2335
  const usernameHash = await sha256hash(username);
2037
- return PublicKey2.findProgramAddressSync([
2336
+ return PublicKey4.findProgramAddressSync([
2038
2337
  USERNAME_DEPOSIT_SEED_BYTES,
2039
2338
  Buffer.from(usernameHash),
2040
2339
  tokenMint.toBuffer()
2041
2340
  ], programId);
2042
2341
  }
2043
2342
  function findVaultPda(tokenMint, programId = PROGRAM_ID) {
2044
- return PublicKey2.findProgramAddressSync([VAULT_SEED_BYTES, tokenMint.toBuffer()], programId);
2343
+ return PublicKey4.findProgramAddressSync([VAULT_SEED_BYTES, tokenMint.toBuffer()], programId);
2045
2344
  }
2046
2345
  function findPermissionPda(account, permissionProgramId = PERMISSION_PROGRAM_ID) {
2047
- return PublicKey2.findProgramAddressSync([PERMISSION_SEED_BYTES, account.toBuffer()], permissionProgramId);
2346
+ return PublicKey4.findProgramAddressSync([PERMISSION_SEED_BYTES, account.toBuffer()], permissionProgramId);
2048
2347
  }
2049
2348
  function findDelegationRecordPda(account, delegationProgramId = DELEGATION_PROGRAM_ID) {
2050
- return PublicKey2.findProgramAddressSync([Buffer.from("delegation"), account.toBuffer()], delegationProgramId);
2349
+ return PublicKey4.findProgramAddressSync([Buffer.from("delegation"), account.toBuffer()], delegationProgramId);
2051
2350
  }
2052
2351
  function findDelegationMetadataPda(account, delegationProgramId = DELEGATION_PROGRAM_ID) {
2053
- return PublicKey2.findProgramAddressSync([Buffer.from("delegation-metadata"), account.toBuffer()], delegationProgramId);
2352
+ return PublicKey4.findProgramAddressSync([Buffer.from("delegation-metadata"), account.toBuffer()], delegationProgramId);
2054
2353
  }
2055
2354
  function findBufferPda(account, ownerProgramId = PROGRAM_ID) {
2056
- return PublicKey2.findProgramAddressSync([Buffer.from("buffer"), account.toBuffer()], ownerProgramId);
2355
+ return PublicKey4.findProgramAddressSync([Buffer.from("buffer"), account.toBuffer()], ownerProgramId);
2057
2356
  }
2058
2357
 
2059
2358
  // src/wallet-adapter.ts
@@ -2192,61 +2491,1621 @@ function createKeypairMessageSigner(keypair) {
2192
2491
  };
2193
2492
  }
2194
2493
 
2195
- // src/LoyalPrivateTransactionsClient.ts
2196
- var KAMINO_API_BASE_URL = "https://api.kamino.finance";
2197
- var KAMINO_MAINNET_ENV = "mainnet-beta";
2198
- var KAMINO_DEVNET_ENV = "devnet";
2199
- function prettyStringify(obj) {
2200
- const json = JSON.stringify(obj, (_key, value) => {
2201
- if (value instanceof PublicKey4)
2202
- return value.toBase58();
2203
- if (typeof value === "bigint")
2204
- return value.toString();
2205
- return value;
2206
- }, 2);
2207
- return json.replace(/\[\s+(\d[\d,\s]*\d)\s+\]/g, (_match, inner) => {
2208
- const items = inner.split(/,\s*/).map((s) => s.trim());
2209
- return `[${items.join(", ")}]`;
2494
+ // src/actions/shieldTokens.ts
2495
+ import { NATIVE_MINT as NATIVE_MINT2 } from "@solana/spl-token";
2496
+ import {
2497
+ Transaction as Transaction4
2498
+ } from "@solana/web3.js";
2499
+
2500
+ // src/rent-estimate.ts
2501
+ import { ACCOUNT_SIZE, getAssociatedTokenAddressSync } from "@solana/spl-token";
2502
+ var DISCRIMINATOR_SIZE = 8;
2503
+ var PUBLIC_KEY_SIZE = 32;
2504
+ var U64_SIZE = 8;
2505
+ var U8_SIZE = 1;
2506
+ var BOOL_SIZE = 1;
2507
+ var VEC_PREFIX_SIZE = 4;
2508
+ var MAGICBLOCK_UNDELEGATE_SESSION_FEE_LAMPORTS = 300000;
2509
+ var DEPOSIT_ACCOUNT_SIZE2 = DISCRIMINATOR_SIZE + PUBLIC_KEY_SIZE + PUBLIC_KEY_SIZE + U64_SIZE;
2510
+ var VAULT_ACCOUNT_SIZE = DISCRIMINATOR_SIZE + U8_SIZE;
2511
+ var PERMISSION_ACCOUNT_SIZE = 567;
2512
+ var DELEGATION_RECORD_ACCOUNT_SIZE = DISCRIMINATOR_SIZE + PUBLIC_KEY_SIZE + PUBLIC_KEY_SIZE + U64_SIZE * 3;
2513
+ function getDelegationMetadataAccountSize(seeds) {
2514
+ return DISCRIMINATOR_SIZE + U64_SIZE + BOOL_SIZE + PUBLIC_KEY_SIZE + VEC_PREFIX_SIZE + seeds.reduce((total, seed) => total + VEC_PREFIX_SIZE + seed.byteLength, 0);
2515
+ }
2516
+ async function estimateNewAccountRentLamports(params) {
2517
+ if (params.accounts.length === 0) {
2518
+ return 0;
2519
+ }
2520
+ const spaces = Array.from(new Set(params.accounts.map((account) => account.space)));
2521
+ const [accountInfos, rentEntries] = await Promise.all([
2522
+ params.connection.getMultipleAccountsInfo(params.accounts.map((account) => account.address)),
2523
+ Promise.all(spaces.map(async (space) => [
2524
+ space,
2525
+ await params.connection.getMinimumBalanceForRentExemption(space)
2526
+ ]))
2527
+ ]);
2528
+ const rentBySpace = new Map(rentEntries);
2529
+ return params.accounts.reduce((total, account, index) => {
2530
+ if (!account.forceCreate && accountInfos[index]) {
2531
+ return total;
2532
+ }
2533
+ return total + (rentBySpace.get(account.space) ?? 0);
2534
+ }, 0);
2535
+ }
2536
+ async function estimateExistingAccountLamports(params) {
2537
+ if (params.accounts.length === 0) {
2538
+ return 0;
2539
+ }
2540
+ const accountInfos = await params.connection.getMultipleAccountsInfo(params.accounts);
2541
+ return accountInfos.reduce((total, accountInfo) => total + (accountInfo?.lamports ?? 0), 0);
2542
+ }
2543
+ async function estimateDepositRentLamports(params) {
2544
+ return estimateNewAccountRentLamports({
2545
+ connection: params.connection,
2546
+ accounts: [
2547
+ {
2548
+ address: params.depositPda,
2549
+ space: DEPOSIT_ACCOUNT_SIZE2,
2550
+ forceCreate: params.forceCreate
2551
+ }
2552
+ ]
2210
2553
  });
2211
2554
  }
2212
- function programFromRpc(signer, commitment, rpcEndpoint, wsEndpoint) {
2213
- const adapter = InternalWalletAdapter.from(signer);
2214
- const baseConnection = new Connection(rpcEndpoint, {
2215
- wsEndpoint,
2216
- commitment
2555
+ async function estimateModifyBalanceRentLamports(params) {
2556
+ const [vaultPda] = findVaultPda(params.tokenMint);
2557
+ const userTokenAccount = getAssociatedTokenAddressSync(params.tokenMint, params.user);
2558
+ const vaultTokenAccount = getAssociatedTokenAddressSync(params.tokenMint, vaultPda, true);
2559
+ const accounts = [
2560
+ { address: vaultPda, space: VAULT_ACCOUNT_SIZE },
2561
+ { address: vaultTokenAccount, space: ACCOUNT_SIZE }
2562
+ ];
2563
+ if (!params.isNativeSol) {
2564
+ accounts.push({ address: userTokenAccount, space: ACCOUNT_SIZE });
2565
+ }
2566
+ return estimateNewAccountRentLamports({
2567
+ connection: params.connection,
2568
+ accounts
2217
2569
  });
2218
- const baseProvider = new AnchorProvider(baseConnection, adapter, {
2219
- commitment
2570
+ }
2571
+ async function estimatePermissionRentLamports(params) {
2572
+ return estimateNewAccountRentLamports({
2573
+ connection: params.connection,
2574
+ accounts: [
2575
+ {
2576
+ address: params.permissionPda,
2577
+ space: PERMISSION_ACCOUNT_SIZE,
2578
+ forceCreate: params.forceCreate
2579
+ }
2580
+ ]
2220
2581
  });
2221
- return new Program(telegram_private_transfer_default, baseProvider);
2222
2582
  }
2223
- function getKaminoApiEnv(accounts) {
2224
- return accounts && isKaminoMainnetModifyBalanceAccounts(accounts) ? KAMINO_MAINNET_ENV : KAMINO_DEVNET_ENV;
2583
+ async function estimateDepositDelegationRentLamports(params) {
2584
+ const [delegationRecordPda] = findDelegationRecordPda(params.depositPda);
2585
+ const [delegationMetadataPda] = findDelegationMetadataPda(params.depositPda);
2586
+ const metadataSize = getDelegationMetadataAccountSize([
2587
+ DEPOSIT_SEED_BYTES,
2588
+ params.user.toBuffer(),
2589
+ params.tokenMint.toBuffer()
2590
+ ]);
2591
+ return estimateNewAccountRentLamports({
2592
+ connection: params.connection,
2593
+ accounts: [
2594
+ {
2595
+ address: delegationRecordPda,
2596
+ space: DELEGATION_RECORD_ACCOUNT_SIZE,
2597
+ forceCreate: params.forceCreate
2598
+ },
2599
+ {
2600
+ address: delegationMetadataPda,
2601
+ space: metadataSize,
2602
+ forceCreate: params.forceCreate
2603
+ }
2604
+ ]
2605
+ });
2225
2606
  }
2226
- function normalizeBigInt(value) {
2227
- if (typeof value === "bigint") {
2228
- return value;
2607
+ async function estimateDepositDelegationRentCreditLamports(params) {
2608
+ const [delegationRecordPda] = findDelegationRecordPda(params.depositPda);
2609
+ const [delegationMetadataPda] = findDelegationMetadataPda(params.depositPda);
2610
+ const delegationAccountLamports = await estimateExistingAccountLamports({
2611
+ connection: params.connection,
2612
+ accounts: [delegationRecordPda, delegationMetadataPda]
2613
+ });
2614
+ const refundableLamports = Math.max(0, delegationAccountLamports - MAGICBLOCK_UNDELEGATE_SESSION_FEE_LAMPORTS);
2615
+ return refundableLamports === 0 ? 0 : -refundableLamports;
2616
+ }
2617
+
2618
+ // src/checks/enshureChecks.ts
2619
+ import { DELEGATION_PROGRAM_ID as DELEGATION_PROGRAM_ID2 } from "@magicblock-labs/ephemeral-rollups-sdk";
2620
+ var ENSURE_FETCH_MAX_ATTEMPTS = 3;
2621
+ var ENSURE_FETCH_INITIAL_DELAY_MS = 150;
2622
+ var ENSURE_FETCH_MAX_DELAY_MS = 1000;
2623
+ var ENSURE_FETCH_BACKOFF_MULTIPLIER = 2;
2624
+ var ENSURE_FETCH_JITTER_RATIO = 0.2;
2625
+ var MULTIPLE_ACCOUNTS_CHUNK_SIZE = 5;
2626
+ async function processEnsureChecks(baseConnection, perConnection, ensure) {
2627
+ const mergedChecks = new Map;
2628
+ for (const { address, delegated, passNotExist, label } of ensure) {
2629
+ const addressKey = address.toBase58();
2630
+ const existing = mergedChecks.get(addressKey);
2631
+ if (!existing) {
2632
+ mergedChecks.set(addressKey, {
2633
+ address,
2634
+ delegated,
2635
+ passNotExist,
2636
+ labels: [label]
2637
+ });
2638
+ continue;
2639
+ }
2640
+ existing.labels.push(label);
2641
+ if (existing.delegated !== delegated) {
2642
+ throw new Error(`Conflicting ensure delegation requirements: ${existing.labels.join(", ")} - ${addressKey}`);
2643
+ }
2644
+ existing.passNotExist = existing.passNotExist && passNotExist;
2229
2645
  }
2230
- if (!Number.isInteger(value) || value < 0) {
2231
- throw new Error(`Expected a non-negative integer amount, received ${value}`);
2646
+ const cache = {
2647
+ baseAccountInfos: new Map,
2648
+ delegationStatuses: new Map,
2649
+ ephemeralAccountInfos: new Map
2650
+ };
2651
+ const uniqueChecks = [...mergedChecks.values()];
2652
+ await primeEnsureBatchCache(baseConnection, perConnection, uniqueChecks, cache);
2653
+ for (const { address, delegated, passNotExist, labels } of uniqueChecks) {
2654
+ const displayLabels = labels.join(", ");
2655
+ if (delegated) {
2656
+ await ensureDelegated(baseConnection, perConnection, address, displayLabels, undefined, cache);
2657
+ } else {
2658
+ await ensureNotDelegated(baseConnection, perConnection, address, displayLabels, passNotExist, cache);
2659
+ }
2232
2660
  }
2233
- return BigInt(value);
2234
2661
  }
2235
- async function fetchKaminoReserveMetrics(args) {
2236
- const url = new URL(`/kamino-market/${args.lendingMarket.toBase58()}/reserves/metrics`, KAMINO_API_BASE_URL);
2237
- url.searchParams.set("env", args.env);
2238
- const response = await fetch(url.toString(), {
2239
- method: "GET",
2240
- headers: {
2241
- accept: "application/json"
2662
+ async function primeEnsureBatchCache(baseConnection, perConnection, checks, cache) {
2663
+ const addresses = checks.map((check) => check.address);
2664
+ const [baseAccountInfos, ephemeralAccountInfos] = await Promise.all([
2665
+ getMultipleAccountsInfoWithRetry(baseConnection, addresses, "base-getMultipleAccountsInfo"),
2666
+ getMultipleAccountsInfoWithRetry(perConnection, addresses, "ephemeral-getMultipleAccountsInfo")
2667
+ ]);
2668
+ for (let index = 0;index < checks.length; index += 1) {
2669
+ const addressKey = checks[index].address.toBase58();
2670
+ cache.baseAccountInfos.set(addressKey, baseAccountInfos[index] ?? null);
2671
+ cache.ephemeralAccountInfos.set(addressKey, ephemeralAccountInfos[index] ?? null);
2672
+ }
2673
+ }
2674
+ async function getMultipleAccountsInfoWithRetry(connection, accounts, label) {
2675
+ if (accounts.length === 0) {
2676
+ return [];
2677
+ }
2678
+ const chunks = [];
2679
+ for (let start = 0;start < accounts.length; start += MULTIPLE_ACCOUNTS_CHUNK_SIZE) {
2680
+ chunks.push(accounts.slice(start, start + MULTIPLE_ACCOUNTS_CHUNK_SIZE));
2681
+ }
2682
+ const chunkResults = await Promise.all(chunks.map((chunk, index) => runEnsureFetchWithRetry(`${label}-chunk-${index + 1}`, () => connection.getMultipleAccountsInfo(chunk))));
2683
+ return chunkResults.flat();
2684
+ }
2685
+ async function ensureNotDelegated(baseConnection, perConnection, account, name, passNotExist, cache) {
2686
+ const baseAccountInfo = await getEnsureBaseAccountInfo(baseConnection, account, cache);
2687
+ if (!baseAccountInfo) {
2688
+ if (passNotExist) {
2689
+ return;
2242
2690
  }
2243
- });
2244
- if (!response.ok) {
2245
- throw new Error(`Kamino reserve metrics request failed with status ${response.status}`);
2691
+ const displayName2 = formatEnsureDisplayName(name);
2692
+ throw new Error(`Account is not exists: ${displayName2}${account.toString()}`);
2246
2693
  }
2247
- const payload = await response.json();
2248
- if (!Array.isArray(payload)) {
2249
- throw new Error("Kamino reserve metrics response was not an array");
2694
+ const isDelegated = baseAccountInfo.owner.equals(DELEGATION_PROGRAM_ID2);
2695
+ const displayName = formatEnsureDisplayName(name);
2696
+ if (isDelegated) {
2697
+ const [ephemeralAccountInfo, delegationStatus] = await Promise.all([
2698
+ getEnsureEphemeralAccountInfo(perConnection, account, cache),
2699
+ getEnsureDelegationStatus(perConnection.rpcEndpoint, account, cache)
2700
+ ]);
2701
+ console.error(`Account is delegated to ER: ${displayName}${account.toString()}`);
2702
+ console.error("/getDelegationStatus", JSON.stringify(delegationStatus, null, 2));
2703
+ console.error("baseAccountInfo", prettyStringify(baseAccountInfo));
2704
+ console.error("ephemeralAccountInfo", prettyStringify(ephemeralAccountInfo));
2705
+ const expectedValidator = getErValidatorForRpcEndpoint(perConnection.rpcEndpoint);
2706
+ const authority = delegationStatus.result?.delegationRecord?.authority;
2707
+ if (authority && authority !== expectedValidator.toString()) {
2708
+ console.error(`Account is delegated on wrong validator: ${displayName}${account.toString()} - validator: ${authority}`);
2709
+ }
2710
+ throw new Error(`Account is delegated to ER: ${displayName}${account.toString()}`);
2711
+ }
2712
+ }
2713
+ async function ensureDelegated(baseConnection, perConnection, account, name, skipValidatorCheck, cache) {
2714
+ const baseAccountInfo = await getEnsureBaseAccountInfo(baseConnection, account, cache);
2715
+ if (!baseAccountInfo) {
2716
+ const displayName2 = formatEnsureDisplayName(name);
2717
+ throw new Error(`Account is not exists: ${displayName2}${account.toString()}`);
2718
+ }
2719
+ const isDelegated = baseAccountInfo.owner.equals(DELEGATION_PROGRAM_ID2);
2720
+ const displayName = formatEnsureDisplayName(name);
2721
+ if (!isDelegated) {
2722
+ const [ephemeralAccountInfo, delegationStatus] = await Promise.all([
2723
+ getEnsureEphemeralAccountInfo(perConnection, account, cache),
2724
+ getEnsureDelegationStatus(perConnection.rpcEndpoint, account, cache)
2725
+ ]);
2726
+ console.error(`Account is not delegated to ER: ${displayName}${account.toString()}`);
2727
+ console.error("/getDelegationStatus:", JSON.stringify(delegationStatus, null, 2));
2728
+ console.error("baseAccountInfo", prettyStringify(baseAccountInfo));
2729
+ console.error("ephemeralAccountInfo", prettyStringify(ephemeralAccountInfo));
2730
+ throw new Error(`Account is not delegated to ER: ${displayName}${account.toString()}`);
2731
+ }
2732
+ if (!skipValidatorCheck) {
2733
+ const [ephemeralAccountInfo, delegationStatus] = await Promise.all([
2734
+ getEnsureEphemeralAccountInfo(perConnection, account, cache),
2735
+ getEnsureDelegationStatus(perConnection.rpcEndpoint, account, cache)
2736
+ ]);
2737
+ if (delegationStatus.result.delegationRecord.authority !== getErValidatorForRpcEndpoint(perConnection.rpcEndpoint).toString()) {
2738
+ console.error(`Account is delegated on wrong validator: ${displayName}${account.toString()} - validator: ${delegationStatus.result.delegationRecord.authority}`);
2739
+ console.error("/getDelegationStatus:", JSON.stringify(delegationStatus, null, 2));
2740
+ console.error("baseAccountInfo", prettyStringify(baseAccountInfo));
2741
+ console.error("ephemeralAccountInfo", prettyStringify(ephemeralAccountInfo));
2742
+ throw new Error(`Account is delegated on wrong validator: ${displayName}${account.toString()} - validator: ${delegationStatus.result.delegationRecord.authority}`);
2743
+ }
2744
+ }
2745
+ }
2746
+ async function getEnsureBaseAccountInfo(baseConnection, account, cache) {
2747
+ const addressKey = account.toBase58();
2748
+ if (cache?.baseAccountInfos.has(addressKey)) {
2749
+ return cache.baseAccountInfos.get(addressKey) ?? null;
2750
+ }
2751
+ const baseAccountInfo = await baseConnection.getAccountInfo(account);
2752
+ cache?.baseAccountInfos.set(addressKey, baseAccountInfo);
2753
+ return baseAccountInfo;
2754
+ }
2755
+ async function getEnsureEphemeralAccountInfo(perConnection, account, cache) {
2756
+ const addressKey = account.toBase58();
2757
+ if (cache?.ephemeralAccountInfos.has(addressKey)) {
2758
+ return cache.ephemeralAccountInfos.get(addressKey) ?? null;
2759
+ }
2760
+ const ephemeralAccountInfo = await perConnection.getAccountInfo(account);
2761
+ cache?.ephemeralAccountInfos.set(addressKey, ephemeralAccountInfo);
2762
+ return ephemeralAccountInfo;
2763
+ }
2764
+ async function getEnsureDelegationStatus(perRpcEndpoint, account, cache) {
2765
+ const addressKey = account.toBase58();
2766
+ const cachedPromise = cache?.delegationStatuses.get(addressKey);
2767
+ if (cachedPromise) {
2768
+ return cachedPromise;
2769
+ }
2770
+ const request = getDelegationStatus(perRpcEndpoint, account);
2771
+ cache?.delegationStatuses.set(addressKey, request);
2772
+ return request;
2773
+ }
2774
+ async function runEnsureFetchWithRetry(label, task) {
2775
+ let nextDelayMs = ENSURE_FETCH_INITIAL_DELAY_MS;
2776
+ let lastError;
2777
+ for (let attempt = 1;attempt <= ENSURE_FETCH_MAX_ATTEMPTS; attempt += 1) {
2778
+ try {
2779
+ return await task();
2780
+ } catch (error) {
2781
+ lastError = error;
2782
+ if (attempt === ENSURE_FETCH_MAX_ATTEMPTS) {
2783
+ break;
2784
+ }
2785
+ console.warn(`[ensure] ${label} attempt ${attempt}/${ENSURE_FETCH_MAX_ATTEMPTS} failed: ${error?.message ?? String(error)}`);
2786
+ const jitter = nextDelayMs * ENSURE_FETCH_JITTER_RATIO;
2787
+ const jitteredDelay = Math.max(0, Math.round(nextDelayMs + (Math.random() * 2 - 1) * jitter));
2788
+ await new Promise((resolve) => setTimeout(resolve, jitteredDelay));
2789
+ nextDelayMs = Math.min(ENSURE_FETCH_MAX_DELAY_MS, Math.round(nextDelayMs * ENSURE_FETCH_BACKOFF_MULTIPLIER));
2790
+ }
2791
+ }
2792
+ throw new Error(`[ensure] ${label} failed after ${ENSURE_FETCH_MAX_ATTEMPTS} attempts: ${lastError?.message ?? String(lastError)}`);
2793
+ }
2794
+ async function getDelegationStatus(perRpcEndpoint, account) {
2795
+ const body = JSON.stringify({
2796
+ jsonrpc: "2.0",
2797
+ id: 1,
2798
+ method: "getDelegationStatus",
2799
+ params: [account.toString()]
2800
+ });
2801
+ const options = {
2802
+ method: "POST",
2803
+ headers: { "Content-Type": "application/json" },
2804
+ body
2805
+ };
2806
+ const expectedValidator = getErValidatorForRpcEndpoint(perRpcEndpoint);
2807
+ const isMainnet = perRpcEndpoint.includes("mainnet-tee");
2808
+ const teeBaseUrl = isMainnet ? "https://mainnet-tee.magicblock.app/" : "https://tee.magicblock.app/";
2809
+ try {
2810
+ const teeRes = await fetch(teeBaseUrl, options);
2811
+ const teeData = await teeRes.json();
2812
+ if (teeData.result?.isDelegated) {
2813
+ return {
2814
+ ...teeData,
2815
+ result: {
2816
+ ...teeData.result,
2817
+ delegationRecord: {
2818
+ authority: expectedValidator.toString()
2819
+ }
2820
+ }
2821
+ };
2822
+ }
2823
+ } catch (e) {
2824
+ console.error("[getDelegationStatus] TEE fetch failed, falling back to devnet-router: Options:", options, "Error:", e);
2825
+ }
2826
+ const routerBaseUrl = isMainnet ? "https://router.magicblock.app/" : "https://devnet-router.magicblock.app/";
2827
+ const res = await fetch(routerBaseUrl, options);
2828
+ const routerData = await res.json();
2829
+ if (routerData.error?.message?.includes(expectedValidator.toString())) {
2830
+ return {
2831
+ ...routerData,
2832
+ result: {
2833
+ isDelegated: true,
2834
+ delegationRecord: {
2835
+ authority: expectedValidator.toString()
2836
+ }
2837
+ }
2838
+ };
2839
+ }
2840
+ return routerData;
2841
+ }
2842
+ function formatEnsureDisplayName(name) {
2843
+ return name ? `${name} - ` : "";
2844
+ }
2845
+
2846
+ // src/wsol.ts
2847
+ import {
2848
+ createAssociatedTokenAccountIdempotentInstruction,
2849
+ createCloseAccountInstruction,
2850
+ createSyncNativeInstruction,
2851
+ getAssociatedTokenAddressSync as getAssociatedTokenAddressSync2,
2852
+ NATIVE_MINT
2853
+ } from "@solana/spl-token";
2854
+ import {
2855
+ SystemProgram
2856
+ } from "@solana/web3.js";
2857
+ function wrapSolToWsolIx({
2858
+ user,
2859
+ payer,
2860
+ lamports
2861
+ }) {
2862
+ const wsolAta = getAssociatedTokenAddressSync2(NATIVE_MINT, user);
2863
+ return [
2864
+ createAssociatedTokenAccountIdempotentInstruction(payer, wsolAta, user, NATIVE_MINT),
2865
+ SystemProgram.transfer({
2866
+ fromPubkey: user,
2867
+ toPubkey: wsolAta,
2868
+ lamports
2869
+ }),
2870
+ createSyncNativeInstruction(wsolAta)
2871
+ ];
2872
+ }
2873
+ function createWsolAta({
2874
+ user,
2875
+ payer
2876
+ }) {
2877
+ const wsolAta = getAssociatedTokenAddressSync2(NATIVE_MINT, user);
2878
+ return createAssociatedTokenAccountIdempotentInstruction(payer, wsolAta, user, NATIVE_MINT);
2879
+ }
2880
+ function closeWsolAta({
2881
+ user,
2882
+ destination
2883
+ }) {
2884
+ const wsolAta = getAssociatedTokenAddressSync2(NATIVE_MINT, user);
2885
+ return createCloseAccountInstruction(wsolAta, destination, user);
2886
+ }
2887
+
2888
+ // src/transaction-debug.ts
2889
+ import {
2890
+ SendTransactionError
2891
+ } from "@solana/web3.js";
2892
+ var MULTIPLE_ACCOUNTS_CHUNK_SIZE2 = 10;
2893
+ function describeAccountInfo(accountInfo) {
2894
+ if (!accountInfo) {
2895
+ return {
2896
+ exists: false,
2897
+ owner: null,
2898
+ executable: null,
2899
+ lamports: null,
2900
+ dataLength: null,
2901
+ rentEpoch: null
2902
+ };
2903
+ }
2904
+ return {
2905
+ exists: true,
2906
+ owner: accountInfo.owner.toBase58(),
2907
+ executable: accountInfo.executable,
2908
+ lamports: accountInfo.lamports,
2909
+ dataLength: accountInfo.data?.length ?? null,
2910
+ rentEpoch: accountInfo.rentEpoch ?? null
2911
+ };
2912
+ }
2913
+ function extractInlineTransactionLogs(error) {
2914
+ const logs = error?.logs ?? error?.transactionLogs;
2915
+ return Array.isArray(logs) ? logs : undefined;
2916
+ }
2917
+ async function getTransactionErrorLogs(error, connection) {
2918
+ const inlineLogs = extractInlineTransactionLogs(error);
2919
+ if (inlineLogs) {
2920
+ return inlineLogs;
2921
+ }
2922
+ if (error instanceof SendTransactionError) {
2923
+ try {
2924
+ const fetchedLogs = await error.getLogs(connection);
2925
+ if (Array.isArray(fetchedLogs)) {
2926
+ return fetchedLogs;
2927
+ }
2928
+ } catch (fetchError) {
2929
+ console.error("[tx-debug] failed to fetch logs via SendTransactionError.getLogs()", {
2930
+ errorName: fetchError?.name ?? "UnknownError",
2931
+ errorMessage: fetchError?.message ?? String(fetchError)
2932
+ });
2933
+ }
2934
+ }
2935
+ return;
2936
+ }
2937
+ function collectTransactionAccounts(tx) {
2938
+ const uniqueAccounts = new Map;
2939
+ if (tx.feePayer) {
2940
+ uniqueAccounts.set(tx.feePayer.toBase58(), tx.feePayer);
2941
+ }
2942
+ for (const instruction of tx.instructions) {
2943
+ uniqueAccounts.set(instruction.programId.toBase58(), instruction.programId);
2944
+ for (const key of instruction.keys) {
2945
+ uniqueAccounts.set(key.pubkey.toBase58(), key.pubkey);
2946
+ }
2947
+ }
2948
+ return [...uniqueAccounts.values()];
2949
+ }
2950
+ function describeTransaction(tx) {
2951
+ const compiledMessage = tx.compileMessage();
2952
+ const accountKeys = compiledMessage.accountKeys;
2953
+ const signedWritableCount = compiledMessage.header.numRequiredSignatures - compiledMessage.header.numReadonlySignedAccounts;
2954
+ const unsignedWritableEndExclusive = accountKeys.length - compiledMessage.header.numReadonlyUnsignedAccounts;
2955
+ const isAccountWritable = (index) => {
2956
+ if (index < compiledMessage.header.numRequiredSignatures) {
2957
+ return index < signedWritableCount;
2958
+ }
2959
+ return index < unsignedWritableEndExclusive;
2960
+ };
2961
+ return {
2962
+ feePayer: tx.feePayer?.toBase58() ?? null,
2963
+ recentBlockhash: tx.recentBlockhash ?? null,
2964
+ lastValidBlockHeight: tx.lastValidBlockHeight,
2965
+ signatureBase64: tx.signature ? Buffer.from(tx.signature).toString("base64") : null,
2966
+ instructionCount: tx.instructions.length,
2967
+ accountKeys: accountKeys.map((account, index) => ({
2968
+ index,
2969
+ pubkey: account.toBase58(),
2970
+ isSigner: index < compiledMessage.header.numRequiredSignatures,
2971
+ isWritable: isAccountWritable(index)
2972
+ })),
2973
+ instructions: tx.instructions.map((instruction, index) => ({
2974
+ index,
2975
+ programId: instruction.programId.toBase58(),
2976
+ programIdIndex: accountKeys.findIndex((account) => account.equals(instruction.programId)),
2977
+ dataLength: instruction.data.length,
2978
+ dataBase64: Buffer.from(instruction.data).toString("base64"),
2979
+ keys: instruction.keys.map((key, keyIndex) => ({
2980
+ index: keyIndex,
2981
+ accountIndex: accountKeys.findIndex((account) => account.equals(key.pubkey)),
2982
+ pubkey: key.pubkey.toBase58(),
2983
+ isSigner: key.isSigner,
2984
+ isWritable: key.isWritable
2985
+ }))
2986
+ }))
2987
+ };
2988
+ }
2989
+ async function getMultipleAccountsInfoInChunks(connection, accounts) {
2990
+ if (accounts.length === 0) {
2991
+ return [];
2992
+ }
2993
+ const chunks = [];
2994
+ for (let start = 0;start < accounts.length; start += MULTIPLE_ACCOUNTS_CHUNK_SIZE2) {
2995
+ chunks.push(accounts.slice(start, start + MULTIPLE_ACCOUNTS_CHUNK_SIZE2));
2996
+ }
2997
+ const results = await Promise.all(chunks.map((chunk) => connection.getMultipleAccountsInfo(chunk)));
2998
+ return results.flat();
2999
+ }
3000
+ async function logFailedTransactionDiagnostics(params) {
3001
+ const { label, connection, tx, error, extraContext } = params;
3002
+ const txAccounts = collectTransactionAccounts(tx);
3003
+ const [errorLogs, accountInfos] = await Promise.all([
3004
+ getTransactionErrorLogs(error, connection),
3005
+ getMultipleAccountsInfoInChunks(connection, txAccounts)
3006
+ ]);
3007
+ console.error(`[${label}] sendAndConfirm failed`, prettyStringify({
3008
+ errorName: error?.name ?? "UnknownError",
3009
+ errorMessage: error?.message ?? String(error),
3010
+ errorLogs,
3011
+ extraContext,
3012
+ transaction: describeTransaction(tx),
3013
+ accountSnapshots: txAccounts.map((account, index) => ({
3014
+ pubkey: account,
3015
+ ...describeAccountInfo(accountInfos[index] ?? null)
3016
+ }))
3017
+ }));
3018
+ try {
3019
+ const simulation = await connection.simulateTransaction(tx);
3020
+ console.error(`[${label}] simulateTransaction result`, prettyStringify({
3021
+ contextSlot: simulation.context.slot,
3022
+ err: simulation.value.err,
3023
+ logs: simulation.value.logs,
3024
+ unitsConsumed: simulation.value.unitsConsumed,
3025
+ returnData: simulation.value.returnData
3026
+ }));
3027
+ } catch (simulationError) {
3028
+ const simulationLogs = await getTransactionErrorLogs(simulationError, connection);
3029
+ console.error(`[${label}] simulateTransaction failed`, prettyStringify({
3030
+ errorName: simulationError?.name ?? "UnknownError",
3031
+ errorMessage: simulationError?.message ?? String(simulationError),
3032
+ logs: simulationLogs
3033
+ }));
3034
+ }
3035
+ }
3036
+ var COMMITMENT_ORDER = {
3037
+ processed: 0,
3038
+ confirmed: 1,
3039
+ finalized: 2
3040
+ };
3041
+ function meetsCommitment(observed, required) {
3042
+ if (!observed)
3043
+ return false;
3044
+ const o = COMMITMENT_ORDER[observed];
3045
+ const r = COMMITMENT_ORDER[required];
3046
+ return o !== undefined && r !== undefined && o >= r;
3047
+ }
3048
+ async function pollForLanding(connection, signature, lastValidBlockHeight, commitment) {
3049
+ const pollIntervalMs = 1500;
3050
+ const maxWallClockMs = 90000;
3051
+ const start = Date.now();
3052
+ while (Date.now() - start < maxWallClockMs) {
3053
+ let status = null;
3054
+ try {
3055
+ const res = await connection.getSignatureStatuses([signature], {
3056
+ searchTransactionHistory: true
3057
+ });
3058
+ status = res.value[0];
3059
+ } catch {}
3060
+ if (status?.err) {
3061
+ throw new Error(`Transaction failed on-chain: ${JSON.stringify(status.err)}`);
3062
+ }
3063
+ if (meetsCommitment(status?.confirmationStatus, commitment)) {
3064
+ return signature;
3065
+ }
3066
+ try {
3067
+ const currentHeight = await connection.getBlockHeight(commitment);
3068
+ if (currentHeight > lastValidBlockHeight && !status) {
3069
+ throw new Error(`Transaction dropped: ${signature} (blockhash expired without landing)`);
3070
+ }
3071
+ } catch {}
3072
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
3073
+ }
3074
+ throw new Error(`Transaction confirmation timed out after 90s: ${signature}`);
3075
+ }
3076
+ async function sendAndConfirmWithDiagnostics(params) {
3077
+ const { label, provider, tx, signers, rpcOptions, extraContext } = params;
3078
+ const connection = provider.connection;
3079
+ const wallet = provider.wallet;
3080
+ if (!wallet) {
3081
+ throw new Error(`[${label}] Provider has no wallet`);
3082
+ }
3083
+ const preflightCommitment = rpcOptions?.preflightCommitment ?? "confirmed";
3084
+ const confirmCommitment = preflightCommitment;
3085
+ const blockhashInfo = await connection.getLatestBlockhash(preflightCommitment);
3086
+ if (!tx.feePayer)
3087
+ tx.feePayer = wallet.publicKey;
3088
+ tx.recentBlockhash = blockhashInfo.blockhash;
3089
+ tx.lastValidBlockHeight = blockhashInfo.lastValidBlockHeight;
3090
+ let signedTx;
3091
+ try {
3092
+ signedTx = await wallet.signTransaction(tx);
3093
+ } catch (error) {
3094
+ throw error;
3095
+ }
3096
+ for (const extraSigner of signers ?? []) {
3097
+ signedTx.partialSign(extraSigner);
3098
+ }
3099
+ let signature;
3100
+ try {
3101
+ signature = await connection.sendRawTransaction(signedTx.serialize(), {
3102
+ skipPreflight: rpcOptions?.skipPreflight ?? false,
3103
+ preflightCommitment,
3104
+ maxRetries: rpcOptions?.maxRetries ?? 3
3105
+ });
3106
+ } catch (error) {
3107
+ await logFailedTransactionDiagnostics({
3108
+ label,
3109
+ connection,
3110
+ tx: signedTx,
3111
+ error,
3112
+ extraContext
3113
+ }).catch((debugError) => {
3114
+ console.error(`[${label}] failed to log transaction diagnostics`, {
3115
+ errorName: debugError?.name ?? "UnknownError",
3116
+ errorMessage: debugError?.message ?? String(debugError)
3117
+ });
3118
+ });
3119
+ throw error;
3120
+ }
3121
+ try {
3122
+ return await pollForLanding(connection, signature, blockhashInfo.lastValidBlockHeight, confirmCommitment);
3123
+ } catch (error) {
3124
+ await logFailedTransactionDiagnostics({
3125
+ label,
3126
+ connection,
3127
+ tx: signedTx,
3128
+ error,
3129
+ extraContext: { ...extraContext, signature }
3130
+ }).catch((debugError) => {
3131
+ console.error(`[${label}] failed to log transaction diagnostics`, {
3132
+ errorName: debugError?.name ?? "UnknownError",
3133
+ errorMessage: debugError?.message ?? String(debugError)
3134
+ });
3135
+ });
3136
+ throw error;
3137
+ }
3138
+ }
3139
+
3140
+ // src/instructions/modifyBalance.ts
3141
+ import { BN } from "@coral-xyz/anchor";
3142
+ import {
3143
+ ASSOCIATED_TOKEN_PROGRAM_ID,
3144
+ getAssociatedTokenAddressSync as getAssociatedTokenAddressSync3,
3145
+ TOKEN_PROGRAM_ID
3146
+ } from "@solana/spl-token";
3147
+ import { SystemProgram as SystemProgram2 } from "@solana/web3.js";
3148
+ async function modifyBalanceIx(program, params) {
3149
+ const { user, tokenMint, amount, increase, payer, passNotExist } = params;
3150
+ const [depositPda] = findDepositPda(user, tokenMint);
3151
+ const [vaultPda] = findVaultPda(tokenMint);
3152
+ const userTokenAccount = getAssociatedTokenAddressSync3(tokenMint, user);
3153
+ const vaultTokenAccount = getAssociatedTokenAddressSync3(tokenMint, vaultPda, true);
3154
+ const kaminoAccounts = getKaminoModifyBalanceAccountsForTokenMint(tokenMint);
3155
+ const vaultCollateralTokenAccount = kaminoAccounts ? getAssociatedTokenAddressSync3(kaminoAccounts.reserveCollateralMint, vaultPda, true) : null;
3156
+ const accounts = {
3157
+ payer,
3158
+ user,
3159
+ vault: vaultPda,
3160
+ deposit: depositPda,
3161
+ userTokenAccount,
3162
+ vaultTokenAccount,
3163
+ tokenMint,
3164
+ tokenProgram: TOKEN_PROGRAM_ID,
3165
+ associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
3166
+ systemProgram: SystemProgram2.programId
3167
+ };
3168
+ const remainingAccounts = kaminoAccounts && vaultCollateralTokenAccount ? [
3169
+ {
3170
+ pubkey: kaminoAccounts.lendingMarket,
3171
+ isSigner: false,
3172
+ isWritable: false
3173
+ },
3174
+ {
3175
+ pubkey: kaminoAccounts.lendingMarketAuthority,
3176
+ isSigner: false,
3177
+ isWritable: false
3178
+ },
3179
+ {
3180
+ pubkey: kaminoAccounts.reserve,
3181
+ isSigner: false,
3182
+ isWritable: true
3183
+ },
3184
+ {
3185
+ pubkey: kaminoAccounts.reserveLiquiditySupply,
3186
+ isSigner: false,
3187
+ isWritable: true
3188
+ },
3189
+ {
3190
+ pubkey: kaminoAccounts.reserveCollateralMint,
3191
+ isSigner: false,
3192
+ isWritable: true
3193
+ },
3194
+ {
3195
+ pubkey: vaultCollateralTokenAccount,
3196
+ isSigner: false,
3197
+ isWritable: true
3198
+ },
3199
+ {
3200
+ pubkey: kaminoAccounts.instructionSysvarAccount,
3201
+ isSigner: false,
3202
+ isWritable: false
3203
+ },
3204
+ {
3205
+ pubkey: kaminoAccounts.klendProgram,
3206
+ isSigner: false,
3207
+ isWritable: false
3208
+ }
3209
+ ] : [];
3210
+ const ix = await program.methods.modifyBalance({ amount: new BN(amount.toString()), increase }).accountsPartial(accounts).remainingAccounts(remainingAccounts).instruction();
3211
+ return {
3212
+ ix,
3213
+ ensure: [
3214
+ {
3215
+ address: depositPda,
3216
+ delegated: false,
3217
+ passNotExist: passNotExist === undefined ? false : passNotExist,
3218
+ label: "modifyBalance-depositPda"
3219
+ }
3220
+ ]
3221
+ };
3222
+ }
3223
+
3224
+ // src/instructions/initializeDeposit.ts
3225
+ import { TOKEN_PROGRAM_ID as TOKEN_PROGRAM_ID2 } from "@solana/spl-token";
3226
+ import { SystemProgram as SystemProgram3 } from "@solana/web3.js";
3227
+ async function initializeDepositIx(program, params) {
3228
+ const { user, tokenMint, payer } = params;
3229
+ const [depositPda] = findDepositPda(user, tokenMint);
3230
+ const ix = await program.methods.initializeDeposit().accountsPartial({
3231
+ payer,
3232
+ user,
3233
+ tokenMint,
3234
+ tokenProgram: TOKEN_PROGRAM_ID2,
3235
+ systemProgram: SystemProgram3.programId
3236
+ }).instruction();
3237
+ return {
3238
+ ix,
3239
+ ensure: [
3240
+ {
3241
+ address: depositPda,
3242
+ delegated: false,
3243
+ passNotExist: true,
3244
+ label: "initializeDeposit-depositPda"
3245
+ }
3246
+ ]
3247
+ };
3248
+ }
3249
+
3250
+ // src/instructions/createPermission.ts
3251
+ import { PERMISSION_PROGRAM_ID as PERMISSION_PROGRAM_ID2 } from "@magicblock-labs/ephemeral-rollups-sdk";
3252
+ async function createPermissionIx(program, params) {
3253
+ const { user, tokenMint, payer, passNotExist } = params;
3254
+ const [depositPda] = findDepositPda(user, tokenMint);
3255
+ const [permissionPda] = findPermissionPda(depositPda);
3256
+ const ix = await program.methods.createPermission().accountsPartial({
3257
+ payer,
3258
+ user,
3259
+ deposit: depositPda,
3260
+ permission: permissionPda,
3261
+ permissionProgram: PERMISSION_PROGRAM_ID2
3262
+ }).instruction();
3263
+ return {
3264
+ ix,
3265
+ ensure: [
3266
+ {
3267
+ address: depositPda,
3268
+ delegated: false,
3269
+ passNotExist: passNotExist === undefined ? true : passNotExist,
3270
+ label: "createPermission-depositPda"
3271
+ },
3272
+ {
3273
+ address: permissionPda,
3274
+ delegated: false,
3275
+ passNotExist: true,
3276
+ label: "createPermission-permissionPda"
3277
+ }
3278
+ ]
3279
+ };
3280
+ }
3281
+
3282
+ // src/instructions/delegateDeposit.ts
3283
+ async function delegateDepositIx(program, params) {
3284
+ const { user, tokenMint, payer, validator, passNotExist } = params;
3285
+ const [depositPda] = findDepositPda(user, tokenMint);
3286
+ const [bufferPda] = findBufferPda(depositPda);
3287
+ const [delegationRecordPda] = findDelegationRecordPda(depositPda);
3288
+ const [delegationMetadataPda] = findDelegationMetadataPda(depositPda);
3289
+ const ix = await program.methods.delegate(user, tokenMint).accountsPartial({
3290
+ payer,
3291
+ bufferDeposit: bufferPda,
3292
+ delegationRecordDeposit: delegationRecordPda,
3293
+ delegationMetadataDeposit: delegationMetadataPda,
3294
+ deposit: depositPda,
3295
+ validator,
3296
+ ownerProgram: PROGRAM_ID,
3297
+ delegationProgram: DELEGATION_PROGRAM_ID
3298
+ }).instruction();
3299
+ return {
3300
+ ix,
3301
+ ensure: [
3302
+ {
3303
+ address: depositPda,
3304
+ delegated: false,
3305
+ passNotExist: passNotExist === undefined ? false : passNotExist,
3306
+ label: "delegateDeposit-depositPda"
3307
+ }
3308
+ ]
3309
+ };
3310
+ }
3311
+
3312
+ // src/instructions/undelegateDeposit.ts
3313
+ async function undelegateDepositIx(perProgram, params) {
3314
+ const { user, tokenMint, payer, sessionToken, magicProgram, magicContext } = params;
3315
+ const [depositPda] = findDepositPda(user, tokenMint);
3316
+ const accounts = {
3317
+ user,
3318
+ payer,
3319
+ deposit: depositPda,
3320
+ magicProgram,
3321
+ magicContext
3322
+ };
3323
+ accounts.sessionToken = sessionToken ?? null;
3324
+ const ix = await perProgram.methods.undelegate().accountsPartial(accounts).instruction();
3325
+ return {
3326
+ ix,
3327
+ ensure: [
3328
+ {
3329
+ address: depositPda,
3330
+ delegated: true,
3331
+ passNotExist: false,
3332
+ label: "undelegateDeposit-depositPda"
3333
+ }
3334
+ ]
3335
+ };
3336
+ }
3337
+
3338
+ // src/actions/undelegateDeposit.ts
3339
+ import {
3340
+ Transaction as Transaction3
3341
+ } from "@solana/web3.js";
3342
+ async function sendPlannedUndelegateDepositTransaction(params) {
3343
+ const { baseProgram, perProgram, transaction, user, tokenMint, rpcOptions } = params;
3344
+ await processEnsureChecks(baseProgram.provider.connection, perProgram.provider.connection, transaction.checks);
3345
+ const [depositPda] = findDepositPda(user, tokenMint);
3346
+ const delegationWatcher = waitForAccountOwnerChange(baseProgram.provider.connection, depositPda, PROGRAM_ID);
3347
+ const tx = new Transaction3().add(...transaction.instructions.map(({ ix }) => ix));
3348
+ let signature;
3349
+ try {
3350
+ signature = await sendAndConfirmWithDiagnostics({
3351
+ label: transaction.label,
3352
+ provider: perProgram.provider,
3353
+ tx,
3354
+ rpcOptions,
3355
+ extraContext: {
3356
+ user,
3357
+ tokenMint,
3358
+ depositPda
3359
+ }
3360
+ });
3361
+ } catch (e) {
3362
+ await delegationWatcher.cancel();
3363
+ throw e;
3364
+ }
3365
+ try {
3366
+ await delegationWatcher.wait();
3367
+ await new Promise((resolve) => setTimeout(resolve, 3000));
3368
+ } catch (err) {
3369
+ console.warn(`[${transaction.label}] delegation watcher did not observe owner change (signature=${signature}); continuing`, err);
3370
+ }
3371
+ return signature;
3372
+ }
3373
+ async function undelegateDeposit(baseProgram, perProgram, params) {
3374
+ const { user, tokenMint } = params;
3375
+ const { ix, ensure } = await undelegateDepositIx(perProgram, params);
3376
+ return sendPlannedUndelegateDepositTransaction({
3377
+ baseProgram,
3378
+ perProgram,
3379
+ transaction: {
3380
+ label: "undelegateDeposit",
3381
+ instructions: [{ ix }],
3382
+ checks: ensure
3383
+ },
3384
+ user,
3385
+ tokenMint,
3386
+ rpcOptions: params.rpcOptions
3387
+ });
3388
+ }
3389
+
3390
+ // src/actions/shieldTokens.ts
3391
+ function labelTransactionInstructions(prefix, instructions) {
3392
+ const labels = [
3393
+ `${prefix}:createAssociatedTokenAccount`,
3394
+ `${prefix}:transfer`,
3395
+ `${prefix}:syncNative`
3396
+ ];
3397
+ return instructions.map((ix, index) => ({
3398
+ label: labels[index] ?? `${prefix}:${index}`,
3399
+ ix
3400
+ }));
3401
+ }
3402
+ async function buildShieldTokensInstructionPlan(params) {
3403
+ const { user, payer, tokenMint, amount, baseProgram, perProgram } = params;
3404
+ const baseConnection = baseProgram.provider.connection;
3405
+ const perRpcEndpoint = perProgram.provider.connection.rpcEndpoint;
3406
+ const isNativeSol = tokenMint.equals(NATIVE_MINT2);
3407
+ const validator = params.validator ?? getErValidatorForRpcEndpoint(perRpcEndpoint);
3408
+ const [depositPda] = findDepositPda(user, tokenMint);
3409
+ const [permissionPda] = findPermissionPda(depositPda);
3410
+ const accountInfos = await getMultipleAccountsInfoWithRetry(baseConnection, [depositPda, permissionPda], "base-getMultipleAccountsInfo");
3411
+ const depositAccountInfo = accountInfos[0] ?? null;
3412
+ const permissionAccountInfo = accountInfos[1] ?? null;
3413
+ const needsUndelegate = depositAccountInfo?.owner.equals(DELEGATION_PROGRAM_ID) ?? false;
3414
+ const [
3415
+ depositRentLamports,
3416
+ modifyBalanceRentLamports,
3417
+ permissionRentLamports,
3418
+ delegationRentLamports
3419
+ ] = await Promise.all([
3420
+ !depositAccountInfo ? estimateDepositRentLamports({ connection: baseConnection, depositPda }) : Promise.resolve(0),
3421
+ estimateModifyBalanceRentLamports({
3422
+ connection: baseConnection,
3423
+ user,
3424
+ tokenMint,
3425
+ isNativeSol
3426
+ }),
3427
+ !permissionAccountInfo ? estimatePermissionRentLamports({
3428
+ connection: baseConnection,
3429
+ permissionPda
3430
+ }) : Promise.resolve(0),
3431
+ estimateDepositDelegationRentLamports({
3432
+ connection: baseConnection,
3433
+ user,
3434
+ tokenMint,
3435
+ depositPda,
3436
+ forceCreate: needsUndelegate
3437
+ })
3438
+ ]);
3439
+ const instructions = [];
3440
+ const checks = [];
3441
+ if (isNativeSol) {
3442
+ const wrapSolInstructions = labelTransactionInstructions("wrapSol", wrapSolToWsolIx({
3443
+ user,
3444
+ payer,
3445
+ lamports: amount
3446
+ }));
3447
+ const transferInstruction = wrapSolInstructions.find((instruction) => instruction.label === "wrapSol:transfer");
3448
+ if (transferInstruction) {
3449
+ transferInstruction.nativeLamports = Number(amount);
3450
+ }
3451
+ instructions.push(...wrapSolInstructions);
3452
+ }
3453
+ if (!depositAccountInfo) {
3454
+ const initializeDepositIxs = await initializeDepositIx(baseProgram, {
3455
+ tokenMint,
3456
+ user,
3457
+ payer
3458
+ });
3459
+ instructions.push({
3460
+ label: "initializeDeposit",
3461
+ ix: initializeDepositIxs.ix,
3462
+ rentLamports: depositRentLamports
3463
+ });
3464
+ checks.push(...initializeDepositIxs.ensure);
3465
+ }
3466
+ const modifyBalanceIxs = await modifyBalanceIx(baseProgram, {
3467
+ tokenMint,
3468
+ user,
3469
+ payer,
3470
+ amount,
3471
+ increase: true,
3472
+ passNotExist: true
3473
+ });
3474
+ instructions.push({
3475
+ label: "modifyBalanceIncrease",
3476
+ ix: modifyBalanceIxs.ix,
3477
+ rentLamports: modifyBalanceRentLamports
3478
+ });
3479
+ checks.push(...modifyBalanceIxs.ensure);
3480
+ if (!permissionAccountInfo) {
3481
+ const createPermissionIxs = await createPermissionIx(baseProgram, {
3482
+ tokenMint,
3483
+ user,
3484
+ payer,
3485
+ passNotExist: true
3486
+ });
3487
+ instructions.push({
3488
+ label: "createPermission",
3489
+ ix: createPermissionIxs.ix,
3490
+ rentLamports: permissionRentLamports
3491
+ });
3492
+ checks.push(...createPermissionIxs.ensure);
3493
+ }
3494
+ const delegateDepositIxs = await delegateDepositIx(baseProgram, {
3495
+ tokenMint,
3496
+ user,
3497
+ payer,
3498
+ validator,
3499
+ passNotExist: true
3500
+ });
3501
+ instructions.push({
3502
+ label: "delegateDeposit",
3503
+ ix: delegateDepositIxs.ix,
3504
+ rentLamports: delegationRentLamports
3505
+ });
3506
+ checks.push(...delegateDepositIxs.ensure);
3507
+ if (isNativeSol) {
3508
+ instructions.push({
3509
+ label: "closeWsolAta",
3510
+ ix: closeWsolAta({
3511
+ user,
3512
+ destination: user
3513
+ })
3514
+ });
3515
+ }
3516
+ return {
3517
+ instructions,
3518
+ checks,
3519
+ needsUndelegate,
3520
+ context: {
3521
+ isNativeSol,
3522
+ validator,
3523
+ depositPda,
3524
+ permissionPda,
3525
+ depositAccountInfo,
3526
+ permissionAccountInfo
3527
+ }
3528
+ };
3529
+ }
3530
+ async function buildShieldTokensTransactionPlan(params) {
3531
+ const instructionPlan = await buildShieldTokensInstructionPlan(params);
3532
+ let preUndelegateTransaction = null;
3533
+ if (instructionPlan.needsUndelegate) {
3534
+ const undelegateRentLamports = await estimateDepositDelegationRentCreditLamports({
3535
+ connection: params.baseProgram.provider.connection,
3536
+ depositPda: instructionPlan.context.depositPda
3537
+ });
3538
+ const undelegateIxs = await undelegateDepositIx(params.perProgram, {
3539
+ user: params.user,
3540
+ payer: params.payer,
3541
+ tokenMint: params.tokenMint,
3542
+ sessionToken: params.sessionToken,
3543
+ magicProgram: params.magicProgram ?? MAGIC_PROGRAM_ID,
3544
+ magicContext: params.magicContext ?? MAGIC_CONTEXT_ID
3545
+ });
3546
+ preUndelegateTransaction = {
3547
+ label: "shield:preUndelegate",
3548
+ cluster: "ephemeral",
3549
+ instructions: [
3550
+ {
3551
+ label: "undelegateDeposit",
3552
+ ix: undelegateIxs.ix,
3553
+ rentLamports: undelegateRentLamports
3554
+ }
3555
+ ],
3556
+ checks: undelegateIxs.ensure
3557
+ };
3558
+ }
3559
+ return {
3560
+ preUndelegateTransaction,
3561
+ baseTransaction: {
3562
+ label: "shield",
3563
+ cluster: "base",
3564
+ instructions: instructionPlan.instructions,
3565
+ checks: instructionPlan.checks
3566
+ },
3567
+ context: instructionPlan.context
3568
+ };
3569
+ }
3570
+ async function shieldTokens(params) {
3571
+ const {
3572
+ user,
3573
+ payer,
3574
+ tokenMint,
3575
+ amount,
3576
+ baseProgram,
3577
+ perProgram,
3578
+ rpcOptions
3579
+ } = params;
3580
+ const baseConnection = baseProgram.provider.connection;
3581
+ const perConnection = perProgram.provider.connection;
3582
+ const plan = await buildShieldTokensTransactionPlan({
3583
+ user,
3584
+ payer,
3585
+ tokenMint,
3586
+ amount,
3587
+ baseProgram,
3588
+ perProgram,
3589
+ validator: params.validator,
3590
+ sessionToken: params.sessionToken,
3591
+ magicProgram: params.magicProgram,
3592
+ magicContext: params.magicContext
3593
+ });
3594
+ const {
3595
+ context: {
3596
+ validator,
3597
+ depositPda,
3598
+ permissionPda,
3599
+ depositAccountInfo,
3600
+ permissionAccountInfo,
3601
+ isNativeSol
3602
+ }
3603
+ } = plan;
3604
+ if (plan.preUndelegateTransaction) {
3605
+ await sendPlannedUndelegateDepositTransaction({
3606
+ baseProgram,
3607
+ perProgram,
3608
+ transaction: plan.preUndelegateTransaction,
3609
+ user,
3610
+ tokenMint,
3611
+ rpcOptions
3612
+ });
3613
+ }
3614
+ await processEnsureChecks(baseConnection, perConnection, plan.baseTransaction.checks);
3615
+ const delegationWatcher = waitForAccountOwnerChange(baseConnection, depositPda, DELEGATION_PROGRAM_ID);
3616
+ const tx = new Transaction4().add(...plan.baseTransaction.instructions.map(({ ix }) => ix));
3617
+ let signature;
3618
+ try {
3619
+ signature = await sendAndConfirmWithDiagnostics({
3620
+ label: "shieldTokens",
3621
+ provider: baseProgram.provider,
3622
+ tx,
3623
+ rpcOptions,
3624
+ extraContext: {
3625
+ user,
3626
+ payer,
3627
+ tokenMint,
3628
+ amount,
3629
+ isNativeSol,
3630
+ validator,
3631
+ depositPda,
3632
+ permissionPda,
3633
+ depositAccountInfo,
3634
+ permissionAccountInfo
3635
+ }
3636
+ });
3637
+ } catch (e) {
3638
+ await delegationWatcher.cancel();
3639
+ throw e;
3640
+ }
3641
+ try {
3642
+ await delegationWatcher.wait();
3643
+ await new Promise((resolve) => setTimeout(resolve, 3000));
3644
+ } catch (err) {
3645
+ console.warn(`[shieldTokens] delegation watcher did not observe owner change (signature=${signature}); continuing`, err);
3646
+ }
3647
+ return signature;
3648
+ }
3649
+
3650
+ // src/actions/unshieldTokens.ts
3651
+ import { NATIVE_MINT as NATIVE_MINT3 } from "@solana/spl-token";
3652
+ import { Transaction as Transaction5 } from "@solana/web3.js";
3653
+
3654
+ // src/instructions/closeDeposit.ts
3655
+ async function closeDepositIx(program, params) {
3656
+ const { user, tokenMint } = params;
3657
+ const [depositPda] = findDepositPda(user, tokenMint);
3658
+ const ix = await program.methods.closeDeposit().accountsPartial({
3659
+ user,
3660
+ deposit: depositPda,
3661
+ tokenMint
3662
+ }).instruction();
3663
+ return {
3664
+ ix,
3665
+ ensure: [
3666
+ {
3667
+ address: depositPda,
3668
+ delegated: false,
3669
+ passNotExist: false,
3670
+ label: "closeDeposit-depositPda"
3671
+ }
3672
+ ]
3673
+ };
3674
+ }
3675
+
3676
+ // src/instructions/closePermission.ts
3677
+ import {
3678
+ createClosePermissionInstruction
3679
+ } from "@magicblock-labs/ephemeral-rollups-sdk";
3680
+ async function closePermissionIx(params) {
3681
+ const { user, tokenMint } = params;
3682
+ const [depositPda] = findDepositPda(user, tokenMint);
3683
+ const [permissionPda] = findPermissionPda(depositPda);
3684
+ const ix = createClosePermissionInstruction({
3685
+ payer: user,
3686
+ authority: [user, true],
3687
+ permissionedAccount: [depositPda, false]
3688
+ });
3689
+ return {
3690
+ ix,
3691
+ ensure: [
3692
+ {
3693
+ address: permissionPda,
3694
+ delegated: false,
3695
+ passNotExist: false,
3696
+ label: "closePermission-permissionPda"
3697
+ }
3698
+ ]
3699
+ };
3700
+ }
3701
+
3702
+ // src/actions/unshieldTokens.ts
3703
+ async function buildUnshieldTokensInstructionPlan(params) {
3704
+ const { user, payer, tokenMint, amount, baseProgram, perProgram } = params;
3705
+ const baseConnection = baseProgram.provider.connection;
3706
+ const perRpcEndpoint = perProgram.provider.connection.rpcEndpoint;
3707
+ const isNativeSol = tokenMint.equals(NATIVE_MINT3);
3708
+ const validator = params.validator ?? getErValidatorForRpcEndpoint(perRpcEndpoint);
3709
+ const [depositPda] = findDepositPda(user, tokenMint);
3710
+ const [permissionPda] = findPermissionPda(depositPda);
3711
+ const depositAccountInfoPromise = baseConnection.getAccountInfo(depositPda);
3712
+ const permissionAccountInfoPromise = baseConnection.getAccountInfo(permissionPda);
3713
+ const modifyBalanceRentLamportsPromise = estimateModifyBalanceRentLamports({
3714
+ connection: baseConnection,
3715
+ user,
3716
+ tokenMint,
3717
+ isNativeSol
3718
+ });
3719
+ const [depositAccountInfo, permissionAccountInfo] = await Promise.all([
3720
+ depositAccountInfoPromise,
3721
+ permissionAccountInfoPromise
3722
+ ]);
3723
+ const needsUndelegate = depositAccountInfo?.owner.equals(DELEGATION_PROGRAM_ID) ?? false;
3724
+ const currentDepositAccount = needsUndelegate ? await perProgram.account.deposit.fetchNullable(depositPda) : depositAccountInfo?.owner.equals(PROGRAM_ID) ? await baseProgram.account.deposit.fetchNullable(depositPda) : null;
3725
+ const currentDepositAmount = currentDepositAccount ? BigInt(currentDepositAccount.amount.toString()) : null;
3726
+ const shouldRedelegate = currentDepositAmount !== null && currentDepositAmount - amount > 0n;
3727
+ const closePermissionRentLamports = shouldRedelegate ? 0 : -(permissionAccountInfo?.lamports ?? 0);
3728
+ const closeDepositRentLamports = shouldRedelegate ? 0 : -(depositAccountInfo?.lamports ?? 0);
3729
+ const [modifyBalanceRentLamports, delegationRentLamports] = await Promise.all([
3730
+ modifyBalanceRentLamportsPromise,
3731
+ shouldRedelegate ? estimateDepositDelegationRentLamports({
3732
+ connection: baseConnection,
3733
+ user,
3734
+ tokenMint,
3735
+ depositPda,
3736
+ forceCreate: needsUndelegate
3737
+ }) : Promise.resolve(0)
3738
+ ]);
3739
+ const instructions = [];
3740
+ const checks = [];
3741
+ if (isNativeSol) {
3742
+ instructions.push(...labelTransactionInstructions("ensureWsolAta", [
3743
+ createWsolAta({ user, payer })
3744
+ ]));
3745
+ }
3746
+ const modifyBalanceIxs = await modifyBalanceIx(baseProgram, {
3747
+ tokenMint,
3748
+ user,
3749
+ payer,
3750
+ amount,
3751
+ increase: false
3752
+ });
3753
+ instructions.push({
3754
+ label: "modifyBalanceDecrease",
3755
+ ix: modifyBalanceIxs.ix,
3756
+ rentLamports: modifyBalanceRentLamports,
3757
+ nativeLamports: isNativeSol ? -Number(amount) : undefined
3758
+ });
3759
+ checks.push(...modifyBalanceIxs.ensure);
3760
+ if (isNativeSol) {
3761
+ instructions.push({
3762
+ label: "closeWsolAta",
3763
+ ix: closeWsolAta({
3764
+ user,
3765
+ destination: user
3766
+ })
3767
+ });
3768
+ }
3769
+ if (shouldRedelegate) {
3770
+ const delegateDepositIxs = await delegateDepositIx(baseProgram, {
3771
+ tokenMint,
3772
+ user,
3773
+ payer,
3774
+ validator
3775
+ });
3776
+ instructions.push({
3777
+ label: "redelegateDeposit",
3778
+ ix: delegateDepositIxs.ix,
3779
+ rentLamports: delegationRentLamports
3780
+ });
3781
+ checks.push(...delegateDepositIxs.ensure);
3782
+ } else {
3783
+ const closePermissionIxs = await closePermissionIx({ user, tokenMint });
3784
+ instructions.push({
3785
+ label: "closePermission",
3786
+ ix: closePermissionIxs.ix,
3787
+ rentLamports: closePermissionRentLamports
3788
+ });
3789
+ checks.push(...closePermissionIxs.ensure);
3790
+ const closeDepositIxs = await closeDepositIx(baseProgram, {
3791
+ user,
3792
+ tokenMint
3793
+ });
3794
+ instructions.push({
3795
+ label: "closeDeposit",
3796
+ ix: closeDepositIxs.ix,
3797
+ rentLamports: closeDepositRentLamports
3798
+ });
3799
+ checks.push(...closeDepositIxs.ensure);
3800
+ }
3801
+ return {
3802
+ instructions,
3803
+ checks,
3804
+ needsUndelegate,
3805
+ shouldRedelegate,
3806
+ context: {
3807
+ isNativeSol,
3808
+ validator,
3809
+ depositPda,
3810
+ currentDepositAmount
3811
+ }
3812
+ };
3813
+ }
3814
+ async function buildUnshieldTokensTransactionPlan(params) {
3815
+ const instructionPlan = await buildUnshieldTokensInstructionPlan(params);
3816
+ let preUndelegateTransaction = null;
3817
+ if (instructionPlan.needsUndelegate) {
3818
+ preUndelegateTransaction = {
3819
+ label: "unshield:undelegate",
3820
+ cluster: "ephemeral",
3821
+ instructions: [],
3822
+ checks: []
3823
+ };
3824
+ const undelegateDepositIxs = await undelegateDepositIx(params.perProgram, {
3825
+ user: params.user,
3826
+ payer: params.payer,
3827
+ tokenMint: params.tokenMint,
3828
+ sessionToken: params.sessionToken,
3829
+ magicProgram: params.magicProgram ?? MAGIC_PROGRAM_ID,
3830
+ magicContext: params.magicContext ?? MAGIC_CONTEXT_ID
3831
+ });
3832
+ const undelegateRentLamports = await estimateDepositDelegationRentCreditLamports({
3833
+ connection: params.baseProgram.provider.connection,
3834
+ depositPda: instructionPlan.context.depositPda
3835
+ });
3836
+ preUndelegateTransaction.instructions.push({
3837
+ label: "undelegateDeposit",
3838
+ ix: undelegateDepositIxs.ix,
3839
+ rentLamports: undelegateRentLamports
3840
+ });
3841
+ preUndelegateTransaction.checks.push(...undelegateDepositIxs.ensure);
3842
+ }
3843
+ return {
3844
+ preUndelegateTransaction,
3845
+ baseTransaction: {
3846
+ label: "unshield",
3847
+ cluster: "base",
3848
+ instructions: instructionPlan.instructions,
3849
+ checks: instructionPlan.checks
3850
+ },
3851
+ shouldRedelegate: instructionPlan.shouldRedelegate,
3852
+ context: instructionPlan.context
3853
+ };
3854
+ }
3855
+ async function unshieldTokens(params) {
3856
+ const {
3857
+ user,
3858
+ payer,
3859
+ tokenMint,
3860
+ amount,
3861
+ baseProgram,
3862
+ perProgram,
3863
+ rpcOptions
3864
+ } = params;
3865
+ const baseConnection = baseProgram.provider.connection;
3866
+ const perConnection = perProgram.provider.connection;
3867
+ const plan = await buildUnshieldTokensTransactionPlan({
3868
+ user,
3869
+ payer,
3870
+ tokenMint,
3871
+ amount,
3872
+ baseProgram,
3873
+ perProgram,
3874
+ validator: params.validator,
3875
+ sessionToken: params.sessionToken,
3876
+ magicProgram: params.magicProgram,
3877
+ magicContext: params.magicContext
3878
+ });
3879
+ const {
3880
+ shouldRedelegate,
3881
+ context: { validator, depositPda, isNativeSol, currentDepositAmount }
3882
+ } = plan;
3883
+ if (plan.preUndelegateTransaction) {
3884
+ await sendPlannedUndelegateDepositTransaction({
3885
+ baseProgram,
3886
+ perProgram,
3887
+ transaction: plan.preUndelegateTransaction,
3888
+ user,
3889
+ tokenMint,
3890
+ rpcOptions
3891
+ });
3892
+ }
3893
+ await processEnsureChecks(baseConnection, perConnection, plan.baseTransaction.checks);
3894
+ const delegationWatcher = shouldRedelegate ? waitForAccountOwnerChange(baseConnection, depositPda, DELEGATION_PROGRAM_ID) : null;
3895
+ const tx = new Transaction5().add(...plan.baseTransaction.instructions.map(({ ix }) => ix));
3896
+ let signature;
3897
+ try {
3898
+ signature = await sendAndConfirmWithDiagnostics({
3899
+ label: "unshieldTokens",
3900
+ provider: baseProgram.provider,
3901
+ tx,
3902
+ rpcOptions,
3903
+ extraContext: {
3904
+ user,
3905
+ payer,
3906
+ tokenMint,
3907
+ amount,
3908
+ isNativeSol,
3909
+ validator,
3910
+ depositPda,
3911
+ currentDepositAmount,
3912
+ shouldRedelegate
3913
+ }
3914
+ });
3915
+ if (delegationWatcher) {
3916
+ await delegationWatcher.wait();
3917
+ await new Promise((resolve) => setTimeout(resolve, 3000));
3918
+ }
3919
+ } catch (e) {
3920
+ await delegationWatcher?.cancel();
3921
+ throw e;
3922
+ }
3923
+ return signature;
3924
+ }
3925
+
3926
+ // src/instructions/initializeUsernameDeposit.ts
3927
+ import { TOKEN_PROGRAM_ID as TOKEN_PROGRAM_ID3 } from "@solana/spl-token";
3928
+ import { SystemProgram as SystemProgram4 } from "@solana/web3.js";
3929
+ async function initializeUsernameDepositIx(program, params) {
3930
+ const { username, tokenMint, payer } = params;
3931
+ validateUsername(username);
3932
+ const [usernameDepositPda] = await findUsernameDepositPda(username, tokenMint);
3933
+ const usernameHash = await sha256hash(username);
3934
+ const ix = await program.methods.initializeUsernameDeposit(usernameHash).accountsPartial({
3935
+ payer,
3936
+ tokenMint,
3937
+ tokenProgram: TOKEN_PROGRAM_ID3,
3938
+ systemProgram: SystemProgram4.programId
3939
+ }).instruction();
3940
+ return {
3941
+ ix,
3942
+ ensure: [
3943
+ {
3944
+ address: usernameDepositPda,
3945
+ delegated: false,
3946
+ passNotExist: true,
3947
+ label: "initializeUsernameDeposit-usernameDepositPda"
3948
+ }
3949
+ ]
3950
+ };
3951
+ }
3952
+
3953
+ // src/fee-estimate.ts
3954
+ import {
3955
+ Transaction as Transaction6
3956
+ } from "@solana/web3.js";
3957
+ function createUnsignedTransaction(params) {
3958
+ return new Transaction6({
3959
+ feePayer: params.feePayer,
3960
+ recentBlockhash: params.blockhash
3961
+ }).add(...params.instructions);
3962
+ }
3963
+ async function getMessageFeeLamports(params) {
3964
+ const fee = await params.connection.getFeeForMessage(params.transaction.compileMessage(), params.commitment);
3965
+ if (fee.value === null) {
3966
+ throw new Error("RPC returned null fee for transaction message");
3967
+ }
3968
+ return fee.value;
3969
+ }
3970
+ async function estimatePlannedTransactionFees(params) {
3971
+ const transactionEstimates = await Promise.all(params.transactions.map(async (transactionPlan, index) => {
3972
+ if (transactionPlan.instructions.length === 0) {
3973
+ throw new Error(`Cannot estimate empty transaction: ${transactionPlan.label}`);
3974
+ }
3975
+ const blockhash = await transactionPlan.connection.getLatestBlockhash(params.commitment);
3976
+ const transaction = createUnsignedTransaction({
3977
+ feePayer: transactionPlan.feePayer,
3978
+ blockhash: blockhash.blockhash,
3979
+ instructions: transactionPlan.instructions.map(({ ix }) => ix)
3980
+ });
3981
+ const feeLamports = await getMessageFeeLamports({
3982
+ connection: transactionPlan.connection,
3983
+ transaction,
3984
+ commitment: params.commitment
3985
+ });
3986
+ const instructions = transactionPlan.instructions.map((instructionPlan, instructionIndex) => ({
3987
+ transactionIndex: index,
3988
+ instructionIndex,
3989
+ label: instructionPlan.label,
3990
+ programId: instructionPlan.ix.programId,
3991
+ rentLamports: instructionPlan.rentLamports ?? 0,
3992
+ nativeLamports: instructionPlan.nativeLamports ?? 0
3993
+ }));
3994
+ const rentLamports = instructions.reduce((total, instruction) => total + instruction.rentLamports, 0);
3995
+ const nativeLamports = instructions.reduce((total, instruction) => total + instruction.nativeLamports, 0);
3996
+ return {
3997
+ index,
3998
+ label: transactionPlan.label,
3999
+ cluster: transactionPlan.cluster,
4000
+ feePayer: transactionPlan.feePayer,
4001
+ blockhash: blockhash.blockhash,
4002
+ lastValidBlockHeight: blockhash.lastValidBlockHeight,
4003
+ instructionCount: transactionPlan.instructions.length,
4004
+ feeLamports,
4005
+ rentLamports,
4006
+ nativeLamports,
4007
+ totalLamports: feeLamports + rentLamports + nativeLamports,
4008
+ instructions
4009
+ };
4010
+ }));
4011
+ const instructionEstimates = transactionEstimates.flatMap((transaction) => transaction.instructions);
4012
+ return {
4013
+ transactions: transactionEstimates,
4014
+ instructions: instructionEstimates,
4015
+ totalFeeLamports: transactionEstimates.reduce((total, transaction) => total + transaction.feeLamports, 0),
4016
+ totalRentLamports: instructionEstimates.reduce((total, instruction) => total + instruction.rentLamports, 0),
4017
+ totalNativeLamports: instructionEstimates.reduce((total, instruction) => total + instruction.nativeLamports, 0)
4018
+ };
4019
+ }
4020
+
4021
+ // src/instructions/closeUsernameDeposit.ts
4022
+ async function closeUsernameDepositIx(program, params) {
4023
+ const { username, tokenMint, authority, session } = params;
4024
+ validateUsername(username);
4025
+ const [depositPda] = await findUsernameDepositPda(username, tokenMint);
4026
+ const ix = await program.methods.closeUsernameDeposit().accountsPartial({
4027
+ authority,
4028
+ deposit: depositPda,
4029
+ tokenMint,
4030
+ session
4031
+ }).instruction();
4032
+ return {
4033
+ ix,
4034
+ ensure: [
4035
+ {
4036
+ address: depositPda,
4037
+ delegated: false,
4038
+ passNotExist: false,
4039
+ label: "closeUsernameDeposit-depositPda"
4040
+ }
4041
+ ]
4042
+ };
4043
+ }
4044
+
4045
+ // src/LoyalPrivateTransactionsClient.ts
4046
+ var KAMINO_API_BASE_URL = "https://api.kamino.finance";
4047
+ var KAMINO_MAINNET_ENV = "mainnet-beta";
4048
+ var KAMINO_DEVNET_ENV = "devnet";
4049
+ function prettyStringify2(obj) {
4050
+ const json = JSON.stringify(obj, (_key, value) => {
4051
+ if (value instanceof PublicKey7)
4052
+ return value.toBase58();
4053
+ if (typeof value === "bigint")
4054
+ return value.toString();
4055
+ return value;
4056
+ }, 2);
4057
+ return json.replace(/\[\s+(\d[\d,\s]*\d)\s+\]/g, (_match, inner) => {
4058
+ const items = inner.split(/,\s*/).map((s) => s.trim());
4059
+ return `[${items.join(", ")}]`;
4060
+ });
4061
+ }
4062
+ function programFromRpc(signer, commitment, rpcEndpoint, wsEndpoint) {
4063
+ const adapter = InternalWalletAdapter.from(signer);
4064
+ const baseConnection = new Connection3(rpcEndpoint, {
4065
+ wsEndpoint,
4066
+ commitment
4067
+ });
4068
+ const baseProvider = new AnchorProvider2(baseConnection, adapter, {
4069
+ commitment
4070
+ });
4071
+ return new Program2(telegram_private_transfer_default, baseProvider);
4072
+ }
4073
+ function getKaminoApiEnv(accounts) {
4074
+ return accounts && isKaminoMainnetModifyBalanceAccounts(accounts) ? KAMINO_MAINNET_ENV : KAMINO_DEVNET_ENV;
4075
+ }
4076
+ function normalizeBigInt(value) {
4077
+ if (typeof value === "bigint") {
4078
+ return value;
4079
+ }
4080
+ if (!Number.isInteger(value) || value < 0) {
4081
+ throw new Error(`Expected a non-negative integer amount, received ${value}`);
4082
+ }
4083
+ return BigInt(value);
4084
+ }
4085
+ function toShieldFlowTransactionPlan(plan) {
4086
+ return {
4087
+ label: plan.label,
4088
+ cluster: plan.cluster,
4089
+ instructions: plan.instructions,
4090
+ checks: plan.checks,
4091
+ postSendOwnerChange: plan.postSendOwnerChange
4092
+ };
4093
+ }
4094
+ async function fetchKaminoReserveMetrics(args) {
4095
+ const url = new URL(`/kamino-market/${args.lendingMarket.toBase58()}/reserves/metrics`, KAMINO_API_BASE_URL);
4096
+ url.searchParams.set("env", args.env);
4097
+ const response = await fetch(url.toString(), {
4098
+ method: "GET",
4099
+ headers: {
4100
+ accept: "application/json"
4101
+ }
4102
+ });
4103
+ if (!response.ok) {
4104
+ throw new Error(`Kamino reserve metrics request failed with status ${response.status}`);
4105
+ }
4106
+ const payload = await response.json();
4107
+ if (!Array.isArray(payload)) {
4108
+ throw new Error("Kamino reserve metrics response was not an array");
2250
4109
  }
2251
4110
  const reserveAddress = args.reserve.toBase58();
2252
4111
  const reserveMetrics = payload.find((item) => {
@@ -2286,7 +4145,7 @@ function deriveMessageSigner(signer) {
2286
4145
  }
2287
4146
  throw new Error("Wallet does not support signMessage, required for PER auth");
2288
4147
  }
2289
- function waitForAccountOwnerChange(connection, account, expectedOwner, timeoutMs = 15000, intervalMs = 1000) {
4148
+ function waitForAccountOwnerChange2(connection, account, expectedOwner, timeoutMs = 15000, intervalMs = 1000) {
2290
4149
  let skipWait;
2291
4150
  const subId = connection.onAccountChange(account, (accountInfo) => {
2292
4151
  if (accountInfo.owner.equals(expectedOwner) && skipWait) {
@@ -2359,10 +4218,7 @@ class LoyalPrivateTransactionsClient {
2359
4218
  let expiresAt;
2360
4219
  if (!authToken) {
2361
4220
  try {
2362
- const isVerified = await verifyTeeRpcIntegrity(ephemeralRpcEndpoint);
2363
- if (!isVerified) {
2364
- console.error("[LoyalClient] TEE RPC integrity verification returned false");
2365
- }
4221
+ await verifyTeeIntegrity(ephemeralRpcEndpoint);
2366
4222
  } catch (e) {
2367
4223
  console.error("[LoyalClient] TEE RPC integrity verification error:", e);
2368
4224
  }
@@ -2377,123 +4233,328 @@ class LoyalPrivateTransactionsClient {
2377
4233
  const ephemeralProgram = programFromRpc(signer, commitment, finalEphemeralRpcEndpoint, finalEphemeralWsEndpoint);
2378
4234
  return new LoyalPrivateTransactionsClient(baseProgram, ephemeralProgram, adapter);
2379
4235
  }
2380
- async initializeDeposit(params) {
2381
- const { user, tokenMint, payer, rpcOptions } = params;
2382
- const [depositPda] = findDepositPda(user, tokenMint);
2383
- await this.ensureNotDelegated(depositPda, "modifyBalance-depositPda", true);
2384
- const signature = await this.baseProgram.methods.initializeDeposit().accountsPartial({
4236
+ async shieldTokens(params) {
4237
+ const payer = params.payer ?? params.user;
4238
+ return shieldTokens({
4239
+ user: params.user,
2385
4240
  payer,
2386
- user,
2387
- tokenMint,
2388
- tokenProgram: TOKEN_PROGRAM_ID,
2389
- systemProgram: SystemProgram.programId
2390
- }).rpc(rpcOptions);
2391
- return signature;
4241
+ tokenMint: params.tokenMint,
4242
+ amount: normalizeBigInt(params.amount),
4243
+ baseProgram: this.baseProgram,
4244
+ perProgram: this.ephemeralProgram,
4245
+ validator: params.validator,
4246
+ sessionToken: params.sessionToken,
4247
+ magicProgram: params.magicProgram,
4248
+ magicContext: params.magicContext,
4249
+ rpcOptions: params.rpcOptions
4250
+ });
2392
4251
  }
2393
- async initializeUsernameDeposit(params) {
2394
- const { username, tokenMint, payer, rpcOptions } = params;
2395
- this.validateUsername(username);
2396
- const [usernameDepositPda] = await findUsernameDepositPda(username, tokenMint);
2397
- await this.ensureNotDelegated(usernameDepositPda, "modifyBalance-depositPda", true);
2398
- const usernameHash = await sha256hash(username);
2399
- const signature = await this.baseProgram.methods.initializeUsernameDeposit(usernameHash).accountsPartial({
4252
+ async unshieldTokens(params) {
4253
+ const payer = params.payer ?? params.user;
4254
+ return unshieldTokens({
4255
+ user: params.user,
2400
4256
  payer,
2401
- tokenMint,
2402
- tokenProgram: TOKEN_PROGRAM_ID,
2403
- systemProgram: SystemProgram.programId
2404
- }).rpc(rpcOptions);
2405
- return signature;
4257
+ tokenMint: params.tokenMint,
4258
+ amount: normalizeBigInt(params.amount),
4259
+ baseProgram: this.baseProgram,
4260
+ perProgram: this.ephemeralProgram,
4261
+ validator: params.validator,
4262
+ sessionToken: params.sessionToken,
4263
+ magicProgram: params.magicProgram,
4264
+ magicContext: params.magicContext,
4265
+ rpcOptions: params.rpcOptions
4266
+ });
2406
4267
  }
2407
- async modifyBalance(params) {
2408
- const {
2409
- user,
2410
- tokenMint,
2411
- amount,
2412
- increase,
4268
+ async buildShieldFlowTransactionPlan(params) {
4269
+ const amount = normalizeBigInt(params.amount);
4270
+ const payer = params.payer ?? params.user;
4271
+ const validator = params.validator ?? this.getExpectedErValidator();
4272
+ const magicProgram = params.magicProgram ?? MAGIC_PROGRAM_ID;
4273
+ const magicContext = params.magicContext ?? MAGIC_CONTEXT_ID;
4274
+ const transactions = [];
4275
+ if (params.kind === "shield") {
4276
+ const shieldPlan = await buildShieldTokensTransactionPlan({
4277
+ user: params.user,
4278
+ payer,
4279
+ tokenMint: params.tokenMint,
4280
+ amount,
4281
+ baseProgram: this.baseProgram,
4282
+ perProgram: this.ephemeralProgram,
4283
+ validator,
4284
+ sessionToken: params.sessionToken,
4285
+ magicProgram,
4286
+ magicContext
4287
+ });
4288
+ if (shieldPlan.preUndelegateTransaction) {
4289
+ transactions.push(toShieldFlowTransactionPlan({
4290
+ ...shieldPlan.preUndelegateTransaction,
4291
+ postSendOwnerChange: {
4292
+ address: shieldPlan.context.depositPda,
4293
+ owner: PROGRAM_ID,
4294
+ bestEffort: true
4295
+ }
4296
+ }));
4297
+ }
4298
+ transactions.push(toShieldFlowTransactionPlan({
4299
+ ...shieldPlan.baseTransaction,
4300
+ postSendOwnerChange: {
4301
+ address: shieldPlan.context.depositPda,
4302
+ owner: DELEGATION_PROGRAM_ID,
4303
+ bestEffort: true
4304
+ }
4305
+ }));
4306
+ } else {
4307
+ const unshieldPlan = await buildUnshieldTokensTransactionPlan({
4308
+ user: params.user,
4309
+ payer,
4310
+ tokenMint: params.tokenMint,
4311
+ amount,
4312
+ baseProgram: this.baseProgram,
4313
+ perProgram: this.ephemeralProgram,
4314
+ validator,
4315
+ sessionToken: params.sessionToken,
4316
+ magicProgram,
4317
+ magicContext
4318
+ });
4319
+ if (unshieldPlan.preUndelegateTransaction) {
4320
+ transactions.push(toShieldFlowTransactionPlan({
4321
+ ...unshieldPlan.preUndelegateTransaction,
4322
+ postSendOwnerChange: {
4323
+ address: unshieldPlan.context.depositPda,
4324
+ owner: PROGRAM_ID,
4325
+ bestEffort: true
4326
+ }
4327
+ }));
4328
+ }
4329
+ transactions.push(toShieldFlowTransactionPlan({
4330
+ ...unshieldPlan.baseTransaction,
4331
+ postSendOwnerChange: unshieldPlan.shouldRedelegate ? {
4332
+ address: unshieldPlan.context.depositPda,
4333
+ owner: DELEGATION_PROGRAM_ID,
4334
+ bestEffort: true
4335
+ } : undefined
4336
+ }));
4337
+ }
4338
+ return {
4339
+ kind: params.kind,
4340
+ user: params.user,
2413
4341
  payer,
2414
- userTokenAccount,
2415
- rpcOptions
2416
- } = params;
2417
- const [depositPda] = findDepositPda(user, tokenMint);
2418
- await this.ensureNotDelegated(depositPda, "modifyBalance-depositPda");
2419
- const [vaultPda] = findVaultPda(tokenMint);
2420
- const vaultTokenAccount = getAssociatedTokenAddressSync(tokenMint, vaultPda, true, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID);
2421
- const kaminoAccounts = getKaminoModifyBalanceAccountsForTokenMint(tokenMint);
2422
- const vaultCollateralTokenAccount = kaminoAccounts ? getAssociatedTokenAddressSync(kaminoAccounts.reserveCollateralMint, vaultPda, true, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID) : null;
2423
- console.log("modifyBalance", {
2424
- payer: payer.toString(),
2425
- user: user.toString(),
2426
- vault: vaultPda.toString(),
2427
- deposit: depositPda.toString(),
2428
- userTokenAccount: userTokenAccount.toString(),
2429
- vaultTokenAccount: vaultTokenAccount.toString(),
2430
- tokenMint: tokenMint.toString(),
2431
- kaminoAccounts: kaminoAccounts ? {
2432
- lendingMarket: kaminoAccounts.lendingMarket.toString(),
2433
- lendingMarketAuthority: kaminoAccounts.lendingMarketAuthority.toString(),
2434
- reserve: kaminoAccounts.reserve.toString(),
2435
- reserveLiquiditySupply: kaminoAccounts.reserveLiquiditySupply.toString(),
2436
- reserveCollateralMint: kaminoAccounts.reserveCollateralMint.toString(),
2437
- vaultCollateralTokenAccount: vaultCollateralTokenAccount?.toString() ?? null
2438
- } : null
4342
+ tokenMint: params.tokenMint,
4343
+ amount,
4344
+ transactions
4345
+ };
4346
+ }
4347
+ async buildShieldTokensTransactionPlan(params) {
4348
+ return this.buildShieldFlowTransactionPlan({
4349
+ ...params,
4350
+ kind: "shield"
2439
4351
  });
2440
- let methodBuilder = this.baseProgram.methods.modifyBalance({ amount: new BN(amount.toString()), increase }).accountsPartial({
2441
- payer,
2442
- user,
2443
- vault: vaultPda,
2444
- deposit: depositPda,
2445
- userTokenAccount,
2446
- vaultTokenAccount,
2447
- tokenMint,
2448
- tokenProgram: TOKEN_PROGRAM_ID,
2449
- associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
2450
- systemProgram: SystemProgram.programId
4352
+ }
4353
+ async buildUnshieldTokensTransactionPlan(params) {
4354
+ return this.buildShieldFlowTransactionPlan({
4355
+ ...params,
4356
+ kind: "unshield"
2451
4357
  });
2452
- if (kaminoAccounts && vaultCollateralTokenAccount) {
2453
- methodBuilder = methodBuilder.remainingAccounts([
2454
- {
2455
- pubkey: kaminoAccounts.lendingMarket,
2456
- isSigner: false,
2457
- isWritable: false
2458
- },
2459
- {
2460
- pubkey: kaminoAccounts.lendingMarketAuthority,
2461
- isSigner: false,
2462
- isWritable: false
2463
- },
2464
- {
2465
- pubkey: kaminoAccounts.reserve,
2466
- isSigner: false,
2467
- isWritable: true
2468
- },
2469
- {
2470
- pubkey: kaminoAccounts.reserveLiquiditySupply,
2471
- isSigner: false,
2472
- isWritable: true
2473
- },
2474
- {
2475
- pubkey: kaminoAccounts.reserveCollateralMint,
2476
- isSigner: false,
2477
- isWritable: true
2478
- },
2479
- {
2480
- pubkey: vaultCollateralTokenAccount,
2481
- isSigner: false,
2482
- isWritable: true
2483
- },
2484
- {
2485
- pubkey: kaminoAccounts.instructionSysvarAccount,
2486
- isSigner: false,
2487
- isWritable: false
2488
- },
2489
- {
2490
- pubkey: kaminoAccounts.klendProgram,
2491
- isSigner: false,
2492
- isWritable: false
4358
+ }
4359
+ async estimateShieldFlowFee(params) {
4360
+ const transactions = params.plan.transactions.map((transaction) => ({
4361
+ label: transaction.label,
4362
+ cluster: transaction.cluster,
4363
+ connection: transaction.cluster === "base" ? this.baseProgram.provider.connection : this.ephemeralProgram.provider.connection,
4364
+ feePayer: params.plan.payer,
4365
+ instructions: transaction.instructions
4366
+ }));
4367
+ if (transactions.length === 0) {
4368
+ throw new Error("Cannot estimate an empty shield flow plan");
4369
+ }
4370
+ const estimate = await estimatePlannedTransactionFees({
4371
+ transactions,
4372
+ commitment: params.commitment
4373
+ });
4374
+ return {
4375
+ kind: params.plan.kind,
4376
+ user: params.plan.user,
4377
+ payer: params.plan.payer,
4378
+ tokenMint: params.plan.tokenMint,
4379
+ amount: params.plan.amount,
4380
+ totalFeeLamports: estimate.totalFeeLamports,
4381
+ totalRentLamports: estimate.totalRentLamports,
4382
+ totalNativeLamports: estimate.totalNativeLamports,
4383
+ feeAndRentLamports: estimate.totalFeeLamports + estimate.totalRentLamports,
4384
+ totalLamports: estimate.totalFeeLamports + estimate.totalRentLamports + estimate.totalNativeLamports,
4385
+ transactions: estimate.transactions,
4386
+ instructions: estimate.instructions,
4387
+ note: "Solana charges protocol fees per transaction message. Instruction rows expose net rent changes (positive locks rent, negative reclaims rent) and nativeLamports for native-SOL token value movement. totalLamports is a cost-style net SOL impact for the common payer=user flow: positive values are debits/costs and negative values are credits/gains. feeAndRentLamports excludes native token principal. If payer differs from user, nativeLamports belongs to the token owner while fees/rent may belong to the payer."
4388
+ };
4389
+ }
4390
+ async estimateShieldTokensFee(params) {
4391
+ if (params.plan.kind !== "shield") {
4392
+ throw new Error("estimateShieldTokensFee expected a shield plan");
4393
+ }
4394
+ return this.estimateShieldFlowFee(params);
4395
+ }
4396
+ async estimateUnshieldTokensFee(params) {
4397
+ if (params.plan.kind !== "unshield") {
4398
+ throw new Error("estimateUnshieldTokensFee expected an unshield plan");
4399
+ }
4400
+ return this.estimateShieldFlowFee(params);
4401
+ }
4402
+ async executeShieldFlowTransactionPlan(params) {
4403
+ if (params.plan.transactions.length === 0) {
4404
+ throw new Error("Cannot execute an empty shield flow plan");
4405
+ }
4406
+ const signatures = [];
4407
+ for (let transactionIndex = 0;transactionIndex < params.plan.transactions.length; transactionIndex += 1) {
4408
+ const transactionPlan = params.plan.transactions[transactionIndex];
4409
+ if (transactionPlan.instructions.length === 0) {
4410
+ throw new Error(`Cannot execute empty transaction plan: ${transactionPlan.label}`);
4411
+ }
4412
+ await processEnsureChecks(this.baseProgram.provider.connection, this.ephemeralProgram.provider.connection, transactionPlan.checks ?? []);
4413
+ const ownerChangeWatcher = transactionPlan.postSendOwnerChange ? waitForAccountOwnerChange2(this.baseProgram.provider.connection, transactionPlan.postSendOwnerChange.address, transactionPlan.postSendOwnerChange.owner) : null;
4414
+ const provider = transactionPlan.cluster === "base" ? this.baseProgram.provider : this.ephemeralProgram.provider;
4415
+ const tx = new Transaction7().add(...transactionPlan.instructions.map(({ ix }) => ix));
4416
+ let signature;
4417
+ try {
4418
+ signature = await sendAndConfirmWithDiagnostics({
4419
+ label: transactionPlan.label,
4420
+ provider,
4421
+ tx,
4422
+ rpcOptions: params.rpcOptions,
4423
+ extraContext: {
4424
+ kind: params.plan.kind,
4425
+ user: params.plan.user,
4426
+ payer: params.plan.payer,
4427
+ tokenMint: params.plan.tokenMint,
4428
+ amount: params.plan.amount,
4429
+ transactionIndex,
4430
+ cluster: transactionPlan.cluster,
4431
+ postSendOwnerChange: transactionPlan.postSendOwnerChange
4432
+ }
4433
+ });
4434
+ } catch (e) {
4435
+ await ownerChangeWatcher?.cancel();
4436
+ throw e;
4437
+ }
4438
+ if (ownerChangeWatcher) {
4439
+ try {
4440
+ await ownerChangeWatcher.wait();
4441
+ await new Promise((resolve) => setTimeout(resolve, 3000));
4442
+ } catch (err) {
4443
+ if (!transactionPlan.postSendOwnerChange?.bestEffort) {
4444
+ throw err;
4445
+ }
4446
+ console.warn(`[${transactionPlan.label}] owner-change watcher did not observe expected owner (signature=${signature}); continuing`, err);
2493
4447
  }
2494
- ]);
4448
+ }
4449
+ signatures.push({
4450
+ index: transactionIndex,
4451
+ label: transactionPlan.label,
4452
+ cluster: transactionPlan.cluster,
4453
+ signature
4454
+ });
4455
+ }
4456
+ return {
4457
+ kind: params.plan.kind,
4458
+ user: params.plan.user,
4459
+ payer: params.plan.payer,
4460
+ tokenMint: params.plan.tokenMint,
4461
+ amount: params.plan.amount,
4462
+ signatures
4463
+ };
4464
+ }
4465
+ async executeShieldTokensTransactionPlan(params) {
4466
+ if (params.plan.kind !== "shield") {
4467
+ throw new Error("executeShieldTokensTransactionPlan expected a shield plan");
2495
4468
  }
2496
- const signature = await methodBuilder.rpc(rpcOptions);
4469
+ return this.executeShieldFlowTransactionPlan(params);
4470
+ }
4471
+ async executeUnshieldTokensTransactionPlan(params) {
4472
+ if (params.plan.kind !== "unshield") {
4473
+ throw new Error("executeUnshieldTokensTransactionPlan expected an unshield plan");
4474
+ }
4475
+ return this.executeShieldFlowTransactionPlan(params);
4476
+ }
4477
+ async initializeDeposit(params) {
4478
+ const { ix, ensure } = await initializeDepositIx(this.baseProgram, params);
4479
+ await processEnsureChecks(this.baseProgram.provider.connection, this.ephemeralProgram.provider.connection, ensure);
4480
+ const tx = new Transaction7().add(ix);
4481
+ return await sendAndConfirmWithDiagnostics({
4482
+ label: "initializeDeposit",
4483
+ provider: this.baseProgram.provider,
4484
+ tx,
4485
+ rpcOptions: params.rpcOptions,
4486
+ extraContext: {
4487
+ user: params.user,
4488
+ tokenMint: params.tokenMint
4489
+ }
4490
+ });
4491
+ }
4492
+ async initializeUsernameDeposit(params) {
4493
+ const { ix, ensure } = await initializeUsernameDepositIx(this.baseProgram, params);
4494
+ await processEnsureChecks(this.baseProgram.provider.connection, this.ephemeralProgram.provider.connection, ensure);
4495
+ const tx = new Transaction7().add(ix);
4496
+ return await sendAndConfirmWithDiagnostics({
4497
+ label: "initializeUsernameDeposit",
4498
+ provider: this.baseProgram.provider,
4499
+ tx,
4500
+ rpcOptions: params.rpcOptions,
4501
+ extraContext: {
4502
+ username: params.username,
4503
+ tokenMint: params.tokenMint
4504
+ }
4505
+ });
4506
+ }
4507
+ async closeDeposit(params) {
4508
+ const { user, tokenMint } = params;
4509
+ const { ix, ensure } = await closeDepositIx(this.baseProgram, params);
4510
+ await processEnsureChecks(this.baseProgram.provider.connection, this.ephemeralProgram.provider.connection, ensure);
4511
+ const tx = new Transaction7().add(ix);
4512
+ return await sendAndConfirmWithDiagnostics({
4513
+ label: "closeDeposit",
4514
+ provider: this.baseProgram.provider,
4515
+ tx,
4516
+ rpcOptions: params.rpcOptions,
4517
+ extraContext: {
4518
+ user,
4519
+ tokenMint
4520
+ }
4521
+ });
4522
+ }
4523
+ async closeUsernameDeposit(params) {
4524
+ const { username, tokenMint, authority, session } = params;
4525
+ const { ix, ensure } = await closeUsernameDepositIx(this.baseProgram, params);
4526
+ await processEnsureChecks(this.baseProgram.provider.connection, this.ephemeralProgram.provider.connection, ensure);
4527
+ const tx = new Transaction7().add(ix);
4528
+ return await sendAndConfirmWithDiagnostics({
4529
+ label: "closeUsernameDeposit",
4530
+ provider: this.baseProgram.provider,
4531
+ tx,
4532
+ rpcOptions: params.rpcOptions,
4533
+ extraContext: {
4534
+ username,
4535
+ tokenMint,
4536
+ authority,
4537
+ session
4538
+ }
4539
+ });
4540
+ }
4541
+ async modifyBalance(params) {
4542
+ const { user, tokenMint } = params;
4543
+ const { ix, ensure } = await modifyBalanceIx(this.baseProgram, params);
4544
+ await processEnsureChecks(this.baseProgram.provider.connection, this.ephemeralProgram.provider.connection, ensure);
4545
+ const tx = new Transaction7().add(ix);
4546
+ const signature = await sendAndConfirmWithDiagnostics({
4547
+ label: "modifyBalance",
4548
+ provider: this.baseProgram.provider,
4549
+ tx,
4550
+ rpcOptions: params.rpcOptions,
4551
+ extraContext: {
4552
+ user,
4553
+ tokenMint,
4554
+ amount: params.amount,
4555
+ increase: params.increase
4556
+ }
4557
+ });
2497
4558
  const deposit = await this.getBaseDeposit(user, tokenMint);
2498
4559
  if (!deposit) {
2499
4560
  throw new Error("Failed to fetch deposit after modification");
@@ -2502,7 +4563,7 @@ class LoyalPrivateTransactionsClient {
2502
4563
  }
2503
4564
  async claimUsernameDepositToDeposit(params) {
2504
4565
  const { username, tokenMint, amount, recipient, session, rpcOptions } = params;
2505
- this.validateUsername(username);
4566
+ validateUsername(username);
2506
4567
  const [sourceUsernameDeposit] = await findUsernameDepositPda(username, tokenMint);
2507
4568
  const [destinationDeposit] = findDepositPda(recipient, tokenMint);
2508
4569
  await this.ensureDelegated(sourceUsernameDeposit, "claimUsernameDepositToDeposit-sourceUsernameDeposit");
@@ -2513,16 +4574,16 @@ class LoyalPrivateTransactionsClient {
2513
4574
  destinationDeposit,
2514
4575
  tokenMint,
2515
4576
  session,
2516
- tokenProgram: TOKEN_PROGRAM_ID
4577
+ tokenProgram: TOKEN_PROGRAM_ID4
2517
4578
  };
2518
- console.log("claimUsernameDepositToDeposit accounts:", prettyStringify(accounts));
4579
+ console.log("claimUsernameDepositToDeposit accounts:", prettyStringify2(accounts));
2519
4580
  const connection = this.baseProgram.provider.connection;
2520
4581
  const [srcInfo, dstInfo, sessionInfo] = await Promise.all([
2521
4582
  connection.getAccountInfo(sourceUsernameDeposit),
2522
4583
  connection.getAccountInfo(destinationDeposit),
2523
4584
  connection.getAccountInfo(session)
2524
4585
  ]);
2525
- console.log("claimUsernameDepositToDeposit sourceUsernameDeposit accountInfo:", prettyStringify({
4586
+ console.log("claimUsernameDepositToDeposit sourceUsernameDeposit accountInfo:", prettyStringify2({
2526
4587
  address: sourceUsernameDeposit.toBase58(),
2527
4588
  exists: !!srcInfo,
2528
4589
  owner: srcInfo?.owner?.toBase58(),
@@ -2530,7 +4591,7 @@ class LoyalPrivateTransactionsClient {
2530
4591
  dataLen: srcInfo?.data?.length,
2531
4592
  executable: srcInfo?.executable
2532
4593
  }));
2533
- console.log("claimUsernameDepositToDeposit destinationDeposit accountInfo:", prettyStringify({
4594
+ console.log("claimUsernameDepositToDeposit destinationDeposit accountInfo:", prettyStringify2({
2534
4595
  address: destinationDeposit.toBase58(),
2535
4596
  exists: !!dstInfo,
2536
4597
  owner: dstInfo?.owner?.toBase58(),
@@ -2538,7 +4599,7 @@ class LoyalPrivateTransactionsClient {
2538
4599
  dataLen: dstInfo?.data?.length,
2539
4600
  executable: dstInfo?.executable
2540
4601
  }));
2541
- console.log("claimUsernameDepositToDeposit session accountInfo:", prettyStringify({
4602
+ console.log("claimUsernameDepositToDeposit session accountInfo:", prettyStringify2({
2542
4603
  address: session.toBase58(),
2543
4604
  exists: !!sessionInfo,
2544
4605
  owner: sessionInfo?.owner?.toBase58(),
@@ -2547,50 +4608,40 @@ class LoyalPrivateTransactionsClient {
2547
4608
  executable: sessionInfo?.executable
2548
4609
  }));
2549
4610
  try {
2550
- const sim = await this.ephemeralProgram.methods.claimUsernameDepositToDeposit(new BN(amount.toString())).accountsPartial(accounts).simulate();
4611
+ const sim = await this.ephemeralProgram.methods.claimUsernameDepositToDeposit(new BN2(amount.toString())).accountsPartial(accounts).simulate();
2551
4612
  console.log("claimUsernameDepositToDeposit simulation logs:", sim.raw);
2552
4613
  } catch (simErr) {
2553
4614
  const simResponse = simErr.simulationResponse;
2554
4615
  console.error("claimUsernameDepositToDeposit simulate FAILED");
2555
4616
  console.error(" error message:", simErr instanceof Error ? simErr.message : String(simErr));
2556
4617
  if (simResponse) {
2557
- console.error(" simulation err:", prettyStringify(simResponse.err));
2558
- console.error(" simulation logs:", prettyStringify(simResponse.logs));
4618
+ console.error(" simulation err:", prettyStringify2(simResponse.err));
4619
+ console.error(" simulation logs:", prettyStringify2(simResponse.logs));
2559
4620
  console.error(" unitsConsumed:", simResponse.unitsConsumed);
2560
4621
  }
2561
4622
  throw simErr;
2562
4623
  }
2563
- const signature = await this.ephemeralProgram.methods.claimUsernameDepositToDeposit(new BN(amount.toString())).accountsPartial(accounts).rpc({ skipPreflight: true, commitment: "confirmed" });
4624
+ const signature = await this.ephemeralProgram.methods.claimUsernameDepositToDeposit(new BN2(amount.toString())).accountsPartial(accounts).rpc({ skipPreflight: true, commitment: "confirmed" });
2564
4625
  return signature;
2565
4626
  }
2566
4627
  async createPermission(params) {
2567
- const { user, tokenMint, payer, rpcOptions } = params;
2568
- const [depositPda] = findDepositPda(user, tokenMint);
2569
- const [permissionPda] = findPermissionPda(depositPda);
2570
- await this.ensureNotDelegated(depositPda, "createPermission-depositPda");
2571
- if (await this.permissionAccountExists(permissionPda)) {
2572
- return null;
2573
- }
2574
- try {
2575
- const signature = await this.baseProgram.methods.createPermission().accountsPartial({
2576
- payer,
2577
- user,
2578
- deposit: depositPda,
2579
- permission: permissionPda,
2580
- permissionProgram: PERMISSION_PROGRAM_ID,
2581
- systemProgram: SystemProgram.programId
2582
- }).rpc(rpcOptions);
2583
- return signature;
2584
- } catch (err) {
2585
- if (this.isAccountAlreadyInUse(err)) {
2586
- return "permission-exists";
4628
+ const { ix, ensure } = await createPermissionIx(this.baseProgram, params);
4629
+ await processEnsureChecks(this.baseProgram.provider.connection, this.ephemeralProgram.provider.connection, ensure);
4630
+ const tx = new Transaction7().add(ix);
4631
+ return await sendAndConfirmWithDiagnostics({
4632
+ label: "createPermission",
4633
+ provider: this.baseProgram.provider,
4634
+ tx,
4635
+ rpcOptions: params.rpcOptions,
4636
+ extraContext: {
4637
+ user: params.user,
4638
+ tokenMint: params.tokenMint
2587
4639
  }
2588
- throw err;
2589
- }
4640
+ });
2590
4641
  }
2591
4642
  async createUsernamePermission(params) {
2592
4643
  const { username, tokenMint, session, authority, payer, rpcOptions } = params;
2593
- this.validateUsername(username);
4644
+ validateUsername(username);
2594
4645
  const [depositPda] = await findUsernameDepositPda(username, tokenMint);
2595
4646
  const [permissionPda] = findPermissionPda(depositPda);
2596
4647
  await this.ensureNotDelegated(depositPda, "createUsernamePermission-depositPda");
@@ -2605,7 +4656,7 @@ class LoyalPrivateTransactionsClient {
2605
4656
  session,
2606
4657
  permission: permissionPda,
2607
4658
  permissionProgram: PERMISSION_PROGRAM_ID,
2608
- systemProgram: SystemProgram.programId
4659
+ systemProgram: SystemProgram5.programId
2609
4660
  }).rpc(rpcOptions);
2610
4661
  return signature;
2611
4662
  } catch (err) {
@@ -2616,35 +4667,35 @@ class LoyalPrivateTransactionsClient {
2616
4667
  }
2617
4668
  }
2618
4669
  async delegateDeposit(params) {
2619
- const { user, tokenMint, payer, validator, rpcOptions } = params;
4670
+ const { user, tokenMint } = params;
4671
+ const { ix, ensure } = await delegateDepositIx(this.baseProgram, params);
4672
+ await processEnsureChecks(this.baseProgram.provider.connection, this.ephemeralProgram.provider.connection, ensure);
2620
4673
  const [depositPda] = findDepositPda(user, tokenMint);
2621
- const [bufferPda] = findBufferPda(depositPda);
2622
- const [delegationRecordPda] = findDelegationRecordPda(depositPda);
2623
- const [delegationMetadataPda] = findDelegationMetadataPda(depositPda);
2624
- await this.ensureNotDelegated(depositPda, "delegateDeposit-depositPda");
2625
- const accounts = {
2626
- payer,
2627
- bufferDeposit: bufferPda,
2628
- delegationRecordDeposit: delegationRecordPda,
2629
- delegationMetadataDeposit: delegationMetadataPda,
2630
- deposit: depositPda,
2631
- validator,
2632
- ownerProgram: PROGRAM_ID,
2633
- delegationProgram: DELEGATION_PROGRAM_ID,
2634
- systemProgram: SystemProgram.programId
2635
- };
2636
- const delegationWatcher = waitForAccountOwnerChange(this.baseProgram.provider.connection, depositPda, DELEGATION_PROGRAM_ID);
4674
+ const delegationWatcher = waitForAccountOwnerChange2(this.baseProgram.provider.connection, depositPda, DELEGATION_PROGRAM_ID);
2637
4675
  let signature;
2638
4676
  try {
2639
- console.log("delegateDeposit Accounts:", prettyStringify(accounts));
2640
- signature = await this.baseProgram.methods.delegate(user, tokenMint).accountsPartial(accounts).rpc(rpcOptions);
2641
- console.log("delegateDeposit: waiting for depositPda owner to be DELEGATION_PROGRAM_ID on base connection...");
2642
- await delegationWatcher.wait();
2643
- await new Promise((resolve) => setTimeout(resolve, 3000));
4677
+ const tx = new Transaction7().add(ix);
4678
+ signature = await sendAndConfirmWithDiagnostics({
4679
+ label: "delegateDeposit",
4680
+ provider: this.baseProgram.provider,
4681
+ tx,
4682
+ rpcOptions: params.rpcOptions,
4683
+ extraContext: {
4684
+ user,
4685
+ tokenMint,
4686
+ depositPda
4687
+ }
4688
+ });
2644
4689
  } catch (e) {
2645
4690
  await delegationWatcher.cancel();
2646
4691
  throw e;
2647
4692
  }
4693
+ try {
4694
+ await delegationWatcher.wait();
4695
+ await new Promise((resolve) => setTimeout(resolve, 3000));
4696
+ } catch (err) {
4697
+ console.warn(`[delegateDeposit] delegation watcher did not observe owner change (signature=${signature}); continuing`, err);
4698
+ }
2648
4699
  return signature;
2649
4700
  }
2650
4701
  async delegateUsernameDeposit(params) {
@@ -2655,7 +4706,7 @@ class LoyalPrivateTransactionsClient {
2655
4706
  validator,
2656
4707
  rpcOptions
2657
4708
  } = params;
2658
- this.validateUsername(username);
4709
+ validateUsername(username);
2659
4710
  const [depositPda] = await findUsernameDepositPda(username, tokenMint);
2660
4711
  const [bufferPda] = findBufferPda(depositPda);
2661
4712
  const [delegationRecordPda] = findDelegationRecordPda(depositPda);
@@ -2670,56 +4721,30 @@ class LoyalPrivateTransactionsClient {
2670
4721
  deposit: depositPda,
2671
4722
  ownerProgram: PROGRAM_ID,
2672
4723
  delegationProgram: DELEGATION_PROGRAM_ID,
2673
- systemProgram: SystemProgram.programId
4724
+ systemProgram: SystemProgram5.programId
2674
4725
  };
2675
4726
  accounts.validator = validator ?? null;
2676
- const delegationWatcher = waitForAccountOwnerChange(this.baseProgram.provider.connection, depositPda, DELEGATION_PROGRAM_ID);
4727
+ const delegationWatcher = waitForAccountOwnerChange2(this.baseProgram.provider.connection, depositPda, DELEGATION_PROGRAM_ID);
2677
4728
  let signature;
2678
4729
  try {
2679
- console.log("delegateUsernameDeposit Accounts:", prettyStringify(accounts));
4730
+ console.log("delegateUsernameDeposit Accounts:", prettyStringify2(accounts));
2680
4731
  signature = await this.baseProgram.methods.delegateUsernameDeposit(usernameHash, tokenMint).accountsPartial(accounts).rpc(rpcOptions);
2681
- console.log("delegateUsernameDeposit: waiting for depositPda owner to be DELEGATION_PROGRAM_ID on base connection...");
2682
- await delegationWatcher.wait();
2683
- await new Promise((resolve) => setTimeout(resolve, 3000));
2684
4732
  } catch (e) {
2685
4733
  await delegationWatcher.cancel();
2686
4734
  throw e;
2687
4735
  }
2688
- return signature;
2689
- }
2690
- async undelegateDeposit(params) {
2691
- const {
2692
- user,
2693
- tokenMint,
2694
- payer,
2695
- sessionToken,
2696
- magicProgram,
2697
- magicContext,
2698
- rpcOptions
2699
- } = params;
2700
- const [depositPda] = findDepositPda(user, tokenMint);
2701
- await this.ensureDelegated(depositPda, "undelegateDeposit-depositPda", true);
2702
- const accounts = {
2703
- user,
2704
- payer,
2705
- deposit: depositPda,
2706
- magicProgram,
2707
- magicContext
2708
- };
2709
- accounts.sessionToken = sessionToken ?? null;
2710
- const delegationWatcher = waitForAccountOwnerChange(this.baseProgram.provider.connection, depositPda, PROGRAM_ID);
2711
- let signature;
2712
4736
  try {
2713
- console.log("undelegateDeposit Accounts:", prettyStringify(accounts));
2714
- signature = await this.ephemeralProgram.methods.undelegate().accountsPartial(accounts).rpc(rpcOptions);
2715
- console.log("undelegateDeposit: waiting for depositPda owner to be PROGRAM_ID on base connection...");
4737
+ console.log("delegateUsernameDeposit: waiting for depositPda owner to be DELEGATION_PROGRAM_ID on base connection...");
2716
4738
  await delegationWatcher.wait();
2717
- } catch (e) {
2718
- await delegationWatcher.cancel();
2719
- throw e;
4739
+ await new Promise((resolve) => setTimeout(resolve, 3000));
4740
+ } catch (err) {
4741
+ console.warn(`[delegateUsernameDeposit] delegation watcher did not observe owner change (signature=${signature}); continuing`, err);
2720
4742
  }
2721
4743
  return signature;
2722
4744
  }
4745
+ async undelegateDeposit(params) {
4746
+ return await undelegateDeposit(this.baseProgram, this.ephemeralProgram, params);
4747
+ }
2723
4748
  async undelegateUsernameDeposit(params) {
2724
4749
  const {
2725
4750
  username,
@@ -2730,7 +4755,7 @@ class LoyalPrivateTransactionsClient {
2730
4755
  magicContext,
2731
4756
  rpcOptions
2732
4757
  } = params;
2733
- this.validateUsername(username);
4758
+ validateUsername(username);
2734
4759
  const [depositPda] = await findUsernameDepositPda(username, tokenMint);
2735
4760
  await this.ensureDelegated(depositPda, "undelegateUsernameDeposit-depositPda");
2736
4761
  const usernameHash = await sha256hash(username);
@@ -2763,7 +4788,7 @@ class LoyalPrivateTransactionsClient {
2763
4788
  sourceDeposit: sourceDepositPda,
2764
4789
  destinationDeposit: destinationDepositPda,
2765
4790
  tokenMint,
2766
- systemProgram: SystemProgram.programId
4791
+ systemProgram: SystemProgram5.programId
2767
4792
  };
2768
4793
  accounts.sessionToken = sessionToken ?? null;
2769
4794
  console.log("transferDeposit Accounts:");
@@ -2771,7 +4796,7 @@ class LoyalPrivateTransactionsClient {
2771
4796
  console.log(key, value && value.toString());
2772
4797
  });
2773
4798
  console.log("-----");
2774
- const signature = await this.ephemeralProgram.methods.transferDeposit(new BN(amount.toString())).accountsPartial(accounts).rpc(rpcOptions);
4799
+ const signature = await this.ephemeralProgram.methods.transferDeposit(new BN2(amount.toString())).accountsPartial(accounts).rpc(rpcOptions);
2775
4800
  return signature;
2776
4801
  }
2777
4802
  async transferToUsernameDeposit(params) {
@@ -2784,7 +4809,7 @@ class LoyalPrivateTransactionsClient {
2784
4809
  sessionToken,
2785
4810
  rpcOptions
2786
4811
  } = params;
2787
- this.validateUsername(username);
4812
+ validateUsername(username);
2788
4813
  const [sourceDepositPda] = findDepositPda(user, tokenMint);
2789
4814
  const [destinationDepositPda] = await findUsernameDepositPda(username, tokenMint);
2790
4815
  await this.ensureDelegated(sourceDepositPda, "transferToUsernameDeposit-sourceDepositPda");
@@ -2795,10 +4820,10 @@ class LoyalPrivateTransactionsClient {
2795
4820
  sourceDeposit: sourceDepositPda,
2796
4821
  destinationDeposit: destinationDepositPda,
2797
4822
  tokenMint,
2798
- systemProgram: SystemProgram.programId
4823
+ systemProgram: SystemProgram5.programId
2799
4824
  };
2800
4825
  accounts.sessionToken = sessionToken ?? null;
2801
- const signature = await this.ephemeralProgram.methods.transferToUsernameDeposit(new BN(amount.toString())).accountsPartial(accounts).rpc(rpcOptions);
4826
+ const signature = await this.ephemeralProgram.methods.transferToUsernameDeposit(new BN2(amount.toString())).accountsPartial(accounts).rpc(rpcOptions);
2802
4827
  return signature;
2803
4828
  }
2804
4829
  async getBaseDeposit(user, tokenMint) {
@@ -2830,43 +4855,11 @@ class LoyalPrivateTransactionsClient {
2830
4855
  }
2831
4856
  }
2832
4857
  async getAllDepositsByUser(user) {
2833
- const userFilter = [
2834
- {
2835
- memcmp: {
2836
- offset: 8,
2837
- bytes: user.toBase58()
2838
- }
2839
- }
2840
- ];
2841
- const [baseResults, ephemeralResults] = await Promise.allSettled([
2842
- this.baseProgram.account.deposit.all(userFilter),
2843
- this.ephemeralProgram.account.deposit.all(userFilter)
2844
- ]);
2845
- const byPda = new Map;
2846
- const ingest = (results, preferOverwrite) => {
2847
- for (const { publicKey, account } of results) {
2848
- const key = publicKey.toBase58();
2849
- if (!preferOverwrite && byPda.has(key))
2850
- continue;
2851
- byPda.set(key, {
2852
- user: account.user,
2853
- tokenMint: account.tokenMint,
2854
- amount: BigInt(account.amount.toString()),
2855
- address: publicKey
2856
- });
2857
- }
2858
- };
2859
- if (baseResults.status === "fulfilled") {
2860
- ingest(baseResults.value, false);
2861
- } else {
2862
- console.warn("[getAllDepositsByUser] base program enumeration failed", baseResults.reason);
2863
- }
2864
- if (ephemeralResults.status === "fulfilled") {
2865
- ingest(ephemeralResults.value, true);
2866
- } else {
2867
- console.warn("[getAllDepositsByUser] ephemeral program enumeration failed", ephemeralResults.reason);
2868
- }
2869
- return Array.from(byPda.values());
4858
+ return enumerateDepositsByUser({
4859
+ user,
4860
+ baseConnection: this.baseProgram.provider.connection,
4861
+ ephemeralConnection: this.ephemeralProgram.provider.connection
4862
+ });
2870
4863
  }
2871
4864
  async getBaseUsernameDeposit(username, tokenMint) {
2872
4865
  const [depositPda] = await findUsernameDepositPda(username, tokenMint);
@@ -2972,14 +4965,6 @@ class LoyalPrivateTransactionsClient {
2972
4965
  getProgramId() {
2973
4966
  return PROGRAM_ID;
2974
4967
  }
2975
- validateUsername(username) {
2976
- if (!username || username.length < 5 || username.length > 32) {
2977
- throw new Error("Username must be between 5 and 32 characters");
2978
- }
2979
- if (!/^[a-z0-9_]+$/.test(username)) {
2980
- throw new Error("Username can only contain lowercase alphanumeric characters and underscores");
2981
- }
2982
- }
2983
4968
  async permissionAccountExists(permission) {
2984
4969
  const info = await this.baseProgram.provider.connection.getAccountInfo(permission);
2985
4970
  return !!info && info.owner.equals(PERMISSION_PROGRAM_ID);
@@ -3011,8 +4996,8 @@ class LoyalPrivateTransactionsClient {
3011
4996
  console.error(`Account is delegated to ER: ${displayName}${account.toString()}`);
3012
4997
  const delegationStatus = await this.getDelegationStatus(account);
3013
4998
  console.error("/getDelegationStatus", JSON.stringify(delegationStatus, null, 2));
3014
- console.error("baseAccountInfo", prettyStringify(baseAccountInfo));
3015
- console.error("ephemeralAccountInfo", prettyStringify(ephemeralAccountInfo));
4999
+ console.error("baseAccountInfo", prettyStringify2(baseAccountInfo));
5000
+ console.error("ephemeralAccountInfo", prettyStringify2(ephemeralAccountInfo));
3016
5001
  const expectedValidator = this.getExpectedErValidator();
3017
5002
  const authority = delegationStatus.result?.delegationRecord?.authority;
3018
5003
  if (authority && authority !== expectedValidator.toString()) {
@@ -3034,14 +5019,14 @@ class LoyalPrivateTransactionsClient {
3034
5019
  if (!isDelegated) {
3035
5020
  console.error(`Account is not delegated to ER: ${displayName}${account.toString()}`);
3036
5021
  console.error("/getDelegationStatus:", JSON.stringify(delegationStatus, null, 2));
3037
- console.error("baseAccountInfo", prettyStringify(baseAccountInfo));
3038
- console.error("ephemeralAccountInfo", prettyStringify(ephemeralAccountInfo));
5022
+ console.error("baseAccountInfo", prettyStringify2(baseAccountInfo));
5023
+ console.error("ephemeralAccountInfo", prettyStringify2(ephemeralAccountInfo));
3039
5024
  throw new Error(`Account is not delegated to ER: ${displayName}${account.toString()}`);
3040
5025
  } else if (!skipValidatorCheck && delegationStatus.result.delegationRecord.authority !== this.getExpectedErValidator().toString()) {
3041
5026
  console.error(`Account is delegated on wrong validator: ${displayName}${account.toString()} - validator: ${delegationStatus.result.delegationRecord.authority}`);
3042
5027
  console.error("/getDelegationStatus:", JSON.stringify(delegationStatus, null, 2));
3043
- console.error("baseAccountInfo", prettyStringify(baseAccountInfo));
3044
- console.error("ephemeralAccountInfo", prettyStringify(ephemeralAccountInfo));
5028
+ console.error("baseAccountInfo", prettyStringify2(baseAccountInfo));
5029
+ console.error("ephemeralAccountInfo", prettyStringify2(ephemeralAccountInfo));
3045
5030
  throw new Error(`Account is delegated on wrong validator: ${displayName}${account.toString()} - validator: ${delegationStatus.result.delegationRecord.authority}`);
3046
5031
  }
3047
5032
  }
@@ -3097,12 +5082,17 @@ class LoyalPrivateTransactionsClient {
3097
5082
  // index.ts
3098
5083
  var IDL = telegram_private_transfer_default;
3099
5084
  export {
3100
- waitForAccountOwnerChange,
5085
+ waitForAccountOwnerChange2 as waitForAccountOwnerChange,
5086
+ unshieldTokens,
3101
5087
  solToLamports,
5088
+ shieldTokens,
5089
+ parseKaminoReserveSnapshotFromAccountData,
3102
5090
  lamportsToSol,
3103
5091
  isWalletLike,
3104
5092
  isKeypair,
5093
+ isKaminoMainnetModifyBalanceAccounts,
3105
5094
  isAnchorProvider,
5095
+ getKaminoModifyBalanceAccountsForTokenMint,
3106
5096
  getErValidatorForSolanaEnv,
3107
5097
  getErValidatorForRpcEndpoint,
3108
5098
  findVaultPda,
@@ -3112,10 +5102,18 @@ export {
3112
5102
  findDelegationRecordPda,
3113
5103
  findDelegationMetadataPda,
3114
5104
  findBufferPda,
5105
+ fetchKaminoReserveSnapshot,
5106
+ enumerateDepositsByUser,
5107
+ calculateKaminoShareAmountForLiquidityAmountRaw,
5108
+ calculateKaminoRedeemableLiquidityAmountRaw,
5109
+ calculateKaminoCollateralValuation,
5110
+ calculateKaminoCollateralExchangeRateSfFromAmounts,
3115
5111
  VAULT_SEED_BYTES,
3116
5112
  VAULT_SEED,
3117
5113
  USERNAME_DEPOSIT_SEED_BYTES,
3118
5114
  USERNAME_DEPOSIT_SEED,
5115
+ USDC_MINT_MAINNET,
5116
+ USDC_MINT_DEVNET,
3119
5117
  PROGRAM_ID,
3120
5118
  PERMISSION_SEED_BYTES,
3121
5119
  PERMISSION_SEED,
@@ -3124,6 +5122,7 @@ export {
3124
5122
  MAGIC_CONTEXT_ID,
3125
5123
  LoyalPrivateTransactionsClient,
3126
5124
  LAMPORTS_PER_SOL,
5125
+ KLEND_PROGRAM_ID,
3127
5126
  IDL,
3128
5127
  ER_VALIDATOR_MAINNET,
3129
5128
  ER_VALIDATOR_DEVNET,