@depthhq/sdk 0.1.2 → 0.1.4

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
@@ -18122,7 +18122,7 @@ https://github.com/browserify/crypto-browserify`);
18122
18122
 
18123
18123
  // src/client/client.ts
18124
18124
  init_buffer();
18125
- import * as anchor8 from "@anchor-lang/core";
18125
+ import * as anchor9 from "@anchor-lang/core";
18126
18126
 
18127
18127
  // ../../node_modules/@noble/hashes/esm/crypto.js
18128
18128
  var crypto2 = typeof globalThis === "object" && "crypto" in globalThis ? globalThis.crypto : undefined;
@@ -43622,13 +43622,13 @@ import {
43622
43622
  circuits as circuits2,
43623
43623
  computationName,
43624
43624
  DEPTH_IDL as DEPTH_IDL3,
43625
- DEPTH_PROGRAM_ID as DEPTH_PROGRAM_ID7,
43626
- marketAddress as marketAddress7,
43625
+ DEPTH_PROGRAM_ID as DEPTH_PROGRAM_ID8,
43626
+ marketAddress as marketAddress8,
43627
43627
  protocolAddress as protocolAddress4,
43628
43628
  recoveryConfigAddress as recoveryConfigAddress2,
43629
43629
  SOL_USD_MARKET_ID as SOL_USD_MARKET_ID3
43630
43630
  } from "@depthhq/bindings";
43631
- import { PublicKey as PublicKey9 } from "@solana/web3.js";
43631
+ import { PublicKey as PublicKey11 } from "@solana/web3.js";
43632
43632
 
43633
43633
  // src/chain/random.ts
43634
43634
  function randomBytes3(length) {
@@ -44335,9 +44335,21 @@ async function getExtendedPublicKey(key) {
44335
44335
  function getExtendedPublicKeySync(key) {
44336
44336
  return getKeyFromHash(sha512s(checkPrivateKey(key)));
44337
44337
  }
44338
+ async function getPublicKey(privateKey) {
44339
+ return (await getExtendedPublicKey(privateKey)).pointBytes;
44340
+ }
44338
44341
  function getPublicKeySync(privateKey) {
44339
44342
  return getExtendedPublicKeySync(privateKey).pointBytes;
44340
44343
  }
44344
+ async function sign(message, privateKey) {
44345
+ message = ensureBytes2(message);
44346
+ const { prefix, scalar, pointBytes } = await getExtendedPublicKey(privateKey);
44347
+ const r = modlLE(await utils.sha512(prefix, message));
44348
+ const R = Point.BASE.multiply(r);
44349
+ const k = modlLE(await utils.sha512(R.toRawBytes(), pointBytes, message));
44350
+ const s = mod2(r + k * scalar, CURVE.l);
44351
+ return new Signature(R, s).toRawBytes();
44352
+ }
44341
44353
  function signSync(message, privateKey) {
44342
44354
  message = ensureBytes2(message);
44343
44355
  const { prefix, scalar, pointBytes } = getExtendedPublicKeySync(privateKey);
@@ -44726,6 +44738,7 @@ import { DEPTH_IDL as DEPTH_IDL2, DEPTH_PROGRAM_ID as DEPTH_PROGRAM_ID3 } from "
44726
44738
  import {
44727
44739
  ComputeBudgetProgram,
44728
44740
  SendTransactionError,
44741
+ PublicKey as PublicKey4,
44729
44742
  Transaction
44730
44743
  } from "@solana/web3.js";
44731
44744
  var queuedInstructions = new Set([
@@ -44768,11 +44781,52 @@ function applyDepthComputeBudget(transaction) {
44768
44781
  }
44769
44782
 
44770
44783
  class DepthProvider extends anchor3.AnchorProvider {
44784
+ cleanup = new Map;
44785
+ deferClose(id, instruction) {
44786
+ this.cleanup.set(id.toBase58(), instruction);
44787
+ }
44788
+ async appendCleanup(transaction) {
44789
+ if (!this.cleanup.size || transaction.signatures.some((item) => item.signature))
44790
+ return [];
44791
+ const entries = [...this.cleanup.entries()].slice(0, 64);
44792
+ const queued = new Set(entries.map(([, instruction]) => instruction));
44793
+ transaction.instructions = transaction.instructions.filter((instruction) => !queued.has(instruction));
44794
+ const accounts2 = await this.connection.getMultipleAccountsInfo(entries.map(([id]) => new PublicKey4(id)), "confirmed");
44795
+ const added = [];
44796
+ for (let index = 0;index < entries.length; index++) {
44797
+ const [id, instruction] = entries[index];
44798
+ if (!accounts2[index]?.owner.equals(DEPTH_PROGRAM_ID3)) {
44799
+ this.cleanup.delete(id);
44800
+ continue;
44801
+ }
44802
+ const candidate = new Transaction({
44803
+ feePayer: transaction.feePayer ?? this.wallet.publicKey,
44804
+ recentBlockhash: transaction.recentBlockhash ?? PublicKey4.default.toBase58()
44805
+ }).add(...transaction.instructions, instruction);
44806
+ try {
44807
+ candidate.serialize({
44808
+ requireAllSignatures: false,
44809
+ verifySignatures: false
44810
+ });
44811
+ } catch {
44812
+ continue;
44813
+ }
44814
+ transaction.add(instruction);
44815
+ added.push(id);
44816
+ }
44817
+ return added;
44818
+ }
44771
44819
  async sendAndConfirm(transaction, signers, options) {
44772
- if (transaction instanceof Transaction)
44820
+ let cleanup = [];
44821
+ if (transaction instanceof Transaction) {
44773
44822
  applyDepthComputeBudget(transaction);
44823
+ cleanup = await this.appendCleanup(transaction);
44824
+ }
44774
44825
  try {
44775
- return await super.sendAndConfirm(transaction, signers, options);
44826
+ const signature = await super.sendAndConfirm(transaction, signers, options);
44827
+ for (const id of cleanup)
44828
+ this.cleanup.delete(id);
44829
+ return signature;
44776
44830
  } catch (error) {
44777
44831
  if (!(error instanceof Error) || !error.message.includes("Unknown action 'undefined'"))
44778
44832
  throw error;
@@ -45023,7 +45077,7 @@ import {
45023
45077
  transferAddress,
45024
45078
  withdrawalAddress
45025
45079
  } from "@depthhq/bindings";
45026
- import { PublicKey as PublicKey4 } from "@solana/web3.js";
45080
+ import { PublicKey as PublicKey5 } from "@solana/web3.js";
45027
45081
 
45028
45082
  // src/chain/transaction.ts
45029
45083
  init_buffer();
@@ -45181,7 +45235,7 @@ class FundingDiscovery {
45181
45235
  const limit = options.limit ?? 20;
45182
45236
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100)
45183
45237
  throw new RangeError("limit must be an integer from 1 to 100");
45184
- const cursor = options.cursor ? new PublicKey4(options.cursor).toBase58() : undefined;
45238
+ const cursor = options.cursor ? new PublicKey5(options.cursor).toBase58() : undefined;
45185
45239
  const accounts2 = await this.connection.getProgramAccounts(this.program.programId, {
45186
45240
  commitment: "confirmed",
45187
45241
  dataSlice: { offset: 0, length: 0 },
@@ -45213,7 +45267,7 @@ class FundingDiscovery {
45213
45267
  const current = status(state);
45214
45268
  if (!state.toMarket || current !== "settled")
45215
45269
  return;
45216
- const orderId = PublicKey4.findProgramAddressSync([
45270
+ const orderId = PublicKey5.findProgramAddressSync([
45217
45271
  Buffer2.from("order"),
45218
45272
  state.market.toBuffer(),
45219
45273
  this.owner.toBuffer(),
@@ -45260,7 +45314,7 @@ class FundingDiscovery {
45260
45314
  const limit = options.limit ?? 20;
45261
45315
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100)
45262
45316
  throw new RangeError("limit must be an integer from 1 to 100");
45263
- const cursor = options.cursor ? new PublicKey4(options.cursor).toBase58() : undefined;
45317
+ const cursor = options.cursor ? new PublicKey5(options.cursor).toBase58() : undefined;
45264
45318
  const types3 = [
45265
45319
  {
45266
45320
  name: "depositReceipt",
@@ -45351,7 +45405,7 @@ class FundingDiscovery {
45351
45405
  throw new Error("Order changed during discovery");
45352
45406
  const bytes = Buffer2.alloc(8);
45353
45407
  bytes.writeBigUInt64LE(nonce);
45354
- canonical = PublicKey4.findProgramAddressSync([
45408
+ canonical = PublicKey5.findProgramAddressSync([
45355
45409
  Buffer2.from("order"),
45356
45410
  state.market.toBuffer(),
45357
45411
  this.owner.toBuffer(),
@@ -45437,15 +45491,17 @@ class FundingClient {
45437
45491
  connection;
45438
45492
  owner;
45439
45493
  queue;
45494
+ settlement;
45440
45495
  market;
45441
45496
  protocol;
45442
45497
  balance;
45443
45498
  marketAccount;
45444
- constructor(program, connection, owner, queue) {
45499
+ constructor(program, connection, owner, queue, settlement = "wallet") {
45445
45500
  this.program = program;
45446
45501
  this.connection = connection;
45447
45502
  this.owner = owner;
45448
45503
  this.queue = queue;
45504
+ this.settlement = settlement;
45449
45505
  this.market = marketAddress3(program.programId);
45450
45506
  this.protocol = protocolAddress2(program.programId);
45451
45507
  this.balance = balanceAddress(owner, program.programId);
@@ -45779,7 +45835,7 @@ class TransferHandle {
45779
45835
  return true;
45780
45836
  if (transfer.state === "rejected" || transfer.state === "refunded" || transfer.state === "cancelled")
45781
45837
  throw new DepthError("MARGIN_REJECTED", "confirmation", `Margin transfer ${transfer.state}`, false, "Read the receipt and private account before retrying", this.id.toBase58());
45782
- if (transfer.state === "escrow") {
45838
+ if (transfer.state === "escrow" && this.client.settlement === "wallet") {
45783
45839
  try {
45784
45840
  await this.settle();
45785
45841
  } catch (error) {
@@ -45883,15 +45939,15 @@ async function balanceState(program, connection, owner, address2) {
45883
45939
  // src/account/token.ts
45884
45940
  init_buffer();
45885
45941
  import {
45886
- PublicKey as PublicKey5,
45942
+ PublicKey as PublicKey6,
45887
45943
  SystemProgram,
45888
45944
  Transaction as Transaction2,
45889
- TransactionInstruction
45945
+ TransactionInstruction as TransactionInstruction2
45890
45946
  } from "@solana/web3.js";
45891
- var tokenProgram = new PublicKey5("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
45892
- var associatedProgram = new PublicKey5("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
45947
+ var tokenProgram = new PublicKey6("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
45948
+ var associatedProgram = new PublicKey6("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
45893
45949
  function associatedTokenAddress(owner, mint) {
45894
- return PublicKey5.findProgramAddressSync([owner.toBuffer(), tokenProgram.toBuffer(), mint.toBuffer()], associatedProgram)[0];
45950
+ return PublicKey6.findProgramAddressSync([owner.toBuffer(), tokenProgram.toBuffer(), mint.toBuffer()], associatedProgram)[0];
45895
45951
  }
45896
45952
  function tokenBalance(info, owner, mint) {
45897
45953
  if (!info.owner.equals(tokenProgram) || info.data.length < 165 || !info.data.subarray(0, 32).equals(mint.toBuffer()) || !info.data.subarray(32, 64).equals(owner.toBuffer()) || info.data[108] !== 1)
@@ -45920,7 +45976,7 @@ async function destinationTokenAccount(provider, owner, mint) {
45920
45976
  const address2 = associatedTokenAddress(owner, mint);
45921
45977
  let info = await provider.connection.getAccountInfo(address2, "confirmed");
45922
45978
  if (!info) {
45923
- const instruction = new TransactionInstruction({
45979
+ const instruction = new TransactionInstruction2({
45924
45980
  programId: associatedProgram,
45925
45981
  data: Buffer2.from([1]),
45926
45982
  keys: [
@@ -45962,7 +46018,7 @@ import {
45962
46018
  marketAddress as marketAddress4,
45963
46019
  SOL_USD_MARKET_ID as SOL_USD_MARKET_ID2
45964
46020
  } from "@depthhq/bindings";
45965
- import { PublicKey as PublicKey6 } from "@solana/web3.js";
46021
+ import { PublicKey as PublicKey7 } from "@solana/web3.js";
45966
46022
 
45967
46023
  // src/position/pnl.ts
45968
46024
  var scale = 100000000000n;
@@ -45970,22 +46026,22 @@ function positionPnl(position, priceE8, fundingIndex, fill) {
45970
46026
  if (position.status !== "ready" || position.position === undefined || position.position === 0n || position.entryPriceE8 === undefined || position.fundingCheckpoint === undefined || position.collateral !== undefined && position.collateral < 0n || priceE8 <= 0n)
45971
46027
  throw new Error("A ready open position and a positive price are required");
45972
46028
  const size = position.position < 0n ? -position.position : position.position;
45973
- const sign = position.position > 0n ? 1n : -1n;
46029
+ const sign2 = position.position > 0n ? 1n : -1n;
45974
46030
  let closing = size;
45975
46031
  let fee = 0n;
45976
46032
  let state = "open";
45977
46033
  if (fill) {
45978
- if (fill.status !== "ready" || fill.side !== -sign || fill.size === undefined || fill.size <= 0n || fill.executionPriceE8 !== priceE8 || fill.fee === undefined || fill.fee < 0n)
46034
+ if (fill.status !== "ready" || fill.side !== -sign2 || fill.size === undefined || fill.size <= 0n || fill.executionPriceE8 !== priceE8 || fill.fee === undefined || fill.fee < 0n)
45979
46035
  throw new Error("A ready opposite-side execution is required");
45980
46036
  closing = fill.size < size ? fill.size : size;
45981
46037
  fee = fill.fee;
45982
46038
  state = fill.size === size ? "closed" : fill.size < size ? "reduced" : "reversed";
45983
46039
  }
45984
- const gross = sign * closing * (priceE8 - position.entryPriceE8) / scale;
46040
+ const gross = sign2 * closing * (priceE8 - position.entryPriceE8) / scale;
45985
46041
  const funding = position.position * (fundingIndex - position.fundingCheckpoint) / scale;
45986
46042
  const net = gross - funding - fee;
45987
46043
  return {
45988
- side: sign > 0n ? "long" : "short",
46044
+ side: sign2 > 0n ? "long" : "short",
45989
46045
  state,
45990
46046
  entryPriceE8: position.entryPriceE8,
45991
46047
  priceE8,
@@ -46192,11 +46248,11 @@ class DepthOperation {
46192
46248
  throw new DepthError(result.status === "pending" ? "FILL_VIEW_PENDING" : "FILL_VIEW_STALE", "confirmation", result.status === "pending" ? "Private fill computation is still pending" : "Private fill view is stale", result.status === "pending", result.status === "pending" ? "Call privateResult again with the same operation" : "Restore this operation and request a fresh fill view", this.id.toBase58());
46193
46249
  this.fill = {
46194
46250
  ...result,
46195
- positionId: marketAccountAddress2(new PublicKey6(snapshot.owner), this.program.programId)
46251
+ positionId: marketAccountAddress2(new PublicKey7(snapshot.owner), this.program.programId)
46196
46252
  };
46197
46253
  }
46198
46254
  try {
46199
- await this.view.close();
46255
+ await this.view.deferClose();
46200
46256
  } catch (error) {
46201
46257
  let remaining;
46202
46258
  try {
@@ -46212,7 +46268,7 @@ class DepthOperation {
46212
46268
  return this.fill;
46213
46269
  }
46214
46270
  cleanupError(cause) {
46215
- return new DepthError("FILL_VIEW_CLEANUP_PENDING", "confirmation", "Private fill was read but its temporary view is still open", true, "Call privateResult again with the same operation to close the view", this.id.toBase58(), { cause });
46271
+ return new DepthError("FILL_VIEW_CLEANUP_PENDING", "confirmation", "Private fill was read but receipt cleanup could not be scheduled", true, "Call privateResult again with the same operation to schedule cleanup", this.id.toBase58(), { cause });
46216
46272
  }
46217
46273
  }
46218
46274
 
@@ -46223,22 +46279,22 @@ import {
46223
46279
  marketAddress as marketAddress5,
46224
46280
  protocolAddress as protocolAddress3
46225
46281
  } from "@depthhq/bindings";
46226
- import { PublicKey as PublicKey7 } from "@solana/web3.js";
46282
+ import { PublicKey as PublicKey8 } from "@solana/web3.js";
46227
46283
 
46228
46284
  class DepthRecovery {
46229
46285
  program;
46230
46286
  connection;
46231
46287
  owner;
46232
46288
  market = marketAddress5();
46233
- winddown = PublicKey7.findProgramAddressSync([Buffer2.from("winddown"), this.market.toBuffer()], DEPTH_PROGRAM_ID5)[0];
46289
+ winddown = PublicKey8.findProgramAddressSync([Buffer2.from("winddown"), this.market.toBuffer()], DEPTH_PROGRAM_ID5)[0];
46234
46290
  marketAccount;
46235
46291
  claim;
46236
46292
  constructor(program, connection, owner) {
46237
46293
  this.program = program;
46238
46294
  this.connection = connection;
46239
46295
  this.owner = owner;
46240
- this.marketAccount = PublicKey7.findProgramAddressSync([Buffer2.from("market-account"), this.market.toBuffer(), owner.toBuffer()], DEPTH_PROGRAM_ID5)[0];
46241
- this.claim = PublicKey7.findProgramAddressSync([Buffer2.from("winddown-claim"), this.marketAccount.toBuffer()], DEPTH_PROGRAM_ID5)[0];
46296
+ this.marketAccount = PublicKey8.findProgramAddressSync([Buffer2.from("market-account"), this.market.toBuffer(), owner.toBuffer()], DEPTH_PROGRAM_ID5)[0];
46297
+ this.claim = PublicKey8.findProgramAddressSync([Buffer2.from("winddown-claim"), this.marketAccount.toBuffer()], DEPTH_PROGRAM_ID5)[0];
46242
46298
  }
46243
46299
  async read(address2, name2) {
46244
46300
  const result = await this.connection.getAccountInfoAndContext(address2, "confirmed");
@@ -46328,7 +46384,7 @@ import {
46328
46384
  marketAddress as marketAddress6,
46329
46385
  transferAddress as transferAddress3
46330
46386
  } from "@depthhq/bindings";
46331
- import { PublicKey as PublicKey8 } from "@solana/web3.js";
46387
+ import { PublicKey as PublicKey9 } from "@solana/web3.js";
46332
46388
 
46333
46389
  // src/history/replay.ts
46334
46390
  function replayFill(state, fill, fundingIndex, maker = false) {
@@ -46407,7 +46463,7 @@ class ExecutionHistory {
46407
46463
  this.requestView = requestView;
46408
46464
  }
46409
46465
  orderAddress(trader, nonce) {
46410
- return PublicKey8.findProgramAddressSync([
46466
+ return PublicKey9.findProgramAddressSync([
46411
46467
  Buffer2.from("order"),
46412
46468
  marketAddress6(this.program.programId).toBuffer(),
46413
46469
  trader.toBuffer(),
@@ -46429,7 +46485,7 @@ class ExecutionHistory {
46429
46485
  }
46430
46486
  const result = this.fills.get(key) ?? await view.wait({ timeoutMs: 180000 });
46431
46487
  if (result.status === "stale") {
46432
- await view.close();
46488
+ await view.deferClose();
46433
46489
  this.views.delete(key);
46434
46490
  throw new Error("Historical private execution changed; retry with the same wallet");
46435
46491
  }
@@ -46437,7 +46493,7 @@ class ExecutionHistory {
46437
46493
  throw new Error("Historical private execution is still pending; retry with the same wallet");
46438
46494
  this.fills.set(key, result);
46439
46495
  try {
46440
- await view.close();
46496
+ await view.deferClose();
46441
46497
  } catch (error) {
46442
46498
  if (await this.connection.getAccountInfo(view.id, "confirmed"))
46443
46499
  throw error;
@@ -46721,7 +46777,9 @@ class DepthView {
46721
46777
  mxePublicKey;
46722
46778
  program;
46723
46779
  connection;
46724
- constructor(id, signature, kind, owner, secret, publicKey, mxePublicKey, program, connection) {
46780
+ source;
46781
+ release;
46782
+ constructor(id, signature, kind, owner, secret, publicKey, mxePublicKey, program, connection, source, release) {
46725
46783
  this.id = id;
46726
46784
  this.signature = signature;
46727
46785
  this.kind = kind;
@@ -46731,6 +46789,8 @@ class DepthView {
46731
46789
  this.mxePublicKey = mxePublicKey;
46732
46790
  this.program = program;
46733
46791
  this.connection = connection;
46792
+ this.source = source;
46793
+ this.release = release;
46734
46794
  }
46735
46795
  async status() {
46736
46796
  const response = await this.connection.getAccountInfoAndContext(this.id, "confirmed");
@@ -46741,6 +46801,9 @@ class DepthView {
46741
46801
  if (!state.owner.equals(this.owner) || !Buffer2.from(state.viewKey).equals(Buffer2.from(this.publicKey))) {
46742
46802
  throw new Error("Private view authorization does not match this request");
46743
46803
  }
46804
+ const source = this.kind === "balance" ? state.balance : this.kind === "market" ? state.marketAccount : this.kind === "fill" ? state.order : state.quote;
46805
+ if (this.source && !source?.equals(this.source))
46806
+ throw new Error("Private view source does not match this request");
46744
46807
  const current = Object.keys(state.status)[0];
46745
46808
  if (current !== "pending" && current !== "ready" && current !== "stale") {
46746
46809
  throw new Error("Private view has an unknown status");
@@ -46805,22 +46868,400 @@ class DepthView {
46805
46868
  await new Promise((resolve) => setTimeout(resolve, Math.min(pollMs, deadline - Date.now())));
46806
46869
  }
46807
46870
  }
46871
+ async deferClose() {
46872
+ if (this.release) {
46873
+ await this.close();
46874
+ return;
46875
+ }
46876
+ const provider = this.program.provider;
46877
+ if (typeof provider.deferClose !== "function") {
46878
+ await this.close();
46879
+ return;
46880
+ }
46881
+ const snapshot = await this.status();
46882
+ if (snapshot.status === "pending")
46883
+ throw new Error("Private view computation is still pending");
46884
+ const instruction = await this.closeBuilder().instruction();
46885
+ provider.deferClose(this.id, instruction);
46886
+ }
46887
+ closeBuilder() {
46888
+ const builder = this.kind === "balance" ? this.program.methods.closeBalanceView() : this.kind === "market" ? this.program.methods.closeMarketView() : this.kind === "fill" ? this.program.methods.closeFillView() : this.program.methods.closeQuoteView();
46889
+ return builder.accountsPartial({ owner: this.owner, view: this.id });
46890
+ }
46808
46891
  async close() {
46809
46892
  const snapshot = await this.status();
46810
46893
  if (snapshot.status === "pending") {
46811
46894
  throw new Error("Private view computation is still pending");
46812
46895
  }
46813
- const builder = this.kind === "balance" ? this.program.methods.closeBalanceView() : this.kind === "market" ? this.program.methods.closeMarketView() : this.kind === "fill" ? this.program.methods.closeFillView() : this.program.methods.closeQuoteView();
46814
- return builder.accountsPartial({ owner: this.owner, view: this.id }).rpc({ commitment: "confirmed" });
46896
+ if (this.release)
46897
+ return this.release();
46898
+ return this.closeBuilder().rpc({ commitment: "confirmed" });
46899
+ }
46900
+ }
46901
+
46902
+ // src/view/session.ts
46903
+ init_buffer();
46904
+ import * as anchor8 from "@anchor-lang/core";
46905
+ import {
46906
+ DEPTH_PROGRAM_ID as DEPTH_PROGRAM_ID7,
46907
+ marketAddress as marketAddress7,
46908
+ viewPermitAddress
46909
+ } from "@depthhq/bindings";
46910
+ import {
46911
+ viewMessage
46912
+ } from "@depthhq/schema";
46913
+ import { PublicKey as PublicKey10 } from "@solana/web3.js";
46914
+
46915
+ // src/view/cache.ts
46916
+ class DeviceViews {
46917
+ database;
46918
+ open() {
46919
+ return this.database ??= new Promise((resolve, reject) => {
46920
+ const request = indexedDB.open("depth-private-views", 1);
46921
+ request.onupgradeneeded = () => {
46922
+ request.result.createObjectStore("keys");
46923
+ request.result.createObjectStore("views");
46924
+ };
46925
+ request.onerror = () => reject(request.error);
46926
+ request.onblocked = () => reject(new Error("Private view storage is blocked"));
46927
+ request.onsuccess = () => resolve(request.result);
46928
+ });
46929
+ }
46930
+ async key(scope, proposed) {
46931
+ const database = await this.open();
46932
+ return new Promise((resolve, reject) => {
46933
+ const transaction = database.transaction("keys", "readwrite");
46934
+ const store = transaction.objectStore("keys");
46935
+ const request = store.get(scope);
46936
+ let value2;
46937
+ request.onsuccess = () => {
46938
+ value2 = request.result ?? proposed;
46939
+ if (!request.result)
46940
+ store.put(value2, scope);
46941
+ };
46942
+ transaction.oncomplete = () => resolve(value2);
46943
+ transaction.onabort = () => reject(transaction.error);
46944
+ transaction.onerror = () => reject(transaction.error);
46945
+ });
46946
+ }
46947
+ async read(id) {
46948
+ const database = await this.open();
46949
+ return new Promise((resolve, reject) => {
46950
+ const transaction = database.transaction("views", "readonly");
46951
+ const request = transaction.objectStore("views").get(id);
46952
+ transaction.oncomplete = () => resolve(request.result);
46953
+ transaction.onabort = () => reject(transaction.error);
46954
+ transaction.onerror = () => reject(transaction.error);
46955
+ });
46956
+ }
46957
+ async write(id, value2) {
46958
+ const database = await this.open();
46959
+ return new Promise((resolve, reject) => {
46960
+ const transaction = database.transaction("views", "readwrite");
46961
+ transaction.objectStore("views").put(value2, id);
46962
+ transaction.oncomplete = () => resolve();
46963
+ transaction.onabort = () => reject(transaction.error);
46964
+ transaction.onerror = () => reject(transaction.error);
46965
+ });
46966
+ }
46967
+ }
46968
+
46969
+ class EncryptedViews {
46970
+ scope;
46971
+ storage;
46972
+ key;
46973
+ constructor(scope, storage) {
46974
+ this.scope = scope;
46975
+ this.storage = storage;
46976
+ }
46977
+ encryptionKey() {
46978
+ return this.key ??= crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, [
46979
+ "encrypt",
46980
+ "decrypt"
46981
+ ]).then((key) => this.storage.key(this.scope, key));
46982
+ }
46983
+ async read(name2) {
46984
+ const id = `${this.scope}:${name2}`;
46985
+ const envelope = await this.storage.read(id);
46986
+ if (!envelope)
46987
+ return;
46988
+ const bytes = await crypto.subtle.decrypt({
46989
+ name: "AES-GCM",
46990
+ iv: envelope.iv,
46991
+ additionalData: new TextEncoder().encode(id)
46992
+ }, await this.encryptionKey(), envelope.data);
46993
+ return JSON.parse(new TextDecoder().decode(bytes));
46994
+ }
46995
+ async write(name2, value2) {
46996
+ const id = `${this.scope}:${name2}`;
46997
+ const iv = crypto.getRandomValues(new Uint8Array(12));
46998
+ const data = await crypto.subtle.encrypt({ name: "AES-GCM", iv, additionalData: new TextEncoder().encode(id) }, await this.encryptionKey(), new TextEncoder().encode(JSON.stringify(value2)));
46999
+ await this.storage.write(id, { iv, data });
47000
+ }
47001
+ }
47002
+
47003
+ class ViewCache {
47004
+ scope;
47005
+ storage;
47006
+ memory = new Map;
47007
+ key;
47008
+ constructor(scope, storage) {
47009
+ this.scope = scope;
47010
+ this.storage = storage;
47011
+ }
47012
+ encryptionKey() {
47013
+ return this.key ??= crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, [
47014
+ "encrypt",
47015
+ "decrypt"
47016
+ ]).then((key) => this.storage.key(this.scope, key));
47017
+ }
47018
+ id(kind, account) {
47019
+ return `${this.scope}:${kind}:${account}`;
47020
+ }
47021
+ async get(kind, account, version) {
47022
+ const id = this.id(kind, account);
47023
+ let snapshot = this.memory.get(id);
47024
+ if (!snapshot && this.storage) {
47025
+ try {
47026
+ const envelope = await this.storage.read(id);
47027
+ if (envelope) {
47028
+ const bytes = await crypto.subtle.decrypt({
47029
+ name: "AES-GCM",
47030
+ iv: envelope.iv,
47031
+ additionalData: new TextEncoder().encode(id)
47032
+ }, await this.encryptionKey(), envelope.data);
47033
+ snapshot = JSON.parse(new TextDecoder().decode(bytes), (_, value2) => value2 && typeof value2 === "object" && typeof value2.$bigint === "string" ? BigInt(value2.$bigint) : value2);
47034
+ }
47035
+ } catch {
47036
+ return;
47037
+ }
47038
+ }
47039
+ if (!snapshot || snapshot.status !== "ready" || snapshot.version !== version || typeof snapshot.id !== "string" || !Number.isSafeInteger(snapshot.slot) || (kind === "balance" ? typeof snapshot.amount !== "bigint" : [
47040
+ "collateral",
47041
+ "reserved",
47042
+ "position",
47043
+ "entryPriceE8",
47044
+ "fundingCheckpoint"
47045
+ ].some((field) => typeof snapshot[field] !== "bigint")))
47046
+ return;
47047
+ this.memory.set(id, snapshot);
47048
+ return { ...snapshot };
47049
+ }
47050
+ async set(kind, account, snapshot) {
47051
+ if (snapshot.status !== "ready")
47052
+ return;
47053
+ const id = this.id(kind, account);
47054
+ this.memory.set(id, { ...snapshot });
47055
+ if (!this.storage)
47056
+ return;
47057
+ try {
47058
+ const iv = crypto.getRandomValues(new Uint8Array(12));
47059
+ const bytes = new TextEncoder().encode(JSON.stringify(snapshot, (_, value2) => typeof value2 === "bigint" ? { $bigint: value2.toString() } : value2));
47060
+ const data = await crypto.subtle.encrypt({ name: "AES-GCM", iv, additionalData: new TextEncoder().encode(id) }, await this.encryptionKey(), bytes);
47061
+ await this.storage.write(id, { iv, data });
47062
+ } catch {}
47063
+ }
47064
+ }
47065
+
47066
+ // src/view/session.ts
47067
+ var identities = new Map;
47068
+ var approvals = new Map;
47069
+ var requests = new Map;
47070
+ var disabled = new Set;
47071
+
47072
+ class ViewSession {
47073
+ program;
47074
+ connection;
47075
+ owner;
47076
+ serviceUrl;
47077
+ scope;
47078
+ storage;
47079
+ pending = new Map;
47080
+ constructor(program, connection, owner, serviceUrl, scope, storage) {
47081
+ this.program = program;
47082
+ this.connection = connection;
47083
+ this.owner = owner;
47084
+ this.serviceUrl = serviceUrl;
47085
+ this.scope = scope;
47086
+ if (storage)
47087
+ this.storage = new EncryptedViews(scope, storage);
47088
+ }
47089
+ identity() {
47090
+ let value2 = identities.get(this.scope);
47091
+ if (!value2) {
47092
+ value2 = (async () => {
47093
+ const saved = await this.storage?.read("authorization");
47094
+ if (saved) {
47095
+ if (![saved.signing, saved.encryption].every((key) => Array.isArray(key) && key.length === 32 && key.every((v) => Number.isInteger(v) && v >= 0 && v <= 255)))
47096
+ throw new Error("Invalid private read identity");
47097
+ return saved;
47098
+ }
47099
+ const identity = {
47100
+ signing: Array.from(randomBytes3(32)),
47101
+ encryption: Array.from(x25519.utils.randomSecretKey())
47102
+ };
47103
+ await this.storage?.write("authorization", identity);
47104
+ return identity;
47105
+ })();
47106
+ identities.set(this.scope, value2);
47107
+ value2.catch(() => identities.delete(this.scope));
47108
+ }
47109
+ return value2;
47110
+ }
47111
+ async keys() {
47112
+ const identity = await this.identity();
47113
+ const secret = Uint8Array.from(identity.encryption);
47114
+ const signing = Uint8Array.from(identity.signing);
47115
+ const reader = new PublicKey10(await getPublicKey(signing));
47116
+ return {
47117
+ signing,
47118
+ secret,
47119
+ reader,
47120
+ viewKey: x25519.getPublicKey(secret),
47121
+ permit: viewPermitAddress(this.owner, reader)
47122
+ };
47123
+ }
47124
+ async status() {
47125
+ const keys = await this.keys();
47126
+ const info = await this.connection.getAccountInfo(keys.permit, "confirmed");
47127
+ if (!info)
47128
+ return { active: false, id: keys.permit.toBase58() };
47129
+ if (!info.owner.equals(DEPTH_PROGRAM_ID7))
47130
+ throw new Error("Invalid private read permission owner");
47131
+ const permit = this.program.coder.accounts.decode("viewPermit", info.data);
47132
+ const expiresAt = permit.expiresAt.toNumber();
47133
+ if (!permit.owner.equals(this.owner) || !permit.reader.equals(keys.reader) || !permit.market.equals(marketAddress7()) || !Buffer2.from(permit.viewKey).equals(Buffer2.from(keys.viewKey)))
47134
+ throw new Error("Private read permission does not match this device");
47135
+ return {
47136
+ id: keys.permit.toBase58(),
47137
+ active: permit.active && expiresAt > Math.floor(Date.now() / 1000) && permit.scopes === 15,
47138
+ expiresAt
47139
+ };
47140
+ }
47141
+ async capability() {
47142
+ if (!this.serviceUrl)
47143
+ throw new Error("A Depth service is required for automatic private reads");
47144
+ const response = await fetch(`${this.serviceUrl}/v1/views`, {
47145
+ cache: "no-store",
47146
+ signal: AbortSignal.timeout(15000)
47147
+ });
47148
+ if (!response.ok || (await response.json()).enabled !== true)
47149
+ throw new Error("Automatic private reads are unavailable");
47150
+ }
47151
+ async authorize() {
47152
+ const { permit, reader, viewKey } = await this.keys();
47153
+ const id = permit.toBase58();
47154
+ const existing = approvals.get(id);
47155
+ if (existing)
47156
+ return existing;
47157
+ const approval = (async () => {
47158
+ if ((await this.status()).active) {
47159
+ disabled.delete(this.scope);
47160
+ await this.storage?.write("disabled", false);
47161
+ return;
47162
+ }
47163
+ await this.capability();
47164
+ const expiresAt = new anchor8.BN(Math.floor(Date.now() / 1000) + 7 * 86400 - 60);
47165
+ await this.program.methods.authorizeViews(reader, Array.from(viewKey), expiresAt, 15).accountsPartial({ owner: this.owner, market: marketAddress7(), permit }).rpc({ commitment: "confirmed" });
47166
+ disabled.delete(this.scope);
47167
+ await this.storage?.write("disabled", false);
47168
+ })().finally(() => approvals.delete(id));
47169
+ approvals.set(id, approval);
47170
+ return approval;
47171
+ }
47172
+ async revoke() {
47173
+ const { permit } = await this.keys();
47174
+ const signature = await this.program.methods.revokeViews().accountsPartial({ owner: this.owner, permit }).rpc({ commitment: "confirmed" });
47175
+ disabled.add(this.scope);
47176
+ await this.storage?.write("disabled", true);
47177
+ return signature;
47178
+ }
47179
+ async post(request, action) {
47180
+ const { signing } = await this.keys();
47181
+ request.expiresAt = Math.floor(Date.now() / 1000) + 120;
47182
+ request.signature = Buffer2.from(await sign(new TextEncoder().encode(viewMessage(DEPTH_PROGRAM_ID7.toBase58(), request, action)), signing)).toString("base64");
47183
+ return fetch(`${this.serviceUrl}/v1/views${action === "close" ? "/close" : ""}`, {
47184
+ method: "POST",
47185
+ headers: { "content-type": "application/json" },
47186
+ body: JSON.stringify(request),
47187
+ signal: AbortSignal.timeout(60000)
47188
+ });
47189
+ }
47190
+ request(kind, source) {
47191
+ const key = `${this.scope}:${kind}:${source.toBase58()}`;
47192
+ const pending = requests.get(key);
47193
+ if (pending)
47194
+ return pending;
47195
+ const request = this.read(kind, source).finally(() => requests.delete(key));
47196
+ requests.set(key, request);
47197
+ return request;
47198
+ }
47199
+ async read(kind, source, retry = true) {
47200
+ if (disabled.has(this.scope) || await this.storage?.read("disabled"))
47201
+ throw new Error("Private reads are disabled on this device");
47202
+ await this.authorize();
47203
+ const keys = await this.keys();
47204
+ const name2 = `request:${kind}:${source.toBase58()}`;
47205
+ let pending = this.pending.get(name2) ?? await this.storage?.read(name2);
47206
+ if (!pending) {
47207
+ pending = { nonce: new anchor8.BN(randomBytes3(8), "le").toString() };
47208
+ await this.storage?.write(name2, pending);
47209
+ this.pending.set(name2, pending);
47210
+ }
47211
+ const request = {
47212
+ owner: this.owner.toBase58(),
47213
+ reader: keys.reader.toBase58(),
47214
+ permit: keys.permit.toBase58(),
47215
+ kind,
47216
+ source: source.toBase58(),
47217
+ nonce: pending.nonce,
47218
+ expiresAt: 0,
47219
+ signature: ""
47220
+ };
47221
+ const response = await this.post(request, "read");
47222
+ if (response.status === 410 && retry) {
47223
+ this.pending.delete(name2);
47224
+ await this.storage?.write(name2, null);
47225
+ return this.read(kind, source, false);
47226
+ }
47227
+ if (!response.ok) {
47228
+ const error = await response.json().catch(() => ({}));
47229
+ if (error.error === "view_source_busy")
47230
+ throw new Error("Private account is processing another operation");
47231
+ throw new Error(`Private read service returned HTTP ${response.status}`);
47232
+ }
47233
+ const receipt = await response.json();
47234
+ const payer = new PublicKey10(receipt.payer);
47235
+ const nonce = new anchor8.BN(request.nonce).toArrayLike(Buffer2, "le", 8);
47236
+ const seeds = kind === "balance" ? [Buffer2.from("view"), payer.toBuffer(), nonce] : kind === "market" ? [Buffer2.from("market-view"), source.toBuffer(), nonce] : [
47237
+ Buffer2.from(`${kind}-view`),
47238
+ source.toBuffer(),
47239
+ payer.toBuffer(),
47240
+ nonce
47241
+ ];
47242
+ const id = PublicKey10.findProgramAddressSync(seeds, DEPTH_PROGRAM_ID7)[0];
47243
+ if (receipt.id !== id.toBase58() || typeof receipt.signature !== "string")
47244
+ throw new Error("Invalid private read receipt");
47245
+ const mxeKey = await getMXEPublicKey(this.program.provider, DEPTH_PROGRAM_ID7);
47246
+ if (!mxeKey)
47247
+ throw new Error("MXE public key is unavailable");
47248
+ return new DepthView(id, receipt.signature, kind, payer, keys.secret, keys.viewKey, mxeKey, this.program, this.connection, source, async () => {
47249
+ this.pending.delete(name2);
47250
+ await this.storage?.write(name2, null);
47251
+ try {
47252
+ await this.post(request, "close");
47253
+ } catch {}
47254
+ return "";
47255
+ });
46815
47256
  }
46816
47257
  }
46817
47258
 
46818
47259
  // src/client/client.ts
46819
- function pda(seeds, programId = DEPTH_PROGRAM_ID7) {
46820
- return PublicKey9.findProgramAddressSync(seeds, programId)[0];
47260
+ function pda(seeds, programId = DEPTH_PROGRAM_ID8) {
47261
+ return PublicKey11.findProgramAddressSync(seeds, programId)[0];
46821
47262
  }
46822
47263
  function nonce() {
46823
- return new anchor8.BN(randomBytes3(8), "le");
47264
+ return new anchor9.BN(randomBytes3(8), "le");
46824
47265
  }
46825
47266
  function enumName2(value2) {
46826
47267
  return Object.keys(value2)[0] ?? "unknown";
@@ -46830,15 +47271,18 @@ class DepthClient {
46830
47271
  options;
46831
47272
  program;
46832
47273
  provider;
47274
+ viewCache;
47275
+ views;
47276
+ viewReads = new Map;
46833
47277
  position;
46834
- marketAddress = marketAddress7();
47278
+ marketAddress = marketAddress8();
46835
47279
  protocolAddress = protocolAddress4();
46836
- mxeAddress = getMXEAccAddress(DEPTH_PROGRAM_ID7);
47280
+ mxeAddress = getMXEAccAddress(DEPTH_PROGRAM_ID8);
46837
47281
  get catalog() {
46838
47282
  return new MarketCatalog(this);
46839
47283
  }
46840
47284
  get funding() {
46841
- return new FundingClient(this.program, this.options.connection, this.options.wallet.publicKey, (name2, offset2) => this.queue(name2, offset2));
47285
+ return new FundingClient(this.program, this.options.connection, this.options.wallet.publicKey, (name2, offset2) => this.queue(name2, offset2), this.options.settlement ?? (this.options.environment && this.options.environment !== "localnet" ? "keeper" : "wallet"));
46842
47286
  }
46843
47287
  get app() {
46844
47288
  return new AppAdmin(this.program, this.options.connection, this.options.wallet.publicKey, this.funding);
@@ -46852,15 +47296,19 @@ class DepthClient {
46852
47296
  }
46853
47297
  constructor(options) {
46854
47298
  this.options = options;
47299
+ const scope = `${"manifest" in options.deployment ? options.deployment.manifest.payload.genesisHash : "localnet"}:${DEPTH_PROGRAM_ID8.toBase58()}:${options.wallet.publicKey.toBase58()}`;
47300
+ const storage = options.privateViews === "device" && "manifest" in options.deployment && typeof indexedDB !== "undefined" && globalThis.crypto?.subtle ? new DeviceViews : undefined;
47301
+ this.viewCache = new ViewCache(scope, storage);
46855
47302
  this.provider = new DepthProvider(options.connection, options.wallet, {
46856
47303
  commitment: "confirmed",
46857
47304
  preflightCommitment: "confirmed"
46858
47305
  });
46859
- this.program = new anchor8.Program(DEPTH_IDL3, this.provider);
47306
+ this.program = new anchor9.Program(DEPTH_IDL3, this.provider);
47307
+ this.views = new ViewSession(this.program, options.connection, options.wallet.publicKey, options.serviceUrl, scope, storage);
46860
47308
  this.position = this.positionActions();
46861
47309
  }
46862
47310
  static async create(options) {
46863
- const appId = typeof options.appId === "string" ? new PublicKey9(options.appId) : options.appId;
47311
+ const appId = typeof options.appId === "string" ? new PublicKey11(options.appId) : options.appId;
46864
47312
  const resolved = {
46865
47313
  ...options,
46866
47314
  ...await resolveConnection(options),
@@ -46873,7 +47321,7 @@ class DepthClient {
46873
47321
  }
46874
47322
  async verifyDeployment() {
46875
47323
  const [programInfo, protocol, market, app, mxe] = await Promise.all([
46876
- this.options.connection.getAccountInfo(DEPTH_PROGRAM_ID7, "confirmed"),
47324
+ this.options.connection.getAccountInfo(DEPTH_PROGRAM_ID8, "confirmed"),
46877
47325
  this.read("Protocol", this.protocolAddress),
46878
47326
  this.read("Market", this.marketAddress),
46879
47327
  this.options.appId ? this.read("AppConfig", appAddress2(this.options.appId)) : Promise.resolve(undefined),
@@ -46881,13 +47329,13 @@ class DepthClient {
46881
47329
  ]);
46882
47330
  if (!programInfo?.executable)
46883
47331
  throw new Error("Depth program is not deployed");
46884
- if (!market.settlementMint.equals(protocol.settlementMint) || !market.vault.equals(protocol.vault) || this.options.appId && !app?.app.equals(this.options.appId) || !mxe.mxeProgramId.equals(DEPTH_PROGRAM_ID7)) {
47332
+ if (!market.settlementMint.equals(protocol.settlementMint) || !market.vault.equals(protocol.vault) || this.options.appId && !app?.app.equals(this.options.appId) || !mxe.mxeProgramId.equals(DEPTH_PROGRAM_ID8)) {
46885
47333
  throw new Error("Depth deployment accounts disagree");
46886
47334
  }
46887
47335
  }
46888
47336
  async read(name2, address2) {
46889
47337
  const info = await this.options.connection.getAccountInfo(address2, "confirmed");
46890
- if (!info || !info.owner.equals(DEPTH_PROGRAM_ID7)) {
47338
+ if (!info || !info.owner.equals(DEPTH_PROGRAM_ID8)) {
46891
47339
  throw new Error(`${name2} account is unavailable`);
46892
47340
  }
46893
47341
  return this.program.coder.accounts.decode(name2[0].toLowerCase() + name2.slice(1), info.data);
@@ -46895,7 +47343,7 @@ class DepthClient {
46895
47343
  async queue(name2, offset2) {
46896
47344
  const mxe = await arciumAccount(this.provider).fetch(this.mxeAddress);
46897
47345
  const cluster = mxe.cluster;
46898
- const definition = getCompDefAccAddress(DEPTH_PROGRAM_ID7, Buffer2.from(getCompDefAccOffset(computationName(name2))).readUInt32LE());
47346
+ const definition = getCompDefAccAddress(DEPTH_PROGRAM_ID8, Buffer2.from(getCompDefAccOffset(computationName(name2))).readUInt32LE());
46899
47347
  const definitionInfo = await this.options.connection.getAccountInfo(definition, "confirmed");
46900
47348
  if (!definitionInfo)
46901
47349
  throw new Error(`${name2} computation is not deployed`);
@@ -46917,7 +47365,7 @@ class DepthClient {
46917
47365
  }
46918
47366
  async market() {
46919
47367
  const response = await this.options.connection.getAccountInfoAndContext(this.marketAddress, "confirmed");
46920
- if (!response.value || !response.value.owner.equals(DEPTH_PROGRAM_ID7)) {
47368
+ if (!response.value || !response.value.owner.equals(DEPTH_PROGRAM_ID8)) {
46921
47369
  throw new Error("Depth market is unavailable");
46922
47370
  }
46923
47371
  return {
@@ -46929,6 +47377,9 @@ class DepthClient {
46929
47377
  operation(id) {
46930
47378
  return new DepthOperation(id, undefined, this.program, this.options.connection, (order) => this.requestFillView(order));
46931
47379
  }
47380
+ async requestQuoteView(quote) {
47381
+ return this.views.request("quote", quote);
47382
+ }
46932
47383
  async requestBalanceView() {
46933
47384
  const owner = this.options.wallet.publicKey;
46934
47385
  const balance = pda([Buffer2.from("balance"), owner.toBuffer()]);
@@ -46976,7 +47427,10 @@ class DepthClient {
46976
47427
  return this.requestView("fill", view, viewNonce, { order });
46977
47428
  }
46978
47429
  async requestView(kind, view, viewNonce, accounts2) {
46979
- const mxePublicKey = await getMXEPublicKey(this.provider, DEPTH_PROGRAM_ID7);
47430
+ const source = accounts2.balance ?? accounts2.marketAccount ?? accounts2.order;
47431
+ if (this.options.viewAuthorization === "session" || this.options.viewAuthorization !== "wallet" && this.options.privateViews === "device" && this.options.serviceUrl)
47432
+ return this.views.request(kind, source);
47433
+ const mxePublicKey = await getMXEPublicKey(this.provider, DEPTH_PROGRAM_ID8);
46980
47434
  if (!mxePublicKey)
46981
47435
  throw new Error("MXE public key is unavailable");
46982
47436
  const secret = x25519.utils.randomSecretKey();
@@ -46991,7 +47445,7 @@ class DepthClient {
46991
47445
  ...kind === "balance" ? {} : { grant: null },
46992
47446
  view
46993
47447
  }).rpc({ commitment: "confirmed" });
46994
- return new DepthView(view, signature, kind, this.options.wallet.publicKey, secret, publicKey, mxePublicKey, this.program, this.options.connection);
47448
+ return new DepthView(view, signature, kind, this.options.wallet.publicKey, secret, publicKey, mxePublicKey, this.program, this.options.connection, source);
46995
47449
  }
46996
47450
  async ensureBalanceReady() {
46997
47451
  const address2 = this.funding.balance;
@@ -47078,7 +47532,7 @@ class DepthClient {
47078
47532
  return this.funding.release(amount, clientNonce ?? BigInt(nonce().toString()));
47079
47533
  }
47080
47534
  async depositRequest(sourceOrAmount, amount, clientNonce) {
47081
- if (sourceOrAmount instanceof PublicKey9) {
47535
+ if (sourceOrAmount instanceof PublicKey11) {
47082
47536
  if (amount === undefined || clientNonce === undefined)
47083
47537
  throw new RangeError("Source deposits require amount and client nonce");
47084
47538
  return this.funding.deposit(sourceOrAmount, amount, clientNonce);
@@ -47089,7 +47543,7 @@ class DepthClient {
47089
47543
  return this.funding.deposit(source, sourceOrAmount, amount ?? BigInt(nonce().toString()));
47090
47544
  }
47091
47545
  async withdrawRequest(destinationOrAmount, amount, clientNonce) {
47092
- if (destinationOrAmount instanceof PublicKey9) {
47546
+ if (destinationOrAmount instanceof PublicKey11) {
47093
47547
  if (amount === undefined || clientNonce === undefined)
47094
47548
  throw new RangeError("Destination withdrawals require amount and client nonce");
47095
47549
  return this.funding.withdraw(destinationOrAmount, amount, clientNonce);
@@ -47124,11 +47578,7 @@ class DepthClient {
47124
47578
  const state = await actions.state();
47125
47579
  if (state.status === "uninitialized")
47126
47580
  return { ...state, status: "uninitialized", amount: 0n };
47127
- const view = await this.requestBalanceView();
47128
- const result = await view.wait(options);
47129
- if (result.status !== "pending")
47130
- await view.close();
47131
- return result;
47581
+ return this.summaryView("balance", state.id, options);
47132
47582
  }
47133
47583
  };
47134
47584
  return Object.assign(() => actions, actions);
@@ -47164,29 +47614,7 @@ class DepthClient {
47164
47614
  summary: async (options = {
47165
47615
  timeoutMs: 180000
47166
47616
  }) => {
47167
- const deadline = Date.now() + options.timeoutMs;
47168
- let view;
47169
- for (;; ) {
47170
- try {
47171
- view = await this.requestMarketView();
47172
- break;
47173
- } catch (error) {
47174
- const code2 = error.error?.errorCode?.code;
47175
- if (code2 !== "WrongMarketAccountStatus" && !String(error).includes("Market account is processing another operation")) {
47176
- throw error;
47177
- }
47178
- if (Date.now() >= deadline)
47179
- throw error;
47180
- await new Promise((resolve) => setTimeout(resolve, 1000));
47181
- }
47182
- }
47183
- const result = await view.wait({
47184
- ...options,
47185
- timeoutMs: Math.max(0, deadline - Date.now())
47186
- });
47187
- if (result.status !== "pending")
47188
- await view.close();
47189
- return result;
47617
+ return this.summaryView("market", address2.toBase58(), options);
47190
47618
  }
47191
47619
  };
47192
47620
  return Object.assign((positionId) => this.positionActions(positionId), actions);
@@ -47232,6 +47660,75 @@ class DepthClient {
47232
47660
  u64(clientNonce, "clientNonce").toArrayLike(Buffer2, "le", 8)
47233
47661
  ]);
47234
47662
  }
47663
+ async deferViewClose(kind, id) {
47664
+ const view = new PublicKey11(id);
47665
+ const method = kind === "balance" ? this.program.methods.closeBalanceView : this.program.methods.closeMarketView;
47666
+ this.provider.deferClose(view, await method().accountsPartial({
47667
+ owner: this.options.wallet.publicKey,
47668
+ view
47669
+ }).instruction());
47670
+ }
47671
+ async viewState(kind, id) {
47672
+ if (kind === "balance")
47673
+ return this.account().state();
47674
+ const state = await this.read("MarketAccount", new PublicKey11(id));
47675
+ if (!state.owner.equals(this.options.wallet.publicKey) || !state.market.equals(this.marketAddress))
47676
+ throw new Error("Position does not belong to this wallet and market");
47677
+ return {
47678
+ status: enumName2(state.status),
47679
+ version: BigInt(state.version.toString())
47680
+ };
47681
+ }
47682
+ summaryView(kind, id, options) {
47683
+ const key = `${kind}:${id}`;
47684
+ const pending = this.viewReads.get(key);
47685
+ if (pending)
47686
+ return pending;
47687
+ const request = this.readSummaryView(kind, id, options).finally(() => {
47688
+ if (this.viewReads.get(key) === request)
47689
+ this.viewReads.delete(key);
47690
+ });
47691
+ this.viewReads.set(key, request);
47692
+ return request;
47693
+ }
47694
+ async readSummaryView(kind, id, options) {
47695
+ if (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 0)
47696
+ throw new RangeError("timeoutMs must be a nonnegative integer");
47697
+ const deadline = Date.now() + options.timeoutMs;
47698
+ for (;; ) {
47699
+ const state = await this.viewState(kind, id);
47700
+ if (state.status === "ready") {
47701
+ const cached = await this.viewCache.get(kind, id, state.version);
47702
+ if (cached) {
47703
+ await this.deferViewClose(kind, cached.id);
47704
+ return cached;
47705
+ }
47706
+ try {
47707
+ const view = kind === "balance" ? await this.requestBalanceView() : await this.requestMarketView();
47708
+ const result = await view.wait({
47709
+ ...options,
47710
+ timeoutMs: Math.max(0, deadline - Date.now())
47711
+ });
47712
+ if (result.status === "pending")
47713
+ return result;
47714
+ await view.deferClose();
47715
+ const latest = await this.viewState(kind, id);
47716
+ if (result.status === "ready" && latest.status === "ready" && latest.version === result.version) {
47717
+ const snapshot = { ...result, readAt: Date.now() };
47718
+ await this.viewCache.set(kind, id, snapshot);
47719
+ return snapshot;
47720
+ }
47721
+ } catch (error) {
47722
+ const code2 = error.error?.errorCode?.code;
47723
+ if (code2 !== "WrongMarketAccountStatus" && !String(error).includes("processing another operation"))
47724
+ throw error;
47725
+ }
47726
+ }
47727
+ if (Date.now() >= deadline)
47728
+ throw new Error("Private account is still processing an operation");
47729
+ await new Promise((resolve) => setTimeout(resolve, Math.min(options.pollMs ?? 1000, deadline - Date.now())));
47730
+ }
47731
+ }
47235
47732
  async submitTrade(input, intent) {
47236
47733
  const appId = this.options.appId;
47237
47734
  if (!appId)
@@ -47263,43 +47760,13 @@ class DepthClient {
47263
47760
  throw new Error("Position does not belong to this wallet and market");
47264
47761
  await this.ensureBalanceReady();
47265
47762
  await this.ensureMarketReady();
47266
- let [market, protocol, account, app] = await Promise.all([
47267
- this.market(),
47268
- this.read("Protocol", this.protocolAddress),
47269
- this.read("MarketAccount", this.accountAddress()),
47270
- this.read("AppConfig", appAddress2(appId))
47271
- ]);
47272
- const deadline = tradeDeadline(input, Math.floor(Date.now() / 1000), market.state.config.maxOrderQueueSeconds);
47763
+ let [market, protocol, account, app] = await this.waitForMarket(input, appId, intent);
47764
+ let deadline;
47273
47765
  tradeLimit(input, "long", BigInt(market.state.oraclePriceE8.toString()));
47274
- this.validateTrade(market.state, protocol, account, app, appId, intent, deadline);
47275
47766
  if (additionalMargin > 0n) {
47276
47767
  const transfer = await this.funding.allocate(additionalMargin, clientNonce);
47277
- const allocationDeadline = Date.now() + 120000;
47278
- for (;; ) {
47279
- const current = await transfer.status();
47280
- if (current.state === "settled")
47281
- break;
47282
- if (current.state === "rejected" || current.state === "refunded")
47283
- throw new Error(`Margin allocation ${transfer.id.toBase58()} ${current.state}`);
47284
- if (current.state === "escrow") {
47285
- try {
47286
- await transfer.settle();
47287
- } catch (error) {
47288
- if ((await transfer.status()).state === "escrow")
47289
- throw error;
47290
- }
47291
- }
47292
- if (Date.now() >= allocationDeadline)
47293
- throw new Error(`Margin allocation ${transfer.id.toBase58()} is pending`);
47294
- await new Promise((resolve) => setTimeout(resolve, 1000));
47295
- }
47296
- [market, protocol, account, app] = await Promise.all([
47297
- this.market(),
47298
- this.read("Protocol", this.protocolAddress),
47299
- this.read("MarketAccount", this.accountAddress()),
47300
- this.read("AppConfig", appAddress2(appId))
47301
- ]);
47302
- this.validateTrade(market.state, protocol, account, app, appId, intent, deadline);
47768
+ await transfer.confirm({ timeoutMs: 120000 });
47769
+ [market, protocol, account, app] = await this.waitForMarket(input, appId, intent);
47303
47770
  }
47304
47771
  const viewDeadline = Date.now() + 120000;
47305
47772
  let position;
@@ -47317,15 +47784,13 @@ class DepthClient {
47317
47784
  await new Promise((resolve) => setTimeout(resolve, 1000));
47318
47785
  continue;
47319
47786
  }
47320
- [market, account] = await Promise.all([
47321
- this.market(),
47322
- this.read("MarketAccount", this.accountAddress())
47323
- ]);
47787
+ [market, protocol, account, app] = await this.waitForMarket(input, appId, intent);
47324
47788
  if (position.status === "ready" && enumName2(account.status) === "ready" && account.version.toString() === position.version.toString()) {
47325
47789
  break;
47326
47790
  }
47327
47791
  await new Promise((resolve) => setTimeout(resolve, 1000));
47328
47792
  }
47793
+ deadline = tradeDeadline(input, Math.floor(Date.now() / 1000), market.state.config.maxOrderQueueSeconds);
47329
47794
  this.validateTrade(market.state, protocol, account, app, appId, intent, deadline);
47330
47795
  if (position.status !== "ready" || position.collateral === undefined || position.position === undefined)
47331
47796
  throw new Error(`Private position view ${position.id} is ${position.status}`);
@@ -47350,11 +47815,11 @@ class DepthClient {
47350
47815
  const keys = [market.state.backstopOffer, ...quotes];
47351
47816
  const records = await this.options.connection.getMultipleAccountsInfo(keys, "confirmed");
47352
47817
  records.forEach((info, index) => {
47353
- if (!info?.owner.equals(DEPTH_PROGRAM_ID7)) {
47818
+ if (!info?.owner.equals(DEPTH_PROGRAM_ID8)) {
47354
47819
  throw new Error(`Market quote snapshot missing account ${index}`);
47355
47820
  }
47356
47821
  });
47357
- const mxePublicKey = await getMXEPublicKey(this.provider, DEPTH_PROGRAM_ID7);
47822
+ const mxePublicKey = await getMXEPublicKey(this.provider, DEPTH_PROGRAM_ID8);
47358
47823
  if (!mxePublicKey)
47359
47824
  throw new Error("MXE public key is unavailable");
47360
47825
  const secret = x25519.utils.randomSecretKey();
@@ -47372,7 +47837,7 @@ class DepthClient {
47372
47837
  }), inputNonce);
47373
47838
  const offset2 = nonce();
47374
47839
  const queue = await this.queue("match_order", offset2);
47375
- const signature = await this.program.methods.submitOrder(offset2, orderNonce, appId, app.feeBps, app.version, new anchor8.BN(deadline), market.state.revision, market.state.oracleTimestamp, ciphertexts, Array.from(inputKey), new anchor8.BN(inputNonce, "le")).accountsPartial({
47840
+ const signature = await this.program.methods.submitOrder(offset2, orderNonce, appId, app.feeBps, app.version, new anchor9.BN(deadline), market.state.revision, market.state.oracleTimestamp, ciphertexts, Array.from(inputKey), new anchor9.BN(inputNonce, "le")).accountsPartial({
47376
47841
  ...queue,
47377
47842
  trader: this.options.wallet.publicKey,
47378
47843
  protocol: this.protocolAddress,
@@ -47395,6 +47860,29 @@ class DepthClient {
47395
47860
  ]).rpc({ commitment: "confirmed" }).catch((error) => submissionError(order.toBase58(), error));
47396
47861
  return new DepthOperation(order, signature, this.program, this.options.connection, (id) => this.requestFillView(id), position);
47397
47862
  }
47863
+ async waitForMarket(input, appId, intent) {
47864
+ const until = Date.now() + 60000;
47865
+ for (;; ) {
47866
+ const [market, protocol, account, app] = await Promise.all([
47867
+ this.market(),
47868
+ this.read("Protocol", this.protocolAddress),
47869
+ this.read("MarketAccount", this.accountAddress()),
47870
+ this.read("AppConfig", appAddress2(appId))
47871
+ ]);
47872
+ const deadline = tradeDeadline(input, Math.floor(Date.now() / 1000), market.state.config.maxOrderQueueSeconds);
47873
+ try {
47874
+ this.validateTrade(market.state, protocol, account, app, appId, intent, deadline);
47875
+ return [market, protocol, account, app];
47876
+ } catch (error) {
47877
+ if (!(error instanceof Error) || ![
47878
+ "Waiting for maker liquidity",
47879
+ "Market is processing another order"
47880
+ ].includes(error.message) || Date.now() >= until)
47881
+ throw error;
47882
+ }
47883
+ await new Promise((resolve) => setTimeout(resolve, 1000));
47884
+ }
47885
+ }
47398
47886
  validateTrade(market, protocol, account, app, appId, intent = "increase", deadline) {
47399
47887
  const now = Math.floor(Date.now() / 1000);
47400
47888
  const marketStatus = enumName2(market.status);
@@ -47407,9 +47895,10 @@ class DepthClient {
47407
47895
  if (enumName2(account.status) !== "ready" || !account.market.equals(this.marketAddress) || !account.owner.equals(this.options.wallet.publicKey)) {
47408
47896
  throw new Error("Market account is unavailable");
47409
47897
  }
47410
- if (market.pendingOrders !== 0 || market.activeQuoteCount === 0) {
47411
- throw new Error("Market is processing another order or has no quotes");
47412
- }
47898
+ if (market.pendingOrders !== 0)
47899
+ throw new Error("Market is processing another order");
47900
+ if (market.activeQuoteCount === 0)
47901
+ throw new Error("Waiting for maker liquidity");
47413
47902
  const age = now - market.oracleTimestamp.toNumber();
47414
47903
  if (age < 0 || age > market.config.maxOracleAgeSeconds) {
47415
47904
  throw new Error("Oracle update is stale");
@@ -47572,6 +48061,7 @@ export {
47572
48061
  captureDeployment,
47573
48062
  authenticateManifest,
47574
48063
  WithdrawalHandle,
48064
+ ViewSession,
47575
48065
  TransferHandle,
47576
48066
  TradeFlow,
47577
48067
  MarketData,