@neutral-trade/sdk 0.2.12 → 0.3.1

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/README.md CHANGED
@@ -147,7 +147,16 @@ This is simpler and doesn't require an additional network request at initializat
147
147
 
148
148
  ## Examples
149
149
 
150
- Check out the [examples](./examples) directory for more usage examples.
150
+ See the [examples](./examples) directory. From the `sdk/` package root (clone this repo):
151
+
152
+ ```bash
153
+ # Local validator default RPC: http://127.0.0.1:8899 — override with SOLANA_RPC_URL
154
+ export SOLANA_KEYPAIR_PATH="$HOME/.config/solana/id.json"
155
+ pnpm example:devnet:deposit
156
+ pnpm example:devnet:withdraw
157
+ ```
158
+
159
+ Uses the built-in devnet registry (`src/registry/vaults.devnet.json`, e.g. vault `100000001`). Your RPC must serve that vault on-chain; override vault id with `DEVNET_BUNDLE_VAULT_ID` if you use `NeutralTrade.create({ registry: [...] })` patterns in a forked script.
151
160
 
152
161
  ## License
153
162
 
package/dist/index.d.mts CHANGED
@@ -3377,18 +3377,17 @@ declare class NeutralTrade {
3377
3377
  userAddress: string;
3378
3378
  }): Promise<UserBalanceResult>;
3379
3379
  /**
3380
- * Build deposit instructions (optional init + requestDeposit). Fetches vault state on-chain.
3380
+ * Build deposit instructions (`initializeBundleDepositor` when needed, then `requestDeposit`).
3381
+ * Fetches bundle and user bundle accounts on-chain.
3381
3382
  */
3382
3383
  buildDepositInstructions({
3383
3384
  vaultId,
3384
3385
  userAddress,
3385
- amount,
3386
- needsInit
3386
+ amount
3387
3387
  }: {
3388
3388
  vaultId: number;
3389
3389
  userAddress: string;
3390
3390
  amount: number;
3391
- needsInit?: boolean;
3392
3391
  }): Promise<TransactionInstruction[]>;
3393
3392
  /**
3394
3393
  * Build request-withdraw instruction. Fetches vault, oracle, and depositor accounts on-chain.
@@ -3412,7 +3411,6 @@ interface BuildBundleDepositInstructionsParams {
3412
3411
  user: PublicKey;
3413
3412
  /** UI token amount (multiplied by 10**on-chain decimals inside). */
3414
3413
  amount: number;
3415
- needsInit?: boolean;
3416
3414
  }
3417
3415
  interface BuildBundleRequestWithdrawInstructionParams {
3418
3416
  bundleProgram: Program<Ntbundle>;
@@ -3422,15 +3420,14 @@ interface BuildBundleRequestWithdrawInstructionParams {
3422
3420
  amount: number;
3423
3421
  }
3424
3422
  /**
3425
- * Optional `initializeBundleDepositor` + `requestDeposit`. Fetches bundle account internally.
3423
+ * `initializeBundleDepositor` (when missing) + `requestDeposit`. Fetches bundle + user bundle internally.
3426
3424
  */
3427
3425
  declare function buildBundleDepositInstructions({
3428
3426
  bundleProgram,
3429
3427
  bundleCluster,
3430
3428
  vault,
3431
3429
  user,
3432
- amount,
3433
- needsInit
3430
+ amount
3434
3431
  }: BuildBundleDepositInstructionsParams): Promise<TransactionInstruction[]>;
3435
3432
  /**
3436
3433
  * `requestWithdrawal` only. Fetches bundle, oracle, and user bundle internally.
package/dist/index.mjs CHANGED
@@ -12847,25 +12847,26 @@ function getDriftProgramId(vault) {
12847
12847
  /**
12848
12848
  * Transform a registry entry to VaultConfig with program IDs
12849
12849
  * - Drift vaults get driftProgramId
12850
- * - Bundle vaults get bundleProgramId
12850
+ * - Bundle vaults get bundleProgramId (registry value or cluster default via {@link getBundleProgramId})
12851
12851
  */
12852
- function toVaultConfig(entry) {
12852
+ function toVaultConfig(entry, cluster = "mainnet") {
12853
12853
  return {
12854
12854
  ...entry,
12855
12855
  driftProgramId: getDriftProgramId(entry),
12856
- bundleProgramId: entry.bundleProgramId
12856
+ bundleProgramId: getBundleProgramId(entry, cluster)
12857
12857
  };
12858
12858
  }
12859
12859
  /**
12860
12860
  * Transform an array of registry entries to VaultRegistry
12861
+ * @param cluster - Used to resolve default bundle program id when `bundleProgramId` is omitted in JSON
12861
12862
  */
12862
- function toVaultRegistry(entries) {
12863
- return Object.fromEntries(entries.map((entry) => [entry.vaultId, toVaultConfig(entry)]));
12863
+ function toVaultRegistry(entries, cluster = "mainnet") {
12864
+ return Object.fromEntries(entries.map((entry) => [entry.vaultId, toVaultConfig(entry, cluster)]));
12864
12865
  }
12865
12866
  const parsedVaultsMainnet = VaultRegistryArraySchema.parse(vaults_default);
12866
12867
  const parsedVaultsDevnet = VaultRegistryArraySchema.parse(vaults_devnet_default);
12867
- const vaultsMainnetRegistry = toVaultRegistry(parsedVaultsMainnet);
12868
- const vaultsDevnetRegistry = toVaultRegistry(parsedVaultsDevnet);
12868
+ const vaultsMainnetRegistry = toVaultRegistry(parsedVaultsMainnet, "mainnet");
12869
+ const vaultsDevnetRegistry = toVaultRegistry(parsedVaultsDevnet, "devnet");
12869
12870
  /** Mainnet registry (default; backward compatible). */
12870
12871
  const vaults = vaultsMainnetRegistry;
12871
12872
  /** Devnet-only vault registry (fixtures / local testing). */
@@ -13102,18 +13103,21 @@ function bundleProgramIdForVault(vault, bundleCluster = "mainnet") {
13102
13103
  return id;
13103
13104
  }
13104
13105
  /**
13105
- * Optional `initializeBundleDepositor` + `requestDeposit`. Fetches bundle account internally.
13106
+ * `initializeBundleDepositor` (when missing) + `requestDeposit`. Fetches bundle + user bundle internally.
13106
13107
  */
13107
- async function buildBundleDepositInstructions({ bundleProgram, bundleCluster = "mainnet", vault, user, amount, needsInit = false }) {
13108
+ async function buildBundleDepositInstructions({ bundleProgram, bundleCluster = "mainnet", vault, user, amount }) {
13108
13109
  assertBundleVault(vault);
13109
13110
  const bundlePDA = new PublicKey(vault.vaultAddress);
13110
13111
  const programId = bundleProgramIdForVault(vault, bundleCluster);
13111
13112
  if (programId !== bundleProgram.programId.toBase58()) throw new Error(`Vault ${vault.vaultId} program id mismatch: vault=${programId}, client=${bundleProgram.programId.toBase58()}`);
13112
- const bundleInfo = await bundleProgram.account.bundle.fetch(bundlePDA);
13113
- const depositAmountBN = new BN(Math.floor(amount * 10 ** bundleInfo.assetDecimals));
13114
13113
  const programPk = bundleProgram.programId;
13115
- const oraclePDA = deriveOraclePDA(bundlePDA, programPk);
13116
13114
  const userPDA = deriveUserPDA(user, bundlePDA, programPk);
13115
+ const [bundleAcc, userBundleAcc] = await bundleProgram.provider.connection.getMultipleAccountsInfo([bundlePDA, userPDA]);
13116
+ if (!bundleAcc?.data?.length) throw new Error(`Bundle account not found for vault ${vault.vaultId}`);
13117
+ const bundleInfo = bundleProgram.coder.accounts.decode("bundle", bundleAcc.data);
13118
+ 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));
13120
+ const oraclePDA = deriveOraclePDA(bundlePDA, programPk);
13117
13121
  const tempDataPDA = deriveTempDataPDA(bundlePDA, programPk);
13118
13122
  const pendingAuthPDA = derivePendingAuthPDA(bundlePDA, programPk);
13119
13123
  const userTokenAcct = getAssociatedTokenAddressSync(bundleInfo.assetAddress, user, true);
@@ -13282,14 +13286,14 @@ var NeutralTrade = class NeutralTrade {
13282
13286
  const cluster = config.bundleCluster ?? "mainnet";
13283
13287
  let vaults$1 = config.includeBuiltInVaults !== false ? { ...getVaultRegistry(cluster) } : {};
13284
13288
  if (config.registry) {
13285
- const localVaults = NeutralTrade.parseVaultRegistryEntries(config.registry, "registry");
13289
+ const localVaults = NeutralTrade.parseVaultRegistryEntries(config.registry, "registry", cluster);
13286
13290
  vaults$1 = {
13287
13291
  ...vaults$1,
13288
13292
  ...localVaults
13289
13293
  };
13290
13294
  }
13291
13295
  if (config.registryUrl) {
13292
- const remoteVaults = await NeutralTrade.fetchVaultsFromRegistry(config.registryUrl);
13296
+ const remoteVaults = await NeutralTrade.fetchVaultsFromRegistry(config.registryUrl, cluster);
13293
13297
  vaults$1 = {
13294
13298
  ...vaults$1,
13295
13299
  ...remoteVaults
@@ -13321,21 +13325,21 @@ var NeutralTrade = class NeutralTrade {
13321
13325
  if (!bundleProgram) throw new Error(`Bundle program client not initialized for vault ${vault.vaultId}: ${programId}. Please add it to bundlePrograms config or registry.`);
13322
13326
  return bundleProgram;
13323
13327
  }
13324
- static parseVaultRegistryEntries(entries, sourceLabel) {
13328
+ static parseVaultRegistryEntries(entries, sourceLabel, cluster) {
13325
13329
  const parseResult = VaultRegistryArraySchema.safeParse(entries);
13326
13330
  if (!parseResult.success) throw new Error(`Invalid ${sourceLabel} data: ${parseResult.error.message}`);
13327
- return toVaultRegistry(parseResult.data);
13331
+ return toVaultRegistry(parseResult.data, cluster);
13328
13332
  }
13329
13333
  /**
13330
13334
  * Fetch vault configurations from a remote registry URL
13331
13335
  * Expects JSON array format and transforms to Record<number, VaultConfig>
13332
13336
  * @throws Error if fetch fails or validation fails
13333
13337
  */
13334
- static async fetchVaultsFromRegistry(registryUrl) {
13338
+ static async fetchVaultsFromRegistry(registryUrl, cluster) {
13335
13339
  const response = await fetch(registryUrl);
13336
13340
  if (!response.ok) throw new Error(`Failed to fetch vaults from registry: ${response.status} ${response.statusText}`);
13337
13341
  const data = await response.json();
13338
- return NeutralTrade.parseVaultRegistryEntries(data, "vault registry");
13342
+ return NeutralTrade.parseVaultRegistryEntries(data, "vault registry", cluster);
13339
13343
  }
13340
13344
  /**
13341
13345
  * Get user balance for Bundle vaults only.
@@ -13357,9 +13361,10 @@ var NeutralTrade = class NeutralTrade {
13357
13361
  });
13358
13362
  }
13359
13363
  /**
13360
- * Build deposit instructions (optional init + requestDeposit). Fetches vault state on-chain.
13364
+ * Build deposit instructions (`initializeBundleDepositor` when needed, then `requestDeposit`).
13365
+ * Fetches bundle and user bundle accounts on-chain.
13361
13366
  */
13362
- async buildDepositInstructions({ vaultId, userAddress, amount, needsInit = false }) {
13367
+ async buildDepositInstructions({ vaultId, userAddress, amount }) {
13363
13368
  const vault = this.vaults[vaultId];
13364
13369
  if (!vault) throw new Error(`Vault config not found for vaultId ${vaultId}`);
13365
13370
  return buildBundleDepositInstructions({
@@ -13367,8 +13372,7 @@ var NeutralTrade = class NeutralTrade {
13367
13372
  bundleCluster: this.bundleCluster,
13368
13373
  vault,
13369
13374
  user: new PublicKey(userAddress),
13370
- amount,
13371
- needsInit
13375
+ amount
13372
13376
  });
13373
13377
  }
13374
13378
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@neutral-trade/sdk",
3
3
  "type": "module",
4
- "version": "0.2.12",
4
+ "version": "0.3.1",
5
5
  "description": "SDK for Neutral Trade vaults",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/neutral-trade/sdk#readme",
@@ -69,6 +69,10 @@
69
69
  "lint": "eslint",
70
70
  "release": "bumpp",
71
71
  "start": "tsx src/index.ts",
72
+ "example:devnet:deposit": "tsx examples/devnet-deposit.ts",
73
+ "example:devnet:withdraw": "tsx examples/devnet-withdraw.ts",
74
+ "diagnose:balances": "tsx diagnose/vault-balance-fetch.ts",
75
+ "diagnose:deposit-conservation": "tsx diagnose/deposit-conservation.ts",
72
76
  "test": "vitest",
73
77
  "typecheck": "tsc",
74
78
  "docs:build": "typedoc",