@depthhq/sdk 0.1.3 → 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.
@@ -12,13 +12,15 @@ import { ExecutionHistory } from "../history/history.js";
12
12
  import type { AmountSelector } from "../trade/amount.js";
13
13
  import { TradeFlow } from "../trade/flow.js";
14
14
  import { DepthView } from "../view/view.js";
15
+ import { ViewSession } from "../view/session.js";
15
16
  import type { BalanceSnapshot } from "../view/view.js";
16
17
  import type { CloseInput, DepthOptions, ResolvedDepthOptions, MarketState, ReductionInput, ReverseInput, TradeInput } from "./types.js";
17
18
  export declare class DepthClient {
18
19
  readonly options: ResolvedDepthOptions;
19
20
  readonly program: anchor.Program;
20
21
  readonly provider: DepthProvider;
21
- private readonly views;
22
+ private readonly viewCache;
23
+ readonly views: ViewSession;
22
24
  private readonly viewReads;
23
25
  readonly position: ReturnType<DepthClient["positionActions"]>;
24
26
  readonly marketAddress: anchor.web3.PublicKey;
@@ -42,6 +44,7 @@ export declare class DepthClient {
42
44
  slot: number;
43
45
  }>;
44
46
  operation(id: PublicKey): DepthOperation;
47
+ requestQuoteView(quote: PublicKey): Promise<DepthView>;
45
48
  requestBalanceView(): Promise<DepthView>;
46
49
  requestMarketView(): Promise<DepthView>;
47
50
  requestFillView(order: PublicKey): Promise<DepthView>;
@@ -12,12 +12,14 @@ export type DepthOptions = ConnectionOptions & {
12
12
  appId?: PublicKey | string;
13
13
  wallet: DepthWallet;
14
14
  privateViews?: "memory" | "device";
15
+ viewAuthorization?: "session" | "wallet";
15
16
  settlement?: "keeper" | "wallet";
16
17
  };
17
18
  export type ResolvedDepthOptions = ResolvedConnection & {
18
19
  appId?: PublicKey;
19
20
  wallet: DepthWallet;
20
21
  privateViews?: "memory" | "device";
22
+ viewAuthorization?: "session" | "wallet";
21
23
  settlement?: "keeper" | "wallet";
22
24
  };
23
25
  export type TradeTerms = {
package/dist/index.d.ts CHANGED
@@ -33,3 +33,4 @@ export { DepthError } from "./operation/error.js";
33
33
  export { DepthProvider } from "./chain/provider.js";
34
34
  export { authenticateManifest, captureDeployment, manifestBytes, verifyDeployment, } from "./chain/manifest.js";
35
35
  export type { DeploymentManifest, SignedManifest, DeploymentPolicy, } from "./chain/manifest.js";
36
+ export { ViewSession } from "./view/session.js";
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 PublicKey10 } 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);
@@ -46014,22 +46026,22 @@ function positionPnl(position, priceE8, fundingIndex, fill) {
46014
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)
46015
46027
  throw new Error("A ready open position and a positive price are required");
46016
46028
  const size = position.position < 0n ? -position.position : position.position;
46017
- const sign = position.position > 0n ? 1n : -1n;
46029
+ const sign2 = position.position > 0n ? 1n : -1n;
46018
46030
  let closing = size;
46019
46031
  let fee = 0n;
46020
46032
  let state = "open";
46021
46033
  if (fill) {
46022
- 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)
46023
46035
  throw new Error("A ready opposite-side execution is required");
46024
46036
  closing = fill.size < size ? fill.size : size;
46025
46037
  fee = fill.fee;
46026
46038
  state = fill.size === size ? "closed" : fill.size < size ? "reduced" : "reversed";
46027
46039
  }
46028
- const gross = sign * closing * (priceE8 - position.entryPriceE8) / scale;
46040
+ const gross = sign2 * closing * (priceE8 - position.entryPriceE8) / scale;
46029
46041
  const funding = position.position * (fundingIndex - position.fundingCheckpoint) / scale;
46030
46042
  const net = gross - funding - fee;
46031
46043
  return {
46032
- side: sign > 0n ? "long" : "short",
46044
+ side: sign2 > 0n ? "long" : "short",
46033
46045
  state,
46034
46046
  entryPriceE8: position.entryPriceE8,
46035
46047
  priceE8,
@@ -46765,7 +46777,9 @@ class DepthView {
46765
46777
  mxePublicKey;
46766
46778
  program;
46767
46779
  connection;
46768
- 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) {
46769
46783
  this.id = id;
46770
46784
  this.signature = signature;
46771
46785
  this.kind = kind;
@@ -46775,6 +46789,8 @@ class DepthView {
46775
46789
  this.mxePublicKey = mxePublicKey;
46776
46790
  this.program = program;
46777
46791
  this.connection = connection;
46792
+ this.source = source;
46793
+ this.release = release;
46778
46794
  }
46779
46795
  async status() {
46780
46796
  const response = await this.connection.getAccountInfoAndContext(this.id, "confirmed");
@@ -46785,6 +46801,9 @@ class DepthView {
46785
46801
  if (!state.owner.equals(this.owner) || !Buffer2.from(state.viewKey).equals(Buffer2.from(this.publicKey))) {
46786
46802
  throw new Error("Private view authorization does not match this request");
46787
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");
46788
46807
  const current = Object.keys(state.status)[0];
46789
46808
  if (current !== "pending" && current !== "ready" && current !== "stale") {
46790
46809
  throw new Error("Private view has an unknown status");
@@ -46850,6 +46869,10 @@ class DepthView {
46850
46869
  }
46851
46870
  }
46852
46871
  async deferClose() {
46872
+ if (this.release) {
46873
+ await this.close();
46874
+ return;
46875
+ }
46853
46876
  const provider = this.program.provider;
46854
46877
  if (typeof provider.deferClose !== "function") {
46855
46878
  await this.close();
@@ -46870,10 +46893,25 @@ class DepthView {
46870
46893
  if (snapshot.status === "pending") {
46871
46894
  throw new Error("Private view computation is still pending");
46872
46895
  }
46896
+ if (this.release)
46897
+ return this.release();
46873
46898
  return this.closeBuilder().rpc({ commitment: "confirmed" });
46874
46899
  }
46875
46900
  }
46876
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
+
46877
46915
  // src/view/cache.ts
46878
46916
  class DeviceViews {
46879
46917
  database;
@@ -46928,6 +46966,40 @@ class DeviceViews {
46928
46966
  }
46929
46967
  }
46930
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
+
46931
47003
  class ViewCache {
46932
47004
  scope;
46933
47005
  storage;
@@ -46991,12 +47063,205 @@ class ViewCache {
46991
47063
  }
46992
47064
  }
46993
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
+ });
47256
+ }
47257
+ }
47258
+
46994
47259
  // src/client/client.ts
46995
- function pda(seeds, programId = DEPTH_PROGRAM_ID7) {
46996
- return PublicKey10.findProgramAddressSync(seeds, programId)[0];
47260
+ function pda(seeds, programId = DEPTH_PROGRAM_ID8) {
47261
+ return PublicKey11.findProgramAddressSync(seeds, programId)[0];
46997
47262
  }
46998
47263
  function nonce() {
46999
- return new anchor8.BN(randomBytes3(8), "le");
47264
+ return new anchor9.BN(randomBytes3(8), "le");
47000
47265
  }
47001
47266
  function enumName2(value2) {
47002
47267
  return Object.keys(value2)[0] ?? "unknown";
@@ -47006,12 +47271,13 @@ class DepthClient {
47006
47271
  options;
47007
47272
  program;
47008
47273
  provider;
47274
+ viewCache;
47009
47275
  views;
47010
47276
  viewReads = new Map;
47011
47277
  position;
47012
- marketAddress = marketAddress7();
47278
+ marketAddress = marketAddress8();
47013
47279
  protocolAddress = protocolAddress4();
47014
- mxeAddress = getMXEAccAddress(DEPTH_PROGRAM_ID7);
47280
+ mxeAddress = getMXEAccAddress(DEPTH_PROGRAM_ID8);
47015
47281
  get catalog() {
47016
47282
  return new MarketCatalog(this);
47017
47283
  }
@@ -47030,16 +47296,19 @@ class DepthClient {
47030
47296
  }
47031
47297
  constructor(options) {
47032
47298
  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);
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);
47034
47302
  this.provider = new DepthProvider(options.connection, options.wallet, {
47035
47303
  commitment: "confirmed",
47036
47304
  preflightCommitment: "confirmed"
47037
47305
  });
47038
- 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);
47039
47308
  this.position = this.positionActions();
47040
47309
  }
47041
47310
  static async create(options) {
47042
- const appId = typeof options.appId === "string" ? new PublicKey10(options.appId) : options.appId;
47311
+ const appId = typeof options.appId === "string" ? new PublicKey11(options.appId) : options.appId;
47043
47312
  const resolved = {
47044
47313
  ...options,
47045
47314
  ...await resolveConnection(options),
@@ -47052,7 +47321,7 @@ class DepthClient {
47052
47321
  }
47053
47322
  async verifyDeployment() {
47054
47323
  const [programInfo, protocol, market, app, mxe] = await Promise.all([
47055
- this.options.connection.getAccountInfo(DEPTH_PROGRAM_ID7, "confirmed"),
47324
+ this.options.connection.getAccountInfo(DEPTH_PROGRAM_ID8, "confirmed"),
47056
47325
  this.read("Protocol", this.protocolAddress),
47057
47326
  this.read("Market", this.marketAddress),
47058
47327
  this.options.appId ? this.read("AppConfig", appAddress2(this.options.appId)) : Promise.resolve(undefined),
@@ -47060,13 +47329,13 @@ class DepthClient {
47060
47329
  ]);
47061
47330
  if (!programInfo?.executable)
47062
47331
  throw new Error("Depth program is not deployed");
47063
- 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)) {
47064
47333
  throw new Error("Depth deployment accounts disagree");
47065
47334
  }
47066
47335
  }
47067
47336
  async read(name2, address2) {
47068
47337
  const info = await this.options.connection.getAccountInfo(address2, "confirmed");
47069
- if (!info || !info.owner.equals(DEPTH_PROGRAM_ID7)) {
47338
+ if (!info || !info.owner.equals(DEPTH_PROGRAM_ID8)) {
47070
47339
  throw new Error(`${name2} account is unavailable`);
47071
47340
  }
47072
47341
  return this.program.coder.accounts.decode(name2[0].toLowerCase() + name2.slice(1), info.data);
@@ -47074,7 +47343,7 @@ class DepthClient {
47074
47343
  async queue(name2, offset2) {
47075
47344
  const mxe = await arciumAccount(this.provider).fetch(this.mxeAddress);
47076
47345
  const cluster = mxe.cluster;
47077
- 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());
47078
47347
  const definitionInfo = await this.options.connection.getAccountInfo(definition, "confirmed");
47079
47348
  if (!definitionInfo)
47080
47349
  throw new Error(`${name2} computation is not deployed`);
@@ -47096,7 +47365,7 @@ class DepthClient {
47096
47365
  }
47097
47366
  async market() {
47098
47367
  const response = await this.options.connection.getAccountInfoAndContext(this.marketAddress, "confirmed");
47099
- if (!response.value || !response.value.owner.equals(DEPTH_PROGRAM_ID7)) {
47368
+ if (!response.value || !response.value.owner.equals(DEPTH_PROGRAM_ID8)) {
47100
47369
  throw new Error("Depth market is unavailable");
47101
47370
  }
47102
47371
  return {
@@ -47108,6 +47377,9 @@ class DepthClient {
47108
47377
  operation(id) {
47109
47378
  return new DepthOperation(id, undefined, this.program, this.options.connection, (order) => this.requestFillView(order));
47110
47379
  }
47380
+ async requestQuoteView(quote) {
47381
+ return this.views.request("quote", quote);
47382
+ }
47111
47383
  async requestBalanceView() {
47112
47384
  const owner = this.options.wallet.publicKey;
47113
47385
  const balance = pda([Buffer2.from("balance"), owner.toBuffer()]);
@@ -47155,7 +47427,10 @@ class DepthClient {
47155
47427
  return this.requestView("fill", view, viewNonce, { order });
47156
47428
  }
47157
47429
  async requestView(kind, view, viewNonce, accounts2) {
47158
- 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);
47159
47434
  if (!mxePublicKey)
47160
47435
  throw new Error("MXE public key is unavailable");
47161
47436
  const secret = x25519.utils.randomSecretKey();
@@ -47170,7 +47445,7 @@ class DepthClient {
47170
47445
  ...kind === "balance" ? {} : { grant: null },
47171
47446
  view
47172
47447
  }).rpc({ commitment: "confirmed" });
47173
- 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);
47174
47449
  }
47175
47450
  async ensureBalanceReady() {
47176
47451
  const address2 = this.funding.balance;
@@ -47257,7 +47532,7 @@ class DepthClient {
47257
47532
  return this.funding.release(amount, clientNonce ?? BigInt(nonce().toString()));
47258
47533
  }
47259
47534
  async depositRequest(sourceOrAmount, amount, clientNonce) {
47260
- if (sourceOrAmount instanceof PublicKey10) {
47535
+ if (sourceOrAmount instanceof PublicKey11) {
47261
47536
  if (amount === undefined || clientNonce === undefined)
47262
47537
  throw new RangeError("Source deposits require amount and client nonce");
47263
47538
  return this.funding.deposit(sourceOrAmount, amount, clientNonce);
@@ -47268,7 +47543,7 @@ class DepthClient {
47268
47543
  return this.funding.deposit(source, sourceOrAmount, amount ?? BigInt(nonce().toString()));
47269
47544
  }
47270
47545
  async withdrawRequest(destinationOrAmount, amount, clientNonce) {
47271
- if (destinationOrAmount instanceof PublicKey10) {
47546
+ if (destinationOrAmount instanceof PublicKey11) {
47272
47547
  if (amount === undefined || clientNonce === undefined)
47273
47548
  throw new RangeError("Destination withdrawals require amount and client nonce");
47274
47549
  return this.funding.withdraw(destinationOrAmount, amount, clientNonce);
@@ -47386,7 +47661,7 @@ class DepthClient {
47386
47661
  ]);
47387
47662
  }
47388
47663
  async deferViewClose(kind, id) {
47389
- const view = new PublicKey10(id);
47664
+ const view = new PublicKey11(id);
47390
47665
  const method = kind === "balance" ? this.program.methods.closeBalanceView : this.program.methods.closeMarketView;
47391
47666
  this.provider.deferClose(view, await method().accountsPartial({
47392
47667
  owner: this.options.wallet.publicKey,
@@ -47396,7 +47671,7 @@ class DepthClient {
47396
47671
  async viewState(kind, id) {
47397
47672
  if (kind === "balance")
47398
47673
  return this.account().state();
47399
- const state = await this.read("MarketAccount", new PublicKey10(id));
47674
+ const state = await this.read("MarketAccount", new PublicKey11(id));
47400
47675
  if (!state.owner.equals(this.options.wallet.publicKey) || !state.market.equals(this.marketAddress))
47401
47676
  throw new Error("Position does not belong to this wallet and market");
47402
47677
  return {
@@ -47423,7 +47698,7 @@ class DepthClient {
47423
47698
  for (;; ) {
47424
47699
  const state = await this.viewState(kind, id);
47425
47700
  if (state.status === "ready") {
47426
- const cached = await this.views.get(kind, id, state.version);
47701
+ const cached = await this.viewCache.get(kind, id, state.version);
47427
47702
  if (cached) {
47428
47703
  await this.deferViewClose(kind, cached.id);
47429
47704
  return cached;
@@ -47436,11 +47711,11 @@ class DepthClient {
47436
47711
  });
47437
47712
  if (result.status === "pending")
47438
47713
  return result;
47439
- await this.deferViewClose(kind, result.id);
47714
+ await view.deferClose();
47440
47715
  const latest = await this.viewState(kind, id);
47441
47716
  if (result.status === "ready" && latest.status === "ready" && latest.version === result.version) {
47442
47717
  const snapshot = { ...result, readAt: Date.now() };
47443
- await this.views.set(kind, id, snapshot);
47718
+ await this.viewCache.set(kind, id, snapshot);
47444
47719
  return snapshot;
47445
47720
  }
47446
47721
  } catch (error) {
@@ -47540,11 +47815,11 @@ class DepthClient {
47540
47815
  const keys = [market.state.backstopOffer, ...quotes];
47541
47816
  const records = await this.options.connection.getMultipleAccountsInfo(keys, "confirmed");
47542
47817
  records.forEach((info, index) => {
47543
- if (!info?.owner.equals(DEPTH_PROGRAM_ID7)) {
47818
+ if (!info?.owner.equals(DEPTH_PROGRAM_ID8)) {
47544
47819
  throw new Error(`Market quote snapshot missing account ${index}`);
47545
47820
  }
47546
47821
  });
47547
- const mxePublicKey = await getMXEPublicKey(this.provider, DEPTH_PROGRAM_ID7);
47822
+ const mxePublicKey = await getMXEPublicKey(this.provider, DEPTH_PROGRAM_ID8);
47548
47823
  if (!mxePublicKey)
47549
47824
  throw new Error("MXE public key is unavailable");
47550
47825
  const secret = x25519.utils.randomSecretKey();
@@ -47562,7 +47837,7 @@ class DepthClient {
47562
47837
  }), inputNonce);
47563
47838
  const offset2 = nonce();
47564
47839
  const queue = await this.queue("match_order", offset2);
47565
- 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({
47566
47841
  ...queue,
47567
47842
  trader: this.options.wallet.publicKey,
47568
47843
  protocol: this.protocolAddress,
@@ -47786,6 +48061,7 @@ export {
47786
48061
  captureDeployment,
47787
48062
  authenticateManifest,
47788
48063
  WithdrawalHandle,
48064
+ ViewSession,
47789
48065
  TransferHandle,
47790
48066
  TradeFlow,
47791
48067
  MarketData,
@@ -17,6 +17,15 @@ export declare class DeviceViews implements ViewStorage {
17
17
  read(id: string): Promise<Envelope | undefined>;
18
18
  write(id: string, value: Envelope): Promise<void>;
19
19
  }
20
+ export declare class EncryptedViews {
21
+ private readonly scope;
22
+ private readonly storage;
23
+ private key?;
24
+ constructor(scope: string, storage: ViewStorage);
25
+ private encryptionKey;
26
+ read<T>(name: string): Promise<T | undefined>;
27
+ write(name: string, value: unknown): Promise<void>;
28
+ }
20
29
  export declare class ViewCache {
21
30
  private readonly scope;
22
31
  private readonly storage?;
@@ -0,0 +1,28 @@
1
+ import * as anchor from "@anchor-lang/core";
2
+ import { type ViewKind } from "@depthhq/schema";
3
+ import { PublicKey, type Connection } from "@solana/web3.js";
4
+ import { type ViewStorage } from "./cache.js";
5
+ import { DepthView } from "./view.js";
6
+ export declare class ViewSession {
7
+ private readonly program;
8
+ private readonly connection;
9
+ private readonly owner;
10
+ private readonly serviceUrl;
11
+ private readonly scope;
12
+ private readonly storage?;
13
+ private readonly pending;
14
+ constructor(program: anchor.Program, connection: Connection, owner: PublicKey, serviceUrl: string | undefined, scope: string, storage?: ViewStorage);
15
+ private identity;
16
+ private keys;
17
+ status(): Promise<{
18
+ active: boolean;
19
+ expiresAt?: number;
20
+ id: string;
21
+ }>;
22
+ private capability;
23
+ authorize(): Promise<void>;
24
+ revoke(): Promise<string>;
25
+ private post;
26
+ request(kind: ViewKind, source: PublicKey): Promise<DepthView>;
27
+ private read;
28
+ }
@@ -51,7 +51,9 @@ export declare class DepthView {
51
51
  private readonly mxePublicKey;
52
52
  private readonly program;
53
53
  private readonly connection;
54
- constructor(id: PublicKey, signature: string, kind: "balance" | "market" | "fill" | "quote", owner: PublicKey, secret: Uint8Array, publicKey: Uint8Array, mxePublicKey: Uint8Array, program: anchor.Program, connection: Connection);
54
+ private readonly source?;
55
+ private readonly release?;
56
+ constructor(id: PublicKey, signature: string, kind: "balance" | "market" | "fill" | "quote", owner: PublicKey, secret: Uint8Array, publicKey: Uint8Array, mxePublicKey: Uint8Array, program: anchor.Program, connection: Connection, source?: PublicKey | undefined, release?: (() => Promise<string>) | undefined);
55
57
  status(): Promise<BalanceSnapshot | PositionSnapshot | FillSnapshot | QuoteBalanceSnapshot>;
56
58
  wait(options: {
57
59
  timeoutMs: number;
package/dist/view/view.js CHANGED
@@ -26583,7 +26583,9 @@ class DepthView {
26583
26583
  mxePublicKey;
26584
26584
  program;
26585
26585
  connection;
26586
- constructor(id, signature, kind, owner, secret, publicKey, mxePublicKey, program, connection) {
26586
+ source;
26587
+ release;
26588
+ constructor(id, signature, kind, owner, secret, publicKey, mxePublicKey, program, connection, source, release) {
26587
26589
  this.id = id;
26588
26590
  this.signature = signature;
26589
26591
  this.kind = kind;
@@ -26593,6 +26595,8 @@ class DepthView {
26593
26595
  this.mxePublicKey = mxePublicKey;
26594
26596
  this.program = program;
26595
26597
  this.connection = connection;
26598
+ this.source = source;
26599
+ this.release = release;
26596
26600
  }
26597
26601
  async status() {
26598
26602
  const response = await this.connection.getAccountInfoAndContext(this.id, "confirmed");
@@ -26603,6 +26607,9 @@ class DepthView {
26603
26607
  if (!state.owner.equals(this.owner) || !Buffer.from(state.viewKey).equals(Buffer.from(this.publicKey))) {
26604
26608
  throw new Error("Private view authorization does not match this request");
26605
26609
  }
26610
+ const source = this.kind === "balance" ? state.balance : this.kind === "market" ? state.marketAccount : this.kind === "fill" ? state.order : state.quote;
26611
+ if (this.source && !source?.equals(this.source))
26612
+ throw new Error("Private view source does not match this request");
26606
26613
  const current = Object.keys(state.status)[0];
26607
26614
  if (current !== "pending" && current !== "ready" && current !== "stale") {
26608
26615
  throw new Error("Private view has an unknown status");
@@ -26668,6 +26675,10 @@ class DepthView {
26668
26675
  }
26669
26676
  }
26670
26677
  async deferClose() {
26678
+ if (this.release) {
26679
+ await this.close();
26680
+ return;
26681
+ }
26671
26682
  const provider = this.program.provider;
26672
26683
  if (typeof provider.deferClose !== "function") {
26673
26684
  await this.close();
@@ -26688,6 +26699,8 @@ class DepthView {
26688
26699
  if (snapshot.status === "pending") {
26689
26700
  throw new Error("Private view computation is still pending");
26690
26701
  }
26702
+ if (this.release)
26703
+ return this.release();
26691
26704
  return this.closeBuilder().rpc({ commitment: "confirmed" });
26692
26705
  }
26693
26706
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@depthhq/sdk",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -44,8 +44,8 @@
44
44
  "@noble/hashes": "1.8.0",
45
45
  "@solana/web3.js": "1.99.0",
46
46
  "buffer": "6.0.3",
47
- "@depthhq/bindings": "0.1.1",
48
- "@depthhq/schema": "0.1.1"
47
+ "@depthhq/bindings": "0.1.2",
48
+ "@depthhq/schema": "0.1.2"
49
49
  },
50
50
  "devDependencies": {
51
51
  "fake-indexeddb": "6.2.4"