@oracle-agent/oracle 0.1.0 → 0.2.1
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/README.md +37 -10
- package/SECURITY.md +11 -11
- package/SETUP.md +76 -16
- package/bin/desk-server.mjs +3 -1
- package/docs/architecture.md +10 -9
- package/package.json +13 -7
- package/protocols/templates/safe-erc20/SECURITY.md +16 -0
- package/protocols/templates/safe-erc20/foundry.toml +12 -0
- package/protocols/templates/safe-erc20/remappings.txt +2 -0
- package/protocols/templates/safe-erc20/src/SafeERC20.sol +53 -0
- package/protocols/templates/safe-erc20/test/SafeERC20.t.sol +72 -0
- package/public/oracle-splash/index.html +648 -598
- package/public/oracle-splash/mesh-graph.png +0 -0
- package/public/oracle-splash/mesh-graph.svg +216 -0
- package/scripts/protocol-template-gate.mjs +15 -0
- package/skills/oracle-protocol-builder/SKILL.md +45 -24
- package/src/data/catalog.mjs +9 -0
- package/src/data/desk-data.mjs +24 -0
- package/src/data/providers/bitcoin-esplora.mjs +2 -16
- package/src/data/providers/cowswap.mjs +9 -96
- package/src/data/providers/evm-rpc.mjs +19 -0
- package/src/data/providers/hl-assets.mjs +165 -0
- package/src/data/providers/hl-info.mjs +7 -1
- package/src/data/providers/hl-outcome.mjs +61 -0
- package/src/data/providers/hl-perps.mjs +40 -35
- package/src/data/providers/magiceden-sol.mjs +7 -6
- package/src/data/providers/poly-clob.mjs +460 -0
- package/src/data/providers/satflow.mjs +5 -4
- package/src/data/providers/solana-rpc.mjs +9 -0
- package/src/data/providers/uniswap-v3.mjs +3 -2
- package/src/index.mjs +1 -0
- package/src/onboarding/agent-keys.mjs +4 -0
- package/src/prepare-envelope.mjs +108 -0
- package/src/protocol-templates/gate.mjs +179 -0
- package/src/protocol-templates/prepare-deploy.mjs +81 -0
- package/src/public-control/bundler-client.mjs +3 -18
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Hyperliquid asset ID resolution — main perps, HIP-3 builder dexs, HIP-4 outcomes.
|
|
2
|
+
// Docs: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/asset-ids
|
|
3
|
+
//
|
|
4
|
+
// Main perps: asset = index in meta.universe
|
|
5
|
+
// HIP-3 builder: asset = 100000 + perp_dex_index * 10000 + index_in_meta
|
|
6
|
+
// HIP-4 outcomes: encoding = 10 * outcome + side; asset = 100_000_000 + encoding
|
|
7
|
+
// coin form: #<encoding>
|
|
8
|
+
|
|
9
|
+
import { hlInfo, hlMetaAndAssetCtxs } from "./hl-info.mjs";
|
|
10
|
+
|
|
11
|
+
const OUTCOME_ASSET_BASE = 100_000_000;
|
|
12
|
+
const BUILDER_PERP_BASE = 100_000;
|
|
13
|
+
|
|
14
|
+
let _dexCache = { at: 0, dexs: null };
|
|
15
|
+
let _metaCache = new Map(); // dex -> { at, universe, ctxs }
|
|
16
|
+
|
|
17
|
+
async function perpDexs(opts = {}) {
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
if (_dexCache.dexs && now - _dexCache.at < 60_000 && !opts.fetchImpl) return _dexCache.dexs;
|
|
20
|
+
const dexs = await hlInfo({ type: "perpDexs" }, opts);
|
|
21
|
+
_dexCache = { at: now, dexs: Array.isArray(dexs) ? dexs : [] };
|
|
22
|
+
return _dexCache.dexs;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function metaForDex(dex = "", opts = {}) {
|
|
26
|
+
const key = dex || "";
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
const hit = _metaCache.get(key);
|
|
29
|
+
// skip cache when caller injects fetchImpl (unit tests / custom transport)
|
|
30
|
+
if (hit && now - hit.at < 30_000 && !opts.fetchImpl) return hit;
|
|
31
|
+
const raw = dex
|
|
32
|
+
? await hlInfo({ type: "metaAndAssetCtxs", dex }, opts)
|
|
33
|
+
: await hlMetaAndAssetCtxs(opts);
|
|
34
|
+
const universe = raw?.[0]?.universe ?? raw?.universe ?? [];
|
|
35
|
+
const ctxs = Array.isArray(raw?.[1]) ? raw[1] : [];
|
|
36
|
+
const entry = { at: now, universe, ctxs };
|
|
37
|
+
_metaCache.set(key, entry);
|
|
38
|
+
return entry;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Parse HIP-4 coin "#1100" or encoding number → { encoding, outcome, side }. */
|
|
42
|
+
export function parseOutcomeCoin(coin) {
|
|
43
|
+
const text = String(coin || "").trim();
|
|
44
|
+
let encoding;
|
|
45
|
+
if (text.startsWith("#")) encoding = Number(text.slice(1));
|
|
46
|
+
else if (/^\d+$/.test(text)) encoding = Number(text);
|
|
47
|
+
else return null;
|
|
48
|
+
if (!Number.isInteger(encoding) || encoding < 0) return null;
|
|
49
|
+
const side = encoding % 10;
|
|
50
|
+
if (side !== 0 && side !== 1) return null;
|
|
51
|
+
const outcome = Math.floor(encoding / 10);
|
|
52
|
+
return { encoding, outcome, side, coin: `#${encoding}`, assetId: OUTCOME_ASSET_BASE + encoding };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function outcomeAssetId(outcome, side) {
|
|
56
|
+
const o = Number(outcome);
|
|
57
|
+
const s = Number(side);
|
|
58
|
+
if (!Number.isInteger(o) || o < 0) throw new Error("hl-assets: outcome id must be a non-negative integer");
|
|
59
|
+
if (s !== 0 && s !== 1) throw new Error("hl-assets: outcome side must be 0 or 1");
|
|
60
|
+
const encoding = 10 * o + s;
|
|
61
|
+
return { encoding, outcome: o, side: s, coin: `#${encoding}`, assetId: OUTCOME_ASSET_BASE + encoding };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Resolve any tradeable HL coin name to asset id + precision.
|
|
66
|
+
* Accepts: "BTC", "xyz:TSLA", "#1100", { outcome, side }, { dex, coin }.
|
|
67
|
+
*/
|
|
68
|
+
export async function resolveHlAsset(args = {}, opts = {}) {
|
|
69
|
+
// HIP-4 explicit
|
|
70
|
+
if (args.outcome != null || (args.coin && String(args.coin).startsWith("#"))) {
|
|
71
|
+
const parsed =
|
|
72
|
+
args.outcome != null
|
|
73
|
+
? outcomeAssetId(args.outcome, args.side ?? args.outcomeSide ?? 0)
|
|
74
|
+
: parseOutcomeCoin(args.coin);
|
|
75
|
+
if (!parsed) throw new Error(`hl-assets: invalid outcome coin ${args.coin}`);
|
|
76
|
+
// szDecimals for outcomes: typically 2-4; use 2 default (binary contracts)
|
|
77
|
+
const szDecimals = args.szDecimals != null ? Number(args.szDecimals) : 2;
|
|
78
|
+
return {
|
|
79
|
+
kind: "outcome",
|
|
80
|
+
coin: parsed.coin,
|
|
81
|
+
assetId: parsed.assetId,
|
|
82
|
+
encoding: parsed.encoding,
|
|
83
|
+
outcome: parsed.outcome,
|
|
84
|
+
side: parsed.side,
|
|
85
|
+
szDecimals,
|
|
86
|
+
maxLeverage: 1,
|
|
87
|
+
dex: null,
|
|
88
|
+
markPx: null,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const rawName = String(args.coin || args.symbol || args.name || "").trim();
|
|
93
|
+
if (!rawName) throw new Error("hl-assets: coin required");
|
|
94
|
+
|
|
95
|
+
// HIP-3 builder dex coin: dex:COIN
|
|
96
|
+
if (rawName.includes(":")) {
|
|
97
|
+
const [dex, ...rest] = rawName.split(":");
|
|
98
|
+
const leaf = rest.join(":");
|
|
99
|
+
if (!dex || !leaf) throw new Error(`hl-assets: bad builder coin ${rawName}`);
|
|
100
|
+
const dexs = await perpDexs(opts);
|
|
101
|
+
const dexIndex = dexs.findIndex((d) => d && d.name === dex);
|
|
102
|
+
if (dexIndex < 1) throw new Error(`hl-assets: unknown builder dex "${dex}"`);
|
|
103
|
+
const { universe, ctxs } = await metaForDex(dex, opts);
|
|
104
|
+
const index = universe.findIndex((a) => a.name === rawName || a.name === `${dex}:${leaf}` || a.name.endsWith(`:${leaf}`));
|
|
105
|
+
if (index < 0) throw new Error(`hl-assets: ${rawName} not in dex ${dex} meta`);
|
|
106
|
+
const asset = universe[index];
|
|
107
|
+
if (asset.isDelisted) throw new Error(`hl-assets: ${rawName} is delisted`);
|
|
108
|
+
const assetId = BUILDER_PERP_BASE + dexIndex * 10_000 + index;
|
|
109
|
+
const ctx = ctxs[index] || {};
|
|
110
|
+
return {
|
|
111
|
+
kind: "hip3",
|
|
112
|
+
coin: asset.name || rawName,
|
|
113
|
+
assetId,
|
|
114
|
+
szDecimals: asset.szDecimals ?? 0,
|
|
115
|
+
maxLeverage: asset.maxLeverage ?? null,
|
|
116
|
+
dex,
|
|
117
|
+
dexIndex,
|
|
118
|
+
indexInMeta: index,
|
|
119
|
+
markPx: ctx.markPx ?? null,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Main dex perps
|
|
124
|
+
const { universe, ctxs } = await metaForDex("", opts);
|
|
125
|
+
const coin = rawName.toUpperCase();
|
|
126
|
+
const index = universe.findIndex((a) => a.name === coin);
|
|
127
|
+
if (index < 0) {
|
|
128
|
+
// optional: bare leaf match on builder dexs (TSLA -> xyz:TSLA)
|
|
129
|
+
if (opts.allowBareBuilderLeaf === true) { // opt-in only; explicit dex:COIN preferred
|
|
130
|
+
try {
|
|
131
|
+
const dexs = await perpDexs(opts);
|
|
132
|
+
for (let di = 1; di < dexs.length; di++) {
|
|
133
|
+
const d = dexs[di];
|
|
134
|
+
if (!d?.name) continue;
|
|
135
|
+
const tryName = `${d.name}:${coin}`;
|
|
136
|
+
try {
|
|
137
|
+
return await resolveHlAsset({ coin: tryName }, opts);
|
|
138
|
+
} catch {
|
|
139
|
+
/* next dex */
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} catch {
|
|
143
|
+
/* offline / mock without perpDexs */
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
throw new Error(`hl-perps: ${coin} is not a listed perp`);
|
|
147
|
+
}
|
|
148
|
+
const asset = universe[index];
|
|
149
|
+
if (asset.isDelisted) throw new Error(`hl-assets: ${coin} is delisted`);
|
|
150
|
+
const ctx = ctxs[index] || {};
|
|
151
|
+
return {
|
|
152
|
+
kind: "main",
|
|
153
|
+
coin: asset.name,
|
|
154
|
+
assetId: index,
|
|
155
|
+
szDecimals: asset.szDecimals ?? 0,
|
|
156
|
+
maxLeverage: asset.maxLeverage ?? null,
|
|
157
|
+
dex: "",
|
|
158
|
+
markPx: ctx.markPx ?? null,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function clearHlAssetCache() {
|
|
163
|
+
_dexCache = { at: 0, dexs: null };
|
|
164
|
+
_metaCache.clear();
|
|
165
|
+
}
|
|
@@ -65,7 +65,13 @@ export async function hlMeta(opts = {}) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
export async function hlMetaAndAssetCtxs(opts = {}) {
|
|
68
|
-
|
|
68
|
+
const body = { type: "metaAndAssetCtxs" };
|
|
69
|
+
if (opts.dex) body.dex = opts.dex;
|
|
70
|
+
return hlInfo(body, opts);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function hlPerpDexs(opts = {}) {
|
|
74
|
+
return hlInfo({ type: "perpDexs" }, opts);
|
|
69
75
|
}
|
|
70
76
|
|
|
71
77
|
export async function hlCandleSnapshot({ coin, interval = "1h", startTime, endTime }, opts = {}) {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// HIP-4 outcome market prepare helpers (local, no VPS).
|
|
2
|
+
import { stampPrepared } from "../../prepare-envelope.mjs";
|
|
3
|
+
import { hlOutcomeMeta } from "./hl-info.mjs";
|
|
4
|
+
import { outcomeAssetId, parseOutcomeCoin } from "./hl-assets.mjs";
|
|
5
|
+
import { hlPreparePerpOrder, hlPrepareCancelOrder } from "./hl-perps.mjs";
|
|
6
|
+
|
|
7
|
+
export async function hlOutcomeList(opts = {}) {
|
|
8
|
+
const meta = await hlOutcomeMeta(opts);
|
|
9
|
+
return {
|
|
10
|
+
provider: "hl-outcome",
|
|
11
|
+
kind: "outcome-meta",
|
|
12
|
+
outcomes: meta?.outcomes || [],
|
|
13
|
+
questions: meta?.questions || [],
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Prepare a HIP-4 outcome order.
|
|
19
|
+
* args: { outcome, side: 0|1|'yes'|'no', sideName?, coin?, ...hlPreparePerpOrder fields }
|
|
20
|
+
*/
|
|
21
|
+
export async function hlPrepareOutcomeOrder(args = {}, opts = {}) {
|
|
22
|
+
let coin = args.coin;
|
|
23
|
+
let outcome = args.outcome;
|
|
24
|
+
let side = args.side;
|
|
25
|
+
if (typeof side === "string") {
|
|
26
|
+
const s = side.toLowerCase();
|
|
27
|
+
if (s === "yes" || s === "0") side = 0;
|
|
28
|
+
else if (s === "no" || s === "1") side = 1;
|
|
29
|
+
}
|
|
30
|
+
if (args.sideName && outcome != null) {
|
|
31
|
+
const meta = await hlOutcomeMeta(opts);
|
|
32
|
+
const o = (meta?.outcomes || []).find((x) => Number(x.outcome) === Number(outcome));
|
|
33
|
+
if (o?.sideSpecs) {
|
|
34
|
+
const idx = o.sideSpecs.findIndex(
|
|
35
|
+
(sp) => String(sp.name).toLowerCase() === String(args.sideName).toLowerCase()
|
|
36
|
+
);
|
|
37
|
+
if (idx >= 0) side = idx;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (!coin) {
|
|
41
|
+
if (outcome == null || side == null) {
|
|
42
|
+
throw new Error("hl-outcome: provide coin (#N) or outcome + side (0/1 or yes/no)");
|
|
43
|
+
}
|
|
44
|
+
coin = outcomeAssetId(outcome, side).coin;
|
|
45
|
+
}
|
|
46
|
+
const prepared = await hlPreparePerpOrder({ ...args, coin }, opts);
|
|
47
|
+
// ensure stamped kind
|
|
48
|
+
return stampPrepared(
|
|
49
|
+
{ ...prepared, kind: "hip4-order", provider: "hl-perps" },
|
|
50
|
+
{ provider: "hl-perps", kind: "hip4-order" }
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function hlPrepareOutcomeCancel(args = {}, opts = {}) {
|
|
55
|
+
let coin = args.coin;
|
|
56
|
+
if (!coin && args.outcome != null) {
|
|
57
|
+
const side = args.side ?? 0;
|
|
58
|
+
coin = outcomeAssetId(args.outcome, side).coin;
|
|
59
|
+
}
|
|
60
|
+
return hlPrepareCancelOrder({ ...args, coin }, opts);
|
|
61
|
+
}
|
|
@@ -12,8 +12,10 @@
|
|
|
12
12
|
// rounded to szDecimals. Integers are always allowed regardless of sig figs.
|
|
13
13
|
// We do that rounding here, once, in decimal — never through a float.
|
|
14
14
|
|
|
15
|
-
import { hlMetaAndAssetCtxs } from "./hl-info.mjs";
|
|
15
|
+
import { hlMetaAndAssetCtxs, hlAllMids } from "./hl-info.mjs";
|
|
16
|
+
import { resolveHlAsset } from "./hl-assets.mjs";
|
|
16
17
|
import { toScaledInteger } from "../../exact-integer.mjs";
|
|
18
|
+
import { stampPrepared } from "../../prepare-envelope.mjs";
|
|
17
19
|
|
|
18
20
|
export const HL_PERP_MAX_SIG_FIGS = 5;
|
|
19
21
|
export const HL_PERP_PRICE_DECIMALS = 6; // 6 - szDecimals for perps
|
|
@@ -102,23 +104,18 @@ export function formatPerpSize(size, szDecimals) {
|
|
|
102
104
|
|
|
103
105
|
/** Resolve a coin to its asset index and precision. Required before ordering. */
|
|
104
106
|
export async function hlPerpAssetInfo(args = {}, opts = {}) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const meta = await hlMetaAndAssetCtxs(opts);
|
|
108
|
-
const universe = meta?.[0]?.universe ?? meta?.universe ?? [];
|
|
109
|
-
const ctxs = Array.isArray(meta?.[1]) ? meta[1] : [];
|
|
110
|
-
const index = universe.findIndex((a) => a.name === coin);
|
|
111
|
-
if (index < 0) throw new Error(`hl-perps: ${coin} is not a listed perp`);
|
|
112
|
-
const asset = universe[index];
|
|
113
|
-
if (asset.isDelisted) throw new Error(`hl-perps: ${coin} is delisted`);
|
|
114
|
-
const ctx = ctxs[index] || {};
|
|
107
|
+
// Resolves main perps, HIP-3 builder dexs (dex:COIN), and HIP-4 outcomes (#N / outcome+side).
|
|
108
|
+
const info = await resolveHlAsset(args, opts);
|
|
115
109
|
return {
|
|
116
|
-
coin,
|
|
117
|
-
assetId:
|
|
118
|
-
szDecimals:
|
|
119
|
-
maxLeverage:
|
|
120
|
-
markPx:
|
|
121
|
-
|
|
110
|
+
coin: info.coin,
|
|
111
|
+
assetId: info.assetId,
|
|
112
|
+
szDecimals: info.szDecimals,
|
|
113
|
+
maxLeverage: info.maxLeverage,
|
|
114
|
+
markPx: info.markPx,
|
|
115
|
+
kind: info.kind,
|
|
116
|
+
dex: info.dex ?? null,
|
|
117
|
+
outcome: info.outcome ?? null,
|
|
118
|
+
side: info.side ?? null,
|
|
122
119
|
};
|
|
123
120
|
}
|
|
124
121
|
|
|
@@ -157,16 +154,16 @@ function envelope(action, nonce) {
|
|
|
157
154
|
action,
|
|
158
155
|
nonce,
|
|
159
156
|
signatureChainId: HL_SIGNATURE_CHAIN_ID,
|
|
160
|
-
// The wallet signs this; Oracle never does.
|
|
161
|
-
requiresUserSignature: true,
|
|
162
|
-
signingReady: false,
|
|
163
|
-
broadcastReady: false,
|
|
164
|
-
executionReady: false,
|
|
165
157
|
submitTo: "https://api.hyperliquid.xyz/exchange",
|
|
166
|
-
note: "Sign
|
|
158
|
+
note: "Sign locally via @oracle-agent/operator or a wallet. Oracle does not submit.",
|
|
167
159
|
};
|
|
168
160
|
}
|
|
169
161
|
|
|
162
|
+
/** Stamp a full HL prepare result (provider/kind + action envelope). */
|
|
163
|
+
function stamped(result) {
|
|
164
|
+
return stampPrepared(result, { provider: result.provider || "hl-perps", kind: result.kind });
|
|
165
|
+
}
|
|
166
|
+
|
|
170
167
|
/**
|
|
171
168
|
* Prepare a perp order.
|
|
172
169
|
*
|
|
@@ -185,7 +182,11 @@ export async function hlPreparePerpOrder(args = {}, opts = {}) {
|
|
|
185
182
|
|
|
186
183
|
let price;
|
|
187
184
|
if (kind === ORDER_TYPES.MARKET) {
|
|
188
|
-
|
|
185
|
+
let mark = Number(info.markPx);
|
|
186
|
+
if (!Number.isFinite(mark)) {
|
|
187
|
+
const mids = await hlAllMids(opts);
|
|
188
|
+
mark = Number(mids?.[info.coin] ?? mids?.[String(info.assetId)] ?? NaN);
|
|
189
|
+
}
|
|
189
190
|
if (!Number.isFinite(mark)) throw new Error("hl-perps: no mark price available for a market order");
|
|
190
191
|
const bps = Number(args.maxSlippageBps ?? 50);
|
|
191
192
|
if (!Number.isInteger(bps) || bps < 0) throw new Error("hl-perps: maxSlippageBps must be a non-negative integer");
|
|
@@ -234,12 +235,16 @@ export async function hlPreparePerpOrder(args = {}, opts = {}) {
|
|
|
234
235
|
const nonce = Number(args.nonce ?? Date.now());
|
|
235
236
|
const notional = Number(price) * Number(size);
|
|
236
237
|
|
|
237
|
-
return {
|
|
238
|
+
return stamped({
|
|
238
239
|
provider: "hl-perps",
|
|
239
240
|
venue: "hyperliquid",
|
|
240
|
-
kind: "perp-order",
|
|
241
|
+
kind: info.kind === "outcome" ? "hip4-order" : info.kind === "hip3" ? "hip3-order" : "perp-order",
|
|
241
242
|
coin: info.coin,
|
|
242
243
|
assetId: info.assetId,
|
|
244
|
+
assetKind: info.kind,
|
|
245
|
+
outcome: info.outcome ?? null,
|
|
246
|
+
outcomeSide: info.side ?? null,
|
|
247
|
+
dex: info.dex ?? null,
|
|
243
248
|
side: isBuy ? "buy" : "sell",
|
|
244
249
|
orderType: kind,
|
|
245
250
|
price,
|
|
@@ -249,7 +254,7 @@ export async function hlPreparePerpOrder(args = {}, opts = {}) {
|
|
|
249
254
|
maxLeverage: info.maxLeverage,
|
|
250
255
|
markPx: info.markPx,
|
|
251
256
|
...envelope(action, nonce),
|
|
252
|
-
};
|
|
257
|
+
});
|
|
253
258
|
}
|
|
254
259
|
|
|
255
260
|
/** Prepare a cancel by order id. */
|
|
@@ -258,14 +263,14 @@ export async function hlPrepareCancelOrder(args = {}, opts = {}) {
|
|
|
258
263
|
const oid = args.orderId ?? args.oid;
|
|
259
264
|
if (oid == null) throw new Error("hl-perps: orderId required");
|
|
260
265
|
const action = { type: "cancel", cancels: [{ a: info.assetId, o: Number(oid) }] };
|
|
261
|
-
return {
|
|
266
|
+
return stamped({
|
|
262
267
|
provider: "hl-perps",
|
|
263
268
|
venue: "hyperliquid",
|
|
264
269
|
kind: "perp-cancel",
|
|
265
270
|
coin: info.coin,
|
|
266
271
|
orderId: Number(oid),
|
|
267
272
|
...envelope(action, Number(args.nonce ?? Date.now())),
|
|
268
|
-
};
|
|
273
|
+
});
|
|
269
274
|
}
|
|
270
275
|
|
|
271
276
|
/**
|
|
@@ -292,7 +297,7 @@ export async function hlPrepareUpdateLeverage(args = {}, opts = {}) {
|
|
|
292
297
|
isCross: mode === MARGIN_MODE.CROSS,
|
|
293
298
|
leverage,
|
|
294
299
|
};
|
|
295
|
-
return {
|
|
300
|
+
return stamped({
|
|
296
301
|
provider: "hl-perps",
|
|
297
302
|
venue: "hyperliquid",
|
|
298
303
|
kind: "perp-leverage",
|
|
@@ -305,7 +310,7 @@ export async function hlPrepareUpdateLeverage(args = {}, opts = {}) {
|
|
|
305
310
|
? `${leverage}x liquidates on roughly a ${(100 / leverage).toFixed(2)}% adverse move before fees`
|
|
306
311
|
: null,
|
|
307
312
|
...envelope(action, Number(args.nonce ?? Date.now())),
|
|
308
|
-
};
|
|
313
|
+
});
|
|
309
314
|
}
|
|
310
315
|
|
|
311
316
|
/** Prepare an isolated-margin adjustment for an existing position. */
|
|
@@ -325,14 +330,14 @@ export async function hlPrepareUpdateIsolatedMargin(args = {}, opts = {}) {
|
|
|
325
330
|
isBuy: true,
|
|
326
331
|
ntli: micro <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(micro) : micro.toString(),
|
|
327
332
|
};
|
|
328
|
-
return {
|
|
333
|
+
return stamped({
|
|
329
334
|
provider: "hl-perps",
|
|
330
335
|
venue: "hyperliquid",
|
|
331
336
|
kind: "perp-isolated-margin",
|
|
332
337
|
coin: info.coin,
|
|
333
338
|
usd: text,
|
|
334
339
|
...envelope(action, Number(args.nonce ?? Date.now())),
|
|
335
|
-
};
|
|
340
|
+
});
|
|
336
341
|
}
|
|
337
342
|
|
|
338
343
|
/**
|
|
@@ -368,7 +373,7 @@ export async function hlPrepareBracketOrder(args = {}, opts = {}) {
|
|
|
368
373
|
if (orders.length === 1) throw new Error("hl-perps: a bracket needs takeProfitPx and/or stopLossPx");
|
|
369
374
|
|
|
370
375
|
const action = { type: "order", orders, grouping: "normalTpsl" };
|
|
371
|
-
return {
|
|
376
|
+
return stamped({
|
|
372
377
|
provider: "hl-perps",
|
|
373
378
|
venue: "hyperliquid",
|
|
374
379
|
kind: "perp-bracket",
|
|
@@ -378,5 +383,5 @@ export async function hlPrepareBracketOrder(args = {}, opts = {}) {
|
|
|
378
383
|
takeProfitPx: args.takeProfitPx == null ? null : formatPerpPrice(args.takeProfitPx, info.szDecimals),
|
|
379
384
|
stopLossPx: args.stopLossPx == null ? null : formatPerpPrice(args.stopLossPx, info.szDecimals),
|
|
380
385
|
...envelope(action, Number(args.nonce ?? Date.now())),
|
|
381
|
-
};
|
|
386
|
+
});
|
|
382
387
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { httpJson } from "../http.mjs";
|
|
9
9
|
import { solanaPubkey } from "./solana-rpc.mjs";
|
|
10
10
|
import { resolveProviderEndpoint, credentialedHeaders } from "../provider-endpoint.mjs";
|
|
11
|
+
import { stampPrepared } from "../../prepare-envelope.mjs";
|
|
11
12
|
|
|
12
13
|
export const MAGICEDEN_SOL_API = "https://api-mainnet.magiceden.dev/v2";
|
|
13
14
|
export const LAMPORTS_PER_SOL = 1_000_000_000;
|
|
@@ -223,7 +224,7 @@ export async function magicEdenSolPrepareBuy(args = {}, opts = {}) {
|
|
|
223
224
|
throw new Error("magiceden: buy_now returned no unsigned transaction payload");
|
|
224
225
|
}
|
|
225
226
|
const transaction = Buffer.from(data).toString("base64");
|
|
226
|
-
return {
|
|
227
|
+
return stampPrepared({
|
|
227
228
|
provider: "magiceden-sol",
|
|
228
229
|
chain: "solana-mainnet-beta",
|
|
229
230
|
kind: "nft-buy",
|
|
@@ -241,7 +242,7 @@ export async function magicEdenSolPrepareBuy(args = {}, opts = {}) {
|
|
|
241
242
|
transaction,
|
|
242
243
|
transactionEncoding: "base64",
|
|
243
244
|
raw,
|
|
244
|
-
};
|
|
245
|
+
});
|
|
245
246
|
}
|
|
246
247
|
|
|
247
248
|
/**
|
|
@@ -269,7 +270,7 @@ export async function magicEdenSolPrepareList(args = {}, opts = {}) {
|
|
|
269
270
|
const data = raw?.v0?.tx?.data ?? raw?.tx?.data ?? null;
|
|
270
271
|
const transaction = Array.isArray(data) ? Buffer.from(data).toString("base64") : null;
|
|
271
272
|
if (!transaction) throw new Error("magiceden: sell returned no transaction payload");
|
|
272
|
-
return {
|
|
273
|
+
return stampPrepared({
|
|
273
274
|
provider: "magiceden-sol",
|
|
274
275
|
chain: "solana-mainnet-beta",
|
|
275
276
|
kind: "nft-list",
|
|
@@ -284,7 +285,7 @@ export async function magicEdenSolPrepareList(args = {}, opts = {}) {
|
|
|
284
285
|
transaction,
|
|
285
286
|
transactionEncoding: "base64",
|
|
286
287
|
raw,
|
|
287
|
-
};
|
|
288
|
+
});
|
|
288
289
|
}
|
|
289
290
|
|
|
290
291
|
/**
|
|
@@ -333,7 +334,7 @@ export async function magicEdenSolPrepareMint(args = {}, opts = {}) {
|
|
|
333
334
|
throw new Error("magiceden: mint returned no unsigned transaction payload");
|
|
334
335
|
}
|
|
335
336
|
const transaction = Buffer.from(data).toString("base64");
|
|
336
|
-
return {
|
|
337
|
+
return stampPrepared({
|
|
337
338
|
provider: "magiceden-sol",
|
|
338
339
|
chain: "solana-mainnet-beta",
|
|
339
340
|
kind: "nft-mint",
|
|
@@ -351,5 +352,5 @@ export async function magicEdenSolPrepareMint(args = {}, opts = {}) {
|
|
|
351
352
|
transaction,
|
|
352
353
|
transactionEncoding: "base64",
|
|
353
354
|
raw,
|
|
354
|
-
};
|
|
355
|
+
});
|
|
355
356
|
}
|