@depthhq/maker-sdk 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/automation/automation.d.ts +24 -0
- package/dist/automation/types.d.ts +40 -0
- package/dist/bot/bot.d.ts +42 -0
- package/dist/bot/policy.d.ts +41 -0
- package/dist/bot/store.d.ts +32 -0
- package/dist/client/client.d.ts +6 -1
- package/dist/client/types.d.ts +3 -1
- package/dist/hedge/access.d.ts +40 -0
- package/dist/hedge/amount.d.ts +2 -0
- package/dist/hedge/engine.d.ts +9 -0
- package/dist/hedge/hyperliquid.d.ts +35 -0
- package/dist/hedge/policy.d.ts +4 -0
- package/dist/hedge/schema.d.ts +2 -0
- package/dist/hedge/setup.d.ts +22 -0
- package/dist/hedge/types.d.ts +89 -0
- package/dist/hedge/wallet.d.ts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +725 -121
- package/dist/index.node.js +751 -143
- package/dist/node.d.ts +10 -0
- package/dist/node.js +1149 -1
- package/dist/quote/quote.d.ts +2 -0
- package/dist/quote/replacement.d.ts +1 -0
- package/package.json +6 -4
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
|
|
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}`.replace(/\/$/, "")}`, {
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
26645
|
-
if (
|
|
26646
|
-
orders.set(
|
|
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
|
|
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
|
|
26662
|
-
if (
|
|
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
|
-
|
|
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 (
|
|
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.
|
|
26947
|
-
|
|
26948
|
-
|
|
26949
|
-
|
|
26950
|
-
|
|
26951
|
-
|
|
26952
|
-
|
|
26953
|
-
|
|
26954
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
27148
|
-
if (sourceOrAmount instanceof
|
|
27149
|
-
if (
|
|
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,
|
|
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,
|
|
27158
|
-
if (destinationOrAmount instanceof
|
|
27159
|
-
if (
|
|
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,
|
|
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,
|
|
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
|
-
|
|
27297
|
-
const
|
|
27298
|
-
|
|
27299
|
-
|
|
27300
|
-
|
|
27301
|
-
|
|
27302
|
-
|
|
27303
|
-
|
|
27304
|
-
|
|
27305
|
-
|
|
27306
|
-
|
|
27307
|
-
|
|
27308
|
-
|
|
27309
|
-
|
|
27310
|
-
|
|
27311
|
-
|
|
27312
|
-
|
|
27313
|
-
|
|
27314
|
-
|
|
27315
|
-
|
|
27316
|
-
|
|
27317
|
-
|
|
27318
|
-
|
|
27319
|
-
|
|
27320
|
-
|
|
27321
|
-
|
|
27322
|
-
|
|
27323
|
-
|
|
27324
|
-
|
|
27325
|
-
|
|
27326
|
-
|
|
27327
|
-
|
|
27328
|
-
|
|
27329
|
-
|
|
27330
|
-
|
|
27331
|
-
|
|
27332
|
-
|
|
27333
|
-
|
|
27334
|
-
|
|
27335
|
-
|
|
27336
|
-
|
|
27337
|
-
|
|
27338
|
-
|
|
27339
|
-
|
|
27340
|
-
|
|
27341
|
-
|
|
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
|
|
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(
|
|
27463
|
-
const state = await this.read("OrderRecord",
|
|
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
|
-
|
|
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 (
|
|
27523
|
-
removeMargin: async (
|
|
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
|
|
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 (
|
|
27555
|
-
const view = await this.requestFillView(
|
|
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
|
};
|