@depthhq/sdk 0.1.1 → 0.1.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.
@@ -7,11 +7,12 @@ export declare class FundingClient {
7
7
  readonly connection: Connection;
8
8
  readonly owner: PublicKey;
9
9
  private readonly queue;
10
+ readonly settlement: "keeper" | "wallet";
10
11
  readonly market: PublicKey;
11
12
  readonly protocol: PublicKey;
12
13
  readonly balance: PublicKey;
13
14
  readonly marketAccount: PublicKey;
14
- constructor(program: anchor.Program, connection: Connection, owner: PublicKey, queue: Queue);
15
+ constructor(program: anchor.Program, connection: Connection, owner: PublicKey, queue: Queue, settlement?: "keeper" | "wallet");
15
16
  private assertOwner;
16
17
  read<T>(name: string, address: PublicKey): Promise<T | undefined>;
17
18
  private recoveryKey;
@@ -76,15 +76,17 @@ class FundingClient {
76
76
  connection;
77
77
  owner;
78
78
  queue;
79
+ settlement;
79
80
  market;
80
81
  protocol;
81
82
  balance;
82
83
  marketAccount;
83
- constructor(program, connection, owner, queue) {
84
+ constructor(program, connection, owner, queue, settlement = "wallet") {
84
85
  this.program = program;
85
86
  this.connection = connection;
86
87
  this.owner = owner;
87
88
  this.queue = queue;
89
+ this.settlement = settlement;
88
90
  this.market = marketAddress(program.programId);
89
91
  this.protocol = protocolAddress(program.programId);
90
92
  this.balance = balanceAddress(owner, program.programId);
@@ -418,7 +420,7 @@ class TransferHandle {
418
420
  return true;
419
421
  if (transfer.state === "rejected" || transfer.state === "refunded" || transfer.state === "cancelled")
420
422
  throw new DepthError("MARGIN_REJECTED", "confirmation", `Margin transfer ${transfer.state}`, false, "Read the receipt and private account before retrying", this.id.toBase58());
421
- if (transfer.state === "escrow") {
423
+ if (transfer.state === "escrow" && this.client.settlement === "wallet") {
422
424
  try {
423
425
  await this.settle();
424
426
  } catch (error) {
@@ -0,0 +1,9 @@
1
+ import * as anchor from "@anchor-lang/core";
2
+ import { Connection, PublicKey } from "@solana/web3.js";
3
+ export type AccountState = {
4
+ id: string;
5
+ status: "uninitialized" | "opening" | "ready" | "crediting" | "withdrawing" | "moving" | "claiming";
6
+ version: bigint;
7
+ slot: number;
8
+ };
9
+ export declare function balanceState(program: anchor.Program, connection: Connection, owner: PublicKey, address: PublicKey): Promise<AccountState>;
@@ -18,3 +18,4 @@ export type ResolvedConnection = {
18
18
  environment?: DepthEnvironment;
19
19
  };
20
20
  export declare function resolveConnection(options: ConnectionOptions): Promise<ResolvedConnection>;
21
+ export declare function prepare_depth(options: ConnectionOptions): Promise<ResolvedConnection>;
@@ -32,5 +32,5 @@ export type DeploymentPolicy = {
32
32
  };
33
33
  export declare function manifestBytes(value: unknown): Buffer;
34
34
  export declare function authenticateManifest(signed: SignedManifest, trusted: PublicKey): Promise<void>;
35
- export declare function captureDeployment(provider: anchor.AnchorProvider, version: number, expiresAt: number): Promise<DeploymentManifest>;
36
- export declare function verifyDeployment(provider: anchor.AnchorProvider, policy: DeploymentPolicy): Promise<void>;
35
+ export declare function captureDeployment(provider: Pick<anchor.Provider, "connection">, version: number, expiresAt: number): Promise<DeploymentManifest>;
36
+ export declare function verifyDeployment(provider: Pick<anchor.Provider, "connection">, policy: DeploymentPolicy): Promise<void>;
@@ -1,6 +1,9 @@
1
1
  import * as anchor from "@anchor-lang/core";
2
- import { Transaction } from "@solana/web3.js";
2
+ import { PublicKey, Transaction, TransactionInstruction } from "@solana/web3.js";
3
3
  export declare function applyDepthComputeBudget(transaction: Transaction): void;
4
4
  export declare class DepthProvider extends anchor.AnchorProvider {
5
+ private readonly cleanup;
6
+ deferClose(id: PublicKey, instruction: TransactionInstruction): void;
7
+ appendCleanup(transaction: Transaction): Promise<string[]>;
5
8
  sendAndConfirm(transaction: Parameters<anchor.AnchorProvider["sendAndConfirm"]>[0], signers?: Parameters<anchor.AnchorProvider["sendAndConfirm"]>[1], options?: Parameters<anchor.AnchorProvider["sendAndConfirm"]>[2]): Promise<string>;
6
9
  }
@@ -1,6 +1,7 @@
1
1
  import * as anchor from "@anchor-lang/core";
2
2
  import { SOL_USD_MARKET_ID } from "@depthhq/bindings";
3
3
  import { PublicKey } from "@solana/web3.js";
4
+ import { DepthProvider } from "../chain/provider.js";
4
5
  import { AppAdmin } from "../app/app.js";
5
6
  import { MarketCatalog } from "../catalog/catalog.js";
6
7
  import { FundingDiscovery } from "../account/discovery.js";
@@ -16,7 +17,9 @@ import type { CloseInput, DepthOptions, ResolvedDepthOptions, MarketState, Reduc
16
17
  export declare class DepthClient {
17
18
  readonly options: ResolvedDepthOptions;
18
19
  readonly program: anchor.Program;
19
- readonly provider: anchor.AnchorProvider;
20
+ readonly provider: DepthProvider;
21
+ private readonly views;
22
+ private readonly viewReads;
20
23
  readonly position: ReturnType<DepthClient["positionActions"]>;
21
24
  readonly marketAddress: anchor.web3.PublicKey;
22
25
  readonly protocolAddress: anchor.web3.PublicKey;
@@ -51,6 +54,7 @@ export declare class DepthClient {
51
54
  private withdrawRequest;
52
55
  private accountActions;
53
56
  readonly account: (() => {
57
+ state: () => Promise<import("../account/state.js").AccountState>;
54
58
  detail: (item: Parameters<FundingDiscovery["detail"]>[0]) => Promise<import("../account/discovery.js").ReceiptDetail>;
55
59
  transaction: (item: Parameters<FundingDiscovery["transaction"]>[0]) => Promise<import("../account/discovery.js").ReceiptDetail>;
56
60
  history: (options?: Parameters<FundingDiscovery["history"]>[0]) => Promise<import("../account/discovery.js").PendingPage>;
@@ -78,6 +82,7 @@ export declare class DepthClient {
78
82
  pollMs?: number;
79
83
  }) => Promise<BalanceSnapshot>;
80
84
  }) & {
85
+ state: () => Promise<import("../account/state.js").AccountState>;
81
86
  detail: (item: Parameters<FundingDiscovery["detail"]>[0]) => Promise<import("../account/discovery.js").ReceiptDetail>;
82
87
  transaction: (item: Parameters<FundingDiscovery["transaction"]>[0]) => Promise<import("../account/discovery.js").ReceiptDetail>;
83
88
  history: (options?: Parameters<FundingDiscovery["history"]>[0]) => Promise<import("../account/discovery.js").PendingPage>;
@@ -131,7 +136,12 @@ export declare class DepthClient {
131
136
  }>;
132
137
  };
133
138
  private orderAddress;
139
+ private deferViewClose;
140
+ private viewState;
141
+ private summaryView;
142
+ private readSummaryView;
134
143
  private submitTrade;
144
+ private waitForMarket;
135
145
  private validateTrade;
136
146
  }
137
147
  export declare function createDepth(options: DepthOptions): Promise<DepthClient>;
@@ -11,10 +11,14 @@ export type DepthWallet = {
11
11
  export type DepthOptions = ConnectionOptions & {
12
12
  appId?: PublicKey | string;
13
13
  wallet: DepthWallet;
14
+ privateViews?: "memory" | "device";
15
+ settlement?: "keeper" | "wallet";
14
16
  };
15
17
  export type ResolvedDepthOptions = ResolvedConnection & {
16
18
  appId?: PublicKey;
17
19
  wallet: DepthWallet;
20
+ privateViews?: "memory" | "device";
21
+ settlement?: "keeper" | "wallet";
18
22
  };
19
23
  export type TradeTerms = {
20
24
  marketId: typeof SOL_USD_MARKET_ID;
package/dist/index.d.ts CHANGED
@@ -26,7 +26,8 @@ export { resolveAmount } from "./trade/amount.js";
26
26
  export type { AmountSelector } from "./trade/amount.js";
27
27
  export type { BalanceSnapshot, FillSnapshot, PositionSnapshot, QuoteBalanceSnapshot, } from "./view/view.js";
28
28
  export type { DepthOptions, DepthWallet, TradeInput, ReductionInput, CloseInput, ReverseInput, TradeIntent, } from "./client/types.js";
29
- export { resolveConnection } from "./chain/environment.js";
29
+ export { resolveConnection, prepare_depth } from "./chain/environment.js";
30
+ export type { AccountState } from "./account/state.js";
30
31
  export type { ConnectionOptions, ResolvedConnection, DepthEnvironment, } from "./chain/environment.js";
31
32
  export { DepthError } from "./operation/error.js";
32
33
  export { DepthProvider } from "./chain/provider.js";
package/dist/index.js CHANGED
@@ -43628,7 +43628,7 @@ import {
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 PublicKey10 } from "@solana/web3.js";
43632
43632
 
43633
43633
  // src/chain/random.ts
43634
43634
  function randomBytes3(length) {
@@ -44491,7 +44491,7 @@ async function captureDeployment(provider, version, expiresAt) {
44491
44491
  const protocol = coder.decode("protocol", protocolInfo.data);
44492
44492
  if (!market.settlementMint.equals(protocol.settlementMint))
44493
44493
  throw new Error("Deployment settlement mints disagree");
44494
- const arcium2 = getArciumProgram(provider);
44494
+ const arcium2 = new anchor2.Program(ARCIUM_IDL, provider);
44495
44495
  const mxeAddress = getMXEAccAddress(DEPTH_PROGRAM_ID);
44496
44496
  const mxe = await arcium2.account.mxeAccount.fetch(mxeAddress);
44497
44497
  if (!mxe.mxeProgramId.equals(DEPTH_PROGRAM_ID))
@@ -44521,6 +44521,7 @@ async function captureDeployment(provider, version, expiresAt) {
44521
44521
  })
44522
44522
  };
44523
44523
  }
44524
+ var verified = new WeakMap;
44524
44525
  async function verifyDeployment(provider, policy) {
44525
44526
  if ("localnet" in policy) {
44526
44527
  const url = new URL(provider.connection.rpcEndpoint);
@@ -44530,9 +44531,28 @@ async function verifyDeployment(provider, policy) {
44530
44531
  }
44531
44532
  await authenticateManifest(policy.manifest, policy.trustedSigner);
44532
44533
  const expected = policy.manifest.payload;
44533
- const actual = await captureDeployment(provider, expected.version, expected.expiresAt);
44534
- if (!manifestBytes(actual).equals(manifestBytes(expected)))
44535
- throw new Error("Signed deployment does not match the connected chain");
44534
+ const key = manifestBytes(policy.manifest).toString("base64");
44535
+ const previous = verified.get(provider.connection);
44536
+ if (previous?.key === key && previous.expiresAt > Date.now())
44537
+ return previous.task;
44538
+ const task = (async () => {
44539
+ const actual = await captureDeployment(provider, expected.version, expected.expiresAt);
44540
+ if (!manifestBytes(actual).equals(manifestBytes(expected)))
44541
+ throw new Error("Signed deployment does not match the connected chain");
44542
+ })();
44543
+ const entry = {
44544
+ key,
44545
+ expiresAt: Math.min(Date.now() + 30000, expected.expiresAt * 1000),
44546
+ task
44547
+ };
44548
+ verified.set(provider.connection, entry);
44549
+ try {
44550
+ await task;
44551
+ } catch (error) {
44552
+ if (verified.get(provider.connection) === entry)
44553
+ verified.delete(provider.connection);
44554
+ throw error;
44555
+ }
44536
44556
  }
44537
44557
 
44538
44558
  // src/chain/network.ts
@@ -44687,6 +44707,11 @@ async function resolveConnection(options) {
44687
44707
  throw unavailable("Depth deployment could not be verified", error);
44688
44708
  }
44689
44709
  }
44710
+ async function prepare_depth(options) {
44711
+ const resolved = await resolveConnection(options);
44712
+ await verifyDeployment({ connection: resolved.connection }, resolved.deployment);
44713
+ return resolved;
44714
+ }
44690
44715
 
44691
44716
  // src/chain/arcium.ts
44692
44717
  function arciumAccount(provider) {
@@ -44701,6 +44726,7 @@ import { DEPTH_IDL as DEPTH_IDL2, DEPTH_PROGRAM_ID as DEPTH_PROGRAM_ID3 } from "
44701
44726
  import {
44702
44727
  ComputeBudgetProgram,
44703
44728
  SendTransactionError,
44729
+ PublicKey as PublicKey4,
44704
44730
  Transaction
44705
44731
  } from "@solana/web3.js";
44706
44732
  var queuedInstructions = new Set([
@@ -44743,11 +44769,52 @@ function applyDepthComputeBudget(transaction) {
44743
44769
  }
44744
44770
 
44745
44771
  class DepthProvider extends anchor3.AnchorProvider {
44772
+ cleanup = new Map;
44773
+ deferClose(id, instruction) {
44774
+ this.cleanup.set(id.toBase58(), instruction);
44775
+ }
44776
+ async appendCleanup(transaction) {
44777
+ if (!this.cleanup.size || transaction.signatures.some((item) => item.signature))
44778
+ return [];
44779
+ const entries = [...this.cleanup.entries()].slice(0, 64);
44780
+ const queued = new Set(entries.map(([, instruction]) => instruction));
44781
+ transaction.instructions = transaction.instructions.filter((instruction) => !queued.has(instruction));
44782
+ const accounts2 = await this.connection.getMultipleAccountsInfo(entries.map(([id]) => new PublicKey4(id)), "confirmed");
44783
+ const added = [];
44784
+ for (let index = 0;index < entries.length; index++) {
44785
+ const [id, instruction] = entries[index];
44786
+ if (!accounts2[index]?.owner.equals(DEPTH_PROGRAM_ID3)) {
44787
+ this.cleanup.delete(id);
44788
+ continue;
44789
+ }
44790
+ const candidate = new Transaction({
44791
+ feePayer: transaction.feePayer ?? this.wallet.publicKey,
44792
+ recentBlockhash: transaction.recentBlockhash ?? PublicKey4.default.toBase58()
44793
+ }).add(...transaction.instructions, instruction);
44794
+ try {
44795
+ candidate.serialize({
44796
+ requireAllSignatures: false,
44797
+ verifySignatures: false
44798
+ });
44799
+ } catch {
44800
+ continue;
44801
+ }
44802
+ transaction.add(instruction);
44803
+ added.push(id);
44804
+ }
44805
+ return added;
44806
+ }
44746
44807
  async sendAndConfirm(transaction, signers, options) {
44747
- if (transaction instanceof Transaction)
44808
+ let cleanup = [];
44809
+ if (transaction instanceof Transaction) {
44748
44810
  applyDepthComputeBudget(transaction);
44811
+ cleanup = await this.appendCleanup(transaction);
44812
+ }
44749
44813
  try {
44750
- return await super.sendAndConfirm(transaction, signers, options);
44814
+ const signature = await super.sendAndConfirm(transaction, signers, options);
44815
+ for (const id of cleanup)
44816
+ this.cleanup.delete(id);
44817
+ return signature;
44751
44818
  } catch (error) {
44752
44819
  if (!(error instanceof Error) || !error.message.includes("Unknown action 'undefined'"))
44753
44820
  throw error;
@@ -44998,7 +45065,7 @@ import {
44998
45065
  transferAddress,
44999
45066
  withdrawalAddress
45000
45067
  } from "@depthhq/bindings";
45001
- import { PublicKey as PublicKey4 } from "@solana/web3.js";
45068
+ import { PublicKey as PublicKey5 } from "@solana/web3.js";
45002
45069
 
45003
45070
  // src/chain/transaction.ts
45004
45071
  init_buffer();
@@ -45156,7 +45223,7 @@ class FundingDiscovery {
45156
45223
  const limit = options.limit ?? 20;
45157
45224
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100)
45158
45225
  throw new RangeError("limit must be an integer from 1 to 100");
45159
- const cursor = options.cursor ? new PublicKey4(options.cursor).toBase58() : undefined;
45226
+ const cursor = options.cursor ? new PublicKey5(options.cursor).toBase58() : undefined;
45160
45227
  const accounts2 = await this.connection.getProgramAccounts(this.program.programId, {
45161
45228
  commitment: "confirmed",
45162
45229
  dataSlice: { offset: 0, length: 0 },
@@ -45188,7 +45255,7 @@ class FundingDiscovery {
45188
45255
  const current = status(state);
45189
45256
  if (!state.toMarket || current !== "settled")
45190
45257
  return;
45191
- const orderId = PublicKey4.findProgramAddressSync([
45258
+ const orderId = PublicKey5.findProgramAddressSync([
45192
45259
  Buffer2.from("order"),
45193
45260
  state.market.toBuffer(),
45194
45261
  this.owner.toBuffer(),
@@ -45235,7 +45302,7 @@ class FundingDiscovery {
45235
45302
  const limit = options.limit ?? 20;
45236
45303
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100)
45237
45304
  throw new RangeError("limit must be an integer from 1 to 100");
45238
- const cursor = options.cursor ? new PublicKey4(options.cursor).toBase58() : undefined;
45305
+ const cursor = options.cursor ? new PublicKey5(options.cursor).toBase58() : undefined;
45239
45306
  const types3 = [
45240
45307
  {
45241
45308
  name: "depositReceipt",
@@ -45326,7 +45393,7 @@ class FundingDiscovery {
45326
45393
  throw new Error("Order changed during discovery");
45327
45394
  const bytes = Buffer2.alloc(8);
45328
45395
  bytes.writeBigUInt64LE(nonce);
45329
- canonical = PublicKey4.findProgramAddressSync([
45396
+ canonical = PublicKey5.findProgramAddressSync([
45330
45397
  Buffer2.from("order"),
45331
45398
  state.market.toBuffer(),
45332
45399
  this.owner.toBuffer(),
@@ -45412,15 +45479,17 @@ class FundingClient {
45412
45479
  connection;
45413
45480
  owner;
45414
45481
  queue;
45482
+ settlement;
45415
45483
  market;
45416
45484
  protocol;
45417
45485
  balance;
45418
45486
  marketAccount;
45419
- constructor(program, connection, owner, queue) {
45487
+ constructor(program, connection, owner, queue, settlement = "wallet") {
45420
45488
  this.program = program;
45421
45489
  this.connection = connection;
45422
45490
  this.owner = owner;
45423
45491
  this.queue = queue;
45492
+ this.settlement = settlement;
45424
45493
  this.market = marketAddress3(program.programId);
45425
45494
  this.protocol = protocolAddress2(program.programId);
45426
45495
  this.balance = balanceAddress(owner, program.programId);
@@ -45754,7 +45823,7 @@ class TransferHandle {
45754
45823
  return true;
45755
45824
  if (transfer.state === "rejected" || transfer.state === "refunded" || transfer.state === "cancelled")
45756
45825
  throw new DepthError("MARGIN_REJECTED", "confirmation", `Margin transfer ${transfer.state}`, false, "Read the receipt and private account before retrying", this.id.toBase58());
45757
- if (transfer.state === "escrow") {
45826
+ if (transfer.state === "escrow" && this.client.settlement === "wallet") {
45758
45827
  try {
45759
45828
  await this.settle();
45760
45829
  } catch (error) {
@@ -45827,18 +45896,46 @@ class WithdrawalHandle {
45827
45896
  }
45828
45897
  }
45829
45898
 
45899
+ // src/account/state.ts
45900
+ async function balanceState(program, connection, owner, address2) {
45901
+ const { context, value: value2 } = await connection.getAccountInfoAndContext(address2, "confirmed");
45902
+ const base = { id: address2.toBase58(), slot: context.slot };
45903
+ if (!value2)
45904
+ return { ...base, status: "uninitialized", version: 0n };
45905
+ if (!value2.owner.equals(program.programId))
45906
+ throw new Error("Balance account is not owned by Depth");
45907
+ const state = program.coder.accounts.decode("balanceAccount", value2.data);
45908
+ if (!state.owner.equals(owner))
45909
+ throw new Error("Balance account does not belong to this wallet");
45910
+ const status3 = Object.keys(state.status)[0];
45911
+ if (![
45912
+ "opening",
45913
+ "ready",
45914
+ "crediting",
45915
+ "withdrawing",
45916
+ "moving",
45917
+ "claiming"
45918
+ ].includes(status3))
45919
+ throw new Error("Balance account has an invalid status");
45920
+ return {
45921
+ ...base,
45922
+ status: status3,
45923
+ version: BigInt(state.version.toString())
45924
+ };
45925
+ }
45926
+
45830
45927
  // src/account/token.ts
45831
45928
  init_buffer();
45832
45929
  import {
45833
- PublicKey as PublicKey5,
45930
+ PublicKey as PublicKey6,
45834
45931
  SystemProgram,
45835
45932
  Transaction as Transaction2,
45836
- TransactionInstruction
45933
+ TransactionInstruction as TransactionInstruction2
45837
45934
  } from "@solana/web3.js";
45838
- var tokenProgram = new PublicKey5("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
45839
- var associatedProgram = new PublicKey5("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
45935
+ var tokenProgram = new PublicKey6("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
45936
+ var associatedProgram = new PublicKey6("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
45840
45937
  function associatedTokenAddress(owner, mint) {
45841
- return PublicKey5.findProgramAddressSync([owner.toBuffer(), tokenProgram.toBuffer(), mint.toBuffer()], associatedProgram)[0];
45938
+ return PublicKey6.findProgramAddressSync([owner.toBuffer(), tokenProgram.toBuffer(), mint.toBuffer()], associatedProgram)[0];
45842
45939
  }
45843
45940
  function tokenBalance(info, owner, mint) {
45844
45941
  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)
@@ -45867,7 +45964,7 @@ async function destinationTokenAccount(provider, owner, mint) {
45867
45964
  const address2 = associatedTokenAddress(owner, mint);
45868
45965
  let info = await provider.connection.getAccountInfo(address2, "confirmed");
45869
45966
  if (!info) {
45870
- const instruction = new TransactionInstruction({
45967
+ const instruction = new TransactionInstruction2({
45871
45968
  programId: associatedProgram,
45872
45969
  data: Buffer2.from([1]),
45873
45970
  keys: [
@@ -45909,7 +46006,7 @@ import {
45909
46006
  marketAddress as marketAddress4,
45910
46007
  SOL_USD_MARKET_ID as SOL_USD_MARKET_ID2
45911
46008
  } from "@depthhq/bindings";
45912
- import { PublicKey as PublicKey6 } from "@solana/web3.js";
46009
+ import { PublicKey as PublicKey7 } from "@solana/web3.js";
45913
46010
 
45914
46011
  // src/position/pnl.ts
45915
46012
  var scale = 100000000000n;
@@ -46139,11 +46236,11 @@ class DepthOperation {
46139
46236
  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());
46140
46237
  this.fill = {
46141
46238
  ...result,
46142
- positionId: marketAccountAddress2(new PublicKey6(snapshot.owner), this.program.programId)
46239
+ positionId: marketAccountAddress2(new PublicKey7(snapshot.owner), this.program.programId)
46143
46240
  };
46144
46241
  }
46145
46242
  try {
46146
- await this.view.close();
46243
+ await this.view.deferClose();
46147
46244
  } catch (error) {
46148
46245
  let remaining;
46149
46246
  try {
@@ -46159,7 +46256,7 @@ class DepthOperation {
46159
46256
  return this.fill;
46160
46257
  }
46161
46258
  cleanupError(cause) {
46162
- 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 });
46259
+ 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 });
46163
46260
  }
46164
46261
  }
46165
46262
 
@@ -46170,22 +46267,22 @@ import {
46170
46267
  marketAddress as marketAddress5,
46171
46268
  protocolAddress as protocolAddress3
46172
46269
  } from "@depthhq/bindings";
46173
- import { PublicKey as PublicKey7 } from "@solana/web3.js";
46270
+ import { PublicKey as PublicKey8 } from "@solana/web3.js";
46174
46271
 
46175
46272
  class DepthRecovery {
46176
46273
  program;
46177
46274
  connection;
46178
46275
  owner;
46179
46276
  market = marketAddress5();
46180
- winddown = PublicKey7.findProgramAddressSync([Buffer2.from("winddown"), this.market.toBuffer()], DEPTH_PROGRAM_ID5)[0];
46277
+ winddown = PublicKey8.findProgramAddressSync([Buffer2.from("winddown"), this.market.toBuffer()], DEPTH_PROGRAM_ID5)[0];
46181
46278
  marketAccount;
46182
46279
  claim;
46183
46280
  constructor(program, connection, owner) {
46184
46281
  this.program = program;
46185
46282
  this.connection = connection;
46186
46283
  this.owner = owner;
46187
- this.marketAccount = PublicKey7.findProgramAddressSync([Buffer2.from("market-account"), this.market.toBuffer(), owner.toBuffer()], DEPTH_PROGRAM_ID5)[0];
46188
- this.claim = PublicKey7.findProgramAddressSync([Buffer2.from("winddown-claim"), this.marketAccount.toBuffer()], DEPTH_PROGRAM_ID5)[0];
46284
+ this.marketAccount = PublicKey8.findProgramAddressSync([Buffer2.from("market-account"), this.market.toBuffer(), owner.toBuffer()], DEPTH_PROGRAM_ID5)[0];
46285
+ this.claim = PublicKey8.findProgramAddressSync([Buffer2.from("winddown-claim"), this.marketAccount.toBuffer()], DEPTH_PROGRAM_ID5)[0];
46189
46286
  }
46190
46287
  async read(address2, name2) {
46191
46288
  const result = await this.connection.getAccountInfoAndContext(address2, "confirmed");
@@ -46275,7 +46372,7 @@ import {
46275
46372
  marketAddress as marketAddress6,
46276
46373
  transferAddress as transferAddress3
46277
46374
  } from "@depthhq/bindings";
46278
- import { PublicKey as PublicKey8 } from "@solana/web3.js";
46375
+ import { PublicKey as PublicKey9 } from "@solana/web3.js";
46279
46376
 
46280
46377
  // src/history/replay.ts
46281
46378
  function replayFill(state, fill, fundingIndex, maker = false) {
@@ -46354,7 +46451,7 @@ class ExecutionHistory {
46354
46451
  this.requestView = requestView;
46355
46452
  }
46356
46453
  orderAddress(trader, nonce) {
46357
- return PublicKey8.findProgramAddressSync([
46454
+ return PublicKey9.findProgramAddressSync([
46358
46455
  Buffer2.from("order"),
46359
46456
  marketAddress6(this.program.programId).toBuffer(),
46360
46457
  trader.toBuffer(),
@@ -46376,7 +46473,7 @@ class ExecutionHistory {
46376
46473
  }
46377
46474
  const result = this.fills.get(key) ?? await view.wait({ timeoutMs: 180000 });
46378
46475
  if (result.status === "stale") {
46379
- await view.close();
46476
+ await view.deferClose();
46380
46477
  this.views.delete(key);
46381
46478
  throw new Error("Historical private execution changed; retry with the same wallet");
46382
46479
  }
@@ -46384,7 +46481,7 @@ class ExecutionHistory {
46384
46481
  throw new Error("Historical private execution is still pending; retry with the same wallet");
46385
46482
  this.fills.set(key, result);
46386
46483
  try {
46387
- await view.close();
46484
+ await view.deferClose();
46388
46485
  } catch (error) {
46389
46486
  if (await this.connection.getAccountInfo(view.id, "confirmed"))
46390
46487
  throw error;
@@ -46752,19 +46849,151 @@ class DepthView {
46752
46849
  await new Promise((resolve) => setTimeout(resolve, Math.min(pollMs, deadline - Date.now())));
46753
46850
  }
46754
46851
  }
46852
+ async deferClose() {
46853
+ const provider = this.program.provider;
46854
+ if (typeof provider.deferClose !== "function") {
46855
+ await this.close();
46856
+ return;
46857
+ }
46858
+ const snapshot = await this.status();
46859
+ if (snapshot.status === "pending")
46860
+ throw new Error("Private view computation is still pending");
46861
+ const instruction = await this.closeBuilder().instruction();
46862
+ provider.deferClose(this.id, instruction);
46863
+ }
46864
+ closeBuilder() {
46865
+ 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();
46866
+ return builder.accountsPartial({ owner: this.owner, view: this.id });
46867
+ }
46755
46868
  async close() {
46756
46869
  const snapshot = await this.status();
46757
46870
  if (snapshot.status === "pending") {
46758
46871
  throw new Error("Private view computation is still pending");
46759
46872
  }
46760
- 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();
46761
- return builder.accountsPartial({ owner: this.owner, view: this.id }).rpc({ commitment: "confirmed" });
46873
+ return this.closeBuilder().rpc({ commitment: "confirmed" });
46874
+ }
46875
+ }
46876
+
46877
+ // src/view/cache.ts
46878
+ class DeviceViews {
46879
+ database;
46880
+ open() {
46881
+ return this.database ??= new Promise((resolve, reject) => {
46882
+ const request = indexedDB.open("depth-private-views", 1);
46883
+ request.onupgradeneeded = () => {
46884
+ request.result.createObjectStore("keys");
46885
+ request.result.createObjectStore("views");
46886
+ };
46887
+ request.onerror = () => reject(request.error);
46888
+ request.onblocked = () => reject(new Error("Private view storage is blocked"));
46889
+ request.onsuccess = () => resolve(request.result);
46890
+ });
46891
+ }
46892
+ async key(scope, proposed) {
46893
+ const database = await this.open();
46894
+ return new Promise((resolve, reject) => {
46895
+ const transaction = database.transaction("keys", "readwrite");
46896
+ const store = transaction.objectStore("keys");
46897
+ const request = store.get(scope);
46898
+ let value2;
46899
+ request.onsuccess = () => {
46900
+ value2 = request.result ?? proposed;
46901
+ if (!request.result)
46902
+ store.put(value2, scope);
46903
+ };
46904
+ transaction.oncomplete = () => resolve(value2);
46905
+ transaction.onabort = () => reject(transaction.error);
46906
+ transaction.onerror = () => reject(transaction.error);
46907
+ });
46908
+ }
46909
+ async read(id) {
46910
+ const database = await this.open();
46911
+ return new Promise((resolve, reject) => {
46912
+ const transaction = database.transaction("views", "readonly");
46913
+ const request = transaction.objectStore("views").get(id);
46914
+ transaction.oncomplete = () => resolve(request.result);
46915
+ transaction.onabort = () => reject(transaction.error);
46916
+ transaction.onerror = () => reject(transaction.error);
46917
+ });
46918
+ }
46919
+ async write(id, value2) {
46920
+ const database = await this.open();
46921
+ return new Promise((resolve, reject) => {
46922
+ const transaction = database.transaction("views", "readwrite");
46923
+ transaction.objectStore("views").put(value2, id);
46924
+ transaction.oncomplete = () => resolve();
46925
+ transaction.onabort = () => reject(transaction.error);
46926
+ transaction.onerror = () => reject(transaction.error);
46927
+ });
46928
+ }
46929
+ }
46930
+
46931
+ class ViewCache {
46932
+ scope;
46933
+ storage;
46934
+ memory = new Map;
46935
+ key;
46936
+ constructor(scope, storage) {
46937
+ this.scope = scope;
46938
+ this.storage = storage;
46939
+ }
46940
+ encryptionKey() {
46941
+ return this.key ??= crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, [
46942
+ "encrypt",
46943
+ "decrypt"
46944
+ ]).then((key) => this.storage.key(this.scope, key));
46945
+ }
46946
+ id(kind, account) {
46947
+ return `${this.scope}:${kind}:${account}`;
46948
+ }
46949
+ async get(kind, account, version) {
46950
+ const id = this.id(kind, account);
46951
+ let snapshot = this.memory.get(id);
46952
+ if (!snapshot && this.storage) {
46953
+ try {
46954
+ const envelope = await this.storage.read(id);
46955
+ if (envelope) {
46956
+ const bytes = await crypto.subtle.decrypt({
46957
+ name: "AES-GCM",
46958
+ iv: envelope.iv,
46959
+ additionalData: new TextEncoder().encode(id)
46960
+ }, await this.encryptionKey(), envelope.data);
46961
+ snapshot = JSON.parse(new TextDecoder().decode(bytes), (_, value2) => value2 && typeof value2 === "object" && typeof value2.$bigint === "string" ? BigInt(value2.$bigint) : value2);
46962
+ }
46963
+ } catch {
46964
+ return;
46965
+ }
46966
+ }
46967
+ if (!snapshot || snapshot.status !== "ready" || snapshot.version !== version || typeof snapshot.id !== "string" || !Number.isSafeInteger(snapshot.slot) || (kind === "balance" ? typeof snapshot.amount !== "bigint" : [
46968
+ "collateral",
46969
+ "reserved",
46970
+ "position",
46971
+ "entryPriceE8",
46972
+ "fundingCheckpoint"
46973
+ ].some((field) => typeof snapshot[field] !== "bigint")))
46974
+ return;
46975
+ this.memory.set(id, snapshot);
46976
+ return { ...snapshot };
46977
+ }
46978
+ async set(kind, account, snapshot) {
46979
+ if (snapshot.status !== "ready")
46980
+ return;
46981
+ const id = this.id(kind, account);
46982
+ this.memory.set(id, { ...snapshot });
46983
+ if (!this.storage)
46984
+ return;
46985
+ try {
46986
+ const iv = crypto.getRandomValues(new Uint8Array(12));
46987
+ const bytes = new TextEncoder().encode(JSON.stringify(snapshot, (_, value2) => typeof value2 === "bigint" ? { $bigint: value2.toString() } : value2));
46988
+ const data = await crypto.subtle.encrypt({ name: "AES-GCM", iv, additionalData: new TextEncoder().encode(id) }, await this.encryptionKey(), bytes);
46989
+ await this.storage.write(id, { iv, data });
46990
+ } catch {}
46762
46991
  }
46763
46992
  }
46764
46993
 
46765
46994
  // src/client/client.ts
46766
46995
  function pda(seeds, programId = DEPTH_PROGRAM_ID7) {
46767
- return PublicKey9.findProgramAddressSync(seeds, programId)[0];
46996
+ return PublicKey10.findProgramAddressSync(seeds, programId)[0];
46768
46997
  }
46769
46998
  function nonce() {
46770
46999
  return new anchor8.BN(randomBytes3(8), "le");
@@ -46777,6 +47006,8 @@ class DepthClient {
46777
47006
  options;
46778
47007
  program;
46779
47008
  provider;
47009
+ views;
47010
+ viewReads = new Map;
46780
47011
  position;
46781
47012
  marketAddress = marketAddress7();
46782
47013
  protocolAddress = protocolAddress4();
@@ -46785,7 +47016,7 @@ class DepthClient {
46785
47016
  return new MarketCatalog(this);
46786
47017
  }
46787
47018
  get funding() {
46788
- return new FundingClient(this.program, this.options.connection, this.options.wallet.publicKey, (name2, offset2) => this.queue(name2, offset2));
47019
+ 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"));
46789
47020
  }
46790
47021
  get app() {
46791
47022
  return new AppAdmin(this.program, this.options.connection, this.options.wallet.publicKey, this.funding);
@@ -46799,6 +47030,7 @@ class DepthClient {
46799
47030
  }
46800
47031
  constructor(options) {
46801
47032
  this.options = options;
47033
+ this.views = new ViewCache(`${"manifest" in options.deployment ? options.deployment.manifest.payload.genesisHash : "localnet"}:${DEPTH_PROGRAM_ID7.toBase58()}:${options.wallet.publicKey.toBase58()}`, options.privateViews === "device" && "manifest" in options.deployment && typeof indexedDB !== "undefined" && globalThis.crypto?.subtle ? new DeviceViews : undefined);
46802
47034
  this.provider = new DepthProvider(options.connection, options.wallet, {
46803
47035
  commitment: "confirmed",
46804
47036
  preflightCommitment: "confirmed"
@@ -46807,7 +47039,7 @@ class DepthClient {
46807
47039
  this.position = this.positionActions();
46808
47040
  }
46809
47041
  static async create(options) {
46810
- const appId = typeof options.appId === "string" ? new PublicKey9(options.appId) : options.appId;
47042
+ const appId = typeof options.appId === "string" ? new PublicKey10(options.appId) : options.appId;
46811
47043
  const resolved = {
46812
47044
  ...options,
46813
47045
  ...await resolveConnection(options),
@@ -47025,7 +47257,7 @@ class DepthClient {
47025
47257
  return this.funding.release(amount, clientNonce ?? BigInt(nonce().toString()));
47026
47258
  }
47027
47259
  async depositRequest(sourceOrAmount, amount, clientNonce) {
47028
- if (sourceOrAmount instanceof PublicKey9) {
47260
+ if (sourceOrAmount instanceof PublicKey10) {
47029
47261
  if (amount === undefined || clientNonce === undefined)
47030
47262
  throw new RangeError("Source deposits require amount and client nonce");
47031
47263
  return this.funding.deposit(sourceOrAmount, amount, clientNonce);
@@ -47036,7 +47268,7 @@ class DepthClient {
47036
47268
  return this.funding.deposit(source, sourceOrAmount, amount ?? BigInt(nonce().toString()));
47037
47269
  }
47038
47270
  async withdrawRequest(destinationOrAmount, amount, clientNonce) {
47039
- if (destinationOrAmount instanceof PublicKey9) {
47271
+ if (destinationOrAmount instanceof PublicKey10) {
47040
47272
  if (amount === undefined || clientNonce === undefined)
47041
47273
  throw new RangeError("Destination withdrawals require amount and client nonce");
47042
47274
  return this.funding.withdraw(destinationOrAmount, amount, clientNonce);
@@ -47051,6 +47283,7 @@ class DepthClient {
47051
47283
  }
47052
47284
  accountActions() {
47053
47285
  const actions = {
47286
+ state: () => balanceState(this.program, this.options.connection, this.options.wallet.publicKey, this.funding.balance),
47054
47287
  detail: (item) => new FundingDiscovery(this.program, this.options.connection, this.options.wallet.publicKey).detail(item),
47055
47288
  transaction: (item) => new FundingDiscovery(this.program, this.options.connection, this.options.wallet.publicKey).transaction(item),
47056
47289
  history: (options) => new FundingDiscovery(this.program, this.options.connection, this.options.wallet.publicKey).history(options),
@@ -47067,11 +47300,10 @@ class DepthClient {
47067
47300
  summary: async (options = {
47068
47301
  timeoutMs: 180000
47069
47302
  }) => {
47070
- const view = await this.requestBalanceView();
47071
- const result = await view.wait(options);
47072
- if (result.status !== "pending")
47073
- await view.close();
47074
- return result;
47303
+ const state = await actions.state();
47304
+ if (state.status === "uninitialized")
47305
+ return { ...state, status: "uninitialized", amount: 0n };
47306
+ return this.summaryView("balance", state.id, options);
47075
47307
  }
47076
47308
  };
47077
47309
  return Object.assign(() => actions, actions);
@@ -47107,29 +47339,7 @@ class DepthClient {
47107
47339
  summary: async (options = {
47108
47340
  timeoutMs: 180000
47109
47341
  }) => {
47110
- const deadline = Date.now() + options.timeoutMs;
47111
- let view;
47112
- for (;; ) {
47113
- try {
47114
- view = await this.requestMarketView();
47115
- break;
47116
- } catch (error) {
47117
- const code2 = error.error?.errorCode?.code;
47118
- if (code2 !== "WrongMarketAccountStatus" && !String(error).includes("Market account is processing another operation")) {
47119
- throw error;
47120
- }
47121
- if (Date.now() >= deadline)
47122
- throw error;
47123
- await new Promise((resolve) => setTimeout(resolve, 1000));
47124
- }
47125
- }
47126
- const result = await view.wait({
47127
- ...options,
47128
- timeoutMs: Math.max(0, deadline - Date.now())
47129
- });
47130
- if (result.status !== "pending")
47131
- await view.close();
47132
- return result;
47342
+ return this.summaryView("market", address2.toBase58(), options);
47133
47343
  }
47134
47344
  };
47135
47345
  return Object.assign((positionId) => this.positionActions(positionId), actions);
@@ -47175,6 +47385,75 @@ class DepthClient {
47175
47385
  u64(clientNonce, "clientNonce").toArrayLike(Buffer2, "le", 8)
47176
47386
  ]);
47177
47387
  }
47388
+ async deferViewClose(kind, id) {
47389
+ const view = new PublicKey10(id);
47390
+ const method = kind === "balance" ? this.program.methods.closeBalanceView : this.program.methods.closeMarketView;
47391
+ this.provider.deferClose(view, await method().accountsPartial({
47392
+ owner: this.options.wallet.publicKey,
47393
+ view
47394
+ }).instruction());
47395
+ }
47396
+ async viewState(kind, id) {
47397
+ if (kind === "balance")
47398
+ return this.account().state();
47399
+ const state = await this.read("MarketAccount", new PublicKey10(id));
47400
+ if (!state.owner.equals(this.options.wallet.publicKey) || !state.market.equals(this.marketAddress))
47401
+ throw new Error("Position does not belong to this wallet and market");
47402
+ return {
47403
+ status: enumName2(state.status),
47404
+ version: BigInt(state.version.toString())
47405
+ };
47406
+ }
47407
+ summaryView(kind, id, options) {
47408
+ const key = `${kind}:${id}`;
47409
+ const pending = this.viewReads.get(key);
47410
+ if (pending)
47411
+ return pending;
47412
+ const request = this.readSummaryView(kind, id, options).finally(() => {
47413
+ if (this.viewReads.get(key) === request)
47414
+ this.viewReads.delete(key);
47415
+ });
47416
+ this.viewReads.set(key, request);
47417
+ return request;
47418
+ }
47419
+ async readSummaryView(kind, id, options) {
47420
+ if (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs < 0)
47421
+ throw new RangeError("timeoutMs must be a nonnegative integer");
47422
+ const deadline = Date.now() + options.timeoutMs;
47423
+ for (;; ) {
47424
+ const state = await this.viewState(kind, id);
47425
+ if (state.status === "ready") {
47426
+ const cached = await this.views.get(kind, id, state.version);
47427
+ if (cached) {
47428
+ await this.deferViewClose(kind, cached.id);
47429
+ return cached;
47430
+ }
47431
+ try {
47432
+ const view = kind === "balance" ? await this.requestBalanceView() : await this.requestMarketView();
47433
+ const result = await view.wait({
47434
+ ...options,
47435
+ timeoutMs: Math.max(0, deadline - Date.now())
47436
+ });
47437
+ if (result.status === "pending")
47438
+ return result;
47439
+ await this.deferViewClose(kind, result.id);
47440
+ const latest = await this.viewState(kind, id);
47441
+ if (result.status === "ready" && latest.status === "ready" && latest.version === result.version) {
47442
+ const snapshot = { ...result, readAt: Date.now() };
47443
+ await this.views.set(kind, id, snapshot);
47444
+ return snapshot;
47445
+ }
47446
+ } catch (error) {
47447
+ const code2 = error.error?.errorCode?.code;
47448
+ if (code2 !== "WrongMarketAccountStatus" && !String(error).includes("processing another operation"))
47449
+ throw error;
47450
+ }
47451
+ }
47452
+ if (Date.now() >= deadline)
47453
+ throw new Error("Private account is still processing an operation");
47454
+ await new Promise((resolve) => setTimeout(resolve, Math.min(options.pollMs ?? 1000, deadline - Date.now())));
47455
+ }
47456
+ }
47178
47457
  async submitTrade(input, intent) {
47179
47458
  const appId = this.options.appId;
47180
47459
  if (!appId)
@@ -47206,43 +47485,13 @@ class DepthClient {
47206
47485
  throw new Error("Position does not belong to this wallet and market");
47207
47486
  await this.ensureBalanceReady();
47208
47487
  await this.ensureMarketReady();
47209
- let [market, protocol, account, app] = await Promise.all([
47210
- this.market(),
47211
- this.read("Protocol", this.protocolAddress),
47212
- this.read("MarketAccount", this.accountAddress()),
47213
- this.read("AppConfig", appAddress2(appId))
47214
- ]);
47215
- const deadline = tradeDeadline(input, Math.floor(Date.now() / 1000), market.state.config.maxOrderQueueSeconds);
47488
+ let [market, protocol, account, app] = await this.waitForMarket(input, appId, intent);
47489
+ let deadline;
47216
47490
  tradeLimit(input, "long", BigInt(market.state.oraclePriceE8.toString()));
47217
- this.validateTrade(market.state, protocol, account, app, appId, intent, deadline);
47218
47491
  if (additionalMargin > 0n) {
47219
47492
  const transfer = await this.funding.allocate(additionalMargin, clientNonce);
47220
- const allocationDeadline = Date.now() + 120000;
47221
- for (;; ) {
47222
- const current = await transfer.status();
47223
- if (current.state === "settled")
47224
- break;
47225
- if (current.state === "rejected" || current.state === "refunded")
47226
- throw new Error(`Margin allocation ${transfer.id.toBase58()} ${current.state}`);
47227
- if (current.state === "escrow") {
47228
- try {
47229
- await transfer.settle();
47230
- } catch (error) {
47231
- if ((await transfer.status()).state === "escrow")
47232
- throw error;
47233
- }
47234
- }
47235
- if (Date.now() >= allocationDeadline)
47236
- throw new Error(`Margin allocation ${transfer.id.toBase58()} is pending`);
47237
- await new Promise((resolve) => setTimeout(resolve, 1000));
47238
- }
47239
- [market, protocol, account, app] = await Promise.all([
47240
- this.market(),
47241
- this.read("Protocol", this.protocolAddress),
47242
- this.read("MarketAccount", this.accountAddress()),
47243
- this.read("AppConfig", appAddress2(appId))
47244
- ]);
47245
- this.validateTrade(market.state, protocol, account, app, appId, intent, deadline);
47493
+ await transfer.confirm({ timeoutMs: 120000 });
47494
+ [market, protocol, account, app] = await this.waitForMarket(input, appId, intent);
47246
47495
  }
47247
47496
  const viewDeadline = Date.now() + 120000;
47248
47497
  let position;
@@ -47260,15 +47509,13 @@ class DepthClient {
47260
47509
  await new Promise((resolve) => setTimeout(resolve, 1000));
47261
47510
  continue;
47262
47511
  }
47263
- [market, account] = await Promise.all([
47264
- this.market(),
47265
- this.read("MarketAccount", this.accountAddress())
47266
- ]);
47512
+ [market, protocol, account, app] = await this.waitForMarket(input, appId, intent);
47267
47513
  if (position.status === "ready" && enumName2(account.status) === "ready" && account.version.toString() === position.version.toString()) {
47268
47514
  break;
47269
47515
  }
47270
47516
  await new Promise((resolve) => setTimeout(resolve, 1000));
47271
47517
  }
47518
+ deadline = tradeDeadline(input, Math.floor(Date.now() / 1000), market.state.config.maxOrderQueueSeconds);
47272
47519
  this.validateTrade(market.state, protocol, account, app, appId, intent, deadline);
47273
47520
  if (position.status !== "ready" || position.collateral === undefined || position.position === undefined)
47274
47521
  throw new Error(`Private position view ${position.id} is ${position.status}`);
@@ -47338,6 +47585,29 @@ class DepthClient {
47338
47585
  ]).rpc({ commitment: "confirmed" }).catch((error) => submissionError(order.toBase58(), error));
47339
47586
  return new DepthOperation(order, signature, this.program, this.options.connection, (id) => this.requestFillView(id), position);
47340
47587
  }
47588
+ async waitForMarket(input, appId, intent) {
47589
+ const until = Date.now() + 60000;
47590
+ for (;; ) {
47591
+ const [market, protocol, account, app] = await Promise.all([
47592
+ this.market(),
47593
+ this.read("Protocol", this.protocolAddress),
47594
+ this.read("MarketAccount", this.accountAddress()),
47595
+ this.read("AppConfig", appAddress2(appId))
47596
+ ]);
47597
+ const deadline = tradeDeadline(input, Math.floor(Date.now() / 1000), market.state.config.maxOrderQueueSeconds);
47598
+ try {
47599
+ this.validateTrade(market.state, protocol, account, app, appId, intent, deadline);
47600
+ return [market, protocol, account, app];
47601
+ } catch (error) {
47602
+ if (!(error instanceof Error) || ![
47603
+ "Waiting for maker liquidity",
47604
+ "Market is processing another order"
47605
+ ].includes(error.message) || Date.now() >= until)
47606
+ throw error;
47607
+ }
47608
+ await new Promise((resolve) => setTimeout(resolve, 1000));
47609
+ }
47610
+ }
47341
47611
  validateTrade(market, protocol, account, app, appId, intent = "increase", deadline) {
47342
47612
  const now = Math.floor(Date.now() / 1000);
47343
47613
  const marketStatus = enumName2(market.status);
@@ -47350,9 +47620,10 @@ class DepthClient {
47350
47620
  if (enumName2(account.status) !== "ready" || !account.market.equals(this.marketAddress) || !account.owner.equals(this.options.wallet.publicKey)) {
47351
47621
  throw new Error("Market account is unavailable");
47352
47622
  }
47353
- if (market.pendingOrders !== 0 || market.activeQuoteCount === 0) {
47354
- throw new Error("Market is processing another order or has no quotes");
47355
- }
47623
+ if (market.pendingOrders !== 0)
47624
+ throw new Error("Market is processing another order");
47625
+ if (market.activeQuoteCount === 0)
47626
+ throw new Error("Waiting for maker liquidity");
47356
47627
  const age = now - market.oracleTimestamp.toNumber();
47357
47628
  if (age < 0 || age > market.config.maxOracleAgeSeconds) {
47358
47629
  throw new Error("Oracle update is stale");
@@ -47506,6 +47777,7 @@ export {
47506
47777
  verifyDeployment,
47507
47778
  resolveConnection,
47508
47779
  resolveAmount,
47780
+ prepare_depth,
47509
47781
  positionPnl,
47510
47782
  manifestBytes,
47511
47783
  create_depth_data,
@@ -0,0 +1,31 @@
1
+ import type { BalanceSnapshot, PositionSnapshot } from "./view.js";
2
+ type Snapshot = BalanceSnapshot | PositionSnapshot;
3
+ type Kind = "balance" | "market";
4
+ type Envelope = {
5
+ iv: Uint8Array;
6
+ data: ArrayBuffer;
7
+ };
8
+ export interface ViewStorage {
9
+ key(scope: string, proposed: CryptoKey): Promise<CryptoKey>;
10
+ read(id: string): Promise<Envelope | undefined>;
11
+ write(id: string, value: Envelope): Promise<void>;
12
+ }
13
+ export declare class DeviceViews implements ViewStorage {
14
+ private database?;
15
+ private open;
16
+ key(scope: string, proposed: CryptoKey): Promise<CryptoKey>;
17
+ read(id: string): Promise<Envelope | undefined>;
18
+ write(id: string, value: Envelope): Promise<void>;
19
+ }
20
+ export declare class ViewCache {
21
+ private readonly scope;
22
+ private readonly storage?;
23
+ private readonly memory;
24
+ private key?;
25
+ constructor(scope: string, storage?: ViewStorage | undefined);
26
+ private encryptionKey;
27
+ private id;
28
+ get<T extends Snapshot>(kind: Kind, account: string, version: bigint): Promise<T | undefined>;
29
+ set(kind: Kind, account: string, snapshot: Snapshot): Promise<void>;
30
+ }
31
+ export {};
@@ -1,9 +1,10 @@
1
1
  import * as anchor from "@anchor-lang/core";
2
2
  import { Connection, PublicKey } from "@solana/web3.js";
3
3
  export type BalanceSnapshot = {
4
- status: "pending" | "ready" | "stale";
4
+ status: "uninitialized" | "pending" | "ready" | "stale";
5
5
  id: string;
6
6
  version: bigint;
7
+ readAt?: number;
7
8
  slot: number;
8
9
  amount?: bigint;
9
10
  };
@@ -11,6 +12,7 @@ export type PositionSnapshot = {
11
12
  status: "pending" | "ready" | "stale";
12
13
  id: string;
13
14
  version: bigint;
15
+ readAt?: number;
14
16
  slot: number;
15
17
  collateral?: bigint;
16
18
  reserved?: bigint;
@@ -55,5 +57,7 @@ export declare class DepthView {
55
57
  timeoutMs: number;
56
58
  pollMs?: number;
57
59
  }): Promise<BalanceSnapshot | PositionSnapshot | FillSnapshot | QuoteBalanceSnapshot>;
60
+ deferClose(): Promise<void>;
61
+ private closeBuilder;
58
62
  close(): Promise<string>;
59
63
  }
package/dist/view/view.js CHANGED
@@ -26667,13 +26667,28 @@ class DepthView {
26667
26667
  await new Promise((resolve) => setTimeout(resolve, Math.min(pollMs, deadline - Date.now())));
26668
26668
  }
26669
26669
  }
26670
+ async deferClose() {
26671
+ const provider = this.program.provider;
26672
+ if (typeof provider.deferClose !== "function") {
26673
+ await this.close();
26674
+ return;
26675
+ }
26676
+ const snapshot = await this.status();
26677
+ if (snapshot.status === "pending")
26678
+ throw new Error("Private view computation is still pending");
26679
+ const instruction = await this.closeBuilder().instruction();
26680
+ provider.deferClose(this.id, instruction);
26681
+ }
26682
+ closeBuilder() {
26683
+ 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();
26684
+ return builder.accountsPartial({ owner: this.owner, view: this.id });
26685
+ }
26670
26686
  async close() {
26671
26687
  const snapshot = await this.status();
26672
26688
  if (snapshot.status === "pending") {
26673
26689
  throw new Error("Private view computation is still pending");
26674
26690
  }
26675
- 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();
26676
- return builder.accountsPartial({ owner: this.owner, view: this.id }).rpc({ commitment: "confirmed" });
26691
+ return this.closeBuilder().rpc({ commitment: "confirmed" });
26677
26692
  }
26678
26693
  }
26679
26694
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@depthhq/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -44,8 +44,11 @@
44
44
  "@noble/hashes": "1.8.0",
45
45
  "@solana/web3.js": "1.99.0",
46
46
  "buffer": "6.0.3",
47
- "@depthhq/schema": "0.1.1",
48
- "@depthhq/bindings": "0.1.1"
47
+ "@depthhq/bindings": "0.1.1",
48
+ "@depthhq/schema": "0.1.1"
49
+ },
50
+ "devDependencies": {
51
+ "fake-indexeddb": "6.2.4"
49
52
  },
50
53
  "scripts": {
51
54
  "build": "node ../../scripts/build-package.mjs sdk",