@neutral-trade/sdk 0.3.1 → 0.3.3

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.d.mts CHANGED
@@ -3383,11 +3383,12 @@ declare class NeutralTrade {
3383
3383
  buildDepositInstructions({
3384
3384
  vaultId,
3385
3385
  userAddress,
3386
- amount
3386
+ amountRaw
3387
3387
  }: {
3388
3388
  vaultId: number;
3389
3389
  userAddress: string;
3390
- amount: number;
3390
+ /** Smallest token units (decimal string), same scale as SPL `amount`. */
3391
+ amountRaw: string;
3391
3392
  }): Promise<TransactionInstruction[]>;
3392
3393
  /**
3393
3394
  * Build request-withdraw instruction. Fetches vault, oracle, and depositor accounts on-chain.
@@ -3395,30 +3396,60 @@ declare class NeutralTrade {
3395
3396
  buildRequestWithdrawInstruction({
3396
3397
  vaultId,
3397
3398
  userAddress,
3398
- amount
3399
+ amountRaw
3399
3400
  }: {
3400
3401
  vaultId: number;
3401
3402
  userAddress: string;
3402
- amount: number;
3403
+ /** Smallest token units (decimal string) to request withdrawing. */
3404
+ amountRaw: string;
3403
3405
  }): Promise<TransactionInstruction>;
3404
3406
  }
3405
3407
  //#endregion
3408
+ //#region src/utils/amount-raw.d.ts
3409
+ /**
3410
+ * Parse `amountRaw`: base-10 digits only, smallest token units (same scale as SPL `amount`).
3411
+ * @throws Error with message `INVALID_AMOUNT_RAW` on invalid input
3412
+ */
3413
+ declare function parseAmountRawToBigInt(amountRaw: string): bigint;
3414
+ /**
3415
+ * Legacy UI path: finite `human` × 10^`decimals`, rounded to nearest integer token unit.
3416
+ * Prefer integrators sending `amountRaw` from a decimal string instead of this helper.
3417
+ * @throws Error with message `INVALID_HUMAN_AMOUNT` when out of range or non-finite
3418
+ */
3419
+ declare function humanFloatToAmountRawString(human: number, decimals: number): string;
3420
+ //#endregion
3406
3421
  //#region src/utils/bundle-instructions.d.ts
3407
3422
  interface BuildBundleDepositInstructionsParams {
3408
3423
  bundleProgram: Program<Ntbundle>;
3409
3424
  bundleCluster?: BundleCluster;
3410
3425
  vault: VaultRegistryEntry;
3411
3426
  user: PublicKey;
3412
- /** UI token amount (multiplied by 10**on-chain decimals inside). */
3413
- amount: number;
3427
+ /** Smallest token units (decimal string), same scale as SPL token `amount`. */
3428
+ amountRaw: string;
3414
3429
  }
3415
3430
  interface BuildBundleRequestWithdrawInstructionParams {
3416
3431
  bundleProgram: Program<Ntbundle>;
3417
3432
  bundleCluster?: BundleCluster;
3418
3433
  vault: VaultRegistryEntry;
3419
3434
  user: PublicKey;
3420
- amount: number;
3435
+ /** Smallest token units (decimal string) to request withdrawing. */
3436
+ amountRaw: string;
3421
3437
  }
3438
+ /**
3439
+ * Shares to burn for `requestWithdrawal`, from integer token raw and on-chain totals.
3440
+ * Full position: pass `amountRaw >= userTokenRaw` where `userTokenRaw = (userShares * totalEquity) / totalShares`.
3441
+ */
3442
+ declare function computeRequestWithdrawalSharesFromAmountRaw({
3443
+ amountRaw,
3444
+ userShares,
3445
+ totalEquity,
3446
+ totalShares
3447
+ }: {
3448
+ amountRaw: bigint;
3449
+ userShares: BN;
3450
+ totalEquity: bigint;
3451
+ totalShares: bigint;
3452
+ }): BN;
3422
3453
  /**
3423
3454
  * `initializeBundleDepositor` (when missing) + `requestDeposit`. Fetches bundle + user bundle internally.
3424
3455
  */
@@ -3427,7 +3458,7 @@ declare function buildBundleDepositInstructions({
3427
3458
  bundleCluster,
3428
3459
  vault,
3429
3460
  user,
3430
- amount
3461
+ amountRaw
3431
3462
  }: BuildBundleDepositInstructionsParams): Promise<TransactionInstruction[]>;
3432
3463
  /**
3433
3464
  * `requestWithdrawal` only. Fetches bundle, oracle, and user bundle internally.
@@ -3437,7 +3468,7 @@ declare function buildBundleRequestWithdrawInstruction({
3437
3468
  bundleCluster,
3438
3469
  vault,
3439
3470
  user,
3440
- amount
3471
+ amountRaw
3441
3472
  }: BuildBundleRequestWithdrawInstructionParams): Promise<TransactionInstruction>;
3442
3473
  //#endregion
3443
3474
  //#region src/utils/pda.d.ts
@@ -3467,4 +3498,4 @@ declare function derivePendingAuthPDA(bundlePDA: PublicKey, programId: PublicKey
3467
3498
  */
3468
3499
  declare function getVaultDepositorAddressSync(programId: PublicKey, vault: PublicKey, authority: PublicKey): PublicKey;
3469
3500
  //#endregion
3470
- export { BUNDLE_PROGRAM_ID_V2_MAINNET, type BundleAccount, type BundleCluster, type BundleProgram, type BundleProvider, DEFAULT_BUNDLE_PROGRAM_ID_DEVNET, DEFAULT_BUNDLE_PROGRAM_ID_MAINNET, DevnetVaultId, NeutralTrade, type NeutralTradeConfig, type NeutralTradeCoreContext, type OracleData, SEED_ORACLE, SEED_PENDING_AUTH, SEED_TEMP, SEED_USER, SupportedChain, SupportedToken, type Token, type UserBalanceResult, type UserBundleAccount, type UserBundleTempData, type VaultBalanceData, VaultCategory, type VaultRegistryEntry as VaultConfig, type VaultRegistry as VaultConfigRecord, VaultId, VaultType, buildBundleDepositInstructions, buildBundleRequestWithdrawInstruction, createBundleProgramById, createDummyWallet, deriveOraclePDA, derivePendingAuthPDA, deriveTempDataPDA, deriveUserPDA, getBundleProgramId, getDefaultBundleProgramIdByCluster, getSolanaTokenDecimals, getSolanaTokenMint, getVaultByAddress, getVaultById, getVaultDepositorAddressSync, getVaultRegistry, isValidVaultAddress, tokens, vaults, vaultsDevnet };
3501
+ export { BUNDLE_PROGRAM_ID_V2_MAINNET, type BuildBundleDepositInstructionsParams, type BuildBundleRequestWithdrawInstructionParams, type BundleAccount, type BundleCluster, type BundleProgram, type BundleProvider, DEFAULT_BUNDLE_PROGRAM_ID_DEVNET, DEFAULT_BUNDLE_PROGRAM_ID_MAINNET, DevnetVaultId, NeutralTrade, type NeutralTradeConfig, type NeutralTradeCoreContext, type OracleData, SEED_ORACLE, SEED_PENDING_AUTH, SEED_TEMP, SEED_USER, SupportedChain, SupportedToken, type Token, type UserBalanceResult, type UserBundleAccount, type UserBundleTempData, type VaultBalanceData, VaultCategory, type VaultRegistryEntry as VaultConfig, type VaultRegistry as VaultConfigRecord, VaultId, VaultType, buildBundleDepositInstructions, buildBundleRequestWithdrawInstruction, computeRequestWithdrawalSharesFromAmountRaw, createBundleProgramById, createDummyWallet, deriveOraclePDA, derivePendingAuthPDA, deriveTempDataPDA, deriveUserPDA, getBundleProgramId, getDefaultBundleProgramIdByCluster, getSolanaTokenDecimals, getSolanaTokenMint, getVaultByAddress, getVaultById, getVaultDepositorAddressSync, getVaultRegistry, humanFloatToAmountRawString, isValidVaultAddress, parseAmountRawToBigInt, tokens, vaults, vaultsDevnet };
package/dist/index.mjs CHANGED
@@ -13085,14 +13085,50 @@ async function getBundleBalances({ vaultIds, userAddress, vaults: vaults$1, bund
13085
13085
  return result;
13086
13086
  }
13087
13087
 
13088
+ //#endregion
13089
+ //#region src/utils/amount-raw.ts
13090
+ /** Max SPL token amount we accept (fits u64). */
13091
+ const U64_MAX = 18446744073709551615n;
13092
+ /**
13093
+ * Parse `amountRaw`: base-10 digits only, smallest token units (same scale as SPL `amount`).
13094
+ * @throws Error with message `INVALID_AMOUNT_RAW` on invalid input
13095
+ */
13096
+ function parseAmountRawToBigInt(amountRaw) {
13097
+ const t = amountRaw.trim();
13098
+ if (!/^\d+$/.test(t)) throw new Error("INVALID_AMOUNT_RAW");
13099
+ const v = BigInt(t);
13100
+ if (v <= 0n) throw new Error("INVALID_AMOUNT_RAW");
13101
+ if (v > U64_MAX) throw new Error("INVALID_AMOUNT_RAW");
13102
+ return v;
13103
+ }
13104
+ /**
13105
+ * Legacy UI path: finite `human` × 10^`decimals`, rounded to nearest integer token unit.
13106
+ * Prefer integrators sending `amountRaw` from a decimal string instead of this helper.
13107
+ * @throws Error with message `INVALID_HUMAN_AMOUNT` when out of range or non-finite
13108
+ */
13109
+ function humanFloatToAmountRawString(human, decimals) {
13110
+ if (!Number.isFinite(human) || human <= 0) throw new Error("INVALID_HUMAN_AMOUNT");
13111
+ if (!Number.isInteger(decimals) || decimals < 0 || decimals > 18) throw new Error("INVALID_HUMAN_AMOUNT");
13112
+ const scaled = human * 10 ** decimals;
13113
+ if (!Number.isFinite(scaled)) throw new Error("INVALID_HUMAN_AMOUNT");
13114
+ const rounded = Math.round(scaled);
13115
+ if (rounded <= 0) throw new Error("INVALID_HUMAN_AMOUNT");
13116
+ return parseAmountRawToBigInt(BigInt(rounded).toString()).toString();
13117
+ }
13118
+
13088
13119
  //#endregion
13089
13120
  //#region src/utils/bundle-instructions.ts
13090
- /** Same share math as legacy app `useBundleRequestWithdrawMutation`. */
13091
- function computeRequestWithdrawalSharesAmount({ amount, userVaultBalance, pps, assetDecimals, userShares }) {
13092
- let sharesAmount;
13093
- if (Number(amount) >= Number(userVaultBalance)) sharesAmount = userShares;
13094
- else sharesAmount = new BN(Math.floor(amount * 10 ** assetDecimals / pps));
13095
- return sharesAmount.lte(userShares) ? sharesAmount : userShares;
13121
+ /**
13122
+ * Shares to burn for `requestWithdrawal`, from integer token raw and on-chain totals.
13123
+ * Full position: pass `amountRaw >= userTokenRaw` where `userTokenRaw = (userShares * totalEquity) / totalShares`.
13124
+ */
13125
+ function computeRequestWithdrawalSharesFromAmountRaw({ amountRaw, userShares, totalEquity, totalShares }) {
13126
+ const us = BigInt(userShares.toString());
13127
+ if (totalShares === 0n || us === 0n) return new BN(0);
13128
+ if (amountRaw >= us * totalEquity / totalShares) return userShares;
13129
+ if (totalEquity === 0n) return new BN(0);
13130
+ const sharesBn = new BN((amountRaw * totalShares / totalEquity).toString());
13131
+ return sharesBn.gt(userShares) ? userShares : sharesBn;
13096
13132
  }
13097
13133
  function assertBundleVault(vault) {
13098
13134
  if (vault.type !== VaultType.Bundle) throw new Error(`Vault ${vault.vaultId} is not a Bundle vault`);
@@ -13105,7 +13141,7 @@ function bundleProgramIdForVault(vault, bundleCluster = "mainnet") {
13105
13141
  /**
13106
13142
  * `initializeBundleDepositor` (when missing) + `requestDeposit`. Fetches bundle + user bundle internally.
13107
13143
  */
13108
- async function buildBundleDepositInstructions({ bundleProgram, bundleCluster = "mainnet", vault, user, amount }) {
13144
+ async function buildBundleDepositInstructions({ bundleProgram, bundleCluster = "mainnet", vault, user, amountRaw }) {
13109
13145
  assertBundleVault(vault);
13110
13146
  const bundlePDA = new PublicKey(vault.vaultAddress);
13111
13147
  const programId = bundleProgramIdForVault(vault, bundleCluster);
@@ -13116,7 +13152,7 @@ async function buildBundleDepositInstructions({ bundleProgram, bundleCluster = "
13116
13152
  if (!bundleAcc?.data?.length) throw new Error(`Bundle account not found for vault ${vault.vaultId}`);
13117
13153
  const bundleInfo = bundleProgram.coder.accounts.decode("bundle", bundleAcc.data);
13118
13154
  const needsInit = (userBundleAcc?.data?.length ? bundleProgram.coder.accounts.decode("userBundleAccount", userBundleAcc.data) : null) === null;
13119
- const depositAmountBN = new BN(Math.floor(amount * 10 ** bundleInfo.assetDecimals));
13155
+ const depositAmountBn = new BN(parseAmountRawToBigInt(amountRaw).toString());
13120
13156
  const oraclePDA = deriveOraclePDA(bundlePDA, programPk);
13121
13157
  const tempDataPDA = deriveTempDataPDA(bundlePDA, programPk);
13122
13158
  const pendingAuthPDA = derivePendingAuthPDA(bundlePDA, programPk);
@@ -13133,7 +13169,7 @@ async function buildBundleDepositInstructions({ bundleProgram, bundleCluster = "
13133
13169
  }).instruction();
13134
13170
  instructions$2.push(initIx);
13135
13171
  }
13136
- const depositIx = await bundleProgram.methods.requestDeposit(depositAmountBN).accounts({
13172
+ const depositIx = await bundleProgram.methods.requestDeposit(depositAmountBn).accounts({
13137
13173
  user,
13138
13174
  userTokenAccount: userTokenAcct,
13139
13175
  pendingDepositTokenAccount: pendingTokenAcct,
@@ -13153,7 +13189,7 @@ async function buildBundleDepositInstructions({ bundleProgram, bundleCluster = "
13153
13189
  /**
13154
13190
  * `requestWithdrawal` only. Fetches bundle, oracle, and user bundle internally.
13155
13191
  */
13156
- async function buildBundleRequestWithdrawInstruction({ bundleProgram, bundleCluster = "mainnet", vault, user, amount }) {
13192
+ async function buildBundleRequestWithdrawInstruction({ bundleProgram, bundleCluster = "mainnet", vault, user, amountRaw }) {
13157
13193
  assertBundleVault(vault);
13158
13194
  const bundlePDA = new PublicKey(vault.vaultAddress);
13159
13195
  const programId = bundleProgramIdForVault(vault, bundleCluster);
@@ -13167,19 +13203,13 @@ async function buildBundleRequestWithdrawInstruction({ bundleProgram, bundleClus
13167
13203
  bundleProgram.account.oracleData.fetch(oraclePDA),
13168
13204
  bundleProgram.account.userBundleAccount.fetch(userPDA)
13169
13205
  ]);
13170
- const pps = calculateOnChainPps({
13171
- oracleAverageExternalEquity: BigInt(oracleData.averageExternalEquity.toString()),
13172
- bundleUnderlyingBalance: BigInt(bundleAccount.bundleUnderlyingBalance.toString()),
13173
- totalShares: BigInt(bundleAccount.totalShares.toString())
13174
- });
13175
- const divisor = 10 ** bundleAccount.assetDecimals;
13176
- const userSharesNum = Number(userBundle.shares.toString());
13177
- const sharesAmount = computeRequestWithdrawalSharesAmount({
13178
- amount,
13179
- userVaultBalance: Math.round(userSharesNum * pps) / divisor,
13180
- pps,
13181
- assetDecimals: bundleAccount.assetDecimals,
13182
- userShares: new BN(userBundle.shares.toString())
13206
+ const totalEquity = BigInt(oracleData.averageExternalEquity.toString()) + BigInt(bundleAccount.bundleUnderlyingBalance.toString());
13207
+ const totalShares = BigInt(bundleAccount.totalShares.toString());
13208
+ const sharesAmount = computeRequestWithdrawalSharesFromAmountRaw({
13209
+ amountRaw: parseAmountRawToBigInt(amountRaw),
13210
+ userShares: new BN(userBundle.shares.toString()),
13211
+ totalEquity,
13212
+ totalShares
13183
13213
  });
13184
13214
  return await bundleProgram.methods.requestWithdrawal(sharesAmount).accounts({
13185
13215
  user,
@@ -13364,7 +13394,7 @@ var NeutralTrade = class NeutralTrade {
13364
13394
  * Build deposit instructions (`initializeBundleDepositor` when needed, then `requestDeposit`).
13365
13395
  * Fetches bundle and user bundle accounts on-chain.
13366
13396
  */
13367
- async buildDepositInstructions({ vaultId, userAddress, amount }) {
13397
+ async buildDepositInstructions({ vaultId, userAddress, amountRaw }) {
13368
13398
  const vault = this.vaults[vaultId];
13369
13399
  if (!vault) throw new Error(`Vault config not found for vaultId ${vaultId}`);
13370
13400
  return buildBundleDepositInstructions({
@@ -13372,13 +13402,13 @@ var NeutralTrade = class NeutralTrade {
13372
13402
  bundleCluster: this.bundleCluster,
13373
13403
  vault,
13374
13404
  user: new PublicKey(userAddress),
13375
- amount
13405
+ amountRaw
13376
13406
  });
13377
13407
  }
13378
13408
  /**
13379
13409
  * Build request-withdraw instruction. Fetches vault, oracle, and depositor accounts on-chain.
13380
13410
  */
13381
- async buildRequestWithdrawInstruction({ vaultId, userAddress, amount }) {
13411
+ async buildRequestWithdrawInstruction({ vaultId, userAddress, amountRaw }) {
13382
13412
  const vault = this.vaults[vaultId];
13383
13413
  if (!vault) throw new Error(`Vault config not found for vaultId ${vaultId}`);
13384
13414
  return buildBundleRequestWithdrawInstruction({
@@ -13386,10 +13416,10 @@ var NeutralTrade = class NeutralTrade {
13386
13416
  bundleCluster: this.bundleCluster,
13387
13417
  vault,
13388
13418
  user: new PublicKey(userAddress),
13389
- amount
13419
+ amountRaw
13390
13420
  });
13391
13421
  }
13392
13422
  };
13393
13423
 
13394
13424
  //#endregion
13395
- export { BUNDLE_PROGRAM_ID_V2_MAINNET, DEFAULT_BUNDLE_PROGRAM_ID_DEVNET, DEFAULT_BUNDLE_PROGRAM_ID_MAINNET, DevnetVaultId, NeutralTrade, SEED_ORACLE, SEED_PENDING_AUTH, SEED_TEMP, SEED_USER, SupportedChain, SupportedToken, VaultCategory, VaultId, VaultType, buildBundleDepositInstructions, buildBundleRequestWithdrawInstruction, createBundleProgramById, createDummyWallet, deriveOraclePDA, derivePendingAuthPDA, deriveTempDataPDA, deriveUserPDA, getBundleProgramId, getDefaultBundleProgramIdByCluster, getSolanaTokenDecimals, getSolanaTokenMint, getVaultByAddress, getVaultById, getVaultDepositorAddressSync, getVaultRegistry, isValidVaultAddress, tokens, vaults, vaultsDevnet };
13425
+ export { BUNDLE_PROGRAM_ID_V2_MAINNET, DEFAULT_BUNDLE_PROGRAM_ID_DEVNET, DEFAULT_BUNDLE_PROGRAM_ID_MAINNET, DevnetVaultId, NeutralTrade, SEED_ORACLE, SEED_PENDING_AUTH, SEED_TEMP, SEED_USER, SupportedChain, SupportedToken, VaultCategory, VaultId, VaultType, buildBundleDepositInstructions, buildBundleRequestWithdrawInstruction, computeRequestWithdrawalSharesFromAmountRaw, createBundleProgramById, createDummyWallet, deriveOraclePDA, derivePendingAuthPDA, deriveTempDataPDA, deriveUserPDA, getBundleProgramId, getDefaultBundleProgramIdByCluster, getSolanaTokenDecimals, getSolanaTokenMint, getVaultByAddress, getVaultById, getVaultDepositorAddressSync, getVaultRegistry, humanFloatToAmountRawString, isValidVaultAddress, parseAmountRawToBigInt, tokens, vaults, vaultsDevnet };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@neutral-trade/sdk",
3
3
  "type": "module",
4
- "version": "0.3.1",
4
+ "version": "0.3.3",
5
5
  "description": "SDK for Neutral Trade vaults",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/neutral-trade/sdk#readme",