@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.node.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/client/client.ts
|
|
2
|
-
import { Buffer as
|
|
2
|
+
import { Buffer as Buffer8 } from "buffer";
|
|
3
3
|
import * as anchor6 from "@anchor-lang/core";
|
|
4
4
|
|
|
5
5
|
// ../../node_modules/@arcium-hq/client/build/index.mjs
|
|
@@ -24910,13 +24910,521 @@ import {
|
|
|
24910
24910
|
} from "@depthhq/bindings";
|
|
24911
24911
|
import { DepthView } from "@depthhq/sdk/views";
|
|
24912
24912
|
import { DepthRecovery } from "@depthhq/sdk/recovery";
|
|
24913
|
-
import { PublicKey as
|
|
24913
|
+
import { PublicKey as PublicKey7 } from "@solana/web3.js";
|
|
24914
24914
|
|
|
24915
24915
|
// src/chain/random.ts
|
|
24916
24916
|
function randomBytes3(length) {
|
|
24917
24917
|
return crypto.getRandomValues(new Uint8Array(length));
|
|
24918
24918
|
}
|
|
24919
24919
|
|
|
24920
|
+
// src/hedge/access.ts
|
|
24921
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
24922
|
+
|
|
24923
|
+
// src/hedge/schema.ts
|
|
24924
|
+
var object = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
24925
|
+
var integer = (value) => Number.isSafeInteger(value) && value >= 0;
|
|
24926
|
+
var amount = (value) => typeof value === "string" && /^-?\d{1,40}$/.test(value);
|
|
24927
|
+
var optional = (value, check) => value === undefined || check(value);
|
|
24928
|
+
var text = (value) => typeof value === "string" && value.length <= 1024;
|
|
24929
|
+
function order(value, receipt = false) {
|
|
24930
|
+
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)
|
|
24931
|
+
return false;
|
|
24932
|
+
if (!receipt)
|
|
24933
|
+
return true;
|
|
24934
|
+
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)));
|
|
24935
|
+
}
|
|
24936
|
+
function hedgeView(value, owner) {
|
|
24937
|
+
if (!object(value) || value.maker !== owner || typeof value.enabled !== "boolean")
|
|
24938
|
+
throw new Error("Maker hedge response belongs to another wallet or is invalid");
|
|
24939
|
+
const snapshot = value.snapshot;
|
|
24940
|
+
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))))
|
|
24941
|
+
throw new Error("Maker hedge response is invalid");
|
|
24942
|
+
return value;
|
|
24943
|
+
}
|
|
24944
|
+
|
|
24945
|
+
// src/hedge/access.ts
|
|
24946
|
+
var accessMessage = (wallet, nonce, expiresAt, scope = "Read hedge history") => `Depth maker access
|
|
24947
|
+
Wallet: ${wallet}
|
|
24948
|
+
Scope: ${scope}
|
|
24949
|
+
Nonce: ${nonce}
|
|
24950
|
+
Expires: ${expiresAt}`;
|
|
24951
|
+
|
|
24952
|
+
class WalletSession {
|
|
24953
|
+
options;
|
|
24954
|
+
scope;
|
|
24955
|
+
prefix;
|
|
24956
|
+
token;
|
|
24957
|
+
constructor(options, scope = "Read hedge history", prefix = "") {
|
|
24958
|
+
this.options = options;
|
|
24959
|
+
this.scope = scope;
|
|
24960
|
+
this.prefix = prefix;
|
|
24961
|
+
}
|
|
24962
|
+
async request(path, init) {
|
|
24963
|
+
if (!this.options.serviceUrl)
|
|
24964
|
+
throw new Error("A maker service URL is required");
|
|
24965
|
+
const response = await fetch(`${this.options.serviceUrl.replace(/\/$/, "")}/v1/maker/${`${this.prefix}${path}`.replace(/\/$/, "")}`, {
|
|
24966
|
+
...init,
|
|
24967
|
+
cache: "no-store",
|
|
24968
|
+
signal: AbortSignal.timeout(15000)
|
|
24969
|
+
});
|
|
24970
|
+
if (response.status === 401) {
|
|
24971
|
+
this.token = undefined;
|
|
24972
|
+
throw new Error("Authorize access with your maker wallet");
|
|
24973
|
+
}
|
|
24974
|
+
if (!response.ok) {
|
|
24975
|
+
const error = await response.json().catch(() => ({}));
|
|
24976
|
+
throw new Error(typeof error.message === "string" ? error.message : response.status === 403 ? "This worker belongs to another maker" : "Maker worker is unavailable");
|
|
24977
|
+
}
|
|
24978
|
+
return response.json();
|
|
24979
|
+
}
|
|
24980
|
+
async authorize() {
|
|
24981
|
+
const wallet = this.options.wallet;
|
|
24982
|
+
if (!wallet.signMessage)
|
|
24983
|
+
throw new Error("This wallet cannot sign access messages");
|
|
24984
|
+
const challenge = await this.request(`challenge?wallet=${wallet.publicKey.toBase58()}`);
|
|
24985
|
+
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))
|
|
24986
|
+
throw new Error("Maker access challenge is invalid");
|
|
24987
|
+
const signature = await wallet.signMessage(new TextEncoder().encode(challenge.message));
|
|
24988
|
+
const session = await this.request("session", {
|
|
24989
|
+
method: "POST",
|
|
24990
|
+
headers: { "content-type": "application/json" },
|
|
24991
|
+
body: JSON.stringify({
|
|
24992
|
+
nonce: challenge.nonce,
|
|
24993
|
+
signature: Buffer2.from(signature).toString("base64")
|
|
24994
|
+
})
|
|
24995
|
+
});
|
|
24996
|
+
if (!/^[0-9a-f]{64}$/.test(session.token) || !Number.isSafeInteger(session.expiresAt) || session.expiresAt <= Date.now() || session.expiresAt > Date.now() + 16 * 60000)
|
|
24997
|
+
throw new Error("Maker access session is invalid");
|
|
24998
|
+
this.token = { value: session.token, expiresAt: session.expiresAt };
|
|
24999
|
+
}
|
|
25000
|
+
async authorizedRequest(path, body) {
|
|
25001
|
+
if (!this.token || this.token.expiresAt <= Date.now())
|
|
25002
|
+
await this.authorize();
|
|
25003
|
+
return this.request(path, {
|
|
25004
|
+
method: body === undefined ? "GET" : "POST",
|
|
25005
|
+
headers: {
|
|
25006
|
+
authorization: `Bearer ${this.token.value}`,
|
|
25007
|
+
"content-type": "application/json"
|
|
25008
|
+
},
|
|
25009
|
+
body: body === undefined ? undefined : JSON.stringify(body)
|
|
25010
|
+
});
|
|
25011
|
+
}
|
|
25012
|
+
disconnect() {
|
|
25013
|
+
this.token = undefined;
|
|
25014
|
+
}
|
|
25015
|
+
}
|
|
25016
|
+
|
|
25017
|
+
class MakerAccess extends WalletSession {
|
|
25018
|
+
async summary() {
|
|
25019
|
+
if (!this.token || this.token.expiresAt <= Date.now()) {
|
|
25020
|
+
this.token = undefined;
|
|
25021
|
+
throw new Error("Authorize access with your maker wallet");
|
|
25022
|
+
}
|
|
25023
|
+
return hedgeView(await this.authorizedRequest("hedges"), this.options.wallet.publicKey.toBase58());
|
|
25024
|
+
}
|
|
25025
|
+
}
|
|
25026
|
+
|
|
25027
|
+
// src/automation/automation.ts
|
|
25028
|
+
import { PublicKey as PublicKey3, SystemProgram, Transaction } from "@solana/web3.js";
|
|
25029
|
+
import { Buffer as Buffer3 } from "buffer";
|
|
25030
|
+
|
|
25031
|
+
// src/hedge/setup.ts
|
|
25032
|
+
import { ExchangeClient, HttpTransport, InfoClient } from "@nktkas/hyperliquid";
|
|
25033
|
+
import {
|
|
25034
|
+
getWalletAddress
|
|
25035
|
+
} from "@nktkas/hyperliquid/signing";
|
|
25036
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
25037
|
+
|
|
25038
|
+
// src/hedge/amount.ts
|
|
25039
|
+
function units(value, decimals, exact = true) {
|
|
25040
|
+
if (!/^-?\d+(?:\.\d+)?$/.test(value))
|
|
25041
|
+
throw new Error("External decimal is invalid");
|
|
25042
|
+
const negative = value.startsWith("-");
|
|
25043
|
+
const [whole, fraction = ""] = value.replace(/^-/, "").split(".");
|
|
25044
|
+
const extra = fraction.slice(decimals);
|
|
25045
|
+
if (exact && /[1-9]/.test(extra))
|
|
25046
|
+
throw new Error("External decimal exceeds precision");
|
|
25047
|
+
const amount2 = BigInt(whole) * 10n ** BigInt(decimals) + BigInt(fraction.slice(0, decimals).padEnd(decimals, "0") || "0");
|
|
25048
|
+
return negative ? -amount2 - (!exact && /[1-9]/.test(extra) ? 1n : 0n) : amount2;
|
|
25049
|
+
}
|
|
25050
|
+
|
|
25051
|
+
// src/hedge/setup.ts
|
|
25052
|
+
async function verify_hyperliquid(options) {
|
|
25053
|
+
const info = new InfoClient({
|
|
25054
|
+
transport: new HttpTransport({ isTestnet: options.network === "testnet" })
|
|
25055
|
+
});
|
|
25056
|
+
const [state, agents] = await Promise.all([
|
|
25057
|
+
info.clearinghouseState({ user: options.account }),
|
|
25058
|
+
info.extraAgents({ user: options.account })
|
|
25059
|
+
]);
|
|
25060
|
+
if (units(state.withdrawable, 6, false) < 10000000n)
|
|
25061
|
+
throw new Error(`Fund Hyperliquid ${options.network} account ${options.account} with at least 10 available USDC`);
|
|
25062
|
+
if (!agents.some((entry) => entry.address.toLowerCase() === options.agent.toLowerCase() && (entry.validUntil === null || entry.validUntil > Date.now())))
|
|
25063
|
+
throw new Error("Approve the displayed agent in Hyperliquid API wallets, or connect its account wallet");
|
|
25064
|
+
const position = state.assetPositions.find((entry) => entry.position.coin === "SOL")?.position;
|
|
25065
|
+
if (position && position.leverage.value > options.maxLeverage)
|
|
25066
|
+
throw new Error("SOL leverage exceeds the configured hedge limit");
|
|
25067
|
+
}
|
|
25068
|
+
async function setup_hyperliquid(options) {
|
|
25069
|
+
if (Boolean(options.wallet) === Boolean(options.accountKey))
|
|
25070
|
+
throw new Error("Provide one Hyperliquid account signer source");
|
|
25071
|
+
if (!Number.isInteger(options.leverage) || options.leverage < 1 || options.leverage > 10)
|
|
25072
|
+
throw new Error("Setup leverage must be 1 to 10");
|
|
25073
|
+
if (Boolean(options.agent) === Boolean(options.agentKey))
|
|
25074
|
+
throw new Error("Provide one Hyperliquid agent identity source");
|
|
25075
|
+
const agent = options.agent ?? privateKeyToAccount(options.agentKey).address;
|
|
25076
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(agent))
|
|
25077
|
+
throw new Error("Hyperliquid agent address is invalid");
|
|
25078
|
+
const wallet = options.wallet ?? privateKeyToAccount(options.accountKey);
|
|
25079
|
+
const account = await getWalletAddress(wallet);
|
|
25080
|
+
if (options.account && account.toLowerCase() !== options.account.toLowerCase())
|
|
25081
|
+
throw new Error("The setup signer does not own the configured Hyperliquid account");
|
|
25082
|
+
if (account.toLowerCase() === agent.toLowerCase())
|
|
25083
|
+
throw new Error("The account and agent must be separate wallets");
|
|
25084
|
+
const transport = new HttpTransport({
|
|
25085
|
+
isTestnet: (options.network ?? "testnet") === "testnet"
|
|
25086
|
+
});
|
|
25087
|
+
const info = new InfoClient({ transport });
|
|
25088
|
+
const state = await info.clearinghouseState({ user: account });
|
|
25089
|
+
if (units(state.marginSummary.accountValue, 6, false) < 10000000n)
|
|
25090
|
+
throw new Error(`Fund Hyperliquid ${options.network ?? "testnet"} account ${account} with at least 10 USDC before setup`);
|
|
25091
|
+
const client = new ExchangeClient({ wallet, transport });
|
|
25092
|
+
const agents = await info.extraAgents({ user: account });
|
|
25093
|
+
if (!agents.some((entry) => entry.address.toLowerCase() === agent.toLowerCase() && (entry.validUntil === null || entry.validUntil > Date.now())))
|
|
25094
|
+
await client.approveAgent({
|
|
25095
|
+
agentAddress: agent,
|
|
25096
|
+
agentName: options.agentName ?? "Depth maker"
|
|
25097
|
+
});
|
|
25098
|
+
const meta = await info.meta();
|
|
25099
|
+
const asset = meta.universe.findIndex((entry) => entry.name === "SOL" && !entry.isDelisted);
|
|
25100
|
+
if (asset < 0)
|
|
25101
|
+
throw new Error("Hyperliquid SOL perpetual is unavailable");
|
|
25102
|
+
await client.updateLeverage({
|
|
25103
|
+
asset,
|
|
25104
|
+
isCross: true,
|
|
25105
|
+
leverage: options.leverage
|
|
25106
|
+
});
|
|
25107
|
+
return {
|
|
25108
|
+
account,
|
|
25109
|
+
agent,
|
|
25110
|
+
network: options.network ?? "testnet",
|
|
25111
|
+
leverage: options.leverage
|
|
25112
|
+
};
|
|
25113
|
+
}
|
|
25114
|
+
|
|
25115
|
+
// src/automation/types.ts
|
|
25116
|
+
import { PublicKey as PublicKey2 } from "@solana/web3.js";
|
|
25117
|
+
|
|
25118
|
+
// src/bot/policy.ts
|
|
25119
|
+
import { positionPnl } from "@depthhq/sdk";
|
|
25120
|
+
|
|
25121
|
+
// src/hedge/policy.ts
|
|
25122
|
+
function validateHedgePolicy(policy) {
|
|
25123
|
+
if (!Number.isInteger(policy.maxLeverage) || policy.maxLeverage < 1 || policy.maxLeverage > 10)
|
|
25124
|
+
throw new RangeError("Maximum hedge leverage must be 1 to 10");
|
|
25125
|
+
if (!Number.isInteger(policy.ratioBps) || policy.ratioBps < 1 || policy.ratioBps > 1e4)
|
|
25126
|
+
throw new RangeError("Hedge ratio must be 1 to 10000 bps");
|
|
25127
|
+
if (policy.thresholdNotional <= 0n || policy.maxOrderNotional <= 0n || policy.maxUnhedgedNotional <= policy.thresholdNotional)
|
|
25128
|
+
throw new RangeError("Hedge notional limits are invalid");
|
|
25129
|
+
for (const value of [
|
|
25130
|
+
policy.batchMs,
|
|
25131
|
+
policy.maxUnhedgedMs,
|
|
25132
|
+
policy.maxPriceAgeMs,
|
|
25133
|
+
policy.orderTtlMs
|
|
25134
|
+
])
|
|
25135
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 300000)
|
|
25136
|
+
throw new RangeError("Hedge time limits must be 1 to 300000 ms");
|
|
25137
|
+
if (policy.batchMs >= policy.maxUnhedgedMs || policy.orderTtlMs > policy.maxUnhedgedMs)
|
|
25138
|
+
throw new RangeError("Hedge batching and order expiry must fit the exposure time limit");
|
|
25139
|
+
if (!Number.isInteger(policy.maxSlippageBps) || policy.maxSlippageBps < 1 || policy.maxSlippageBps > 100)
|
|
25140
|
+
throw new RangeError("Hedge slippage must be 1 to 100 bps");
|
|
25141
|
+
}
|
|
25142
|
+
|
|
25143
|
+
// src/bot/policy.ts
|
|
25144
|
+
function validateBotPolicy(policy) {
|
|
25145
|
+
if (policy.maxCapacity <= 0n || policy.maxInventoryNotional <= 0n || policy.maxCapacity > policy.maxInventoryNotional)
|
|
25146
|
+
throw new RangeError("Maker capacity must fit a positive inventory limit");
|
|
25147
|
+
for (const [name, value] of Object.entries(policy))
|
|
25148
|
+
if (typeof value === "number" && (!Number.isSafeInteger(value) || value <= 0) && !(["inventorySkewBps", "executionCostBps"].includes(name) && value === 0))
|
|
25149
|
+
throw new RangeError(`${name} must be a positive integer`);
|
|
25150
|
+
if (policy.refreshSeconds >= policy.lifetimeSeconds)
|
|
25151
|
+
throw new RangeError("Refresh must precede quote expiry");
|
|
25152
|
+
if (policy.spreadBps >= 20000 || policy.repriceBps > 1e4)
|
|
25153
|
+
throw new RangeError("Maker price bounds are invalid");
|
|
25154
|
+
if ((policy.inventorySkewBps ?? 0) > 100 || (policy.executionCostBps ?? 0) > 100)
|
|
25155
|
+
throw new RangeError("Maker pricing adjustments must be 0 to 100 bps");
|
|
25156
|
+
if ((policy.fundingHours ?? 1) > 168)
|
|
25157
|
+
throw new RangeError("Maker funding horizon must be at most 168 hours");
|
|
25158
|
+
const reserve = policy.gasReserveLamports ?? 50000000n;
|
|
25159
|
+
if (reserve < 10000000n || reserve > BigInt(Number.MAX_SAFE_INTEGER))
|
|
25160
|
+
throw new RangeError("Maker SOL reserve must be at least 10000000 lamports");
|
|
25161
|
+
}
|
|
25162
|
+
|
|
25163
|
+
// src/automation/types.ts
|
|
25164
|
+
var amount2 = (value, name) => {
|
|
25165
|
+
if (typeof value !== "string" || !/^\d{1,20}$/.test(value) || BigInt(value) > (1n << 64n) - 1n)
|
|
25166
|
+
throw new Error(`${name} must be an unsigned integer amount`);
|
|
25167
|
+
return BigInt(value);
|
|
25168
|
+
};
|
|
25169
|
+
var object2 = (value, names) => {
|
|
25170
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !names.includes(key)))
|
|
25171
|
+
throw new Error("Automation configuration is invalid");
|
|
25172
|
+
return value;
|
|
25173
|
+
};
|
|
25174
|
+
var integer2 = (value, fallback) => {
|
|
25175
|
+
const result = value ?? fallback;
|
|
25176
|
+
if (typeof result !== "number" || !Number.isSafeInteger(result))
|
|
25177
|
+
throw new Error("Automation limits must be integers");
|
|
25178
|
+
return result;
|
|
25179
|
+
};
|
|
25180
|
+
function automationConfig(value) {
|
|
25181
|
+
const input = object2(value, ["quote", "hedge"]);
|
|
25182
|
+
const quote = object2(input.quote, [
|
|
25183
|
+
"maxCapacity",
|
|
25184
|
+
"maxInventoryNotional",
|
|
25185
|
+
"spreadBps",
|
|
25186
|
+
"repriceBps",
|
|
25187
|
+
"lifetimeSeconds",
|
|
25188
|
+
"refreshSeconds",
|
|
25189
|
+
"gasReserveLamports",
|
|
25190
|
+
"inventorySkewBps",
|
|
25191
|
+
"executionCostBps",
|
|
25192
|
+
"fundingHours"
|
|
25193
|
+
]);
|
|
25194
|
+
const policy = {
|
|
25195
|
+
maxCapacity: amount2(quote.maxCapacity, "Capacity"),
|
|
25196
|
+
maxInventoryNotional: amount2(quote.maxInventoryNotional, "Inventory limit"),
|
|
25197
|
+
spreadBps: integer2(quote.spreadBps),
|
|
25198
|
+
repriceBps: integer2(quote.repriceBps),
|
|
25199
|
+
lifetimeSeconds: integer2(quote.lifetimeSeconds),
|
|
25200
|
+
refreshSeconds: integer2(quote.refreshSeconds),
|
|
25201
|
+
gasReserveLamports: amount2(quote.gasReserveLamports ?? "50000000", "SOL reserve"),
|
|
25202
|
+
inventorySkewBps: integer2(quote.inventorySkewBps, 15),
|
|
25203
|
+
executionCostBps: integer2(quote.executionCostBps, 5),
|
|
25204
|
+
fundingHours: integer2(quote.fundingHours, 1)
|
|
25205
|
+
};
|
|
25206
|
+
validateBotPolicy(policy);
|
|
25207
|
+
if (policy.lifetimeSeconds > 86400 || policy.refreshSeconds > 86400)
|
|
25208
|
+
throw new Error("Quote lifetime must be at most one day");
|
|
25209
|
+
if (input.hedge === undefined)
|
|
25210
|
+
return { quote: policy };
|
|
25211
|
+
const hedge = object2(input.hedge, ["account", "policy"]);
|
|
25212
|
+
if (typeof hedge.account !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(hedge.account))
|
|
25213
|
+
throw new Error("Hyperliquid account address is invalid");
|
|
25214
|
+
const risk = object2(hedge.policy, [
|
|
25215
|
+
"ratioBps",
|
|
25216
|
+
"thresholdNotional",
|
|
25217
|
+
"maxUnhedgedNotional",
|
|
25218
|
+
"maxOrderNotional",
|
|
25219
|
+
"batchMs",
|
|
25220
|
+
"maxUnhedgedMs",
|
|
25221
|
+
"maxSlippageBps",
|
|
25222
|
+
"maxPriceAgeMs",
|
|
25223
|
+
"orderTtlMs",
|
|
25224
|
+
"maxLeverage"
|
|
25225
|
+
]);
|
|
25226
|
+
const hedgePolicy = {
|
|
25227
|
+
ratioBps: integer2(risk.ratioBps),
|
|
25228
|
+
thresholdNotional: amount2(risk.thresholdNotional, "Hedge threshold"),
|
|
25229
|
+
maxUnhedgedNotional: amount2(risk.maxUnhedgedNotional, "Exposure limit"),
|
|
25230
|
+
maxOrderNotional: amount2(risk.maxOrderNotional, "Order limit"),
|
|
25231
|
+
batchMs: integer2(risk.batchMs),
|
|
25232
|
+
maxUnhedgedMs: integer2(risk.maxUnhedgedMs),
|
|
25233
|
+
maxSlippageBps: integer2(risk.maxSlippageBps),
|
|
25234
|
+
maxPriceAgeMs: integer2(risk.maxPriceAgeMs),
|
|
25235
|
+
orderTtlMs: integer2(risk.orderTtlMs),
|
|
25236
|
+
maxLeverage: integer2(risk.maxLeverage)
|
|
25237
|
+
};
|
|
25238
|
+
validateHedgePolicy(hedgePolicy);
|
|
25239
|
+
return {
|
|
25240
|
+
quote: policy,
|
|
25241
|
+
hedge: {
|
|
25242
|
+
account: hedge.account.toLowerCase(),
|
|
25243
|
+
policy: hedgePolicy
|
|
25244
|
+
}
|
|
25245
|
+
};
|
|
25246
|
+
}
|
|
25247
|
+
var automationWire = (config) => JSON.parse(JSON.stringify(config, (_key, value) => typeof value === "bigint" ? value.toString() : value));
|
|
25248
|
+
function automationView(value, owner, environment) {
|
|
25249
|
+
if (value === null)
|
|
25250
|
+
return null;
|
|
25251
|
+
const view = object2(value, [
|
|
25252
|
+
"owner",
|
|
25253
|
+
"environment",
|
|
25254
|
+
"revision",
|
|
25255
|
+
"operator",
|
|
25256
|
+
"agent",
|
|
25257
|
+
"config",
|
|
25258
|
+
"desired",
|
|
25259
|
+
"phase",
|
|
25260
|
+
"updatedAt",
|
|
25261
|
+
"reason",
|
|
25262
|
+
"bot",
|
|
25263
|
+
"hedge",
|
|
25264
|
+
"funding"
|
|
25265
|
+
]);
|
|
25266
|
+
if (view.owner !== owner || view.environment !== environment)
|
|
25267
|
+
throw new Error("Automation belongs to another wallet or network");
|
|
25268
|
+
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)) || ![
|
|
25269
|
+
"configured",
|
|
25270
|
+
"starting",
|
|
25271
|
+
"running",
|
|
25272
|
+
"pausing",
|
|
25273
|
+
"paused",
|
|
25274
|
+
"blocked"
|
|
25275
|
+
].includes(String(view.phase)) || view.reason !== undefined && typeof view.reason !== "string")
|
|
25276
|
+
throw new Error("Automation response is invalid");
|
|
25277
|
+
automationConfig(view.config);
|
|
25278
|
+
if (view.bot !== undefined) {
|
|
25279
|
+
const bot = object2(view.bot, ["phase", "reason", "quoteId", "fillCount"]);
|
|
25280
|
+
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))
|
|
25281
|
+
throw new Error("Automation status is invalid");
|
|
25282
|
+
}
|
|
25283
|
+
hedgeView(view.hedge, owner);
|
|
25284
|
+
if (view.funding !== undefined) {
|
|
25285
|
+
const funding = object2(view.funding, ["signature", "lamports", "status"]);
|
|
25286
|
+
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)))
|
|
25287
|
+
throw new Error("Operator funding receipt is invalid");
|
|
25288
|
+
amount2(funding.lamports, "Operator funding");
|
|
25289
|
+
}
|
|
25290
|
+
return view;
|
|
25291
|
+
}
|
|
25292
|
+
|
|
25293
|
+
// src/automation/automation.ts
|
|
25294
|
+
class MakerAutomation extends WalletSession {
|
|
25295
|
+
maker;
|
|
25296
|
+
constructor(maker) {
|
|
25297
|
+
super(maker.options, "Manage maker automation", "automation/");
|
|
25298
|
+
this.maker = maker;
|
|
25299
|
+
}
|
|
25300
|
+
view(value) {
|
|
25301
|
+
return automationView(value, this.maker.options.wallet.publicKey.toBase58(), this.maker.options.environment ?? "");
|
|
25302
|
+
}
|
|
25303
|
+
async summary() {
|
|
25304
|
+
if (!this.token || this.token.expiresAt <= Date.now())
|
|
25305
|
+
throw new Error("Authorize automation with your maker wallet");
|
|
25306
|
+
return this.view(await this.authorizedRequest(""));
|
|
25307
|
+
}
|
|
25308
|
+
async configure(config, revision) {
|
|
25309
|
+
const wire = automationWire(config);
|
|
25310
|
+
automationConfig(wire);
|
|
25311
|
+
if (revision === undefined) {
|
|
25312
|
+
if (!this.token || this.token.expiresAt <= Date.now())
|
|
25313
|
+
await this.authorize();
|
|
25314
|
+
revision = (await this.summary())?.revision ?? 0;
|
|
25315
|
+
}
|
|
25316
|
+
if (!Number.isSafeInteger(revision) || revision < 0)
|
|
25317
|
+
throw new Error("Automation revision is invalid");
|
|
25318
|
+
return this.view(await this.authorizedRequest("configure", { revision, config: wire }));
|
|
25319
|
+
}
|
|
25320
|
+
async approve(profile, options = {}) {
|
|
25321
|
+
this.view(profile);
|
|
25322
|
+
const current = await this.summary();
|
|
25323
|
+
if (!current || current.revision !== profile.revision || current.operator !== profile.operator || current.agent !== profile.agent)
|
|
25324
|
+
throw new Error("Automation configuration changed; review it again");
|
|
25325
|
+
if (!this.maker.options.maker.equals(this.maker.options.wallet.publicKey))
|
|
25326
|
+
throw new Error("The maker owner must approve automation");
|
|
25327
|
+
if (current.desired !== "paused" || !["configured", "paused"].includes(current.phase))
|
|
25328
|
+
throw new Error("Pause automation before approving its operator");
|
|
25329
|
+
const config = automationConfig(current.config);
|
|
25330
|
+
const grantSeconds = options.grantSeconds ?? 86400;
|
|
25331
|
+
if (!Number.isSafeInteger(grantSeconds) || grantSeconds < 60 || grantSeconds > 604800)
|
|
25332
|
+
throw new Error("Operator approval must last 1 minute to 7 days");
|
|
25333
|
+
const target = options.operatorFundingLamports ?? config.quote.gasReserveLamports + 200000000n;
|
|
25334
|
+
if (target < config.quote.gasReserveLamports || target > 10000000000n)
|
|
25335
|
+
throw new Error("Operator funding must cover the reserve and stay within 10 SOL");
|
|
25336
|
+
if (config.hedge) {
|
|
25337
|
+
const network = profile.environment === "devnet" ? "testnet" : "mainnet";
|
|
25338
|
+
if (options.hedgeWallet)
|
|
25339
|
+
await setup_hyperliquid({
|
|
25340
|
+
wallet: options.hedgeWallet,
|
|
25341
|
+
account: config.hedge.account,
|
|
25342
|
+
agent: profile.agent,
|
|
25343
|
+
network,
|
|
25344
|
+
leverage: config.hedge.policy.maxLeverage,
|
|
25345
|
+
agentName: `Depth ${profile.agent.slice(2, 10)}`
|
|
25346
|
+
});
|
|
25347
|
+
else
|
|
25348
|
+
await verify_hyperliquid({
|
|
25349
|
+
account: config.hedge.account,
|
|
25350
|
+
agent: profile.agent,
|
|
25351
|
+
network,
|
|
25352
|
+
maxLeverage: config.hedge.policy.maxLeverage
|
|
25353
|
+
});
|
|
25354
|
+
}
|
|
25355
|
+
const operator = new PublicKey3(profile.operator);
|
|
25356
|
+
const balance = BigInt(await this.maker.options.connection.getBalance(operator, "confirmed"));
|
|
25357
|
+
const signatures = [];
|
|
25358
|
+
const recovered = this.view(await this.authorizedRequest("fund", { revision: current.revision }));
|
|
25359
|
+
if (recovered.funding?.status === "pending")
|
|
25360
|
+
signatures.push((await this.waitFunding(current.revision)).signature);
|
|
25361
|
+
else if (balance < target) {
|
|
25362
|
+
const blockhash = await this.maker.options.connection.getLatestBlockhash("confirmed");
|
|
25363
|
+
const transaction = new Transaction({
|
|
25364
|
+
...blockhash,
|
|
25365
|
+
feePayer: this.maker.options.wallet.publicKey
|
|
25366
|
+
}).add(SystemProgram.transfer({
|
|
25367
|
+
fromPubkey: this.maker.options.wallet.publicKey,
|
|
25368
|
+
toPubkey: operator,
|
|
25369
|
+
lamports: target - balance
|
|
25370
|
+
}));
|
|
25371
|
+
const signed = await this.maker.options.wallet.signTransaction(transaction);
|
|
25372
|
+
this.view(await this.authorizedRequest("fund", {
|
|
25373
|
+
revision: current.revision,
|
|
25374
|
+
transaction: Buffer3.from(signed.serialize()).toString("base64"),
|
|
25375
|
+
lastValidBlockHeight: blockhash.lastValidBlockHeight,
|
|
25376
|
+
targetLamports: target.toString()
|
|
25377
|
+
}));
|
|
25378
|
+
signatures.push((await this.waitFunding(current.revision)).signature);
|
|
25379
|
+
}
|
|
25380
|
+
signatures.push(await this.maker.operator.authorize({
|
|
25381
|
+
operator,
|
|
25382
|
+
maxCapacity: config.quote.maxCapacity,
|
|
25383
|
+
expiresAt: Math.floor(Date.now() / 1000) + grantSeconds,
|
|
25384
|
+
canView: true
|
|
25385
|
+
}));
|
|
25386
|
+
return { profile: current, signatures };
|
|
25387
|
+
}
|
|
25388
|
+
async waitFunding(revision) {
|
|
25389
|
+
const deadline = Date.now() + 120000;
|
|
25390
|
+
for (;; ) {
|
|
25391
|
+
const profile = this.view(await this.authorizedRequest("fund", { revision }));
|
|
25392
|
+
const receipt = profile.funding;
|
|
25393
|
+
if (!receipt)
|
|
25394
|
+
throw new Error("Operator funding receipt is unavailable");
|
|
25395
|
+
if (receipt.status === "confirmed")
|
|
25396
|
+
return receipt;
|
|
25397
|
+
if (receipt.status !== "pending")
|
|
25398
|
+
throw new Error(`Operator funding ${receipt.status}; retry approval after reviewing its receipt`);
|
|
25399
|
+
if (Date.now() >= deadline)
|
|
25400
|
+
throw new Error("Operator funding is pending; its signed transaction is saved and approval can resume");
|
|
25401
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
25402
|
+
}
|
|
25403
|
+
}
|
|
25404
|
+
async command(action, revision) {
|
|
25405
|
+
if (revision === undefined) {
|
|
25406
|
+
if (!this.token || this.token.expiresAt <= Date.now())
|
|
25407
|
+
await this.authorize();
|
|
25408
|
+
const profile = await this.summary();
|
|
25409
|
+
if (!profile)
|
|
25410
|
+
throw new Error("Configure maker automation first");
|
|
25411
|
+
revision = profile.revision;
|
|
25412
|
+
}
|
|
25413
|
+
if (!Number.isSafeInteger(revision) || revision < 1)
|
|
25414
|
+
throw new Error("Automation revision is invalid");
|
|
25415
|
+
return this.view(await this.authorizedRequest(action, { revision }));
|
|
25416
|
+
}
|
|
25417
|
+
start(revision) {
|
|
25418
|
+
return this.command("start", revision);
|
|
25419
|
+
}
|
|
25420
|
+
pause(revision) {
|
|
25421
|
+
return this.command("pause", revision);
|
|
25422
|
+
}
|
|
25423
|
+
resume(revision) {
|
|
25424
|
+
return this.start(revision);
|
|
25425
|
+
}
|
|
25426
|
+
}
|
|
25427
|
+
|
|
24920
25428
|
// src/client/client.ts
|
|
24921
25429
|
import {
|
|
24922
25430
|
DepthClient,
|
|
@@ -24927,7 +25435,7 @@ import {
|
|
|
24927
25435
|
} from "@depthhq/sdk";
|
|
24928
25436
|
|
|
24929
25437
|
// src/chain/arcium.ts
|
|
24930
|
-
import { Buffer as
|
|
25438
|
+
import { Buffer as Buffer4 } from "buffer";
|
|
24931
25439
|
import { computationName, DEPTH_PROGRAM_ID } from "@depthhq/bindings";
|
|
24932
25440
|
function mxeAccount(provider) {
|
|
24933
25441
|
const arcium2 = getArciumProgram(provider);
|
|
@@ -24939,7 +25447,7 @@ async function queueAccounts(provider, connection, name, offset) {
|
|
|
24939
25447
|
if (!mxe.mxeProgramId.equals(DEPTH_PROGRAM_ID)) {
|
|
24940
25448
|
throw new Error("MXE is registered for another program");
|
|
24941
25449
|
}
|
|
24942
|
-
const definition = getCompDefAccAddress(DEPTH_PROGRAM_ID,
|
|
25450
|
+
const definition = getCompDefAccAddress(DEPTH_PROGRAM_ID, Buffer4.from(getCompDefAccOffset(computationName(name))).readUInt32LE());
|
|
24943
25451
|
if (!await connection.getAccountInfo(definition, "confirmed")) {
|
|
24944
25452
|
throw new Error(`${name} computation is not deployed`);
|
|
24945
25453
|
}
|
|
@@ -24954,11 +25462,11 @@ async function queueAccounts(provider, connection, name, offset) {
|
|
|
24954
25462
|
}
|
|
24955
25463
|
|
|
24956
25464
|
// src/backstop/backstop.ts
|
|
24957
|
-
import { Buffer as
|
|
25465
|
+
import { Buffer as Buffer5 } from "buffer";
|
|
24958
25466
|
import * as anchor2 from "@anchor-lang/core";
|
|
24959
25467
|
import { DEPTH_PROGRAM_ID as DEPTH_PROGRAM_ID2, SOL_USD_MARKET_ID } from "@depthhq/bindings";
|
|
24960
25468
|
import { DepthError } from "@depthhq/sdk";
|
|
24961
|
-
import { PublicKey as
|
|
25469
|
+
import { PublicKey as PublicKey4 } from "@solana/web3.js";
|
|
24962
25470
|
function u64(value, name) {
|
|
24963
25471
|
if (typeof value !== "bigint" || value < 0n || value > (1n << 64n) - 1n)
|
|
24964
25472
|
throw new RangeError(`${name} must be an unsigned 64-bit bigint`);
|
|
@@ -24974,8 +25482,8 @@ function timing(options) {
|
|
|
24974
25482
|
return { timeoutMs, pollMs };
|
|
24975
25483
|
}
|
|
24976
25484
|
function address2(market, maker, nonce) {
|
|
24977
|
-
const bytes = u64(nonce, "clientNonce").toArrayLike(
|
|
24978
|
-
return
|
|
25485
|
+
const bytes = u64(nonce, "clientNonce").toArrayLike(Buffer5, "le", 8);
|
|
25486
|
+
return PublicKey4.findProgramAddressSync([Buffer5.from("backstop"), market.toBuffer(), maker.toBuffer(), bytes], DEPTH_PROGRAM_ID2)[0];
|
|
24979
25487
|
}
|
|
24980
25488
|
async function poll(read, terminal, options) {
|
|
24981
25489
|
const { timeoutMs, pollMs } = timing(options);
|
|
@@ -25017,7 +25525,7 @@ class BackstopHandle {
|
|
|
25017
25525
|
]);
|
|
25018
25526
|
const now = Math.floor(Date.now() / 1000);
|
|
25019
25527
|
const margin = (input.maxCapacity * BigInt(market.config.initialMarginBps) + 9999n) / 10000n;
|
|
25020
|
-
if (!market.backstopOffer.equals(
|
|
25528
|
+
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()))
|
|
25021
25529
|
throw new Error("Backstop offer is outside the current market policy");
|
|
25022
25530
|
if (!account.owner.equals(client.options.maker) || !account.market.equals(client.marketAddress) || !("ready" in account.status))
|
|
25023
25531
|
throw new Error("Maker market account is unavailable");
|
|
@@ -25103,9 +25611,9 @@ class BackstopRelease {
|
|
|
25103
25611
|
}
|
|
25104
25612
|
|
|
25105
25613
|
// src/fill/history.ts
|
|
25106
|
-
import { Buffer as
|
|
25614
|
+
import { Buffer as Buffer6 } from "buffer";
|
|
25107
25615
|
import * as anchor3 from "@anchor-lang/core";
|
|
25108
|
-
import { PublicKey as
|
|
25616
|
+
import { PublicKey as PublicKey5 } from "@solana/web3.js";
|
|
25109
25617
|
|
|
25110
25618
|
class FillHistory {
|
|
25111
25619
|
client;
|
|
@@ -25144,20 +25652,20 @@ class FillHistory {
|
|
|
25144
25652
|
...transaction.transaction.message.compiledInstructions.map((ix) => ({
|
|
25145
25653
|
programIdIndex: ix.programIdIndex,
|
|
25146
25654
|
accounts: ix.accountKeyIndexes,
|
|
25147
|
-
data:
|
|
25655
|
+
data: Buffer6.from(ix.data)
|
|
25148
25656
|
})),
|
|
25149
25657
|
...(transaction.meta?.innerInstructions ?? []).flatMap((group) => group.instructions.map((ix) => ({
|
|
25150
25658
|
programIdIndex: ix.programIdIndex,
|
|
25151
25659
|
accounts: ix.accounts,
|
|
25152
|
-
data:
|
|
25660
|
+
data: Buffer6.from(anchor3.utils.bytes.bs58.decode(ix.data))
|
|
25153
25661
|
})))
|
|
25154
25662
|
];
|
|
25155
25663
|
for (const ix of instructions2) {
|
|
25156
|
-
if (!keys.get(ix.programIdIndex)?.equals(this.client.program.programId) || !ix.data.subarray(0, 8).equals(
|
|
25664
|
+
if (!keys.get(ix.programIdIndex)?.equals(this.client.program.programId) || !ix.data.subarray(0, 8).equals(Buffer6.from(definition.discriminator)))
|
|
25157
25665
|
continue;
|
|
25158
|
-
const
|
|
25159
|
-
if (
|
|
25160
|
-
orders.set(
|
|
25666
|
+
const order2 = keys.get(ix.accounts[orderIndex]);
|
|
25667
|
+
if (order2)
|
|
25668
|
+
orders.set(order2.toBase58(), {
|
|
25161
25669
|
signature: signature.signature,
|
|
25162
25670
|
slot: signature.slot
|
|
25163
25671
|
});
|
|
@@ -25168,12 +25676,12 @@ class FillHistory {
|
|
|
25168
25676
|
const fills = [];
|
|
25169
25677
|
for (let i = 0;i < addresses.length; i += 100) {
|
|
25170
25678
|
const batch = addresses.slice(i, i + 100);
|
|
25171
|
-
const accounts2 = await connection.getMultipleAccountsInfo(batch.map((id) => new
|
|
25679
|
+
const accounts2 = await connection.getMultipleAccountsInfo(batch.map((id) => new PublicKey5(id)), "finalized");
|
|
25172
25680
|
accounts2.forEach((info, index) => {
|
|
25173
25681
|
if (!info || !info.owner.equals(this.client.program.programId))
|
|
25174
25682
|
throw new Error("Finalized fill receipt is unavailable");
|
|
25175
|
-
const
|
|
25176
|
-
if (
|
|
25683
|
+
const order2 = this.client.program.coder.accounts.decode("orderRecord", info.data);
|
|
25684
|
+
if (order2.maker.equals(this.client.options.maker) && order2.market.equals(this.client.marketAddress) && "trade" in order2.kind && "filled" in order2.status) {
|
|
25177
25685
|
fills.push({ id: batch[index], ...orders.get(batch[index]) });
|
|
25178
25686
|
}
|
|
25179
25687
|
});
|
|
@@ -25430,52 +25938,79 @@ class QuoteHandle {
|
|
|
25430
25938
|
}
|
|
25431
25939
|
async cancel() {
|
|
25432
25940
|
const snapshot = await this.status();
|
|
25941
|
+
if (["cancelled", "filled", "rejected", "releasing"].includes(snapshot.status))
|
|
25942
|
+
return snapshot;
|
|
25433
25943
|
const actor = this.client.options.wallet.publicKey;
|
|
25434
25944
|
const grant = actor.equals(this.client.options.maker) ? undefined : this.client.grantAddress();
|
|
25435
25945
|
if (snapshot.status === "validating" || snapshot.status === "validated") {
|
|
25436
|
-
|
|
25946
|
+
return this.cancelStep(() => this.client.program.methods.cancelPreparedQuote().accountsPartial({
|
|
25437
25947
|
actor,
|
|
25438
25948
|
grant: grant ?? this.client.program.programId,
|
|
25439
25949
|
quote: this.id
|
|
25440
|
-
}).rpc({ commitment: "confirmed" });
|
|
25441
|
-
return this.status();
|
|
25950
|
+
}).rpc({ commitment: "confirmed" }), ["cancelled", "rejected"]);
|
|
25442
25951
|
}
|
|
25443
25952
|
if (snapshot.status === "active" || snapshot.status === "waiting") {
|
|
25444
|
-
await this.client.program.methods.stopQuote().accountsPartial({
|
|
25953
|
+
await this.cancelStep(() => this.client.program.methods.stopQuote().accountsPartial({
|
|
25445
25954
|
actor,
|
|
25446
25955
|
market: this.client.marketAddress,
|
|
25447
25956
|
quoteQueue: this.client.quoteQueueAddress,
|
|
25448
25957
|
grant: grant ?? this.client.program.programId,
|
|
25449
25958
|
quote: this.id
|
|
25450
|
-
}).rpc({ commitment: "confirmed" });
|
|
25959
|
+
}).rpc({ commitment: "confirmed" }), ["closing", "releasing", "cancelled", "filled"]);
|
|
25451
25960
|
} else if (snapshot.status !== "closing") {
|
|
25452
25961
|
throw new Error("Quote cannot be cancelled in its current state");
|
|
25453
25962
|
}
|
|
25454
25963
|
const current = await this.status();
|
|
25455
|
-
if (
|
|
25964
|
+
if (current.status !== "closing")
|
|
25456
25965
|
return current;
|
|
25457
25966
|
if (current.attachedOrders > 0)
|
|
25458
25967
|
return current;
|
|
25968
|
+
const [market, makerAccount] = await Promise.all([
|
|
25969
|
+
this.client.read("Market", this.client.marketAddress),
|
|
25970
|
+
this.client.read("MarketAccount", this.client.marketAccount())
|
|
25971
|
+
]);
|
|
25972
|
+
if (market.pendingOrders > 0 || name(makerAccount.status) !== "ready")
|
|
25973
|
+
return current;
|
|
25459
25974
|
const offset = new anchor4.BN(randomBytes3(8), "le");
|
|
25460
25975
|
const queue = await this.client.queue("release_quote", offset);
|
|
25461
|
-
this.
|
|
25462
|
-
|
|
25463
|
-
|
|
25464
|
-
|
|
25465
|
-
|
|
25466
|
-
|
|
25467
|
-
|
|
25468
|
-
|
|
25469
|
-
|
|
25976
|
+
return this.cancelStep(async () => {
|
|
25977
|
+
this.releaseSignature = await this.client.program.methods.releaseQuote(offset).accountsPartial({
|
|
25978
|
+
...queue,
|
|
25979
|
+
payer: actor,
|
|
25980
|
+
protocol: this.client.protocolAddress,
|
|
25981
|
+
market: this.client.marketAddress,
|
|
25982
|
+
marketAccount: this.client.marketAccount(),
|
|
25983
|
+
grant: grant ?? this.client.program.programId,
|
|
25984
|
+
quote: this.id
|
|
25985
|
+
}).rpc({ commitment: "confirmed" });
|
|
25986
|
+
}, ["releasing", "cancelled", "filled"]);
|
|
25987
|
+
}
|
|
25988
|
+
async cancelStep(submit, completed) {
|
|
25989
|
+
try {
|
|
25990
|
+
await submit();
|
|
25991
|
+
} catch (error) {
|
|
25992
|
+
const snapshot = await this.status();
|
|
25993
|
+
if (completed.includes(snapshot.status))
|
|
25994
|
+
return snapshot;
|
|
25995
|
+
throw error;
|
|
25996
|
+
}
|
|
25470
25997
|
return this.status();
|
|
25471
25998
|
}
|
|
25999
|
+
async close() {
|
|
26000
|
+
if (!this.client.options.wallet.publicKey.equals(this.client.options.maker))
|
|
26001
|
+
throw new Error("Only the maker owner can reclaim quote rent");
|
|
26002
|
+
const snapshot = await this.status();
|
|
26003
|
+
if (!["cancelled", "filled", "rejected"].includes(snapshot.status) || snapshot.attachedOrders > 0)
|
|
26004
|
+
throw new Error("Release the quote and settle its orders before closing");
|
|
26005
|
+
return this.client.program.methods.closeQuote().accountsPartial({ maker: this.client.options.maker, quote: this.id }).rpc({ commitment: "confirmed" });
|
|
26006
|
+
}
|
|
25472
26007
|
}
|
|
25473
26008
|
|
|
25474
26009
|
// src/quote/replacement.ts
|
|
25475
26010
|
import * as anchor5 from "@anchor-lang/core";
|
|
25476
26011
|
import { DEPTH_PROGRAM_ID as DEPTH_PROGRAM_ID3 } from "@depthhq/bindings";
|
|
25477
|
-
import { PublicKey as
|
|
25478
|
-
import { Buffer as
|
|
26012
|
+
import { PublicKey as PublicKey6 } from "@solana/web3.js";
|
|
26013
|
+
import { Buffer as Buffer7 } from "buffer";
|
|
25479
26014
|
|
|
25480
26015
|
class QuoteReplacement {
|
|
25481
26016
|
client;
|
|
@@ -25489,9 +26024,9 @@ class QuoteReplacement {
|
|
|
25489
26024
|
if (nonce < 0n || nonce > (1n << 64n) - 1n) {
|
|
25490
26025
|
throw new RangeError("clientNonce must be an unsigned 64-bit bigint");
|
|
25491
26026
|
}
|
|
25492
|
-
const bytes = new anchor5.BN(nonce.toString()).toArrayLike(
|
|
25493
|
-
this.nextId =
|
|
25494
|
-
|
|
26027
|
+
const bytes = new anchor5.BN(nonce.toString()).toArrayLike(Buffer7, "le", 8);
|
|
26028
|
+
this.nextId = PublicKey6.findProgramAddressSync([
|
|
26029
|
+
Buffer7.from("quote"),
|
|
25495
26030
|
client.marketAddress.toBuffer(),
|
|
25496
26031
|
client.options.maker.toBuffer(),
|
|
25497
26032
|
bytes
|
|
@@ -25515,17 +26050,7 @@ class QuoteReplacement {
|
|
|
25515
26050
|
phase: next?.status === "active" || next?.status === "filled" ? "active" : next?.status === "rejected" ? "failed" : next ? "publishing" : stopped ? "ready" : "stopping"
|
|
25516
26051
|
};
|
|
25517
26052
|
}
|
|
25518
|
-
async
|
|
25519
|
-
if (input.clientNonce !== undefined && input.clientNonce !== this.nonce) {
|
|
25520
|
-
throw new Error("Replacement terms use a different client nonce");
|
|
25521
|
-
}
|
|
25522
|
-
const timeoutMs = options.timeoutMs ?? 60000;
|
|
25523
|
-
const pollMs = options.pollMs ?? 1000;
|
|
25524
|
-
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0 || !Number.isSafeInteger(pollMs) || pollMs < 100) {
|
|
25525
|
-
throw new RangeError("Replacement timeout or polling interval is invalid");
|
|
25526
|
-
}
|
|
25527
|
-
const deadline = Date.now() + timeoutMs;
|
|
25528
|
-
const previous = this.client.quote.get(this.priorId);
|
|
26053
|
+
async begin() {
|
|
25529
26054
|
const prior = await this.client.quoteState(this.priorId);
|
|
25530
26055
|
const recorded = prior.replacementNonce == null ? undefined : BigInt(prior.replacementNonce.toString());
|
|
25531
26056
|
if (recorded !== undefined && recorded !== this.nonce) {
|
|
@@ -25539,6 +26064,17 @@ class QuoteReplacement {
|
|
|
25539
26064
|
quote: this.priorId
|
|
25540
26065
|
}).rpc({ commitment: "confirmed" });
|
|
25541
26066
|
}
|
|
26067
|
+
}
|
|
26068
|
+
async resume(input, options = {}) {
|
|
26069
|
+
if (input.clientNonce !== undefined && input.clientNonce !== this.nonce)
|
|
26070
|
+
throw new Error("Replacement terms use a different client nonce");
|
|
26071
|
+
const timeoutMs = options.timeoutMs ?? 60000;
|
|
26072
|
+
const pollMs = options.pollMs ?? 1000;
|
|
26073
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0 || !Number.isSafeInteger(pollMs) || pollMs < 100)
|
|
26074
|
+
throw new RangeError("Replacement timeout or polling interval is invalid");
|
|
26075
|
+
const deadline = Date.now() + timeoutMs;
|
|
26076
|
+
const previous = this.client.quote.get(this.priorId);
|
|
26077
|
+
await this.begin();
|
|
25542
26078
|
for (;; ) {
|
|
25543
26079
|
const snapshot = await previous.status();
|
|
25544
26080
|
if (["cancelled", "filled"].includes(snapshot.status))
|
|
@@ -25564,7 +26100,7 @@ class QuoteReplacement {
|
|
|
25564
26100
|
|
|
25565
26101
|
// src/client/client.ts
|
|
25566
26102
|
function pda(seeds) {
|
|
25567
|
-
return
|
|
26103
|
+
return PublicKey7.findProgramAddressSync(seeds, DEPTH_PROGRAM_ID4)[0];
|
|
25568
26104
|
}
|
|
25569
26105
|
function u642(value, name2) {
|
|
25570
26106
|
if (typeof value !== "bigint" || value < 0n || value > (1n << 64n) - 1n) {
|
|
@@ -25575,12 +26111,25 @@ function u642(value, name2) {
|
|
|
25575
26111
|
function offset() {
|
|
25576
26112
|
return new anchor6.BN(randomBytes3(8), "le");
|
|
25577
26113
|
}
|
|
26114
|
+
async function retryQuote(action, deadline) {
|
|
26115
|
+
for (;; ) {
|
|
26116
|
+
try {
|
|
26117
|
+
return await action();
|
|
26118
|
+
} catch (error) {
|
|
26119
|
+
if (!(error instanceof anchor6.AnchorError) || error.error.errorCode.code !== "WrongMarketAccountStatus" || Date.now() >= deadline)
|
|
26120
|
+
throw error;
|
|
26121
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
26122
|
+
}
|
|
26123
|
+
}
|
|
26124
|
+
}
|
|
25578
26125
|
function stateName(value) {
|
|
25579
26126
|
return Object.keys(value)[0] ?? "unknown";
|
|
25580
26127
|
}
|
|
25581
26128
|
|
|
25582
26129
|
class MakerClient {
|
|
25583
26130
|
options;
|
|
26131
|
+
hedges;
|
|
26132
|
+
automation;
|
|
25584
26133
|
provider;
|
|
25585
26134
|
program;
|
|
25586
26135
|
position;
|
|
@@ -25608,6 +26157,8 @@ class MakerClient {
|
|
|
25608
26157
|
}
|
|
25609
26158
|
constructor(options) {
|
|
25610
26159
|
this.options = options;
|
|
26160
|
+
this.hedges = new MakerAccess(options);
|
|
26161
|
+
this.automation = new MakerAutomation(this);
|
|
25611
26162
|
this.provider = new DepthProvider(options.connection, options.wallet, {
|
|
25612
26163
|
commitment: "confirmed",
|
|
25613
26164
|
preflightCommitment: "confirmed"
|
|
@@ -25617,7 +26168,7 @@ class MakerClient {
|
|
|
25617
26168
|
this.backstop = Object.assign((id) => new BackstopHandle(id, this), { offer: (input) => BackstopHandle.offer(this, input) });
|
|
25618
26169
|
}
|
|
25619
26170
|
static async create(options) {
|
|
25620
|
-
const appId = typeof options.appId === "string" ? new
|
|
26171
|
+
const appId = typeof options.appId === "string" ? new PublicKey7(options.appId) : options.appId;
|
|
25621
26172
|
if (options.maker && options.makerOwner && !options.maker.equals(options.makerOwner)) {
|
|
25622
26173
|
throw new Error("maker and makerOwner identify different accounts");
|
|
25623
26174
|
}
|
|
@@ -25661,21 +26212,21 @@ class MakerClient {
|
|
|
25661
26212
|
openMarket: async () => this.funding.openMarket(),
|
|
25662
26213
|
summary: async () => (await this.owned()).account.summary(),
|
|
25663
26214
|
pending: async () => (await this.owned()).account.pending(),
|
|
25664
|
-
deposit: async (sourceOrAmount,
|
|
25665
|
-
if (sourceOrAmount instanceof
|
|
25666
|
-
if (
|
|
26215
|
+
deposit: async (sourceOrAmount, amount3, clientNonce) => {
|
|
26216
|
+
if (sourceOrAmount instanceof PublicKey7) {
|
|
26217
|
+
if (amount3 === undefined || clientNonce === undefined)
|
|
25667
26218
|
throw new RangeError("Source deposits require amount and nonce");
|
|
25668
|
-
return this.funding.deposit(sourceOrAmount,
|
|
26219
|
+
return this.funding.deposit(sourceOrAmount, amount3, clientNonce);
|
|
25669
26220
|
}
|
|
25670
26221
|
return (await this.owned()).account.deposit(sourceOrAmount);
|
|
25671
26222
|
},
|
|
25672
26223
|
withdrawalHandle: (clientNonce) => this.funding.withdrawalHandle(clientNonce),
|
|
25673
26224
|
depositHandle: (nonce) => this.funding.depositHandle(nonce),
|
|
25674
|
-
withdraw: async (destinationOrAmount,
|
|
25675
|
-
if (destinationOrAmount instanceof
|
|
25676
|
-
if (
|
|
26225
|
+
withdraw: async (destinationOrAmount, amount3, clientNonce) => {
|
|
26226
|
+
if (destinationOrAmount instanceof PublicKey7) {
|
|
26227
|
+
if (amount3 === undefined || clientNonce === undefined)
|
|
25677
26228
|
throw new RangeError("Destination withdrawals require amount and nonce");
|
|
25678
|
-
return this.funding.withdraw(destinationOrAmount,
|
|
26229
|
+
return this.funding.withdraw(destinationOrAmount, amount3, clientNonce);
|
|
25679
26230
|
}
|
|
25680
26231
|
return (await this.owned()).account.withdraw(destinationOrAmount);
|
|
25681
26232
|
},
|
|
@@ -25697,21 +26248,21 @@ class MakerClient {
|
|
|
25697
26248
|
};
|
|
25698
26249
|
marketAccount() {
|
|
25699
26250
|
return pda([
|
|
25700
|
-
|
|
26251
|
+
Buffer8.from("market-account"),
|
|
25701
26252
|
this.marketAddress.toBuffer(),
|
|
25702
26253
|
this.options.maker.toBuffer()
|
|
25703
26254
|
]);
|
|
25704
26255
|
}
|
|
25705
26256
|
grantAddress(operator = this.options.wallet.publicKey) {
|
|
25706
26257
|
return pda([
|
|
25707
|
-
|
|
26258
|
+
Buffer8.from("operator"),
|
|
25708
26259
|
this.marketAddress.toBuffer(),
|
|
25709
26260
|
this.options.maker.toBuffer(),
|
|
25710
26261
|
operator.toBuffer()
|
|
25711
26262
|
]);
|
|
25712
26263
|
}
|
|
25713
|
-
async read(name2, address3) {
|
|
25714
|
-
const info = await this.options.connection.getAccountInfo(address3,
|
|
26264
|
+
async read(name2, address3, commitment = "confirmed") {
|
|
26265
|
+
const info = await this.options.connection.getAccountInfo(address3, commitment);
|
|
25715
26266
|
if (!info || !info.owner.equals(DEPTH_PROGRAM_ID4)) {
|
|
25716
26267
|
throw new Error(`${name2} account is unavailable`);
|
|
25717
26268
|
}
|
|
@@ -25795,74 +26346,76 @@ class MakerClient {
|
|
|
25795
26346
|
replacement: (prior, clientNonce) => new QuoteReplacement(this, prior, clientNonce),
|
|
25796
26347
|
publish: async (request) => {
|
|
25797
26348
|
const clientNonce = request.clientNonce ?? BigInt(new anchor6.BN(randomBytes3(8), "le").toString());
|
|
25798
|
-
const quoteNonce = u642(clientNonce, "clientNonce");
|
|
25799
|
-
const id = pda([
|
|
25800
|
-
Buffer6.from("quote"),
|
|
25801
|
-
this.marketAddress.toBuffer(),
|
|
25802
|
-
this.options.maker.toBuffer(),
|
|
25803
|
-
quoteNonce.toArrayLike(Buffer6, "le", 8)
|
|
25804
|
-
]);
|
|
25805
|
-
if (await this.options.connection.getAccountInfo(id, "confirmed")) {
|
|
25806
|
-
const existing = await this.quoteState(id);
|
|
25807
|
-
if (BigInt(existing.capacity.toString()) !== request.maxCapacity || "expiresAt" in request && existing.expiry.toNumber() !== request.expiresAt) {
|
|
25808
|
-
throw new Error("Quote nonce is already bound to different public terms");
|
|
25809
|
-
}
|
|
25810
|
-
return this.quote.get(id);
|
|
25811
|
-
}
|
|
25812
26349
|
const readyDeadline = Date.now() + 60000;
|
|
25813
|
-
|
|
25814
|
-
const
|
|
25815
|
-
|
|
25816
|
-
|
|
25817
|
-
|
|
25818
|
-
|
|
25819
|
-
|
|
25820
|
-
|
|
25821
|
-
|
|
25822
|
-
|
|
25823
|
-
|
|
25824
|
-
|
|
25825
|
-
|
|
25826
|
-
|
|
25827
|
-
|
|
25828
|
-
|
|
25829
|
-
|
|
25830
|
-
|
|
25831
|
-
|
|
25832
|
-
|
|
25833
|
-
|
|
25834
|
-
|
|
25835
|
-
|
|
25836
|
-
|
|
25837
|
-
|
|
25838
|
-
|
|
25839
|
-
|
|
25840
|
-
|
|
25841
|
-
|
|
25842
|
-
|
|
25843
|
-
|
|
25844
|
-
|
|
25845
|
-
|
|
25846
|
-
|
|
25847
|
-
|
|
25848
|
-
|
|
25849
|
-
|
|
25850
|
-
|
|
25851
|
-
|
|
25852
|
-
|
|
25853
|
-
|
|
25854
|
-
|
|
25855
|
-
|
|
25856
|
-
|
|
25857
|
-
|
|
25858
|
-
|
|
26350
|
+
return retryQuote(async () => {
|
|
26351
|
+
const quoteNonce = u642(clientNonce, "clientNonce");
|
|
26352
|
+
const id = pda([
|
|
26353
|
+
Buffer8.from("quote"),
|
|
26354
|
+
this.marketAddress.toBuffer(),
|
|
26355
|
+
this.options.maker.toBuffer(),
|
|
26356
|
+
quoteNonce.toArrayLike(Buffer8, "le", 8)
|
|
26357
|
+
]);
|
|
26358
|
+
if (await this.options.connection.getAccountInfo(id, "confirmed")) {
|
|
26359
|
+
const existing = await this.quoteState(id);
|
|
26360
|
+
if (BigInt(existing.capacity.toString()) !== request.maxCapacity || "expiresAt" in request && existing.expiry.toNumber() !== request.expiresAt) {
|
|
26361
|
+
throw new Error("Quote nonce is already bound to different public terms");
|
|
26362
|
+
}
|
|
26363
|
+
return this.quote.get(id);
|
|
26364
|
+
}
|
|
26365
|
+
for (;; ) {
|
|
26366
|
+
const account = await this.read("MarketAccount", this.marketAccount());
|
|
26367
|
+
if (stateName(account.status) === "ready")
|
|
26368
|
+
break;
|
|
26369
|
+
if (Date.now() >= readyDeadline) {
|
|
26370
|
+
throw new Error("Maker account is processing another operation");
|
|
26371
|
+
}
|
|
26372
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
26373
|
+
}
|
|
26374
|
+
const market = await this.read("Market", this.marketAddress);
|
|
26375
|
+
const input = "tiers" in request ? request : this.flatQuote(request, market, clientNonce);
|
|
26376
|
+
this.validateQuote(input, market);
|
|
26377
|
+
const grant = await this.grantFor(input.maxCapacity, input.expiresAt);
|
|
26378
|
+
const mxePublicKey = await getMXEPublicKey(this.provider, DEPTH_PROGRAM_ID4);
|
|
26379
|
+
if (!mxePublicKey)
|
|
26380
|
+
throw new Error("MXE public key is unavailable");
|
|
26381
|
+
const secret = x25519.utils.randomSecretKey();
|
|
26382
|
+
const inputKey = x25519.getPublicKey(secret);
|
|
26383
|
+
const inputNonce = randomBytes3(16);
|
|
26384
|
+
const cipher = new RescueCipher(x25519.getSharedSecret(secret, mxePublicKey));
|
|
26385
|
+
const bid = Array.from({ length: 4 }, (_, i) => input.tiers[i]?.bidE8 ?? 0n);
|
|
26386
|
+
const ask = Array.from({ length: 4 }, (_, i) => input.tiers[i]?.askE8 ?? 0n);
|
|
26387
|
+
const reciprocal = (price) => {
|
|
26388
|
+
if (price === 0n)
|
|
26389
|
+
return 0n;
|
|
26390
|
+
if (price < 100000000n)
|
|
26391
|
+
throw new RangeError("SOL quote prices must be at least $1");
|
|
26392
|
+
return (1n << 76n) / price;
|
|
26393
|
+
};
|
|
26394
|
+
const ciphertexts = cipher.encrypt(packQuoteCurve({
|
|
26395
|
+
bid,
|
|
26396
|
+
ask,
|
|
26397
|
+
bidReciprocal: bid.map(reciprocal),
|
|
26398
|
+
askReciprocal: ask.map(reciprocal)
|
|
26399
|
+
}), inputNonce);
|
|
26400
|
+
const computationOffset = offset();
|
|
26401
|
+
const queue = await this.queue("prepare_quote", computationOffset);
|
|
26402
|
+
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({
|
|
26403
|
+
...queue,
|
|
26404
|
+
payer: this.options.wallet.publicKey,
|
|
26405
|
+
market: this.marketAddress,
|
|
26406
|
+
marketAccount: this.marketAccount(),
|
|
26407
|
+
grant: grant ?? this.program.programId,
|
|
26408
|
+
quote: id
|
|
26409
|
+
}).rpc({ commitment: "confirmed" });
|
|
26410
|
+
return new QuoteHandle(id, signature, this);
|
|
26411
|
+
}, readyDeadline);
|
|
25859
26412
|
},
|
|
25860
26413
|
get: (id) => new QuoteHandle(id, undefined, this),
|
|
25861
26414
|
list: async (options = {}) => {
|
|
25862
26415
|
const limit = options.limit ?? 20;
|
|
25863
26416
|
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100)
|
|
25864
26417
|
throw new RangeError("limit must be an integer from 1 to 100");
|
|
25865
|
-
const cursor = options.cursor ? new
|
|
26418
|
+
const cursor = options.cursor ? new PublicKey7(options.cursor).toBase58() : undefined;
|
|
25866
26419
|
const accounts2 = await this.options.connection.getProgramAccounts(DEPTH_PROGRAM_ID4, {
|
|
25867
26420
|
commitment: "confirmed",
|
|
25868
26421
|
dataSlice: { offset: 0, length: 0 },
|
|
@@ -25893,10 +26446,10 @@ class MakerClient {
|
|
|
25893
26446
|
throw new Error("Quote changed during discovery");
|
|
25894
26447
|
const quote = this.program.coder.accounts.decode("quoteRecord", info.data);
|
|
25895
26448
|
const canonical = pda([
|
|
25896
|
-
|
|
26449
|
+
Buffer8.from("quote"),
|
|
25897
26450
|
this.marketAddress.toBuffer(),
|
|
25898
26451
|
this.options.maker.toBuffer(),
|
|
25899
|
-
quote.nonce.toArrayLike(
|
|
26452
|
+
quote.nonce.toArrayLike(Buffer8, "le", 8)
|
|
25900
26453
|
]);
|
|
25901
26454
|
if (!id.equals(canonical) || !quote.maker.equals(this.options.maker) || !quote.market.equals(this.marketAddress))
|
|
25902
26455
|
throw new Error("Quote identity changed during discovery");
|
|
@@ -25966,9 +26519,9 @@ class MakerClient {
|
|
|
25966
26519
|
const grant = await this.viewGrant();
|
|
25967
26520
|
const viewNonce = offset();
|
|
25968
26521
|
const view = pda([
|
|
25969
|
-
|
|
26522
|
+
Buffer8.from("market-view"),
|
|
25970
26523
|
this.marketAccount().toBuffer(),
|
|
25971
|
-
viewNonce.toArrayLike(
|
|
26524
|
+
viewNonce.toArrayLike(Buffer8, "le", 8)
|
|
25972
26525
|
]);
|
|
25973
26526
|
return this.requestView("market", view, viewNonce, {
|
|
25974
26527
|
market: this.marketAddress,
|
|
@@ -25976,21 +26529,21 @@ class MakerClient {
|
|
|
25976
26529
|
grant: grant ?? null
|
|
25977
26530
|
});
|
|
25978
26531
|
}
|
|
25979
|
-
async requestFillView(
|
|
25980
|
-
const state = await this.read("OrderRecord",
|
|
26532
|
+
async requestFillView(order2) {
|
|
26533
|
+
const state = await this.read("OrderRecord", order2);
|
|
25981
26534
|
if (!state.maker.equals(this.options.maker) || !state.market.equals(this.marketAddress) || stateName(state.status) !== "filled" || stateName(state.kind) !== "trade") {
|
|
25982
26535
|
throw new Error("Fill is not a finalized trade by this maker");
|
|
25983
26536
|
}
|
|
25984
26537
|
const grant = await this.viewGrant();
|
|
25985
26538
|
const viewNonce = offset();
|
|
25986
26539
|
const view = pda([
|
|
25987
|
-
|
|
25988
|
-
|
|
26540
|
+
Buffer8.from("fill-view"),
|
|
26541
|
+
order2.toBuffer(),
|
|
25989
26542
|
this.options.wallet.publicKey.toBuffer(),
|
|
25990
|
-
viewNonce.toArrayLike(
|
|
26543
|
+
viewNonce.toArrayLike(Buffer8, "le", 8)
|
|
25991
26544
|
]);
|
|
25992
26545
|
return this.requestView("fill", view, viewNonce, {
|
|
25993
|
-
order,
|
|
26546
|
+
order: order2,
|
|
25994
26547
|
grant: grant ?? null
|
|
25995
26548
|
});
|
|
25996
26549
|
}
|
|
@@ -26001,10 +26554,10 @@ class MakerClient {
|
|
|
26001
26554
|
const grant = await this.viewGrant();
|
|
26002
26555
|
const viewNonce = offset();
|
|
26003
26556
|
const view = pda([
|
|
26004
|
-
|
|
26557
|
+
Buffer8.from("quote-view"),
|
|
26005
26558
|
quote.toBuffer(),
|
|
26006
26559
|
this.options.wallet.publicKey.toBuffer(),
|
|
26007
|
-
viewNonce.toArrayLike(
|
|
26560
|
+
viewNonce.toArrayLike(Buffer8, "le", 8)
|
|
26008
26561
|
]);
|
|
26009
26562
|
return this.requestView("quote", view, viewNonce, {
|
|
26010
26563
|
market: this.marketAddress,
|
|
@@ -26036,8 +26589,8 @@ class MakerClient {
|
|
|
26036
26589
|
const actions = {
|
|
26037
26590
|
id: address3,
|
|
26038
26591
|
cancelOpen: () => this.funding.cancelOpenMarket(),
|
|
26039
|
-
addMargin: async (
|
|
26040
|
-
removeMargin: async (
|
|
26592
|
+
addMargin: async (amount3, clientNonce) => (await this.owned()).position.addMargin(amount3, clientNonce),
|
|
26593
|
+
removeMargin: async (amount3, clientNonce) => (await this.owned()).position.removeMargin(amount3, clientNonce),
|
|
26041
26594
|
summary: async (options = { timeoutMs: 30000 }) => {
|
|
26042
26595
|
const view = await this.requestMarketView();
|
|
26043
26596
|
const result = await view.wait(options);
|
|
@@ -26057,7 +26610,7 @@ class MakerClient {
|
|
|
26057
26610
|
const store = createCursorStore(genesis, this.options.maker.toBase58(), this.options.cursorDirectory);
|
|
26058
26611
|
const cursor = await store.load();
|
|
26059
26612
|
return new FillHistory(this).subscribe(async (fill) => {
|
|
26060
|
-
const execution = await this.fills.get(new
|
|
26613
|
+
const execution = await this.fills.get(new PublicKey7(fill.id));
|
|
26061
26614
|
if (execution.status !== "ready")
|
|
26062
26615
|
throw new Error(`Private maker fill ${fill.id} is ${execution.status}`);
|
|
26063
26616
|
await options.onFill({ ...fill, execution });
|
|
@@ -26068,8 +26621,8 @@ class MakerClient {
|
|
|
26068
26621
|
onError: options.onError
|
|
26069
26622
|
});
|
|
26070
26623
|
},
|
|
26071
|
-
get: async (
|
|
26072
|
-
const view = await this.requestFillView(
|
|
26624
|
+
get: async (order2, options = { timeoutMs: 30000 }) => {
|
|
26625
|
+
const view = await this.requestFillView(order2);
|
|
26073
26626
|
const result = await view.wait(options);
|
|
26074
26627
|
if (result.status !== "pending")
|
|
26075
26628
|
await view.close();
|
|
@@ -26110,12 +26663,67 @@ class MakerClient {
|
|
|
26110
26663
|
async function createDepthMaker(options) {
|
|
26111
26664
|
return MakerClient.create(options);
|
|
26112
26665
|
}
|
|
26666
|
+
// src/hedge/wallet.ts
|
|
26667
|
+
async function connect_hyperliquid(provider) {
|
|
26668
|
+
const accounts2 = await provider.request({ method: "eth_requestAccounts" });
|
|
26669
|
+
if (!Array.isArray(accounts2) || typeof accounts2[0] !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(accounts2[0]))
|
|
26670
|
+
throw new Error("The Ethereum wallet did not provide an account");
|
|
26671
|
+
const address3 = accounts2[0];
|
|
26672
|
+
const check = async () => {
|
|
26673
|
+
const current = await provider.request({ method: "eth_accounts" });
|
|
26674
|
+
if (!Array.isArray(current) || typeof current[0] !== "string" || current[0].toLowerCase() !== address3.toLowerCase())
|
|
26675
|
+
throw new Error("The Ethereum wallet account changed; connect it again");
|
|
26676
|
+
};
|
|
26677
|
+
return {
|
|
26678
|
+
async getAddresses() {
|
|
26679
|
+
await check();
|
|
26680
|
+
return [address3];
|
|
26681
|
+
},
|
|
26682
|
+
async getChainId() {
|
|
26683
|
+
const chain = await provider.request({ method: "eth_chainId" });
|
|
26684
|
+
if (typeof chain !== "string" || !/^0x[0-9a-fA-F]+$/.test(chain) || !Number.isSafeInteger(Number(chain)))
|
|
26685
|
+
throw new Error("Ethereum wallet chain is invalid");
|
|
26686
|
+
return Number(chain);
|
|
26687
|
+
},
|
|
26688
|
+
async signTypedData(params) {
|
|
26689
|
+
await check();
|
|
26690
|
+
const signature = await provider.request({
|
|
26691
|
+
method: "eth_signTypedData_v4",
|
|
26692
|
+
params: [
|
|
26693
|
+
address3,
|
|
26694
|
+
JSON.stringify({
|
|
26695
|
+
...params,
|
|
26696
|
+
types: {
|
|
26697
|
+
EIP712Domain: [
|
|
26698
|
+
{ name: "name", type: "string" },
|
|
26699
|
+
{ name: "version", type: "string" },
|
|
26700
|
+
{ name: "chainId", type: "uint256" },
|
|
26701
|
+
{ name: "verifyingContract", type: "address" }
|
|
26702
|
+
],
|
|
26703
|
+
...params.types
|
|
26704
|
+
}
|
|
26705
|
+
})
|
|
26706
|
+
]
|
|
26707
|
+
});
|
|
26708
|
+
if (typeof signature !== "string" || !/^0x[0-9a-fA-F]{130}$/.test(signature))
|
|
26709
|
+
throw new Error("Ethereum wallet returned an invalid signature");
|
|
26710
|
+
return signature;
|
|
26711
|
+
}
|
|
26712
|
+
};
|
|
26713
|
+
}
|
|
26113
26714
|
export {
|
|
26715
|
+
setup_hyperliquid,
|
|
26114
26716
|
createDepthMaker as create_depth_maker,
|
|
26115
26717
|
createDepthMaker,
|
|
26718
|
+
connect_hyperliquid,
|
|
26719
|
+
automationWire,
|
|
26720
|
+
automationView,
|
|
26721
|
+
automationConfig,
|
|
26116
26722
|
QuoteReplacement,
|
|
26117
26723
|
QuoteHandle,
|
|
26118
26724
|
MakerClient,
|
|
26725
|
+
MakerAutomation,
|
|
26726
|
+
MakerAccess,
|
|
26119
26727
|
BackstopRelease,
|
|
26120
26728
|
BackstopHandle
|
|
26121
26729
|
};
|