@depthhq/maker-sdk 0.1.0 → 0.1.2

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
@@ -26399,13 +26399,517 @@ import {
26399
26399
  } from "@depthhq/bindings";
26400
26400
  import { DepthView } from "@depthhq/sdk/views";
26401
26401
  import { DepthRecovery } from "@depthhq/sdk/recovery";
26402
- import { PublicKey as PublicKey5 } from "@solana/web3.js";
26402
+ import { PublicKey as PublicKey7 } from "@solana/web3.js";
26403
26403
 
26404
26404
  // src/chain/random.ts
26405
26405
  function randomBytes3(length) {
26406
26406
  return crypto.getRandomValues(new Uint8Array(length));
26407
26407
  }
26408
26408
 
26409
+ // src/hedge/schema.ts
26410
+ var object = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
26411
+ var integer = (value) => Number.isSafeInteger(value) && value >= 0;
26412
+ var amount = (value) => typeof value === "string" && /^-?\d{1,40}$/.test(value);
26413
+ var optional = (value, check) => value === undefined || check(value);
26414
+ var text = (value) => typeof value === "string" && value.length <= 1024;
26415
+ function order(value, receipt = false) {
26416
+ if (!object(value) || typeof value.cloid !== "string" || !/^0x[0-9a-f]{32}$/.test(value.cloid) || !integer(value.nonce) || !integer(value.expiresAt) || value.expiresAt <= value.nonce || typeof value.buy !== "boolean" || typeof value.reduceOnly !== "boolean" || !amount(value.size) || BigInt(value.size) <= 0n || !amount(value.limitPriceE8) || BigInt(value.limitPriceE8) <= 0n)
26417
+ return false;
26418
+ if (!receipt)
26419
+ return true;
26420
+ return ["filled", "cancelled", "rejected"].includes(String(value.status)) && integer(value.completedAt) && value.completedAt >= value.nonce && amount(value.filledSize) && BigInt(value.filledSize) >= 0n && BigInt(value.filledSize) <= BigInt(value.size) && optional(value.orderId, integer) && optional(value.averagePriceE8, amount) && optional(value.fee, amount) && optional(value.reason, text) && optional(value.hashes, (hashes) => Array.isArray(hashes) && hashes.length <= 2000 && hashes.every((hash) => typeof hash === "string" && /^0x[0-9a-fA-F]{64}$/.test(hash)));
26421
+ }
26422
+ function hedgeView(value, owner) {
26423
+ if (!object(value) || value.maker !== owner || typeof value.enabled !== "boolean")
26424
+ throw new Error("Maker hedge response belongs to another wallet or is invalid");
26425
+ const snapshot = value.snapshot;
26426
+ if (snapshot !== null && (!value.enabled || !object(snapshot) || snapshot.venue !== "hyperliquid" || !["testnet", "mainnet"].includes(String(snapshot.network)) || typeof snapshot.account !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(snapshot.account) || !integer(snapshot.updatedAt) || !["balanced", "waiting", "pending", "blocked"].includes(String(snapshot.phase)) || !optional(snapshot.reason, text) || !optional(snapshot.depthPosition, amount) || !optional(snapshot.externalPosition, amount) || !optional(snapshot.unhedgedPosition, amount) || !optional(snapshot.pending, order) || !Array.isArray(snapshot.history) || snapshot.history.length > 50 || !snapshot.history.every((record) => order(record, true))))
26427
+ throw new Error("Maker hedge response is invalid");
26428
+ return value;
26429
+ }
26430
+
26431
+ // src/hedge/access.ts
26432
+ var accessMessage = (wallet, nonce, expiresAt, scope = "Read hedge history") => `Depth maker access
26433
+ Wallet: ${wallet}
26434
+ Scope: ${scope}
26435
+ Nonce: ${nonce}
26436
+ Expires: ${expiresAt}`;
26437
+
26438
+ class WalletSession {
26439
+ options;
26440
+ scope;
26441
+ prefix;
26442
+ token;
26443
+ constructor(options, scope = "Read hedge history", prefix = "") {
26444
+ this.options = options;
26445
+ this.scope = scope;
26446
+ this.prefix = prefix;
26447
+ }
26448
+ async request(path, init) {
26449
+ if (!this.options.serviceUrl)
26450
+ throw new Error("A maker service URL is required");
26451
+ const response = await fetch(`${this.options.serviceUrl.replace(/\/$/, "")}/v1/maker/${this.prefix}${path}`, {
26452
+ ...init,
26453
+ cache: "no-store",
26454
+ signal: AbortSignal.timeout(15000)
26455
+ });
26456
+ if (response.status === 401) {
26457
+ this.token = undefined;
26458
+ throw new Error("Authorize access with your maker wallet");
26459
+ }
26460
+ if (!response.ok) {
26461
+ const error = await response.json().catch(() => ({}));
26462
+ throw new Error(typeof error.message === "string" ? error.message : response.status === 403 ? "This worker belongs to another maker" : "Maker worker is unavailable");
26463
+ }
26464
+ return response.json();
26465
+ }
26466
+ async authorize() {
26467
+ const wallet = this.options.wallet;
26468
+ if (!wallet.signMessage)
26469
+ throw new Error("This wallet cannot sign access messages");
26470
+ const challenge = await this.request(`challenge?wallet=${wallet.publicKey.toBase58()}`);
26471
+ if (challenge.wallet !== wallet.publicKey.toBase58() || !/^[0-9a-f]{64}$/.test(challenge.nonce) || !Number.isSafeInteger(challenge.expiresAt) || challenge.expiresAt <= Date.now() || challenge.expiresAt > Date.now() + 120000 || challenge.message !== accessMessage(challenge.wallet, challenge.nonce, challenge.expiresAt, this.scope))
26472
+ throw new Error("Maker access challenge is invalid");
26473
+ const signature = await wallet.signMessage(new TextEncoder().encode(challenge.message));
26474
+ const session = await this.request("session", {
26475
+ method: "POST",
26476
+ headers: { "content-type": "application/json" },
26477
+ body: JSON.stringify({
26478
+ nonce: challenge.nonce,
26479
+ signature: Buffer.from(signature).toString("base64")
26480
+ })
26481
+ });
26482
+ if (!/^[0-9a-f]{64}$/.test(session.token) || !Number.isSafeInteger(session.expiresAt) || session.expiresAt <= Date.now() || session.expiresAt > Date.now() + 16 * 60000)
26483
+ throw new Error("Maker access session is invalid");
26484
+ this.token = { value: session.token, expiresAt: session.expiresAt };
26485
+ }
26486
+ async authorizedRequest(path, body) {
26487
+ if (!this.token || this.token.expiresAt <= Date.now())
26488
+ await this.authorize();
26489
+ return this.request(path, {
26490
+ method: body === undefined ? "GET" : "POST",
26491
+ headers: {
26492
+ authorization: `Bearer ${this.token.value}`,
26493
+ "content-type": "application/json"
26494
+ },
26495
+ body: body === undefined ? undefined : JSON.stringify(body)
26496
+ });
26497
+ }
26498
+ disconnect() {
26499
+ this.token = undefined;
26500
+ }
26501
+ }
26502
+
26503
+ class MakerAccess extends WalletSession {
26504
+ async summary() {
26505
+ if (!this.token || this.token.expiresAt <= Date.now()) {
26506
+ this.token = undefined;
26507
+ throw new Error("Authorize access with your maker wallet");
26508
+ }
26509
+ return hedgeView(await this.authorizedRequest("hedges"), this.options.wallet.publicKey.toBase58());
26510
+ }
26511
+ }
26512
+
26513
+ // src/automation/automation.ts
26514
+ import { PublicKey as PublicKey3, SystemProgram, Transaction } from "@solana/web3.js";
26515
+
26516
+ // src/hedge/setup.ts
26517
+ import { ExchangeClient, HttpTransport, InfoClient } from "@nktkas/hyperliquid";
26518
+ import {
26519
+ getWalletAddress
26520
+ } from "@nktkas/hyperliquid/signing";
26521
+ import { privateKeyToAccount } from "viem/accounts";
26522
+
26523
+ // src/hedge/amount.ts
26524
+ function units(value, decimals, exact = true) {
26525
+ if (!/^-?\d+(?:\.\d+)?$/.test(value))
26526
+ throw new Error("External decimal is invalid");
26527
+ const negative = value.startsWith("-");
26528
+ const [whole, fraction = ""] = value.replace(/^-/, "").split(".");
26529
+ const extra = fraction.slice(decimals);
26530
+ if (exact && /[1-9]/.test(extra))
26531
+ throw new Error("External decimal exceeds precision");
26532
+ const amount2 = BigInt(whole) * 10n ** BigInt(decimals) + BigInt(fraction.slice(0, decimals).padEnd(decimals, "0") || "0");
26533
+ return negative ? -amount2 - (!exact && /[1-9]/.test(extra) ? 1n : 0n) : amount2;
26534
+ }
26535
+
26536
+ // src/hedge/setup.ts
26537
+ async function verify_hyperliquid(options) {
26538
+ const info = new InfoClient({
26539
+ transport: new HttpTransport({ isTestnet: options.network === "testnet" })
26540
+ });
26541
+ const [state, agents] = await Promise.all([
26542
+ info.clearinghouseState({ user: options.account }),
26543
+ info.extraAgents({ user: options.account })
26544
+ ]);
26545
+ if (units(state.withdrawable, 6, false) < 10000000n)
26546
+ throw new Error(`Fund Hyperliquid ${options.network} account ${options.account} with at least 10 available USDC`);
26547
+ if (!agents.some((entry) => entry.address.toLowerCase() === options.agent.toLowerCase() && (entry.validUntil === null || entry.validUntil > Date.now())))
26548
+ throw new Error("Approve the displayed agent in Hyperliquid API wallets, or connect its account wallet");
26549
+ const position = state.assetPositions.find((entry) => entry.position.coin === "SOL")?.position;
26550
+ if (position && position.leverage.value > options.maxLeverage)
26551
+ throw new Error("SOL leverage exceeds the configured hedge limit");
26552
+ }
26553
+ async function setup_hyperliquid(options) {
26554
+ if (Boolean(options.wallet) === Boolean(options.accountKey))
26555
+ throw new Error("Provide one Hyperliquid account signer source");
26556
+ if (!Number.isInteger(options.leverage) || options.leverage < 1 || options.leverage > 10)
26557
+ throw new Error("Setup leverage must be 1 to 10");
26558
+ if (Boolean(options.agent) === Boolean(options.agentKey))
26559
+ throw new Error("Provide one Hyperliquid agent identity source");
26560
+ const agent = options.agent ?? privateKeyToAccount(options.agentKey).address;
26561
+ if (!/^0x[0-9a-fA-F]{40}$/.test(agent))
26562
+ throw new Error("Hyperliquid agent address is invalid");
26563
+ const wallet = options.wallet ?? privateKeyToAccount(options.accountKey);
26564
+ const account = await getWalletAddress(wallet);
26565
+ if (options.account && account.toLowerCase() !== options.account.toLowerCase())
26566
+ throw new Error("The setup signer does not own the configured Hyperliquid account");
26567
+ if (account.toLowerCase() === agent.toLowerCase())
26568
+ throw new Error("The account and agent must be separate wallets");
26569
+ const transport = new HttpTransport({
26570
+ isTestnet: (options.network ?? "testnet") === "testnet"
26571
+ });
26572
+ const info = new InfoClient({ transport });
26573
+ const state = await info.clearinghouseState({ user: account });
26574
+ if (units(state.marginSummary.accountValue, 6, false) < 10000000n)
26575
+ throw new Error(`Fund Hyperliquid ${options.network ?? "testnet"} account ${account} with at least 10 USDC before setup`);
26576
+ const client = new ExchangeClient({ wallet, transport });
26577
+ const agents = await info.extraAgents({ user: account });
26578
+ if (!agents.some((entry) => entry.address.toLowerCase() === agent.toLowerCase() && (entry.validUntil === null || entry.validUntil > Date.now())))
26579
+ await client.approveAgent({
26580
+ agentAddress: agent,
26581
+ agentName: options.agentName ?? "Depth maker"
26582
+ });
26583
+ const meta = await info.meta();
26584
+ const asset = meta.universe.findIndex((entry) => entry.name === "SOL" && !entry.isDelisted);
26585
+ if (asset < 0)
26586
+ throw new Error("Hyperliquid SOL perpetual is unavailable");
26587
+ await client.updateLeverage({
26588
+ asset,
26589
+ isCross: true,
26590
+ leverage: options.leverage
26591
+ });
26592
+ return {
26593
+ account,
26594
+ agent,
26595
+ network: options.network ?? "testnet",
26596
+ leverage: options.leverage
26597
+ };
26598
+ }
26599
+
26600
+ // src/automation/types.ts
26601
+ import { PublicKey as PublicKey2 } from "@solana/web3.js";
26602
+
26603
+ // src/bot/policy.ts
26604
+ import { positionPnl } from "@depthhq/sdk";
26605
+
26606
+ // src/hedge/policy.ts
26607
+ function validateHedgePolicy(policy) {
26608
+ if (!Number.isInteger(policy.maxLeverage) || policy.maxLeverage < 1 || policy.maxLeverage > 10)
26609
+ throw new RangeError("Maximum hedge leverage must be 1 to 10");
26610
+ if (!Number.isInteger(policy.ratioBps) || policy.ratioBps < 1 || policy.ratioBps > 1e4)
26611
+ throw new RangeError("Hedge ratio must be 1 to 10000 bps");
26612
+ if (policy.thresholdNotional <= 0n || policy.maxOrderNotional <= 0n || policy.maxUnhedgedNotional <= policy.thresholdNotional)
26613
+ throw new RangeError("Hedge notional limits are invalid");
26614
+ for (const value of [
26615
+ policy.batchMs,
26616
+ policy.maxUnhedgedMs,
26617
+ policy.maxPriceAgeMs,
26618
+ policy.orderTtlMs
26619
+ ])
26620
+ if (!Number.isSafeInteger(value) || value < 1 || value > 300000)
26621
+ throw new RangeError("Hedge time limits must be 1 to 300000 ms");
26622
+ if (policy.batchMs >= policy.maxUnhedgedMs || policy.orderTtlMs > policy.maxUnhedgedMs)
26623
+ throw new RangeError("Hedge batching and order expiry must fit the exposure time limit");
26624
+ if (!Number.isInteger(policy.maxSlippageBps) || policy.maxSlippageBps < 1 || policy.maxSlippageBps > 100)
26625
+ throw new RangeError("Hedge slippage must be 1 to 100 bps");
26626
+ }
26627
+
26628
+ // src/bot/policy.ts
26629
+ function validateBotPolicy(policy) {
26630
+ if (policy.maxCapacity <= 0n || policy.maxInventoryNotional <= 0n || policy.maxCapacity > policy.maxInventoryNotional)
26631
+ throw new RangeError("Maker capacity must fit a positive inventory limit");
26632
+ for (const [name, value] of Object.entries(policy))
26633
+ if (typeof value === "number" && (!Number.isSafeInteger(value) || value <= 0) && !(["inventorySkewBps", "executionCostBps"].includes(name) && value === 0))
26634
+ throw new RangeError(`${name} must be a positive integer`);
26635
+ if (policy.refreshSeconds >= policy.lifetimeSeconds)
26636
+ throw new RangeError("Refresh must precede quote expiry");
26637
+ if (policy.spreadBps >= 20000 || policy.repriceBps > 1e4)
26638
+ throw new RangeError("Maker price bounds are invalid");
26639
+ if ((policy.inventorySkewBps ?? 0) > 100 || (policy.executionCostBps ?? 0) > 100)
26640
+ throw new RangeError("Maker pricing adjustments must be 0 to 100 bps");
26641
+ if ((policy.fundingHours ?? 1) > 168)
26642
+ throw new RangeError("Maker funding horizon must be at most 168 hours");
26643
+ const reserve = policy.gasReserveLamports ?? 50000000n;
26644
+ if (reserve < 10000000n || reserve > BigInt(Number.MAX_SAFE_INTEGER))
26645
+ throw new RangeError("Maker SOL reserve must be at least 10000000 lamports");
26646
+ }
26647
+
26648
+ // src/automation/types.ts
26649
+ var amount2 = (value, name) => {
26650
+ if (typeof value !== "string" || !/^\d{1,20}$/.test(value) || BigInt(value) > (1n << 64n) - 1n)
26651
+ throw new Error(`${name} must be an unsigned integer amount`);
26652
+ return BigInt(value);
26653
+ };
26654
+ var object2 = (value, names) => {
26655
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !names.includes(key)))
26656
+ throw new Error("Automation configuration is invalid");
26657
+ return value;
26658
+ };
26659
+ var integer2 = (value, fallback) => {
26660
+ const result = value ?? fallback;
26661
+ if (typeof result !== "number" || !Number.isSafeInteger(result))
26662
+ throw new Error("Automation limits must be integers");
26663
+ return result;
26664
+ };
26665
+ function automationConfig(value) {
26666
+ const input = object2(value, ["quote", "hedge"]);
26667
+ const quote = object2(input.quote, [
26668
+ "maxCapacity",
26669
+ "maxInventoryNotional",
26670
+ "spreadBps",
26671
+ "repriceBps",
26672
+ "lifetimeSeconds",
26673
+ "refreshSeconds",
26674
+ "gasReserveLamports",
26675
+ "inventorySkewBps",
26676
+ "executionCostBps",
26677
+ "fundingHours"
26678
+ ]);
26679
+ const policy = {
26680
+ maxCapacity: amount2(quote.maxCapacity, "Capacity"),
26681
+ maxInventoryNotional: amount2(quote.maxInventoryNotional, "Inventory limit"),
26682
+ spreadBps: integer2(quote.spreadBps),
26683
+ repriceBps: integer2(quote.repriceBps),
26684
+ lifetimeSeconds: integer2(quote.lifetimeSeconds),
26685
+ refreshSeconds: integer2(quote.refreshSeconds),
26686
+ gasReserveLamports: amount2(quote.gasReserveLamports ?? "50000000", "SOL reserve"),
26687
+ inventorySkewBps: integer2(quote.inventorySkewBps, 15),
26688
+ executionCostBps: integer2(quote.executionCostBps, 5),
26689
+ fundingHours: integer2(quote.fundingHours, 1)
26690
+ };
26691
+ validateBotPolicy(policy);
26692
+ if (policy.lifetimeSeconds > 86400 || policy.refreshSeconds > 86400)
26693
+ throw new Error("Quote lifetime must be at most one day");
26694
+ if (input.hedge === undefined)
26695
+ return { quote: policy };
26696
+ const hedge = object2(input.hedge, ["account", "policy"]);
26697
+ if (typeof hedge.account !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(hedge.account))
26698
+ throw new Error("Hyperliquid account address is invalid");
26699
+ const risk = object2(hedge.policy, [
26700
+ "ratioBps",
26701
+ "thresholdNotional",
26702
+ "maxUnhedgedNotional",
26703
+ "maxOrderNotional",
26704
+ "batchMs",
26705
+ "maxUnhedgedMs",
26706
+ "maxSlippageBps",
26707
+ "maxPriceAgeMs",
26708
+ "orderTtlMs",
26709
+ "maxLeverage"
26710
+ ]);
26711
+ const hedgePolicy = {
26712
+ ratioBps: integer2(risk.ratioBps),
26713
+ thresholdNotional: amount2(risk.thresholdNotional, "Hedge threshold"),
26714
+ maxUnhedgedNotional: amount2(risk.maxUnhedgedNotional, "Exposure limit"),
26715
+ maxOrderNotional: amount2(risk.maxOrderNotional, "Order limit"),
26716
+ batchMs: integer2(risk.batchMs),
26717
+ maxUnhedgedMs: integer2(risk.maxUnhedgedMs),
26718
+ maxSlippageBps: integer2(risk.maxSlippageBps),
26719
+ maxPriceAgeMs: integer2(risk.maxPriceAgeMs),
26720
+ orderTtlMs: integer2(risk.orderTtlMs),
26721
+ maxLeverage: integer2(risk.maxLeverage)
26722
+ };
26723
+ validateHedgePolicy(hedgePolicy);
26724
+ return {
26725
+ quote: policy,
26726
+ hedge: {
26727
+ account: hedge.account.toLowerCase(),
26728
+ policy: hedgePolicy
26729
+ }
26730
+ };
26731
+ }
26732
+ var automationWire = (config) => JSON.parse(JSON.stringify(config, (_key, value) => typeof value === "bigint" ? value.toString() : value));
26733
+ function automationView(value, owner, environment) {
26734
+ if (value === null)
26735
+ return null;
26736
+ const view = object2(value, [
26737
+ "owner",
26738
+ "environment",
26739
+ "revision",
26740
+ "operator",
26741
+ "agent",
26742
+ "config",
26743
+ "desired",
26744
+ "phase",
26745
+ "updatedAt",
26746
+ "reason",
26747
+ "bot",
26748
+ "hedge",
26749
+ "funding"
26750
+ ]);
26751
+ if (view.owner !== owner || view.environment !== environment)
26752
+ throw new Error("Automation belongs to another wallet or network");
26753
+ if (typeof view.operator !== "string" || new PublicKey2(view.operator).toBase58() !== view.operator || typeof view.agent !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(view.agent) || integer2(view.revision) < 1 || integer2(view.updatedAt) < 1 || !["running", "paused"].includes(String(view.desired)) || ![
26754
+ "configured",
26755
+ "starting",
26756
+ "running",
26757
+ "pausing",
26758
+ "paused",
26759
+ "blocked"
26760
+ ].includes(String(view.phase)) || view.reason !== undefined && typeof view.reason !== "string")
26761
+ throw new Error("Automation response is invalid");
26762
+ automationConfig(view.config);
26763
+ if (view.bot !== undefined) {
26764
+ const bot = object2(view.bot, ["phase", "reason", "quoteId", "fillCount"]);
26765
+ if (!["active", "pending", "idle", "stopped"].includes(String(bot.phase)) || integer2(bot.fillCount) < 0 || bot.reason !== undefined && typeof bot.reason !== "string" || bot.quoteId !== undefined && (typeof bot.quoteId !== "string" || new PublicKey2(bot.quoteId).toBase58() !== bot.quoteId))
26766
+ throw new Error("Automation status is invalid");
26767
+ }
26768
+ hedgeView(view.hedge, owner);
26769
+ if (view.funding !== undefined) {
26770
+ const funding = object2(view.funding, ["signature", "lamports", "status"]);
26771
+ if (typeof funding.signature !== "string" || !/^[1-9A-HJ-NP-Za-km-z]{64,90}$/.test(funding.signature) || !["pending", "confirmed", "expired", "failed"].includes(String(funding.status)))
26772
+ throw new Error("Operator funding receipt is invalid");
26773
+ amount2(funding.lamports, "Operator funding");
26774
+ }
26775
+ return view;
26776
+ }
26777
+
26778
+ // src/automation/automation.ts
26779
+ class MakerAutomation extends WalletSession {
26780
+ maker;
26781
+ constructor(maker) {
26782
+ super(maker.options, "Manage maker automation", "automation/");
26783
+ this.maker = maker;
26784
+ }
26785
+ view(value) {
26786
+ return automationView(value, this.maker.options.wallet.publicKey.toBase58(), this.maker.options.environment ?? "");
26787
+ }
26788
+ async summary() {
26789
+ if (!this.token || this.token.expiresAt <= Date.now())
26790
+ throw new Error("Authorize automation with your maker wallet");
26791
+ return this.view(await this.authorizedRequest(""));
26792
+ }
26793
+ async configure(config, revision) {
26794
+ const wire = automationWire(config);
26795
+ automationConfig(wire);
26796
+ if (revision === undefined) {
26797
+ if (!this.token || this.token.expiresAt <= Date.now())
26798
+ await this.authorize();
26799
+ revision = (await this.summary())?.revision ?? 0;
26800
+ }
26801
+ if (!Number.isSafeInteger(revision) || revision < 0)
26802
+ throw new Error("Automation revision is invalid");
26803
+ return this.view(await this.authorizedRequest("configure", { revision, config: wire }));
26804
+ }
26805
+ async approve(profile, options = {}) {
26806
+ this.view(profile);
26807
+ const current = await this.summary();
26808
+ if (!current || current.revision !== profile.revision || current.operator !== profile.operator || current.agent !== profile.agent)
26809
+ throw new Error("Automation configuration changed; review it again");
26810
+ if (!this.maker.options.maker.equals(this.maker.options.wallet.publicKey))
26811
+ throw new Error("The maker owner must approve automation");
26812
+ if (current.desired !== "paused" || !["configured", "paused"].includes(current.phase))
26813
+ throw new Error("Pause automation before approving its operator");
26814
+ const config = automationConfig(current.config);
26815
+ const grantSeconds = options.grantSeconds ?? 86400;
26816
+ if (!Number.isSafeInteger(grantSeconds) || grantSeconds < 60 || grantSeconds > 604800)
26817
+ throw new Error("Operator approval must last 1 minute to 7 days");
26818
+ const target = options.operatorFundingLamports ?? config.quote.gasReserveLamports + 200000000n;
26819
+ if (target < config.quote.gasReserveLamports || target > 10000000000n)
26820
+ throw new Error("Operator funding must cover the reserve and stay within 10 SOL");
26821
+ if (config.hedge) {
26822
+ const network = profile.environment === "devnet" ? "testnet" : "mainnet";
26823
+ if (options.hedgeWallet)
26824
+ await setup_hyperliquid({
26825
+ wallet: options.hedgeWallet,
26826
+ account: config.hedge.account,
26827
+ agent: profile.agent,
26828
+ network,
26829
+ leverage: config.hedge.policy.maxLeverage,
26830
+ agentName: `Depth ${profile.agent.slice(2, 10)}`
26831
+ });
26832
+ else
26833
+ await verify_hyperliquid({
26834
+ account: config.hedge.account,
26835
+ agent: profile.agent,
26836
+ network,
26837
+ maxLeverage: config.hedge.policy.maxLeverage
26838
+ });
26839
+ }
26840
+ const operator = new PublicKey3(profile.operator);
26841
+ const balance = BigInt(await this.maker.options.connection.getBalance(operator, "confirmed"));
26842
+ const signatures = [];
26843
+ const recovered = this.view(await this.authorizedRequest("fund", { revision: current.revision }));
26844
+ if (recovered.funding?.status === "pending")
26845
+ signatures.push((await this.waitFunding(current.revision)).signature);
26846
+ else if (balance < target) {
26847
+ const blockhash = await this.maker.options.connection.getLatestBlockhash("confirmed");
26848
+ const transaction = new Transaction({
26849
+ ...blockhash,
26850
+ feePayer: this.maker.options.wallet.publicKey
26851
+ }).add(SystemProgram.transfer({
26852
+ fromPubkey: this.maker.options.wallet.publicKey,
26853
+ toPubkey: operator,
26854
+ lamports: target - balance
26855
+ }));
26856
+ const signed = await this.maker.options.wallet.signTransaction(transaction);
26857
+ this.view(await this.authorizedRequest("fund", {
26858
+ revision: current.revision,
26859
+ transaction: Buffer.from(signed.serialize()).toString("base64"),
26860
+ lastValidBlockHeight: blockhash.lastValidBlockHeight,
26861
+ targetLamports: target.toString()
26862
+ }));
26863
+ signatures.push((await this.waitFunding(current.revision)).signature);
26864
+ }
26865
+ signatures.push(await this.maker.operator.authorize({
26866
+ operator,
26867
+ maxCapacity: config.quote.maxCapacity,
26868
+ expiresAt: Math.floor(Date.now() / 1000) + grantSeconds,
26869
+ canView: true
26870
+ }));
26871
+ return { profile: current, signatures };
26872
+ }
26873
+ async waitFunding(revision) {
26874
+ const deadline = Date.now() + 120000;
26875
+ for (;; ) {
26876
+ const profile = this.view(await this.authorizedRequest("fund", { revision }));
26877
+ const receipt = profile.funding;
26878
+ if (!receipt)
26879
+ throw new Error("Operator funding receipt is unavailable");
26880
+ if (receipt.status === "confirmed")
26881
+ return receipt;
26882
+ if (receipt.status !== "pending")
26883
+ throw new Error(`Operator funding ${receipt.status}; retry approval after reviewing its receipt`);
26884
+ if (Date.now() >= deadline)
26885
+ throw new Error("Operator funding is pending; its signed transaction is saved and approval can resume");
26886
+ await new Promise((resolve) => setTimeout(resolve, 1000));
26887
+ }
26888
+ }
26889
+ async command(action, revision) {
26890
+ if (revision === undefined) {
26891
+ if (!this.token || this.token.expiresAt <= Date.now())
26892
+ await this.authorize();
26893
+ const profile = await this.summary();
26894
+ if (!profile)
26895
+ throw new Error("Configure maker automation first");
26896
+ revision = profile.revision;
26897
+ }
26898
+ if (!Number.isSafeInteger(revision) || revision < 1)
26899
+ throw new Error("Automation revision is invalid");
26900
+ return this.view(await this.authorizedRequest(action, { revision }));
26901
+ }
26902
+ start(revision) {
26903
+ return this.command("start", revision);
26904
+ }
26905
+ pause(revision) {
26906
+ return this.command("pause", revision);
26907
+ }
26908
+ resume(revision) {
26909
+ return this.start(revision);
26910
+ }
26911
+ }
26912
+
26409
26913
  // src/client/client.ts
26410
26914
  import {
26411
26915
  DepthClient,
@@ -26445,7 +26949,7 @@ async function queueAccounts(provider, connection, name, offset) {
26445
26949
  import * as anchor2 from "@anchor-lang/core";
26446
26950
  import { DEPTH_PROGRAM_ID as DEPTH_PROGRAM_ID2, SOL_USD_MARKET_ID } from "@depthhq/bindings";
26447
26951
  import { DepthError } from "@depthhq/sdk";
26448
- import { PublicKey as PublicKey2 } from "@solana/web3.js";
26952
+ import { PublicKey as PublicKey4 } from "@solana/web3.js";
26449
26953
  function u64(value, name) {
26450
26954
  if (typeof value !== "bigint" || value < 0n || value > (1n << 64n) - 1n)
26451
26955
  throw new RangeError(`${name} must be an unsigned 64-bit bigint`);
@@ -26462,7 +26966,7 @@ function timing(options) {
26462
26966
  }
26463
26967
  function address2(market, maker, nonce) {
26464
26968
  const bytes = u64(nonce, "clientNonce").toArrayLike(Buffer, "le", 8);
26465
- return PublicKey2.findProgramAddressSync([Buffer.from("backstop"), market.toBuffer(), maker.toBuffer(), bytes], DEPTH_PROGRAM_ID2)[0];
26969
+ return PublicKey4.findProgramAddressSync([Buffer.from("backstop"), market.toBuffer(), maker.toBuffer(), bytes], DEPTH_PROGRAM_ID2)[0];
26466
26970
  }
26467
26971
  async function poll(read2, terminal, options) {
26468
26972
  const { timeoutMs, pollMs } = timing(options);
@@ -26504,7 +27008,7 @@ class BackstopHandle {
26504
27008
  ]);
26505
27009
  const now = Math.floor(Date.now() / 1000);
26506
27010
  const margin = (input.maxCapacity * BigInt(market.config.initialMarginBps) + 9999n) / 10000n;
26507
- if (!market.backstopOffer.equals(PublicKey2.default) || market.pendingOrders !== 0 || Object.keys(market.status)[0] === "settling" || input.maxCapacity > BigInt(market.config.maxAccountNotional.toString()) || margin < BigInt(market.config.backstopMinCollateral.toString()))
27011
+ if (!market.backstopOffer.equals(PublicKey4.default) || market.pendingOrders !== 0 || Object.keys(market.status)[0] === "settling" || input.maxCapacity > BigInt(market.config.maxAccountNotional.toString()) || margin < BigInt(market.config.backstopMinCollateral.toString()))
26508
27012
  throw new Error("Backstop offer is outside the current market policy");
26509
27013
  if (!account.owner.equals(client.options.maker) || !account.market.equals(client.marketAddress) || !("ready" in account.status))
26510
27014
  throw new Error("Maker market account is unavailable");
@@ -26591,7 +27095,7 @@ class BackstopRelease {
26591
27095
 
26592
27096
  // src/fill/history.ts
26593
27097
  import * as anchor3 from "@anchor-lang/core";
26594
- import { PublicKey as PublicKey3 } from "@solana/web3.js";
27098
+ import { PublicKey as PublicKey5 } from "@solana/web3.js";
26595
27099
 
26596
27100
  class FillHistory {
26597
27101
  client;
@@ -26641,9 +27145,9 @@ class FillHistory {
26641
27145
  for (const ix of instructions2) {
26642
27146
  if (!keys.get(ix.programIdIndex)?.equals(this.client.program.programId) || !ix.data.subarray(0, 8).equals(Buffer.from(definition.discriminator)))
26643
27147
  continue;
26644
- const order = keys.get(ix.accounts[orderIndex]);
26645
- if (order)
26646
- orders.set(order.toBase58(), {
27148
+ const order2 = keys.get(ix.accounts[orderIndex]);
27149
+ if (order2)
27150
+ orders.set(order2.toBase58(), {
26647
27151
  signature: signature.signature,
26648
27152
  slot: signature.slot
26649
27153
  });
@@ -26654,12 +27158,12 @@ class FillHistory {
26654
27158
  const fills = [];
26655
27159
  for (let i2 = 0;i2 < addresses.length; i2 += 100) {
26656
27160
  const batch = addresses.slice(i2, i2 + 100);
26657
- const accounts2 = await connection.getMultipleAccountsInfo(batch.map((id) => new PublicKey3(id)), "finalized");
27161
+ const accounts2 = await connection.getMultipleAccountsInfo(batch.map((id) => new PublicKey5(id)), "finalized");
26658
27162
  accounts2.forEach((info, index) => {
26659
27163
  if (!info || !info.owner.equals(this.client.program.programId))
26660
27164
  throw new Error("Finalized fill receipt is unavailable");
26661
- const order = this.client.program.coder.accounts.decode("orderRecord", info.data);
26662
- if (order.maker.equals(this.client.options.maker) && order.market.equals(this.client.marketAddress) && "trade" in order.kind && "filled" in order.status) {
27165
+ const order2 = this.client.program.coder.accounts.decode("orderRecord", info.data);
27166
+ if (order2.maker.equals(this.client.options.maker) && order2.market.equals(this.client.marketAddress) && "trade" in order2.kind && "filled" in order2.status) {
26663
27167
  fills.push({ id: batch[index], ...orders.get(batch[index]) });
26664
27168
  }
26665
27169
  });
@@ -26915,51 +27419,78 @@ class QuoteHandle {
26915
27419
  }
26916
27420
  async cancel() {
26917
27421
  const snapshot = await this.status();
27422
+ if (["cancelled", "filled", "rejected", "releasing"].includes(snapshot.status))
27423
+ return snapshot;
26918
27424
  const actor = this.client.options.wallet.publicKey;
26919
27425
  const grant = actor.equals(this.client.options.maker) ? undefined : this.client.grantAddress();
26920
27426
  if (snapshot.status === "validating" || snapshot.status === "validated") {
26921
- await this.client.program.methods.cancelPreparedQuote().accountsPartial({
27427
+ return this.cancelStep(() => this.client.program.methods.cancelPreparedQuote().accountsPartial({
26922
27428
  actor,
26923
27429
  grant: grant ?? this.client.program.programId,
26924
27430
  quote: this.id
26925
- }).rpc({ commitment: "confirmed" });
26926
- return this.status();
27431
+ }).rpc({ commitment: "confirmed" }), ["cancelled", "rejected"]);
26927
27432
  }
26928
27433
  if (snapshot.status === "active" || snapshot.status === "waiting") {
26929
- await this.client.program.methods.stopQuote().accountsPartial({
27434
+ await this.cancelStep(() => this.client.program.methods.stopQuote().accountsPartial({
26930
27435
  actor,
26931
27436
  market: this.client.marketAddress,
26932
27437
  quoteQueue: this.client.quoteQueueAddress,
26933
27438
  grant: grant ?? this.client.program.programId,
26934
27439
  quote: this.id
26935
- }).rpc({ commitment: "confirmed" });
27440
+ }).rpc({ commitment: "confirmed" }), ["closing", "releasing", "cancelled", "filled"]);
26936
27441
  } else if (snapshot.status !== "closing") {
26937
27442
  throw new Error("Quote cannot be cancelled in its current state");
26938
27443
  }
26939
27444
  const current = await this.status();
26940
- if (snapshot.status === "waiting")
27445
+ if (current.status !== "closing")
26941
27446
  return current;
26942
27447
  if (current.attachedOrders > 0)
26943
27448
  return current;
27449
+ const [market, makerAccount] = await Promise.all([
27450
+ this.client.read("Market", this.client.marketAddress),
27451
+ this.client.read("MarketAccount", this.client.marketAccount())
27452
+ ]);
27453
+ if (market.pendingOrders > 0 || name(makerAccount.status) !== "ready")
27454
+ return current;
26944
27455
  const offset = new anchor4.BN(randomBytes3(8), "le");
26945
27456
  const queue = await this.client.queue("release_quote", offset);
26946
- this.releaseSignature = await this.client.program.methods.releaseQuote(offset).accountsPartial({
26947
- ...queue,
26948
- payer: actor,
26949
- protocol: this.client.protocolAddress,
26950
- market: this.client.marketAddress,
26951
- marketAccount: this.client.marketAccount(),
26952
- grant: grant ?? this.client.program.programId,
26953
- quote: this.id
26954
- }).rpc({ commitment: "confirmed" });
27457
+ return this.cancelStep(async () => {
27458
+ this.releaseSignature = await this.client.program.methods.releaseQuote(offset).accountsPartial({
27459
+ ...queue,
27460
+ payer: actor,
27461
+ protocol: this.client.protocolAddress,
27462
+ market: this.client.marketAddress,
27463
+ marketAccount: this.client.marketAccount(),
27464
+ grant: grant ?? this.client.program.programId,
27465
+ quote: this.id
27466
+ }).rpc({ commitment: "confirmed" });
27467
+ }, ["releasing", "cancelled", "filled"]);
27468
+ }
27469
+ async cancelStep(submit, completed) {
27470
+ try {
27471
+ await submit();
27472
+ } catch (error) {
27473
+ const snapshot = await this.status();
27474
+ if (completed.includes(snapshot.status))
27475
+ return snapshot;
27476
+ throw error;
27477
+ }
26955
27478
  return this.status();
26956
27479
  }
27480
+ async close() {
27481
+ if (!this.client.options.wallet.publicKey.equals(this.client.options.maker))
27482
+ throw new Error("Only the maker owner can reclaim quote rent");
27483
+ const snapshot = await this.status();
27484
+ if (!["cancelled", "filled", "rejected"].includes(snapshot.status) || snapshot.attachedOrders > 0)
27485
+ throw new Error("Release the quote and settle its orders before closing");
27486
+ return this.client.program.methods.closeQuote().accountsPartial({ maker: this.client.options.maker, quote: this.id }).rpc({ commitment: "confirmed" });
27487
+ }
26957
27488
  }
26958
27489
 
26959
27490
  // src/quote/replacement.ts
26960
27491
  import * as anchor5 from "@anchor-lang/core";
26961
27492
  import { DEPTH_PROGRAM_ID as DEPTH_PROGRAM_ID3 } from "@depthhq/bindings";
26962
- import { PublicKey as PublicKey4 } from "@solana/web3.js";
27493
+ import { PublicKey as PublicKey6 } from "@solana/web3.js";
26963
27494
  class QuoteReplacement {
26964
27495
  client;
26965
27496
  priorId;
@@ -26973,7 +27504,7 @@ class QuoteReplacement {
26973
27504
  throw new RangeError("clientNonce must be an unsigned 64-bit bigint");
26974
27505
  }
26975
27506
  const bytes = new anchor5.BN(nonce.toString()).toArrayLike(Buffer, "le", 8);
26976
- this.nextId = PublicKey4.findProgramAddressSync([
27507
+ this.nextId = PublicKey6.findProgramAddressSync([
26977
27508
  Buffer.from("quote"),
26978
27509
  client.marketAddress.toBuffer(),
26979
27510
  client.options.maker.toBuffer(),
@@ -26998,17 +27529,7 @@ class QuoteReplacement {
26998
27529
  phase: next?.status === "active" || next?.status === "filled" ? "active" : next?.status === "rejected" ? "failed" : next ? "publishing" : stopped ? "ready" : "stopping"
26999
27530
  };
27000
27531
  }
27001
- async resume(input, options = {}) {
27002
- if (input.clientNonce !== undefined && input.clientNonce !== this.nonce) {
27003
- throw new Error("Replacement terms use a different client nonce");
27004
- }
27005
- const timeoutMs = options.timeoutMs ?? 60000;
27006
- const pollMs = options.pollMs ?? 1000;
27007
- if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0 || !Number.isSafeInteger(pollMs) || pollMs < 100) {
27008
- throw new RangeError("Replacement timeout or polling interval is invalid");
27009
- }
27010
- const deadline = Date.now() + timeoutMs;
27011
- const previous = this.client.quote.get(this.priorId);
27532
+ async begin() {
27012
27533
  const prior = await this.client.quoteState(this.priorId);
27013
27534
  const recorded = prior.replacementNonce == null ? undefined : BigInt(prior.replacementNonce.toString());
27014
27535
  if (recorded !== undefined && recorded !== this.nonce) {
@@ -27022,6 +27543,17 @@ class QuoteReplacement {
27022
27543
  quote: this.priorId
27023
27544
  }).rpc({ commitment: "confirmed" });
27024
27545
  }
27546
+ }
27547
+ async resume(input, options = {}) {
27548
+ if (input.clientNonce !== undefined && input.clientNonce !== this.nonce)
27549
+ throw new Error("Replacement terms use a different client nonce");
27550
+ const timeoutMs = options.timeoutMs ?? 60000;
27551
+ const pollMs = options.pollMs ?? 1000;
27552
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0 || !Number.isSafeInteger(pollMs) || pollMs < 100)
27553
+ throw new RangeError("Replacement timeout or polling interval is invalid");
27554
+ const deadline = Date.now() + timeoutMs;
27555
+ const previous = this.client.quote.get(this.priorId);
27556
+ await this.begin();
27025
27557
  for (;; ) {
27026
27558
  const snapshot = await previous.status();
27027
27559
  if (["cancelled", "filled"].includes(snapshot.status))
@@ -27047,7 +27579,7 @@ class QuoteReplacement {
27047
27579
 
27048
27580
  // src/client/client.ts
27049
27581
  function pda(seeds) {
27050
- return PublicKey5.findProgramAddressSync(seeds, DEPTH_PROGRAM_ID4)[0];
27582
+ return PublicKey7.findProgramAddressSync(seeds, DEPTH_PROGRAM_ID4)[0];
27051
27583
  }
27052
27584
  function u642(value, name2) {
27053
27585
  if (typeof value !== "bigint" || value < 0n || value > (1n << 64n) - 1n) {
@@ -27058,12 +27590,25 @@ function u642(value, name2) {
27058
27590
  function offset() {
27059
27591
  return new anchor6.BN(randomBytes3(8), "le");
27060
27592
  }
27593
+ async function retryQuote(action, deadline) {
27594
+ for (;; ) {
27595
+ try {
27596
+ return await action();
27597
+ } catch (error) {
27598
+ if (!(error instanceof anchor6.AnchorError) || error.error.errorCode.code !== "WrongMarketAccountStatus" || Date.now() >= deadline)
27599
+ throw error;
27600
+ await new Promise((resolve) => setTimeout(resolve, 1000));
27601
+ }
27602
+ }
27603
+ }
27061
27604
  function stateName(value) {
27062
27605
  return Object.keys(value)[0] ?? "unknown";
27063
27606
  }
27064
27607
 
27065
27608
  class MakerClient {
27066
27609
  options;
27610
+ hedges;
27611
+ automation;
27067
27612
  provider;
27068
27613
  program;
27069
27614
  position;
@@ -27091,6 +27636,8 @@ class MakerClient {
27091
27636
  }
27092
27637
  constructor(options) {
27093
27638
  this.options = options;
27639
+ this.hedges = new MakerAccess(options);
27640
+ this.automation = new MakerAutomation(this);
27094
27641
  this.provider = new DepthProvider(options.connection, options.wallet, {
27095
27642
  commitment: "confirmed",
27096
27643
  preflightCommitment: "confirmed"
@@ -27100,7 +27647,7 @@ class MakerClient {
27100
27647
  this.backstop = Object.assign((id) => new BackstopHandle(id, this), { offer: (input) => BackstopHandle.offer(this, input) });
27101
27648
  }
27102
27649
  static async create(options) {
27103
- const appId = typeof options.appId === "string" ? new PublicKey5(options.appId) : options.appId;
27650
+ const appId = typeof options.appId === "string" ? new PublicKey7(options.appId) : options.appId;
27104
27651
  if (options.maker && options.makerOwner && !options.maker.equals(options.makerOwner)) {
27105
27652
  throw new Error("maker and makerOwner identify different accounts");
27106
27653
  }
@@ -27144,21 +27691,21 @@ class MakerClient {
27144
27691
  openMarket: async () => this.funding.openMarket(),
27145
27692
  summary: async () => (await this.owned()).account.summary(),
27146
27693
  pending: async () => (await this.owned()).account.pending(),
27147
- deposit: async (sourceOrAmount, amount, clientNonce) => {
27148
- if (sourceOrAmount instanceof PublicKey5) {
27149
- if (amount === undefined || clientNonce === undefined)
27694
+ deposit: async (sourceOrAmount, amount3, clientNonce) => {
27695
+ if (sourceOrAmount instanceof PublicKey7) {
27696
+ if (amount3 === undefined || clientNonce === undefined)
27150
27697
  throw new RangeError("Source deposits require amount and nonce");
27151
- return this.funding.deposit(sourceOrAmount, amount, clientNonce);
27698
+ return this.funding.deposit(sourceOrAmount, amount3, clientNonce);
27152
27699
  }
27153
27700
  return (await this.owned()).account.deposit(sourceOrAmount);
27154
27701
  },
27155
27702
  withdrawalHandle: (clientNonce) => this.funding.withdrawalHandle(clientNonce),
27156
27703
  depositHandle: (nonce) => this.funding.depositHandle(nonce),
27157
- withdraw: async (destinationOrAmount, amount, clientNonce) => {
27158
- if (destinationOrAmount instanceof PublicKey5) {
27159
- if (amount === undefined || clientNonce === undefined)
27704
+ withdraw: async (destinationOrAmount, amount3, clientNonce) => {
27705
+ if (destinationOrAmount instanceof PublicKey7) {
27706
+ if (amount3 === undefined || clientNonce === undefined)
27160
27707
  throw new RangeError("Destination withdrawals require amount and nonce");
27161
- return this.funding.withdraw(destinationOrAmount, amount, clientNonce);
27708
+ return this.funding.withdraw(destinationOrAmount, amount3, clientNonce);
27162
27709
  }
27163
27710
  return (await this.owned()).account.withdraw(destinationOrAmount);
27164
27711
  },
@@ -27193,8 +27740,8 @@ class MakerClient {
27193
27740
  operator.toBuffer()
27194
27741
  ]);
27195
27742
  }
27196
- async read(name2, address3) {
27197
- const info = await this.options.connection.getAccountInfo(address3, "confirmed");
27743
+ async read(name2, address3, commitment = "confirmed") {
27744
+ const info = await this.options.connection.getAccountInfo(address3, commitment);
27198
27745
  if (!info || !info.owner.equals(DEPTH_PROGRAM_ID4)) {
27199
27746
  throw new Error(`${name2} account is unavailable`);
27200
27747
  }
@@ -27278,74 +27825,76 @@ class MakerClient {
27278
27825
  replacement: (prior, clientNonce) => new QuoteReplacement(this, prior, clientNonce),
27279
27826
  publish: async (request) => {
27280
27827
  const clientNonce = request.clientNonce ?? BigInt(new anchor6.BN(randomBytes3(8), "le").toString());
27281
- const quoteNonce = u642(clientNonce, "clientNonce");
27282
- const id = pda([
27283
- Buffer.from("quote"),
27284
- this.marketAddress.toBuffer(),
27285
- this.options.maker.toBuffer(),
27286
- quoteNonce.toArrayLike(Buffer, "le", 8)
27287
- ]);
27288
- if (await this.options.connection.getAccountInfo(id, "confirmed")) {
27289
- const existing = await this.quoteState(id);
27290
- if (BigInt(existing.capacity.toString()) !== request.maxCapacity || "expiresAt" in request && existing.expiry.toNumber() !== request.expiresAt) {
27291
- throw new Error("Quote nonce is already bound to different public terms");
27292
- }
27293
- return this.quote.get(id);
27294
- }
27295
27828
  const readyDeadline = Date.now() + 60000;
27296
- for (;; ) {
27297
- const account = await this.read("MarketAccount", this.marketAccount());
27298
- if (stateName(account.status) === "ready")
27299
- break;
27300
- if (Date.now() >= readyDeadline) {
27301
- throw new Error("Maker account is processing another operation");
27302
- }
27303
- await new Promise((resolve) => setTimeout(resolve, 1000));
27304
- }
27305
- const market = await this.read("Market", this.marketAddress);
27306
- const input = "tiers" in request ? request : this.flatQuote(request, market, clientNonce);
27307
- this.validateQuote(input, market);
27308
- const grant = await this.grantFor(input.maxCapacity, input.expiresAt);
27309
- const mxePublicKey = await getMXEPublicKey(this.provider, DEPTH_PROGRAM_ID4);
27310
- if (!mxePublicKey)
27311
- throw new Error("MXE public key is unavailable");
27312
- const secret = x25519.utils.randomSecretKey();
27313
- const inputKey = x25519.getPublicKey(secret);
27314
- const inputNonce = randomBytes3(16);
27315
- const cipher = new RescueCipher(x25519.getSharedSecret(secret, mxePublicKey));
27316
- const bid = Array.from({ length: 4 }, (_, i2) => input.tiers[i2]?.bidE8 ?? 0n);
27317
- const ask = Array.from({ length: 4 }, (_, i2) => input.tiers[i2]?.askE8 ?? 0n);
27318
- const reciprocal = (price) => {
27319
- if (price === 0n)
27320
- return 0n;
27321
- if (price < 100000000n)
27322
- throw new RangeError("SOL quote prices must be at least $1");
27323
- return (1n << 76n) / price;
27324
- };
27325
- const ciphertexts = cipher.encrypt(packQuoteCurve({
27326
- bid,
27327
- ask,
27328
- bidReciprocal: bid.map(reciprocal),
27329
- askReciprocal: ask.map(reciprocal)
27330
- }), inputNonce);
27331
- const computationOffset = offset();
27332
- const queue = await this.queue("prepare_quote", computationOffset);
27333
- const signature = await this.program.methods.submitQuote(computationOffset, quoteNonce, u642(input.maxCapacity, "maxCapacity"), input.tiers.length, new anchor6.BN(input.expiresAt), market.revision, market.oracleTimestamp, ciphertexts, Array.from(inputKey), new anchor6.BN(inputNonce, "le")).accountsPartial({
27334
- ...queue,
27335
- payer: this.options.wallet.publicKey,
27336
- market: this.marketAddress,
27337
- marketAccount: this.marketAccount(),
27338
- grant: grant ?? this.program.programId,
27339
- quote: id
27340
- }).rpc({ commitment: "confirmed" });
27341
- return new QuoteHandle(id, signature, this);
27829
+ return retryQuote(async () => {
27830
+ const quoteNonce = u642(clientNonce, "clientNonce");
27831
+ const id = pda([
27832
+ Buffer.from("quote"),
27833
+ this.marketAddress.toBuffer(),
27834
+ this.options.maker.toBuffer(),
27835
+ quoteNonce.toArrayLike(Buffer, "le", 8)
27836
+ ]);
27837
+ if (await this.options.connection.getAccountInfo(id, "confirmed")) {
27838
+ const existing = await this.quoteState(id);
27839
+ if (BigInt(existing.capacity.toString()) !== request.maxCapacity || "expiresAt" in request && existing.expiry.toNumber() !== request.expiresAt) {
27840
+ throw new Error("Quote nonce is already bound to different public terms");
27841
+ }
27842
+ return this.quote.get(id);
27843
+ }
27844
+ for (;; ) {
27845
+ const account = await this.read("MarketAccount", this.marketAccount());
27846
+ if (stateName(account.status) === "ready")
27847
+ break;
27848
+ if (Date.now() >= readyDeadline) {
27849
+ throw new Error("Maker account is processing another operation");
27850
+ }
27851
+ await new Promise((resolve) => setTimeout(resolve, 1000));
27852
+ }
27853
+ const market = await this.read("Market", this.marketAddress);
27854
+ const input = "tiers" in request ? request : this.flatQuote(request, market, clientNonce);
27855
+ this.validateQuote(input, market);
27856
+ const grant = await this.grantFor(input.maxCapacity, input.expiresAt);
27857
+ const mxePublicKey = await getMXEPublicKey(this.provider, DEPTH_PROGRAM_ID4);
27858
+ if (!mxePublicKey)
27859
+ throw new Error("MXE public key is unavailable");
27860
+ const secret = x25519.utils.randomSecretKey();
27861
+ const inputKey = x25519.getPublicKey(secret);
27862
+ const inputNonce = randomBytes3(16);
27863
+ const cipher = new RescueCipher(x25519.getSharedSecret(secret, mxePublicKey));
27864
+ const bid = Array.from({ length: 4 }, (_, i2) => input.tiers[i2]?.bidE8 ?? 0n);
27865
+ const ask = Array.from({ length: 4 }, (_, i2) => input.tiers[i2]?.askE8 ?? 0n);
27866
+ const reciprocal = (price) => {
27867
+ if (price === 0n)
27868
+ return 0n;
27869
+ if (price < 100000000n)
27870
+ throw new RangeError("SOL quote prices must be at least $1");
27871
+ return (1n << 76n) / price;
27872
+ };
27873
+ const ciphertexts = cipher.encrypt(packQuoteCurve({
27874
+ bid,
27875
+ ask,
27876
+ bidReciprocal: bid.map(reciprocal),
27877
+ askReciprocal: ask.map(reciprocal)
27878
+ }), inputNonce);
27879
+ const computationOffset = offset();
27880
+ const queue = await this.queue("prepare_quote", computationOffset);
27881
+ const signature = await this.program.methods.submitQuote(computationOffset, quoteNonce, u642(input.maxCapacity, "maxCapacity"), input.tiers.length, new anchor6.BN(input.expiresAt), market.revision, market.oracleTimestamp, ciphertexts, Array.from(inputKey), new anchor6.BN(inputNonce, "le")).accountsPartial({
27882
+ ...queue,
27883
+ payer: this.options.wallet.publicKey,
27884
+ market: this.marketAddress,
27885
+ marketAccount: this.marketAccount(),
27886
+ grant: grant ?? this.program.programId,
27887
+ quote: id
27888
+ }).rpc({ commitment: "confirmed" });
27889
+ return new QuoteHandle(id, signature, this);
27890
+ }, readyDeadline);
27342
27891
  },
27343
27892
  get: (id) => new QuoteHandle(id, undefined, this),
27344
27893
  list: async (options = {}) => {
27345
27894
  const limit = options.limit ?? 20;
27346
27895
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100)
27347
27896
  throw new RangeError("limit must be an integer from 1 to 100");
27348
- const cursor = options.cursor ? new PublicKey5(options.cursor).toBase58() : undefined;
27897
+ const cursor = options.cursor ? new PublicKey7(options.cursor).toBase58() : undefined;
27349
27898
  const accounts2 = await this.options.connection.getProgramAccounts(DEPTH_PROGRAM_ID4, {
27350
27899
  commitment: "confirmed",
27351
27900
  dataSlice: { offset: 0, length: 0 },
@@ -27459,8 +28008,8 @@ class MakerClient {
27459
28008
  grant: grant ?? null
27460
28009
  });
27461
28010
  }
27462
- async requestFillView(order) {
27463
- const state = await this.read("OrderRecord", order);
28011
+ async requestFillView(order2) {
28012
+ const state = await this.read("OrderRecord", order2);
27464
28013
  if (!state.maker.equals(this.options.maker) || !state.market.equals(this.marketAddress) || stateName(state.status) !== "filled" || stateName(state.kind) !== "trade") {
27465
28014
  throw new Error("Fill is not a finalized trade by this maker");
27466
28015
  }
@@ -27468,12 +28017,12 @@ class MakerClient {
27468
28017
  const viewNonce = offset();
27469
28018
  const view = pda([
27470
28019
  Buffer.from("fill-view"),
27471
- order.toBuffer(),
28020
+ order2.toBuffer(),
27472
28021
  this.options.wallet.publicKey.toBuffer(),
27473
28022
  viewNonce.toArrayLike(Buffer, "le", 8)
27474
28023
  ]);
27475
28024
  return this.requestView("fill", view, viewNonce, {
27476
- order,
28025
+ order: order2,
27477
28026
  grant: grant ?? null
27478
28027
  });
27479
28028
  }
@@ -27519,8 +28068,8 @@ class MakerClient {
27519
28068
  const actions = {
27520
28069
  id: address3,
27521
28070
  cancelOpen: () => this.funding.cancelOpenMarket(),
27522
- addMargin: async (amount, clientNonce) => (await this.owned()).position.addMargin(amount, clientNonce),
27523
- removeMargin: async (amount, clientNonce) => (await this.owned()).position.removeMargin(amount, clientNonce),
28071
+ addMargin: async (amount3, clientNonce) => (await this.owned()).position.addMargin(amount3, clientNonce),
28072
+ removeMargin: async (amount3, clientNonce) => (await this.owned()).position.removeMargin(amount3, clientNonce),
27524
28073
  summary: async (options = { timeoutMs: 30000 }) => {
27525
28074
  const view = await this.requestMarketView();
27526
28075
  const result = await view.wait(options);
@@ -27540,7 +28089,7 @@ class MakerClient {
27540
28089
  const store = createCursorStore(genesis, this.options.maker.toBase58(), this.options.cursorDirectory);
27541
28090
  const cursor = await store.load();
27542
28091
  return new FillHistory(this).subscribe(async (fill) => {
27543
- const execution = await this.fills.get(new PublicKey5(fill.id));
28092
+ const execution = await this.fills.get(new PublicKey7(fill.id));
27544
28093
  if (execution.status !== "ready")
27545
28094
  throw new Error(`Private maker fill ${fill.id} is ${execution.status}`);
27546
28095
  await options.onFill({ ...fill, execution });
@@ -27551,8 +28100,8 @@ class MakerClient {
27551
28100
  onError: options.onError
27552
28101
  });
27553
28102
  },
27554
- get: async (order, options = { timeoutMs: 30000 }) => {
27555
- const view = await this.requestFillView(order);
28103
+ get: async (order2, options = { timeoutMs: 30000 }) => {
28104
+ const view = await this.requestFillView(order2);
27556
28105
  const result = await view.wait(options);
27557
28106
  if (result.status !== "pending")
27558
28107
  await view.close();
@@ -27593,12 +28142,67 @@ class MakerClient {
27593
28142
  async function createDepthMaker(options) {
27594
28143
  return MakerClient.create(options);
27595
28144
  }
28145
+ // src/hedge/wallet.ts
28146
+ async function connect_hyperliquid(provider) {
28147
+ const accounts2 = await provider.request({ method: "eth_requestAccounts" });
28148
+ if (!Array.isArray(accounts2) || typeof accounts2[0] !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(accounts2[0]))
28149
+ throw new Error("The Ethereum wallet did not provide an account");
28150
+ const address3 = accounts2[0];
28151
+ const check = async () => {
28152
+ const current = await provider.request({ method: "eth_accounts" });
28153
+ if (!Array.isArray(current) || typeof current[0] !== "string" || current[0].toLowerCase() !== address3.toLowerCase())
28154
+ throw new Error("The Ethereum wallet account changed; connect it again");
28155
+ };
28156
+ return {
28157
+ async getAddresses() {
28158
+ await check();
28159
+ return [address3];
28160
+ },
28161
+ async getChainId() {
28162
+ const chain = await provider.request({ method: "eth_chainId" });
28163
+ if (typeof chain !== "string" || !/^0x[0-9a-fA-F]+$/.test(chain) || !Number.isSafeInteger(Number(chain)))
28164
+ throw new Error("Ethereum wallet chain is invalid");
28165
+ return Number(chain);
28166
+ },
28167
+ async signTypedData(params) {
28168
+ await check();
28169
+ const signature = await provider.request({
28170
+ method: "eth_signTypedData_v4",
28171
+ params: [
28172
+ address3,
28173
+ JSON.stringify({
28174
+ ...params,
28175
+ types: {
28176
+ EIP712Domain: [
28177
+ { name: "name", type: "string" },
28178
+ { name: "version", type: "string" },
28179
+ { name: "chainId", type: "uint256" },
28180
+ { name: "verifyingContract", type: "address" }
28181
+ ],
28182
+ ...params.types
28183
+ }
28184
+ })
28185
+ ]
28186
+ });
28187
+ if (typeof signature !== "string" || !/^0x[0-9a-fA-F]{130}$/.test(signature))
28188
+ throw new Error("Ethereum wallet returned an invalid signature");
28189
+ return signature;
28190
+ }
28191
+ };
28192
+ }
27596
28193
  export {
28194
+ setup_hyperliquid,
27597
28195
  createDepthMaker as create_depth_maker,
27598
28196
  createDepthMaker,
28197
+ connect_hyperliquid,
28198
+ automationWire,
28199
+ automationView,
28200
+ automationConfig,
27599
28201
  QuoteReplacement,
27600
28202
  QuoteHandle,
27601
28203
  MakerClient,
28204
+ MakerAutomation,
28205
+ MakerAccess,
27602
28206
  BackstopRelease,
27603
28207
  BackstopHandle
27604
28208
  };