@continuumdao/ctm-mpc-defi 0.2.24 → 0.2.26
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/agent/catalog.cjs +1070 -19
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.d.ts +2384 -1
- package/dist/agent/catalog.js +1027 -20
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/skills/arcus/SKILL.md +76 -0
- package/dist/agent/skills/gmx/SKILL.md +2 -0
- package/dist/agent/skills/hyperliquid/SKILL.md +12 -3
- package/dist/agent/skills/morpho/SKILL.md +39 -0
- package/dist/agent/skills/uniswap-v4/SKILL.md +5 -0
- package/dist/agent/skills/venice/SKILL.md +29 -0
- package/dist/core/index.cjs +55 -0
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.ts +28 -2
- package/dist/core/index.js +54 -1
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +55 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +54 -1
- package/dist/index.js.map +1 -1
- package/dist/protocols/evm/arcus/index.cjs +2068 -0
- package/dist/protocols/evm/arcus/index.cjs.map +1 -0
- package/dist/protocols/evm/arcus/index.d.ts +728 -0
- package/dist/protocols/evm/arcus/index.js +1970 -0
- package/dist/protocols/evm/arcus/index.js.map +1 -0
- package/dist/protocols/evm/morpho/index.cjs +76 -2
- package/dist/protocols/evm/morpho/index.cjs.map +1 -1
- package/dist/protocols/evm/morpho/index.d.ts +22 -1
- package/dist/protocols/evm/morpho/index.js +68 -3
- package/dist/protocols/evm/morpho/index.js.map +1 -1
- package/dist/protocols/evm/uniswap-v4/index.cjs.map +1 -1
- package/dist/protocols/evm/uniswap-v4/index.d.ts +1 -1
- package/dist/protocols/evm/uniswap-v4/index.js.map +1 -1
- package/dist/protocols/evm/venice/index.cjs +936 -0
- package/dist/protocols/evm/venice/index.cjs.map +1 -0
- package/dist/protocols/evm/venice/index.d.ts +238 -0
- package/dist/protocols/evm/venice/index.js +901 -0
- package/dist/protocols/evm/venice/index.js.map +1 -0
- package/package.json +11 -1
|
@@ -0,0 +1,1970 @@
|
|
|
1
|
+
import { parseAbi, getAddress, createPublicClient, http, erc20Abi, hashMessage, encodeFunctionData, defineChain, parseGwei, serializeTransaction, keccak256, hashTypedData, stringToHex } from 'viem';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import { getClientIdFromKeyGenResult, fetchChainFeeParams, gasLimitFromEstimateAndChainConfig, gweiToDecimalString, proposalTxParamsToFeeSnapshot, alignEip1559FeesWithLatestBase } from '@continuumdao/continuum-node-sdk';
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __esm = (fn, res) => function __init() {
|
|
8
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
9
|
+
};
|
|
10
|
+
var __export = (target, all) => {
|
|
11
|
+
for (var name in all)
|
|
12
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/protocols/evm/arcus/support.ts
|
|
16
|
+
var support_exports = {};
|
|
17
|
+
__export(support_exports, {
|
|
18
|
+
ARCUS_SUPPORTED_CHAIN_IDS: () => ARCUS_SUPPORTED_CHAIN_IDS,
|
|
19
|
+
arcusApiMetaBaseUrl: () => arcusApiMetaBaseUrl,
|
|
20
|
+
arcusPerpApiBaseUrl: () => arcusPerpApiBaseUrl,
|
|
21
|
+
arcusSpotIndexerBaseUrl: () => arcusSpotIndexerBaseUrl,
|
|
22
|
+
isArcusChainSupported: () => isArcusChainSupported
|
|
23
|
+
});
|
|
24
|
+
function isArcusChainSupported(chainId) {
|
|
25
|
+
return ARCUS_SUPPORTED_CHAIN_IDS.includes(chainId);
|
|
26
|
+
}
|
|
27
|
+
function arcusPerpApiBaseUrl(_chainId) {
|
|
28
|
+
return "https://api.arcus.xyz";
|
|
29
|
+
}
|
|
30
|
+
function arcusSpotIndexerBaseUrl(_chainId) {
|
|
31
|
+
return "https://indexer.spot.arcus.xyz";
|
|
32
|
+
}
|
|
33
|
+
function arcusApiMetaBaseUrl(_chainId) {
|
|
34
|
+
return "https://api.arcus.xyz/v1/api-meta";
|
|
35
|
+
}
|
|
36
|
+
var ARCUS_SUPPORTED_CHAIN_IDS;
|
|
37
|
+
var init_support = __esm({
|
|
38
|
+
"src/protocols/evm/arcus/support.ts"() {
|
|
39
|
+
ARCUS_SUPPORTED_CHAIN_IDS = [4663];
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// src/protocols/evm/arcus/constants.ts
|
|
44
|
+
var ARCUS_PROTOCOL_ID, ARCUS_DEFAULT_OHLCV_INTERVAL, ARCUS_DEFAULT_LOOKBACK_DAYS, ARCUS_MAX_CANDLE_COUNT, ARCUS_DEFAULT_CANDLE_COUNT, ARCUS_PERP_OHLCV_INTERVALS, ARCUS_SPOT_OHLCV_INTERVALS, ARCUS_PERP_INTERVAL_MS, ARCUS_SPOT_INTERVAL_SEC, ARCUS_USDG_ADDRESS_4663;
|
|
45
|
+
var init_constants = __esm({
|
|
46
|
+
"src/protocols/evm/arcus/constants.ts"() {
|
|
47
|
+
ARCUS_PROTOCOL_ID = "arcus";
|
|
48
|
+
ARCUS_DEFAULT_OHLCV_INTERVAL = "1h";
|
|
49
|
+
ARCUS_DEFAULT_LOOKBACK_DAYS = 14;
|
|
50
|
+
ARCUS_MAX_CANDLE_COUNT = 1500;
|
|
51
|
+
ARCUS_DEFAULT_CANDLE_COUNT = 200;
|
|
52
|
+
ARCUS_PERP_OHLCV_INTERVALS = [
|
|
53
|
+
"1m",
|
|
54
|
+
"3m",
|
|
55
|
+
"5m",
|
|
56
|
+
"15m",
|
|
57
|
+
"30m",
|
|
58
|
+
"1h",
|
|
59
|
+
"2h",
|
|
60
|
+
"4h",
|
|
61
|
+
"8h",
|
|
62
|
+
"12h",
|
|
63
|
+
"1d",
|
|
64
|
+
"3d",
|
|
65
|
+
"1w"
|
|
66
|
+
];
|
|
67
|
+
ARCUS_SPOT_OHLCV_INTERVALS = ["1m", "5m", "15m", "1h", "1d"];
|
|
68
|
+
ARCUS_PERP_INTERVAL_MS = {
|
|
69
|
+
"1m": 6e4,
|
|
70
|
+
"3m": 18e4,
|
|
71
|
+
"5m": 3e5,
|
|
72
|
+
"15m": 9e5,
|
|
73
|
+
"30m": 18e5,
|
|
74
|
+
"1h": 36e5,
|
|
75
|
+
"2h": 72e5,
|
|
76
|
+
"4h": 144e5,
|
|
77
|
+
"8h": 288e5,
|
|
78
|
+
"12h": 432e5,
|
|
79
|
+
"1d": 864e5,
|
|
80
|
+
"3d": 2592e5,
|
|
81
|
+
"1w": 6048e5
|
|
82
|
+
};
|
|
83
|
+
ARCUS_SPOT_INTERVAL_SEC = {
|
|
84
|
+
"1m": 60,
|
|
85
|
+
"5m": 300,
|
|
86
|
+
"15m": 900,
|
|
87
|
+
"1h": 3600,
|
|
88
|
+
"1d": 86400
|
|
89
|
+
};
|
|
90
|
+
ARCUS_USDG_ADDRESS_4663 = "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168";
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// src/protocols/evm/arcus/api.ts
|
|
95
|
+
var api_exports = {};
|
|
96
|
+
__export(api_exports, {
|
|
97
|
+
arcusFetchAccount: () => arcusFetchAccount,
|
|
98
|
+
arcusFetchAccountLeverages: () => arcusFetchAccountLeverages,
|
|
99
|
+
arcusFetchAllMids: () => arcusFetchAllMids,
|
|
100
|
+
arcusFetchApiKeys: () => arcusFetchApiKeys,
|
|
101
|
+
arcusFetchOpenOrders: () => arcusFetchOpenOrders,
|
|
102
|
+
arcusFetchPerpCandles: () => arcusFetchPerpCandles,
|
|
103
|
+
arcusFetchPerpMarkets: () => arcusFetchPerpMarkets,
|
|
104
|
+
arcusFetchPerpOhlcvRange: () => arcusFetchPerpOhlcvRange,
|
|
105
|
+
arcusFetchPositions: () => arcusFetchPositions,
|
|
106
|
+
arcusFetchSpotOhlcvRange: () => arcusFetchSpotOhlcvRange,
|
|
107
|
+
arcusFetchSpotOverview: () => arcusFetchSpotOverview,
|
|
108
|
+
arcusFetchSpotTokenCandles: () => arcusFetchSpotTokenCandles,
|
|
109
|
+
arcusMapPerpCandles: () => arcusMapPerpCandles,
|
|
110
|
+
arcusMapSpotCandles: () => arcusMapSpotCandles,
|
|
111
|
+
arcusResolvePerpOhlcvWindow: () => arcusResolvePerpOhlcvWindow
|
|
112
|
+
});
|
|
113
|
+
async function arcusJsonGet(url) {
|
|
114
|
+
const res = await fetch(url, { headers: { Accept: "application/json" } });
|
|
115
|
+
const text = await res.text();
|
|
116
|
+
let body = null;
|
|
117
|
+
if (text.trim()) {
|
|
118
|
+
try {
|
|
119
|
+
body = JSON.parse(text);
|
|
120
|
+
} catch {
|
|
121
|
+
throw new Error(`Arcus API returned non-JSON (${res.status}): ${text.slice(0, 200)}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (!res.ok) {
|
|
125
|
+
const err = body && typeof body === "object" && body !== null && "error" in body ? String(body.error) : text.slice(0, 200);
|
|
126
|
+
throw new Error(`Arcus API ${res.status}: ${err}`);
|
|
127
|
+
}
|
|
128
|
+
return body;
|
|
129
|
+
}
|
|
130
|
+
function arcusMapPerpCandles(raw) {
|
|
131
|
+
return (raw ?? []).map((c) => ({
|
|
132
|
+
timestampMs: Math.floor(c.openTime / 1e3),
|
|
133
|
+
open: c.open,
|
|
134
|
+
high: c.high,
|
|
135
|
+
low: c.low,
|
|
136
|
+
close: c.close,
|
|
137
|
+
volume: c.volume ?? "0",
|
|
138
|
+
...c.isFinal != null ? { isFinal: c.isFinal } : {}
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
async function arcusFetchPerpMarkets(args) {
|
|
142
|
+
const base = arcusPerpApiBaseUrl(args.chainId);
|
|
143
|
+
const data = await arcusJsonGet(`${base}/v1/markets`);
|
|
144
|
+
return { markets: data.markets ?? [] };
|
|
145
|
+
}
|
|
146
|
+
async function arcusFetchPerpCandles(args) {
|
|
147
|
+
const base = arcusPerpApiBaseUrl(args.chainId);
|
|
148
|
+
const toMicros = args.toMicros ?? Date.now() * 1e3;
|
|
149
|
+
const countback = Math.min(args.countback ?? ARCUS_DEFAULT_CANDLE_COUNT, ARCUS_MAX_CANDLE_COUNT);
|
|
150
|
+
const params = new URLSearchParams({
|
|
151
|
+
market: args.market.trim(),
|
|
152
|
+
timeframe: args.timeframe,
|
|
153
|
+
to: String(toMicros),
|
|
154
|
+
countback: String(countback)
|
|
155
|
+
});
|
|
156
|
+
if (args.fromMicros != null) {
|
|
157
|
+
params.delete("countback");
|
|
158
|
+
params.set("from", String(args.fromMicros));
|
|
159
|
+
}
|
|
160
|
+
const data = await arcusJsonGet(`${base}/v1/candles?${params}`);
|
|
161
|
+
return { candles: arcusMapPerpCandles(data.candles) };
|
|
162
|
+
}
|
|
163
|
+
function arcusResolvePerpOhlcvWindow(args) {
|
|
164
|
+
const endTimeMs = args.endTimeMs ?? Date.now();
|
|
165
|
+
const intervalMs = ARCUS_PERP_INTERVAL_MS[args.interval] ?? ARCUS_PERP_INTERVAL_MS["15m"];
|
|
166
|
+
let startTimeMs;
|
|
167
|
+
if (args.startTimeMs != null) {
|
|
168
|
+
startTimeMs = args.startTimeMs;
|
|
169
|
+
} else if (args.lookbackHours != null && args.lookbackHours > 0) {
|
|
170
|
+
startTimeMs = endTimeMs - args.lookbackHours * 36e5;
|
|
171
|
+
} else {
|
|
172
|
+
const days = args.lookbackDays ?? 14;
|
|
173
|
+
startTimeMs = endTimeMs - days * 864e5;
|
|
174
|
+
}
|
|
175
|
+
const spanBars = Math.ceil((endTimeMs - startTimeMs) / intervalMs) + 1;
|
|
176
|
+
const countback = Math.min(
|
|
177
|
+
args.candleCount ?? spanBars,
|
|
178
|
+
ARCUS_MAX_CANDLE_COUNT
|
|
179
|
+
);
|
|
180
|
+
return { startTimeMs, endTimeMs, countback };
|
|
181
|
+
}
|
|
182
|
+
async function arcusFetchPerpOhlcvRange(args) {
|
|
183
|
+
const interval = args.interval ?? ARCUS_DEFAULT_OHLCV_INTERVAL;
|
|
184
|
+
const window = arcusResolvePerpOhlcvWindow({ ...args, interval });
|
|
185
|
+
const { candles } = await arcusFetchPerpCandles({
|
|
186
|
+
chainId: args.chainId,
|
|
187
|
+
market: args.market,
|
|
188
|
+
timeframe: interval,
|
|
189
|
+
toMicros: window.endTimeMs * 1e3,
|
|
190
|
+
fromMicros: args.startTimeMs != null ? window.startTimeMs * 1e3 : void 0,
|
|
191
|
+
countback: window.countback
|
|
192
|
+
});
|
|
193
|
+
const sorted = [...candles].sort((a, b) => a.timestampMs - b.timestampMs);
|
|
194
|
+
return {
|
|
195
|
+
market: args.market.trim(),
|
|
196
|
+
interval,
|
|
197
|
+
marketKind: "perp",
|
|
198
|
+
startTimeMs: sorted[0]?.timestampMs ?? window.startTimeMs,
|
|
199
|
+
endTimeMs: sorted[sorted.length - 1]?.timestampMs ?? window.endTimeMs,
|
|
200
|
+
expectedBars: window.countback,
|
|
201
|
+
candleCount: sorted.length,
|
|
202
|
+
latestCandle: sorted[sorted.length - 1] ?? null,
|
|
203
|
+
candles: sorted,
|
|
204
|
+
fetchedAtMs: Date.now(),
|
|
205
|
+
dataSource: "arcus"
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
async function arcusFetchAccountLeverages(args) {
|
|
209
|
+
const base = arcusPerpApiBaseUrl(args.chainId);
|
|
210
|
+
const params = new URLSearchParams({ address: args.address.trim() });
|
|
211
|
+
if (args.accountIndex != null) {
|
|
212
|
+
params.set("accountIndex", String(args.accountIndex));
|
|
213
|
+
}
|
|
214
|
+
const data = await arcusJsonGet(`${base}/v1/leverages?${params}`);
|
|
215
|
+
return { leverages: data.leverages ?? [] };
|
|
216
|
+
}
|
|
217
|
+
async function arcusFetchAllMids(args) {
|
|
218
|
+
const base = arcusPerpApiBaseUrl(args.chainId);
|
|
219
|
+
const data = await arcusJsonGet(`${base}/v1/mids`);
|
|
220
|
+
return data?.mids ?? {};
|
|
221
|
+
}
|
|
222
|
+
async function arcusFetchAccount(args) {
|
|
223
|
+
const base = arcusPerpApiBaseUrl(args.chainId);
|
|
224
|
+
const params = new URLSearchParams({ address: args.address.trim() });
|
|
225
|
+
return arcusJsonGet(`${base}/v1/account?${params}`);
|
|
226
|
+
}
|
|
227
|
+
async function arcusFetchPositions(args) {
|
|
228
|
+
const base = arcusPerpApiBaseUrl(args.chainId);
|
|
229
|
+
const params = new URLSearchParams({ address: args.address.trim() });
|
|
230
|
+
return arcusJsonGet(`${base}/v1/positions?${params}`);
|
|
231
|
+
}
|
|
232
|
+
async function arcusFetchOpenOrders(args) {
|
|
233
|
+
const base = arcusPerpApiBaseUrl(args.chainId);
|
|
234
|
+
const params = new URLSearchParams({ address: args.address.trim() });
|
|
235
|
+
return arcusJsonGet(`${base}/v1/openOrders?${params}`);
|
|
236
|
+
}
|
|
237
|
+
async function arcusFetchApiKeys(args) {
|
|
238
|
+
const base = arcusPerpApiBaseUrl(args.chainId);
|
|
239
|
+
const params = new URLSearchParams({ address: args.address.trim() });
|
|
240
|
+
return arcusJsonGet(`${base}/v1/getApiKeys?${params}`);
|
|
241
|
+
}
|
|
242
|
+
async function arcusFetchSpotOverview(args) {
|
|
243
|
+
const base = arcusApiMetaBaseUrl(args.chainId);
|
|
244
|
+
const data = await arcusJsonGet(`${base}/spot/overview`);
|
|
245
|
+
return { tokens: Array.isArray(data) ? data : [] };
|
|
246
|
+
}
|
|
247
|
+
function arcusMapSpotCandles(raw) {
|
|
248
|
+
return (raw ?? []).map((c) => ({
|
|
249
|
+
timestampMs: c.t * 1e3,
|
|
250
|
+
open: String(c.o),
|
|
251
|
+
high: String(c.h),
|
|
252
|
+
low: String(c.l),
|
|
253
|
+
close: String(c.c),
|
|
254
|
+
volume: c.volume != null ? String(c.volume) : "0"
|
|
255
|
+
}));
|
|
256
|
+
}
|
|
257
|
+
async function arcusFetchSpotTokenCandles(args) {
|
|
258
|
+
const { arcusSpotIndexerBaseUrl: arcusSpotIndexerBaseUrl2 } = await Promise.resolve().then(() => (init_support(), support_exports));
|
|
259
|
+
const base = arcusSpotIndexerBaseUrl2(args.chainId);
|
|
260
|
+
const addr = args.tokenAddress.trim();
|
|
261
|
+
const params = new URLSearchParams({
|
|
262
|
+
resolution: args.resolution ?? "1h",
|
|
263
|
+
networkId: String(args.chainId)
|
|
264
|
+
});
|
|
265
|
+
if (args.fromSec != null) params.set("from", String(args.fromSec));
|
|
266
|
+
if (args.toSec != null) params.set("to", String(args.toSec));
|
|
267
|
+
if (args.limit != null) {
|
|
268
|
+
const toSec = args.toSec ?? Math.floor(Date.now() / 1e3);
|
|
269
|
+
const resSec = { "1m": 60, "5m": 300, "15m": 900, "1h": 3600, "1d": 86400 }[args.resolution ?? "1h"] ?? 3600;
|
|
270
|
+
params.set("from", String(toSec - args.limit * resSec));
|
|
271
|
+
params.set("to", String(toSec));
|
|
272
|
+
}
|
|
273
|
+
const data = await arcusJsonGet(`${base}/token-candles/${addr}?${params}`);
|
|
274
|
+
const candles = arcusMapSpotCandles(data.candles);
|
|
275
|
+
return {
|
|
276
|
+
tokenAddress: data.address ?? addr,
|
|
277
|
+
candles: [...candles].sort((a, b) => a.timestampMs - b.timestampMs)
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
async function arcusFetchSpotOhlcvRange(args) {
|
|
281
|
+
let tokenAddress = args.tokenAddress?.trim();
|
|
282
|
+
let ticker = args.ticker?.trim();
|
|
283
|
+
if (!tokenAddress) {
|
|
284
|
+
const { tokens } = await arcusFetchSpotOverview({ chainId: args.chainId });
|
|
285
|
+
const match = tokens.find(
|
|
286
|
+
(t) => ticker && t.ticker.toUpperCase() === ticker.toUpperCase() || tokenAddress && t.contractAddress.toLowerCase() === tokenAddress.toLowerCase()
|
|
287
|
+
);
|
|
288
|
+
if (!match) throw new Error(`Arcus spot token not found for ${ticker ?? tokenAddress ?? "unknown"}.`);
|
|
289
|
+
tokenAddress = match.contractAddress;
|
|
290
|
+
ticker = match.ticker;
|
|
291
|
+
}
|
|
292
|
+
const interval = args.interval ?? "1h";
|
|
293
|
+
const lookbackDays = args.lookbackDays ?? 14;
|
|
294
|
+
const toSec = Math.floor(Date.now() / 1e3);
|
|
295
|
+
const fromSec = toSec - lookbackDays * 86400;
|
|
296
|
+
const { candles, tokenAddress: resolved } = await arcusFetchSpotTokenCandles({
|
|
297
|
+
chainId: args.chainId,
|
|
298
|
+
tokenAddress,
|
|
299
|
+
resolution: interval,
|
|
300
|
+
fromSec,
|
|
301
|
+
toSec,
|
|
302
|
+
limit: args.candleCount
|
|
303
|
+
});
|
|
304
|
+
return {
|
|
305
|
+
market: ticker ?? resolved,
|
|
306
|
+
tokenAddress: resolved,
|
|
307
|
+
interval,
|
|
308
|
+
marketKind: "spot",
|
|
309
|
+
startTimeMs: candles[0]?.timestampMs ?? fromSec * 1e3,
|
|
310
|
+
endTimeMs: candles[candles.length - 1]?.timestampMs ?? toSec * 1e3,
|
|
311
|
+
candleCount: candles.length,
|
|
312
|
+
latestCandle: candles[candles.length - 1] ?? null,
|
|
313
|
+
candles,
|
|
314
|
+
fetchedAtMs: Date.now(),
|
|
315
|
+
dataSource: "arcus"
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
var init_api = __esm({
|
|
319
|
+
"src/protocols/evm/arcus/api.ts"() {
|
|
320
|
+
init_constants();
|
|
321
|
+
init_support();
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// src/core/registry.ts
|
|
326
|
+
var modules = [];
|
|
327
|
+
function registerProtocolModule(mod) {
|
|
328
|
+
const existing = modules.findIndex((m) => m.id === mod.id);
|
|
329
|
+
if (existing >= 0) {
|
|
330
|
+
modules[existing] = mod;
|
|
331
|
+
} else {
|
|
332
|
+
modules.push(mod);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/protocols/evm/arcus/index.ts
|
|
337
|
+
init_support();
|
|
338
|
+
init_support();
|
|
339
|
+
init_constants();
|
|
340
|
+
init_api();
|
|
341
|
+
|
|
342
|
+
// src/protocols/evm/arcus/markets.ts
|
|
343
|
+
init_api();
|
|
344
|
+
var ARCUS_PERP_MARKET_STATUS_ONLINE = "ONLINE";
|
|
345
|
+
function arcusIsPerpMarketOnline(status) {
|
|
346
|
+
return String(status ?? "").trim().toUpperCase() === ARCUS_PERP_MARKET_STATUS_ONLINE;
|
|
347
|
+
}
|
|
348
|
+
function arcusAssertPerpMarketOnline(market) {
|
|
349
|
+
if (!arcusIsPerpMarketOnline(market.status)) {
|
|
350
|
+
const label = market.status.trim() || "not ONLINE";
|
|
351
|
+
throw new Error(`Arcus perp market ${market.marketDisplayName} is ${label} and cannot be used for new orders.`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
var ROBINHOOD_CHAIN = {
|
|
355
|
+
id: 4663,
|
|
356
|
+
name: "Robinhood Chain",
|
|
357
|
+
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
|
358
|
+
rpcUrls: { default: { http: ["https://rpc.mainnet.chain.robinhood.com"] } }
|
|
359
|
+
};
|
|
360
|
+
function arcusMaxLeverageFromInitialMarginFraction(initialMarginFraction) {
|
|
361
|
+
const imf = Number(initialMarginFraction);
|
|
362
|
+
if (!Number.isFinite(imf) || imf <= 0) return null;
|
|
363
|
+
return Math.max(1, Math.floor(1 / imf));
|
|
364
|
+
}
|
|
365
|
+
function formatArcusLeverageLabel(leverage) {
|
|
366
|
+
if (leverage == null || !Number.isFinite(leverage) || leverage <= 0) return null;
|
|
367
|
+
return `${Math.round(leverage)}x cross`;
|
|
368
|
+
}
|
|
369
|
+
function arcusLookupMidFromMids(mids, market) {
|
|
370
|
+
const resolved = market.trim();
|
|
371
|
+
if (!resolved) return "";
|
|
372
|
+
const base = resolved.replace(/-USD$/i, "");
|
|
373
|
+
return mids[resolved] ?? mids[base] ?? mids[`${base}-USD`] ?? "";
|
|
374
|
+
}
|
|
375
|
+
function parseArcusLeverageLabel(label) {
|
|
376
|
+
const match = String(label ?? "").match(/(\d+(?:\.\d+)?)/);
|
|
377
|
+
if (!match) return null;
|
|
378
|
+
const n = Number(match[1]);
|
|
379
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
380
|
+
}
|
|
381
|
+
function arcusResolveEffectiveLeverage(args) {
|
|
382
|
+
const entry = args.leverages.find((l) => l.marketDisplayName === args.marketDisplayName);
|
|
383
|
+
const marketRow = args.markets.find((m) => m.marketDisplayName === args.marketDisplayName);
|
|
384
|
+
const leverage = entry?.leverage ?? null;
|
|
385
|
+
return {
|
|
386
|
+
leverage,
|
|
387
|
+
leverageLabel: formatArcusLeverageLabel(leverage),
|
|
388
|
+
maxLeverage: arcusMaxLeverageFromInitialMarginFraction(marketRow?.initialMarginFraction)
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
async function arcusSearchPerpMarkets(args) {
|
|
392
|
+
const { arcusFetchPerpMarkets: arcusFetchPerpMarkets2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
393
|
+
const q = args.query.trim().toUpperCase();
|
|
394
|
+
const { markets } = await arcusFetchPerpMarkets2({ chainId: args.chainId });
|
|
395
|
+
const filtered = markets.filter(
|
|
396
|
+
(m) => m.marketDisplayName.toUpperCase().includes(q) || m.baseAsset.toUpperCase().includes(q) || String(m.marketId) === q
|
|
397
|
+
);
|
|
398
|
+
const limit = args.limit ?? 20;
|
|
399
|
+
return { markets: filtered.slice(0, limit), query: args.query };
|
|
400
|
+
}
|
|
401
|
+
async function arcusSearchSpotMarkets(args) {
|
|
402
|
+
const q = args.query.trim().toUpperCase();
|
|
403
|
+
const { tokens } = await arcusFetchSpotOverview({ chainId: args.chainId });
|
|
404
|
+
const filtered = tokens.filter(
|
|
405
|
+
(t) => t.ticker.toUpperCase().includes(q) || t.name.toUpperCase().includes(q) || t.contractAddress.toLowerCase().includes(q.toLowerCase())
|
|
406
|
+
);
|
|
407
|
+
const limit = args.limit ?? 20;
|
|
408
|
+
return { tokens: filtered.slice(0, limit), query: args.query };
|
|
409
|
+
}
|
|
410
|
+
async function arcusFetchSpotBalances(args) {
|
|
411
|
+
const executor = getAddress(args.executorAddress);
|
|
412
|
+
const { tokens } = await arcusFetchSpotOverview({ chainId: args.chainId });
|
|
413
|
+
const client = createPublicClient({
|
|
414
|
+
chain: ROBINHOOD_CHAIN,
|
|
415
|
+
transport: http(ROBINHOOD_CHAIN.rpcUrls.default.http[0])
|
|
416
|
+
});
|
|
417
|
+
const holdings = [];
|
|
418
|
+
for (const token of tokens) {
|
|
419
|
+
let addr;
|
|
420
|
+
try {
|
|
421
|
+
addr = getAddress(token.contractAddress);
|
|
422
|
+
} catch {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
const [balance, decimals] = await Promise.all([
|
|
427
|
+
client.readContract({ address: addr, abi: erc20Abi, functionName: "balanceOf", args: [executor] }),
|
|
428
|
+
client.readContract({ address: addr, abi: erc20Abi, functionName: "decimals" })
|
|
429
|
+
]);
|
|
430
|
+
if (balance > 0n) {
|
|
431
|
+
const dec = Number(decimals);
|
|
432
|
+
const human = Number(balance) / 10 ** dec;
|
|
433
|
+
holdings.push({
|
|
434
|
+
ticker: token.ticker,
|
|
435
|
+
contractAddress: addr,
|
|
436
|
+
name: token.name,
|
|
437
|
+
balanceHuman: human.toString(),
|
|
438
|
+
decimals: dec
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
} catch {
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return { holdings, executorAddress: executor };
|
|
445
|
+
}
|
|
446
|
+
function arcusResolvePerpMarketDisplayName(markets, query) {
|
|
447
|
+
const q = query.trim().toUpperCase();
|
|
448
|
+
if (!q) return null;
|
|
449
|
+
const exact = markets.find((m) => m.marketDisplayName.toUpperCase() === q);
|
|
450
|
+
if (exact) return exact.marketDisplayName;
|
|
451
|
+
const base = markets.find((m) => m.baseAsset.toUpperCase() === q);
|
|
452
|
+
if (base) return base.marketDisplayName;
|
|
453
|
+
const partial = markets.find((m) => m.marketDisplayName.toUpperCase().includes(q));
|
|
454
|
+
return partial?.marketDisplayName ?? null;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// src/protocols/evm/arcus/reads.ts
|
|
458
|
+
init_api();
|
|
459
|
+
init_constants();
|
|
460
|
+
function wrapOhlcvEnvelope(ohlcv) {
|
|
461
|
+
return {
|
|
462
|
+
ohlcv,
|
|
463
|
+
dataSource: "arcus",
|
|
464
|
+
chainId: 4663
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
async function arcusFetchMarketsSummary(args) {
|
|
468
|
+
const { markets } = await arcusFetchPerpMarkets(args);
|
|
469
|
+
return {
|
|
470
|
+
markets: markets.map((m) => ({
|
|
471
|
+
marketDisplayName: m.marketDisplayName,
|
|
472
|
+
marketId: m.marketId,
|
|
473
|
+
baseAsset: m.baseAsset,
|
|
474
|
+
category: m.category,
|
|
475
|
+
status: m.status,
|
|
476
|
+
isOnline: arcusIsPerpMarketOnline(m.status),
|
|
477
|
+
maxLeverage: arcusMaxLeverageFromInitialMarginFraction(m.initialMarginFraction),
|
|
478
|
+
markPrice: m.markPrice ?? null
|
|
479
|
+
})),
|
|
480
|
+
marketKind: "perp"
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
async function arcusSearchMarketsSummary(args) {
|
|
484
|
+
return arcusSearchPerpMarkets(args);
|
|
485
|
+
}
|
|
486
|
+
async function arcusSpotFetchMarketsSummary(args) {
|
|
487
|
+
const { tokens } = await arcusFetchSpotOverview(args);
|
|
488
|
+
return {
|
|
489
|
+
tokens: tokens.map((t) => ({
|
|
490
|
+
ticker: t.ticker,
|
|
491
|
+
contractAddress: t.contractAddress,
|
|
492
|
+
name: t.name,
|
|
493
|
+
logoUrl: t.logoUrl ?? null,
|
|
494
|
+
price: t.quote?.price ?? null
|
|
495
|
+
})),
|
|
496
|
+
marketKind: "spot"
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
async function arcusSpotSearchMarketsSummary(args) {
|
|
500
|
+
return arcusSearchSpotMarkets(args);
|
|
501
|
+
}
|
|
502
|
+
async function arcusFetchOhlcvSummary(args) {
|
|
503
|
+
const { markets } = await arcusFetchPerpMarkets({ chainId: args.chainId });
|
|
504
|
+
const rawMarket = (args.market ?? args.coin ?? "").trim();
|
|
505
|
+
const resolved = arcusResolvePerpMarketDisplayName(markets, rawMarket) ?? (rawMarket.includes("-") ? rawMarket : rawMarket ? `${rawMarket.toUpperCase()}-USD` : "");
|
|
506
|
+
if (!resolved) throw new Error("Arcus perp market is required (market or coin).");
|
|
507
|
+
const ohlcv = await arcusFetchPerpOhlcvRange({ ...args, market: resolved });
|
|
508
|
+
return {
|
|
509
|
+
...wrapOhlcvEnvelope(ohlcv),
|
|
510
|
+
resolvedMarket: resolved
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
async function arcusSpotFetchOhlcvSummary(args) {
|
|
514
|
+
const ticker = args.ticker ?? args.currencySymbol;
|
|
515
|
+
const ohlcv = await arcusFetchSpotOhlcvRange({
|
|
516
|
+
chainId: args.chainId,
|
|
517
|
+
tokenAddress: args.tokenAddress,
|
|
518
|
+
ticker,
|
|
519
|
+
interval: args.interval,
|
|
520
|
+
lookbackDays: args.lookbackDays,
|
|
521
|
+
candleCount: args.candleCount
|
|
522
|
+
});
|
|
523
|
+
return wrapOhlcvEnvelope(ohlcv);
|
|
524
|
+
}
|
|
525
|
+
async function arcusFetchMarketSnapshotSummary(args) {
|
|
526
|
+
const { markets } = await arcusFetchPerpMarkets({ chainId: args.chainId });
|
|
527
|
+
const rawMarket = (args.market ?? args.coin ?? "").trim();
|
|
528
|
+
const resolved = arcusResolvePerpMarketDisplayName(markets, rawMarket) ?? (rawMarket.includes("-") ? rawMarket : `${rawMarket.toUpperCase()}-USD`);
|
|
529
|
+
const mids = await arcusFetchAllMids({ chainId: args.chainId });
|
|
530
|
+
const mid = arcusLookupMidFromMids(mids, resolved);
|
|
531
|
+
const ohlcv = await arcusFetchPerpOhlcvRange({
|
|
532
|
+
chainId: args.chainId,
|
|
533
|
+
market: resolved,
|
|
534
|
+
interval: args.interval ?? ARCUS_DEFAULT_OHLCV_INTERVAL,
|
|
535
|
+
candleCount: args.candleLimit ?? 48
|
|
536
|
+
});
|
|
537
|
+
return {
|
|
538
|
+
snapshot: {
|
|
539
|
+
market: resolved,
|
|
540
|
+
midUsd: mid,
|
|
541
|
+
interval: ohlcv.interval,
|
|
542
|
+
latestCandle: ohlcv.latestCandle,
|
|
543
|
+
candles: ohlcv.candles,
|
|
544
|
+
candleCount: ohlcv.candleCount,
|
|
545
|
+
fetchedAtMs: Date.now()
|
|
546
|
+
},
|
|
547
|
+
resolvedMarket: resolved
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
function withAddress(args) {
|
|
551
|
+
return { ...args, address: args.executorAddress };
|
|
552
|
+
}
|
|
553
|
+
async function arcusFetchAccountSummary(args) {
|
|
554
|
+
const account = await arcusFetchAccount(withAddress(args));
|
|
555
|
+
return { account, executorAddress: args.executorAddress };
|
|
556
|
+
}
|
|
557
|
+
async function arcusFetchPositionsSummary(args) {
|
|
558
|
+
const data = await arcusFetchPositions(withAddress(args));
|
|
559
|
+
return { positions: data.positions ?? [], executorAddress: args.executorAddress };
|
|
560
|
+
}
|
|
561
|
+
async function arcusFetchOpenOrdersSummary(args) {
|
|
562
|
+
const data = await arcusFetchOpenOrders(withAddress(args));
|
|
563
|
+
return { orders: data.orders ?? [], executorAddress: args.executorAddress };
|
|
564
|
+
}
|
|
565
|
+
async function arcusFetchLeveragesSummary(args) {
|
|
566
|
+
const { leverages } = await arcusFetchAccountLeverages({
|
|
567
|
+
chainId: args.chainId,
|
|
568
|
+
address: args.executorAddress,
|
|
569
|
+
accountIndex: args.accountIndex
|
|
570
|
+
});
|
|
571
|
+
return { leverages, executorAddress: args.executorAddress };
|
|
572
|
+
}
|
|
573
|
+
async function arcusFetchOpenContextSummary(args) {
|
|
574
|
+
const addrArgs = withAddress(args);
|
|
575
|
+
const accountIndex = args.accountIndex ?? 0;
|
|
576
|
+
const [account, positions, orders, apiKeys, leveragesResult, marketsResult] = await Promise.all([
|
|
577
|
+
arcusFetchAccount(addrArgs).catch(() => null),
|
|
578
|
+
arcusFetchPositions(addrArgs).catch(() => ({ positions: [] })),
|
|
579
|
+
arcusFetchOpenOrders(addrArgs).catch(() => ({ orders: [] })),
|
|
580
|
+
arcusFetchApiKeys(addrArgs).catch(() => ({ apiKeys: [] })),
|
|
581
|
+
arcusFetchAccountLeverages({
|
|
582
|
+
chainId: args.chainId,
|
|
583
|
+
address: args.executorAddress,
|
|
584
|
+
accountIndex
|
|
585
|
+
}).catch(() => ({ leverages: [] })),
|
|
586
|
+
arcusFetchPerpMarkets({ chainId: args.chainId }).catch(() => ({ markets: [] }))
|
|
587
|
+
]);
|
|
588
|
+
const rawMarket = (args.market ?? "").trim();
|
|
589
|
+
let activeAsset = null;
|
|
590
|
+
if (rawMarket) {
|
|
591
|
+
const resolved = arcusResolvePerpMarketDisplayName(marketsResult.markets, rawMarket) ?? (rawMarket.includes("-") ? rawMarket : `${rawMarket.toUpperCase()}-USD`);
|
|
592
|
+
const marketRow = marketsResult.markets.find((m) => m.marketDisplayName === resolved);
|
|
593
|
+
const effective = arcusResolveEffectiveLeverage({
|
|
594
|
+
marketDisplayName: resolved,
|
|
595
|
+
leverages: leveragesResult.leverages,
|
|
596
|
+
markets: marketsResult.markets
|
|
597
|
+
});
|
|
598
|
+
activeAsset = {
|
|
599
|
+
market: resolved,
|
|
600
|
+
leverage: effective.leverage,
|
|
601
|
+
leverageLabel: effective.leverageLabel,
|
|
602
|
+
maxLeverage: effective.maxLeverage,
|
|
603
|
+
markPrice: marketRow?.markPrice ?? null
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
return {
|
|
607
|
+
account,
|
|
608
|
+
positions: positions.positions ?? [],
|
|
609
|
+
orders: orders.orders ?? [],
|
|
610
|
+
apiKeys: apiKeys.apiKeys ?? [],
|
|
611
|
+
leverages: leveragesResult.leverages,
|
|
612
|
+
activeAsset,
|
|
613
|
+
executorAddress: args.executorAddress,
|
|
614
|
+
hasRegisteredApiKey: Array.isArray(apiKeys.apiKeys) && apiKeys.apiKeys.length > 0
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
async function arcusSpotFetchBalancesSummary(args) {
|
|
618
|
+
return arcusFetchSpotBalances(args);
|
|
619
|
+
}
|
|
620
|
+
function arcusCanonicalJson(payload) {
|
|
621
|
+
const keys = Object.keys(payload).sort();
|
|
622
|
+
const parts = [];
|
|
623
|
+
for (const k of keys) {
|
|
624
|
+
const v = payload[k];
|
|
625
|
+
if (v === void 0 || v === null) continue;
|
|
626
|
+
let encoded;
|
|
627
|
+
if (typeof v === "string") {
|
|
628
|
+
encoded = JSON.stringify(v.toLowerCase().startsWith("0x") ? v.toLowerCase() : v);
|
|
629
|
+
} else {
|
|
630
|
+
encoded = JSON.stringify(v);
|
|
631
|
+
}
|
|
632
|
+
parts.push(`${JSON.stringify(k)}:${encoded}`);
|
|
633
|
+
}
|
|
634
|
+
return `{${parts.join(",")}}`;
|
|
635
|
+
}
|
|
636
|
+
function arcusPayloadMessageHex(canonicalJson) {
|
|
637
|
+
return `0x${Buffer.from(canonicalJson, "utf8").toString("hex")}`;
|
|
638
|
+
}
|
|
639
|
+
function arcusPayloadMsgHashHex(canonicalJson) {
|
|
640
|
+
return createHash("sha256").update(canonicalJson, "utf8").digest("hex");
|
|
641
|
+
}
|
|
642
|
+
function arcusNanosecondTimestamp() {
|
|
643
|
+
return String(BigInt(Date.now()) * 1000000n);
|
|
644
|
+
}
|
|
645
|
+
function humanPriceToQuantums(priceHuman, tickSize) {
|
|
646
|
+
const price = Number(priceHuman);
|
|
647
|
+
const tick = Number(tickSize);
|
|
648
|
+
if (!Number.isFinite(price) || !Number.isFinite(tick) || tick <= 0) {
|
|
649
|
+
throw new Error(`Invalid price or tickSize: ${priceHuman}, ${tickSize}`);
|
|
650
|
+
}
|
|
651
|
+
return BigInt(Math.round(price / tick));
|
|
652
|
+
}
|
|
653
|
+
function humanSizeToQuantums(szHuman, stepSize) {
|
|
654
|
+
const sz = Number(szHuman);
|
|
655
|
+
const step = Number(stepSize);
|
|
656
|
+
if (!Number.isFinite(sz) || !Number.isFinite(step) || step <= 0) {
|
|
657
|
+
throw new Error(`Invalid size or stepSize: ${szHuman}, ${stepSize}`);
|
|
658
|
+
}
|
|
659
|
+
return BigInt(Math.round(sz / step));
|
|
660
|
+
}
|
|
661
|
+
function buildArcusPlaceOrderPayload(args) {
|
|
662
|
+
const timeInForce = args.timeInForce ?? args.orderType ?? 0;
|
|
663
|
+
const ct = BigInt(args.timestampNs);
|
|
664
|
+
const payload = {
|
|
665
|
+
ad: getAddress(args.address),
|
|
666
|
+
ai: args.accountIndex ?? 0,
|
|
667
|
+
ct: Number(ct),
|
|
668
|
+
g: args.grouping ?? 0,
|
|
669
|
+
m: args.marketId,
|
|
670
|
+
op: args.op ?? 1,
|
|
671
|
+
p: Number(args.priceQuantums),
|
|
672
|
+
q: Number(args.sizeQuantums),
|
|
673
|
+
r: args.reduceOnly ? 1 : 0,
|
|
674
|
+
s: args.side,
|
|
675
|
+
t: timeInForce,
|
|
676
|
+
v: args.version ?? 1
|
|
677
|
+
};
|
|
678
|
+
if (args.clientId?.trim()) payload.c = args.clientId.trim();
|
|
679
|
+
return payload;
|
|
680
|
+
}
|
|
681
|
+
function buildArcusCancelOrderPayload(args) {
|
|
682
|
+
const payload = {
|
|
683
|
+
ad: getAddress(args.address),
|
|
684
|
+
ai: args.accountIndex ?? 0,
|
|
685
|
+
ct: Number(BigInt(args.timestampNs)),
|
|
686
|
+
id: args.orderId,
|
|
687
|
+
m: args.marketId,
|
|
688
|
+
op: 2,
|
|
689
|
+
v: 1
|
|
690
|
+
};
|
|
691
|
+
if (args.clientId?.trim()) payload.c = args.clientId.trim();
|
|
692
|
+
return payload;
|
|
693
|
+
}
|
|
694
|
+
function buildArcusTpslPayload(args) {
|
|
695
|
+
const base = buildArcusPlaceOrderPayload({ ...args, op: 4, reduceOnly: true });
|
|
696
|
+
return { ...base, tpslType: args.tpslType };
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// src/core/purpose.ts
|
|
700
|
+
function mergePurposeText(purposeText, purposeSuffix) {
|
|
701
|
+
const t = (purposeText ?? "").trim();
|
|
702
|
+
const suffix = (purposeSuffix ?? "").trim();
|
|
703
|
+
if (!suffix) return t;
|
|
704
|
+
return t ? `${t}
|
|
705
|
+
|
|
706
|
+
${suffix}` : suffix;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// src/core/envelope.ts
|
|
710
|
+
function finalizeMultisign(input) {
|
|
711
|
+
const { keyGen, destinationChainID, legs } = input;
|
|
712
|
+
if (legs.length === 0) {
|
|
713
|
+
throw new Error("finalizeMultisign requires at least one leg");
|
|
714
|
+
}
|
|
715
|
+
const ph = (keyGen.pubkeyhex ?? "").trim();
|
|
716
|
+
if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
|
|
717
|
+
const keyList = keyGen.keylist ?? [];
|
|
718
|
+
const clientId = getClientIdFromKeyGenResult(keyGen);
|
|
719
|
+
const first = legs[0];
|
|
720
|
+
const messageHashes = legs.map((l) => l.msgHash);
|
|
721
|
+
const messageRawBatch = legs.map((l) => l.msgRaw);
|
|
722
|
+
const batchMeta = legs.map((l) => ({
|
|
723
|
+
destinationAddress: l.destinationAddress,
|
|
724
|
+
signatureText: l.signatureText,
|
|
725
|
+
...l.audit
|
|
726
|
+
}));
|
|
727
|
+
const proposalTxParams = legs.map((l) => l.proposalTxParams).filter((p) => p != null && typeof p === "object");
|
|
728
|
+
const extraPayload = {
|
|
729
|
+
batchMeta,
|
|
730
|
+
...input.extraJSON ?? {}
|
|
731
|
+
};
|
|
732
|
+
const extraJSON = JSON.stringify(extraPayload, (_, v) => typeof v === "bigint" ? v.toString() : v);
|
|
733
|
+
const bodyForSign = {
|
|
734
|
+
keyList,
|
|
735
|
+
pubKey: ph,
|
|
736
|
+
msgHash: messageHashes[0],
|
|
737
|
+
msgRaw: first.msgRaw,
|
|
738
|
+
destinationChainID,
|
|
739
|
+
destinationAddress: input.destinationAddress ?? first.destinationAddress,
|
|
740
|
+
extraJSON,
|
|
741
|
+
signatureText: first.signatureText,
|
|
742
|
+
purpose: mergePurposeText(input.purposeText, input.purposeSuffix),
|
|
743
|
+
...first.feeSnapshot
|
|
744
|
+
};
|
|
745
|
+
if (legs.length > 1) {
|
|
746
|
+
bodyForSign.messageHashes = messageHashes;
|
|
747
|
+
bodyForSign.messageRawBatch = messageRawBatch;
|
|
748
|
+
}
|
|
749
|
+
if (proposalTxParams.length > 0) {
|
|
750
|
+
bodyForSign.proposalTxParams = proposalTxParams;
|
|
751
|
+
}
|
|
752
|
+
const valueWei = first.valueWei;
|
|
753
|
+
if (valueWei != null && valueWei > 0n) {
|
|
754
|
+
bodyForSign.value = valueWei.toString();
|
|
755
|
+
}
|
|
756
|
+
if (clientId) bodyForSign.clientId = clientId;
|
|
757
|
+
if (input.expiryDate != null && input.expiryDate > 0) {
|
|
758
|
+
bodyForSign.expiryDate = Math.floor(input.expiryDate);
|
|
759
|
+
}
|
|
760
|
+
return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
|
|
761
|
+
}
|
|
762
|
+
var PAYLOAD_SIGN_ED25519_REQUEST_KIND = "payload_sign_ed25519";
|
|
763
|
+
function msgHashNo0xFromBytes(bytes) {
|
|
764
|
+
return Buffer.from(bytes).toString("hex");
|
|
765
|
+
}
|
|
766
|
+
function payloadMsgRawHex(messageUtf8, delivery) {
|
|
767
|
+
const envelope = {
|
|
768
|
+
version: 1,
|
|
769
|
+
messageUtf8,
|
|
770
|
+
delivery
|
|
771
|
+
};
|
|
772
|
+
return stringToHex(JSON.stringify(envelope));
|
|
773
|
+
}
|
|
774
|
+
function buildPayloadSignEd25519MultisignBody(args) {
|
|
775
|
+
const messageBytes = Buffer.from(args.messageUtf8, "utf8");
|
|
776
|
+
const msgHash = msgHashNo0xFromBytes(messageBytes);
|
|
777
|
+
const extraJSON = {
|
|
778
|
+
signRequestKind: PAYLOAD_SIGN_ED25519_REQUEST_KIND,
|
|
779
|
+
payloadSignEd25519: {
|
|
780
|
+
version: 1,
|
|
781
|
+
messageHex: msgHash,
|
|
782
|
+
messageLength: messageBytes.length
|
|
783
|
+
},
|
|
784
|
+
delivery: args.delivery
|
|
785
|
+
};
|
|
786
|
+
if (args.billingKeyGenPubKey?.trim()) {
|
|
787
|
+
extraJSON.billingKeyGenPubKey = args.billingKeyGenPubKey.trim();
|
|
788
|
+
}
|
|
789
|
+
return finalizeMultisign({
|
|
790
|
+
keyGen: args.keyGen,
|
|
791
|
+
purposeText: args.purposeText,
|
|
792
|
+
purposeSuffix: args.purposeSuffix,
|
|
793
|
+
destinationChainID: args.destinationChainID,
|
|
794
|
+
destinationAddress: args.destinationAddress,
|
|
795
|
+
extraJSON,
|
|
796
|
+
expiryDate: args.expiryDate,
|
|
797
|
+
legs: [
|
|
798
|
+
{
|
|
799
|
+
msgHash,
|
|
800
|
+
msgRaw: payloadMsgRawHex(args.messageUtf8, args.delivery),
|
|
801
|
+
destinationAddress: args.destinationAddress,
|
|
802
|
+
signatureText: args.signatureText ?? "Arcus Ed25519 payload sign",
|
|
803
|
+
audit: {
|
|
804
|
+
kind: "PAYLOAD_SIGN_ED25519",
|
|
805
|
+
deliveryKind: args.delivery.kind,
|
|
806
|
+
messageLength: messageBytes.length,
|
|
807
|
+
...args.audit ?? {}
|
|
808
|
+
},
|
|
809
|
+
feeSnapshot: {}
|
|
810
|
+
}
|
|
811
|
+
]
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// src/protocols/evm/arcus/multisign.ts
|
|
816
|
+
init_api();
|
|
817
|
+
|
|
818
|
+
// src/protocols/evm/arcus/keygenPair.ts
|
|
819
|
+
function assertArcusPairedKeyGens(args) {
|
|
820
|
+
const secpType = (args.secp256k1KeyGen.keyType ?? "secp256k1").toLowerCase();
|
|
821
|
+
const edType = (args.ed25519KeyGen.keyType ?? "ed25519").toLowerCase();
|
|
822
|
+
if (!secpType.includes("secp256k1")) {
|
|
823
|
+
throw new Error(`Arcus custody KeyGen must be secp256k1, got ${secpType}`);
|
|
824
|
+
}
|
|
825
|
+
if (!edType.includes("ed25519")) {
|
|
826
|
+
throw new Error(`Arcus API KeyGen must be ed25519, got ${edType}`);
|
|
827
|
+
}
|
|
828
|
+
const secpGroup = (args.secp256k1KeyGen.groupId ?? "").trim();
|
|
829
|
+
const edGroup = (args.ed25519KeyGen.groupId ?? "").trim();
|
|
830
|
+
if (secpGroup && edGroup && secpGroup !== edGroup) {
|
|
831
|
+
throw new Error(`Arcus KeyGen GroupId mismatch: secp256k1=${secpGroup} ed25519=${edGroup}`);
|
|
832
|
+
}
|
|
833
|
+
const secpPk = (args.secp256k1KeyGen.pubkeyhex ?? "").trim();
|
|
834
|
+
const edPk = (args.ed25519KeyGen.pubkeyhex ?? "").trim();
|
|
835
|
+
if (!secpPk) throw new Error("secp256k1 KeyGen pubkeyhex is required");
|
|
836
|
+
if (!edPk) throw new Error("ed25519 KeyGen pubkeyhex is required");
|
|
837
|
+
return { secp256k1: args.secp256k1KeyGen, ed25519: args.ed25519KeyGen };
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// src/protocols/evm/arcus/multisign.ts
|
|
841
|
+
init_constants();
|
|
842
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
843
|
+
async function resolvePerpMarket(chainId, market) {
|
|
844
|
+
const { markets } = await arcusFetchPerpMarkets({ chainId });
|
|
845
|
+
const resolved = arcusResolvePerpMarketDisplayName(markets, market) ?? (market.includes("-") ? market : `${market.toUpperCase()}-USD`);
|
|
846
|
+
const row = markets.find((m) => m.marketDisplayName === resolved);
|
|
847
|
+
if (!row) throw new Error(`Arcus perp market not found: ${market}`);
|
|
848
|
+
return row;
|
|
849
|
+
}
|
|
850
|
+
function scheme2CanonicalJson(body) {
|
|
851
|
+
return arcusCanonicalJson(body);
|
|
852
|
+
}
|
|
853
|
+
async function buildArcusCreateApiKeyMultisign(args) {
|
|
854
|
+
const paired = assertArcusPairedKeyGens({
|
|
855
|
+
secp256k1KeyGen: args.secp256k1KeyGen,
|
|
856
|
+
ed25519KeyGen: args.ed25519KeyGen
|
|
857
|
+
});
|
|
858
|
+
const address = getAddress(args.executorAddress);
|
|
859
|
+
const publicKey = args.ed25519PubKeyHex.trim().replace(/^0x/i, "");
|
|
860
|
+
if (publicKey.length !== 64) {
|
|
861
|
+
throw new Error("ed25519PubKeyHex must be 64 hex chars (32-byte Ed25519 public key)");
|
|
862
|
+
}
|
|
863
|
+
const validUntil = args.validUntilMs ?? Date.now() + 365 * 864e5;
|
|
864
|
+
const body = {
|
|
865
|
+
address: address.toLowerCase(),
|
|
866
|
+
publicKey,
|
|
867
|
+
apiWalletName: (args.apiWalletName ?? "Continuum").trim(),
|
|
868
|
+
validUntil
|
|
869
|
+
};
|
|
870
|
+
const canonical = arcusCanonicalJson(body);
|
|
871
|
+
const digest = hashMessage(canonical);
|
|
872
|
+
const msgHash = digest.startsWith("0x") ? digest.slice(2) : digest;
|
|
873
|
+
return finalizeMultisign({
|
|
874
|
+
keyGen: paired.secp256k1,
|
|
875
|
+
purposeText: args.purposeText,
|
|
876
|
+
destinationChainID: String(args.chainId),
|
|
877
|
+
destinationAddress: address,
|
|
878
|
+
expiryDate: args.expiryDate,
|
|
879
|
+
extraJSON: {
|
|
880
|
+
signRequestKind: "personal_sign",
|
|
881
|
+
delivery: {
|
|
882
|
+
kind: "arcus_create_api_key",
|
|
883
|
+
chainId: args.chainId,
|
|
884
|
+
body,
|
|
885
|
+
canonical
|
|
886
|
+
}
|
|
887
|
+
},
|
|
888
|
+
legs: [
|
|
889
|
+
{
|
|
890
|
+
msgHash,
|
|
891
|
+
msgRaw: `0x${Buffer.from(canonical, "utf8").toString("hex")}`,
|
|
892
|
+
destinationAddress: address,
|
|
893
|
+
signatureText: "Arcus createApiKey personal_sign",
|
|
894
|
+
audit: {
|
|
895
|
+
kind: "PERSONAL_SIGN",
|
|
896
|
+
protocol: ARCUS_PROTOCOL_ID,
|
|
897
|
+
action: "createApiKey",
|
|
898
|
+
address,
|
|
899
|
+
publicKey
|
|
900
|
+
},
|
|
901
|
+
feeSnapshot: {}
|
|
902
|
+
}
|
|
903
|
+
]
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
async function buildArcusPlaceOrderMultisign(args) {
|
|
907
|
+
const paired = assertArcusPairedKeyGens({
|
|
908
|
+
secp256k1KeyGen: args.secp256k1KeyGen,
|
|
909
|
+
ed25519KeyGen: args.ed25519KeyGen
|
|
910
|
+
});
|
|
911
|
+
const marketRow = await resolvePerpMarket(args.chainId, args.market);
|
|
912
|
+
arcusAssertPerpMarketOnline(marketRow);
|
|
913
|
+
const address = getAddress(args.executorAddress);
|
|
914
|
+
const timestampNs = arcusNanosecondTimestamp();
|
|
915
|
+
const goodTilNs = String(BigInt(timestampNs) + 90n * 86400n * 1000000000n);
|
|
916
|
+
const timeInForce = args.timeInForce ?? args.tif ?? args.orderType ?? 0;
|
|
917
|
+
const tp = args.takeProfitTriggerPxHuman?.trim();
|
|
918
|
+
const sl = args.stopLossTriggerPxHuman?.trim();
|
|
919
|
+
if (args.reduceOnly && (tp || sl)) {
|
|
920
|
+
throw new Error("reduceOnly cannot be combined with takeProfitTriggerPxHuman or stopLossTriggerPxHuman.");
|
|
921
|
+
}
|
|
922
|
+
const priceQuantums = humanPriceToQuantums(args.limitPxHuman, marketRow.tickSize);
|
|
923
|
+
const sizeQuantums = humanSizeToQuantums(args.szHuman, marketRow.stepSize);
|
|
924
|
+
const entryPayload = buildArcusPlaceOrderPayload({
|
|
925
|
+
address,
|
|
926
|
+
accountIndex: args.accountIndex,
|
|
927
|
+
clientId: args.clientId,
|
|
928
|
+
timestampNs,
|
|
929
|
+
marketId: marketRow.marketId,
|
|
930
|
+
priceQuantums,
|
|
931
|
+
sizeQuantums,
|
|
932
|
+
side: args.isBuy ? 0 : 1,
|
|
933
|
+
timeInForce,
|
|
934
|
+
reduceOnly: args.reduceOnly,
|
|
935
|
+
grouping: 0
|
|
936
|
+
});
|
|
937
|
+
entryPayload.g = Number(goodTilNs);
|
|
938
|
+
const legs = [
|
|
939
|
+
{
|
|
940
|
+
canonical: arcusCanonicalJson(entryPayload),
|
|
941
|
+
audit: { leg: "entry", market: marketRow.marketDisplayName }
|
|
942
|
+
}
|
|
943
|
+
];
|
|
944
|
+
if (tp) {
|
|
945
|
+
const tpPayload = buildArcusTpslPayload({
|
|
946
|
+
address,
|
|
947
|
+
accountIndex: args.accountIndex,
|
|
948
|
+
clientId: args.clientId,
|
|
949
|
+
timestampNs,
|
|
950
|
+
marketId: marketRow.marketId,
|
|
951
|
+
priceQuantums: humanPriceToQuantums(tp, marketRow.tickSize),
|
|
952
|
+
sizeQuantums,
|
|
953
|
+
side: args.isBuy ? 1 : 0,
|
|
954
|
+
orderType: 0,
|
|
955
|
+
grouping: 0,
|
|
956
|
+
tpslType: "TAKE_PROFIT"
|
|
957
|
+
});
|
|
958
|
+
tpPayload.g = Number(goodTilNs);
|
|
959
|
+
legs.push({
|
|
960
|
+
canonical: arcusCanonicalJson(tpPayload),
|
|
961
|
+
audit: { leg: "takeProfit", triggerPxHuman: tp }
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
if (sl) {
|
|
965
|
+
const slPayload = buildArcusTpslPayload({
|
|
966
|
+
address,
|
|
967
|
+
accountIndex: args.accountIndex,
|
|
968
|
+
clientId: args.clientId,
|
|
969
|
+
timestampNs,
|
|
970
|
+
marketId: marketRow.marketId,
|
|
971
|
+
priceQuantums: humanPriceToQuantums(sl, marketRow.tickSize),
|
|
972
|
+
sizeQuantums,
|
|
973
|
+
side: args.isBuy ? 1 : 0,
|
|
974
|
+
orderType: 0,
|
|
975
|
+
grouping: 0,
|
|
976
|
+
tpslType: "STOP_LOSS"
|
|
977
|
+
});
|
|
978
|
+
slPayload.g = Number(goodTilNs);
|
|
979
|
+
legs.push({
|
|
980
|
+
canonical: arcusCanonicalJson(slPayload),
|
|
981
|
+
audit: { leg: "stopLoss", triggerPxHuman: sl }
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
if (legs.length === 1) {
|
|
985
|
+
const canonical = legs[0].canonical;
|
|
986
|
+
return buildPayloadSignEd25519MultisignBody({
|
|
987
|
+
keyGen: paired.ed25519,
|
|
988
|
+
purposeText: args.purposeText,
|
|
989
|
+
destinationChainID: String(args.chainId),
|
|
990
|
+
destinationAddress: ZERO_ADDRESS,
|
|
991
|
+
messageUtf8: canonical,
|
|
992
|
+
billingKeyGenPubKey: paired.secp256k1.pubkeyhex,
|
|
993
|
+
expiryDate: args.expiryDate,
|
|
994
|
+
delivery: {
|
|
995
|
+
kind: "arcus_exchange",
|
|
996
|
+
chainId: args.chainId,
|
|
997
|
+
endpoint: "placeOrder",
|
|
998
|
+
timestampNs,
|
|
999
|
+
apiKeyPubKeyHex: paired.ed25519.pubkeyhex,
|
|
1000
|
+
body: JSON.parse(canonical)
|
|
1001
|
+
},
|
|
1002
|
+
audit: {
|
|
1003
|
+
protocol: ARCUS_PROTOCOL_ID,
|
|
1004
|
+
action: "placeOrder",
|
|
1005
|
+
market: marketRow.marketDisplayName,
|
|
1006
|
+
isBuy: args.isBuy,
|
|
1007
|
+
limitPxHuman: args.limitPxHuman,
|
|
1008
|
+
szHuman: args.szHuman,
|
|
1009
|
+
leverageHuman: args.leverageHuman?.trim() || null,
|
|
1010
|
+
timeInForce,
|
|
1011
|
+
reduceOnly: Boolean(args.reduceOnly),
|
|
1012
|
+
takeProfitTriggerPxHuman: tp ?? null,
|
|
1013
|
+
stopLossTriggerPxHuman: sl ?? null
|
|
1014
|
+
}
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
const batchLegs = legs.map((leg, i) => {
|
|
1018
|
+
const messageBytes = Buffer.from(leg.canonical, "utf8");
|
|
1019
|
+
return {
|
|
1020
|
+
msgHash: messageBytes.toString("hex"),
|
|
1021
|
+
msgRaw: `0x${messageBytes.toString("hex")}`,
|
|
1022
|
+
destinationAddress: ZERO_ADDRESS,
|
|
1023
|
+
signatureText: `Arcus bracket leg ${i + 1}/${legs.length}`,
|
|
1024
|
+
audit: { protocol: ARCUS_PROTOCOL_ID, action: "placeOrder", ...leg.audit },
|
|
1025
|
+
feeSnapshot: {}
|
|
1026
|
+
};
|
|
1027
|
+
});
|
|
1028
|
+
return finalizeMultisign({
|
|
1029
|
+
keyGen: paired.ed25519,
|
|
1030
|
+
purposeText: args.purposeText,
|
|
1031
|
+
destinationChainID: String(args.chainId),
|
|
1032
|
+
destinationAddress: ZERO_ADDRESS,
|
|
1033
|
+
expiryDate: args.expiryDate,
|
|
1034
|
+
extraJSON: {
|
|
1035
|
+
signRequestKind: PAYLOAD_SIGN_ED25519_REQUEST_KIND,
|
|
1036
|
+
billingKeyGenPubKey: paired.secp256k1.pubkeyhex,
|
|
1037
|
+
delivery: {
|
|
1038
|
+
kind: "arcus_exchange",
|
|
1039
|
+
chainId: args.chainId,
|
|
1040
|
+
endpoint: "batchPlaceOrders",
|
|
1041
|
+
timestampNs,
|
|
1042
|
+
grouping: "positionTpsl",
|
|
1043
|
+
legCount: legs.length,
|
|
1044
|
+
apiKeyPubKeyHex: paired.ed25519.pubkeyhex
|
|
1045
|
+
},
|
|
1046
|
+
batchMeta: batchLegs.map((l) => l.audit)
|
|
1047
|
+
},
|
|
1048
|
+
legs: batchLegs
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
async function buildArcusCloseMultisign(args) {
|
|
1052
|
+
return buildArcusPlaceOrderMultisign({
|
|
1053
|
+
...args,
|
|
1054
|
+
isBuy: !args.isLong,
|
|
1055
|
+
reduceOnly: true,
|
|
1056
|
+
timeInForce: args.timeInForce ?? 2,
|
|
1057
|
+
takeProfitTriggerPxHuman: void 0,
|
|
1058
|
+
stopLossTriggerPxHuman: void 0
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
async function buildArcusCancelOrderMultisign(args) {
|
|
1062
|
+
const paired = assertArcusPairedKeyGens({
|
|
1063
|
+
secp256k1KeyGen: args.secp256k1KeyGen,
|
|
1064
|
+
ed25519KeyGen: args.ed25519KeyGen
|
|
1065
|
+
});
|
|
1066
|
+
const marketRow = await resolvePerpMarket(args.chainId, args.market);
|
|
1067
|
+
const address = getAddress(args.executorAddress);
|
|
1068
|
+
const timestampNs = arcusNanosecondTimestamp();
|
|
1069
|
+
const payload = buildArcusCancelOrderPayload({
|
|
1070
|
+
address,
|
|
1071
|
+
accountIndex: args.accountIndex,
|
|
1072
|
+
clientId: args.clientId,
|
|
1073
|
+
timestampNs,
|
|
1074
|
+
marketId: marketRow.marketId,
|
|
1075
|
+
orderId: args.orderId
|
|
1076
|
+
});
|
|
1077
|
+
const canonical = arcusCanonicalJson(payload);
|
|
1078
|
+
return buildPayloadSignEd25519MultisignBody({
|
|
1079
|
+
keyGen: paired.ed25519,
|
|
1080
|
+
purposeText: args.purposeText,
|
|
1081
|
+
destinationChainID: String(args.chainId),
|
|
1082
|
+
destinationAddress: ZERO_ADDRESS,
|
|
1083
|
+
messageUtf8: canonical,
|
|
1084
|
+
billingKeyGenPubKey: paired.secp256k1.pubkeyhex,
|
|
1085
|
+
expiryDate: args.expiryDate,
|
|
1086
|
+
delivery: {
|
|
1087
|
+
kind: "arcus_exchange",
|
|
1088
|
+
chainId: args.chainId,
|
|
1089
|
+
endpoint: "cancelOrder",
|
|
1090
|
+
timestampNs,
|
|
1091
|
+
apiKeyPubKeyHex: paired.ed25519.pubkeyhex,
|
|
1092
|
+
body: payload
|
|
1093
|
+
},
|
|
1094
|
+
audit: {
|
|
1095
|
+
protocol: ARCUS_PROTOCOL_ID,
|
|
1096
|
+
action: "cancelOrder",
|
|
1097
|
+
market: marketRow.marketDisplayName,
|
|
1098
|
+
orderId: args.orderId
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
async function buildArcusSetLeverageMultisign(args) {
|
|
1103
|
+
const paired = assertArcusPairedKeyGens({
|
|
1104
|
+
secp256k1KeyGen: args.secp256k1KeyGen,
|
|
1105
|
+
ed25519KeyGen: args.ed25519KeyGen
|
|
1106
|
+
});
|
|
1107
|
+
const marketRow = await resolvePerpMarket(args.chainId, args.market);
|
|
1108
|
+
arcusAssertPerpMarketOnline(marketRow);
|
|
1109
|
+
const address = getAddress(args.executorAddress);
|
|
1110
|
+
const timestampNs = arcusNanosecondTimestamp();
|
|
1111
|
+
const body = {
|
|
1112
|
+
address: address.toLowerCase(),
|
|
1113
|
+
accountIndex: args.accountIndex ?? 0,
|
|
1114
|
+
marketId: marketRow.marketId,
|
|
1115
|
+
leverage: args.leverage
|
|
1116
|
+
};
|
|
1117
|
+
const canonical = scheme2CanonicalJson(body);
|
|
1118
|
+
const action = "setLeverage";
|
|
1119
|
+
const messageUtf8 = `${timestampNs}${action}${canonical}`;
|
|
1120
|
+
return buildPayloadSignEd25519MultisignBody({
|
|
1121
|
+
keyGen: paired.ed25519,
|
|
1122
|
+
purposeText: args.purposeText,
|
|
1123
|
+
destinationChainID: String(args.chainId),
|
|
1124
|
+
destinationAddress: ZERO_ADDRESS,
|
|
1125
|
+
messageUtf8,
|
|
1126
|
+
billingKeyGenPubKey: paired.secp256k1.pubkeyhex,
|
|
1127
|
+
expiryDate: args.expiryDate,
|
|
1128
|
+
signatureText: "Arcus setLeverage (Scheme 2)",
|
|
1129
|
+
delivery: {
|
|
1130
|
+
kind: "arcus_exchange",
|
|
1131
|
+
chainId: args.chainId,
|
|
1132
|
+
endpoint: action,
|
|
1133
|
+
timestampNs,
|
|
1134
|
+
scheme: 2,
|
|
1135
|
+
apiKeyPubKeyHex: paired.ed25519.pubkeyhex,
|
|
1136
|
+
body,
|
|
1137
|
+
canonical
|
|
1138
|
+
},
|
|
1139
|
+
audit: {
|
|
1140
|
+
protocol: ARCUS_PROTOCOL_ID,
|
|
1141
|
+
action,
|
|
1142
|
+
market: args.market,
|
|
1143
|
+
leverage: args.leverage
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
async function buildEvmMultisignBodyArcusPlaceOrderBatch(args) {
|
|
1148
|
+
return buildArcusPlaceOrderMultisign(args);
|
|
1149
|
+
}
|
|
1150
|
+
async function buildEvmMultisignBodyArcusCreateApiKeyBatch(args) {
|
|
1151
|
+
return buildArcusCreateApiKeyMultisign(args);
|
|
1152
|
+
}
|
|
1153
|
+
async function buildEvmMultisignBodyArcusCancelOrderBatch(args) {
|
|
1154
|
+
return buildArcusCancelOrderMultisign(args);
|
|
1155
|
+
}
|
|
1156
|
+
async function buildEvmMultisignBodyArcusSetLeverageBatch(args) {
|
|
1157
|
+
return buildArcusSetLeverageMultisign(args);
|
|
1158
|
+
}
|
|
1159
|
+
async function buildEvmMultisignBodyArcusCloseBatch(args) {
|
|
1160
|
+
return buildArcusCloseMultisign(args);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// src/protocols/evm/arcus/pendingClose.ts
|
|
1164
|
+
function parsePositiveSize(value) {
|
|
1165
|
+
if (value == null || !String(value).trim()) return null;
|
|
1166
|
+
const n = Number.parseFloat(String(value).trim());
|
|
1167
|
+
if (!Number.isFinite(n) || n <= 0) return null;
|
|
1168
|
+
return n;
|
|
1169
|
+
}
|
|
1170
|
+
function arcusOrderClosesPosition(position, order) {
|
|
1171
|
+
return position.isLong ? !order.isBuy : order.isBuy;
|
|
1172
|
+
}
|
|
1173
|
+
function arcusOrderCountsAsPendingClose(order) {
|
|
1174
|
+
return Boolean(order.reduceOnly) || Boolean(order.isTrigger);
|
|
1175
|
+
}
|
|
1176
|
+
function arcusPendingCloseForPosition(args) {
|
|
1177
|
+
const positionSize = parsePositiveSize(args.position.sizeHuman);
|
|
1178
|
+
if (positionSize == null) {
|
|
1179
|
+
return { pendingCloseSz: 0, pendingClosePct: 0 };
|
|
1180
|
+
}
|
|
1181
|
+
let pendingCloseSz = 0;
|
|
1182
|
+
for (const order of args.orders) {
|
|
1183
|
+
if (order.marketDisplayName !== args.position.key) continue;
|
|
1184
|
+
if (!arcusOrderClosesPosition(args.position, order)) continue;
|
|
1185
|
+
if (!arcusOrderCountsAsPendingClose(order)) continue;
|
|
1186
|
+
const sz = parsePositiveSize(order.sizeHuman);
|
|
1187
|
+
if (sz != null) pendingCloseSz += sz;
|
|
1188
|
+
}
|
|
1189
|
+
const pendingClosePct = Math.min(100, pendingCloseSz / positionSize * 100);
|
|
1190
|
+
return { pendingCloseSz, pendingClosePct };
|
|
1191
|
+
}
|
|
1192
|
+
function arcusPendingCloseByMarket(args) {
|
|
1193
|
+
const out = /* @__PURE__ */ new Map();
|
|
1194
|
+
for (const position of args.positions) {
|
|
1195
|
+
out.set(position.key, arcusPendingCloseForPosition({ position, orders: args.orders }));
|
|
1196
|
+
}
|
|
1197
|
+
return out;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// src/protocols/evm/arcus/positionDisplay.ts
|
|
1201
|
+
function parsePositiveDecimal(value) {
|
|
1202
|
+
const trimmed = String(value ?? "").trim().replace(/,/g, "");
|
|
1203
|
+
if (!trimmed || !/^\d+(\.\d+)?$/.test(trimmed)) return null;
|
|
1204
|
+
const n = Number(trimmed);
|
|
1205
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
1206
|
+
}
|
|
1207
|
+
function floorToStep(value, step) {
|
|
1208
|
+
if (!Number.isFinite(value) || value <= 0 || !Number.isFinite(step) || step <= 0) return null;
|
|
1209
|
+
const steps = Math.floor(value / step + 1e-12);
|
|
1210
|
+
if (steps <= 0) return null;
|
|
1211
|
+
return steps * step;
|
|
1212
|
+
}
|
|
1213
|
+
function formatArcusDisplaySize(value, stepSize) {
|
|
1214
|
+
const step = stepSize ? parsePositiveDecimal(stepSize) : null;
|
|
1215
|
+
const floored = step != null ? floorToStep(value, step) : value;
|
|
1216
|
+
if (floored == null || floored <= 0) return "0";
|
|
1217
|
+
const abs = Math.abs(floored);
|
|
1218
|
+
const maxFrac = abs >= 1 ? 4 : abs >= 0.01 ? 6 : 8;
|
|
1219
|
+
return floored.toLocaleString(void 0, { maximumFractionDigits: maxFrac });
|
|
1220
|
+
}
|
|
1221
|
+
function marketStepSize(markets, marketDisplayName) {
|
|
1222
|
+
return markets.find((m) => m.marketDisplayName === marketDisplayName)?.stepSize ?? null;
|
|
1223
|
+
}
|
|
1224
|
+
function parseArcusSideIsBuy(raw) {
|
|
1225
|
+
const s = String(raw ?? "").trim().toUpperCase();
|
|
1226
|
+
if (s === "BUY" || s === "LONG" || s === "0") return true;
|
|
1227
|
+
if (s === "SELL" || s === "SHORT" || s === "1") return false;
|
|
1228
|
+
return null;
|
|
1229
|
+
}
|
|
1230
|
+
function arcusParsePositionDisplayRow(raw, markets) {
|
|
1231
|
+
if (!raw || typeof raw !== "object") return null;
|
|
1232
|
+
const row = raw;
|
|
1233
|
+
const marketDisplayName = String(row.marketDisplayName ?? "").trim();
|
|
1234
|
+
if (!marketDisplayName) return null;
|
|
1235
|
+
const side = String(row.side ?? "").toUpperCase();
|
|
1236
|
+
const isLong = side === "LONG" || side === "BUY";
|
|
1237
|
+
const stepSize = marketStepSize(markets, marketDisplayName);
|
|
1238
|
+
const step = stepSize ? parsePositiveDecimal(stepSize) : null;
|
|
1239
|
+
const markPx = parsePositiveDecimal(String(row.markPx ?? row.markPrice ?? ""));
|
|
1240
|
+
const notional = Math.abs(parsePositiveDecimal(String(row.positionValueNotional ?? row.notional ?? "")) ?? 0);
|
|
1241
|
+
let sizeHuman = null;
|
|
1242
|
+
if (notional > 0 && markPx != null && markPx > 0) {
|
|
1243
|
+
sizeHuman = formatArcusDisplaySize(notional / markPx, stepSize);
|
|
1244
|
+
} else {
|
|
1245
|
+
const sizeRaw = Math.abs(Number(String(row.size ?? row.sizeHuman ?? "").trim()));
|
|
1246
|
+
if (Number.isFinite(sizeRaw) && sizeRaw > 0 && step != null) {
|
|
1247
|
+
sizeHuman = formatArcusDisplaySize(sizeRaw * step, stepSize);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
if (!sizeHuman || !parsePositiveDecimal(sizeHuman)) return null;
|
|
1251
|
+
const levRaw = row.leverage;
|
|
1252
|
+
const leverageLabel = levRaw != null && String(levRaw).trim() ? `${Math.round(Number(levRaw))}x cross` : null;
|
|
1253
|
+
return {
|
|
1254
|
+
key: marketDisplayName,
|
|
1255
|
+
marketDisplayName,
|
|
1256
|
+
isLong,
|
|
1257
|
+
sizeHuman,
|
|
1258
|
+
entryPxHuman: String(row.entryPx ?? row.entryPrice ?? "").trim() || null,
|
|
1259
|
+
markPxHuman: markPx != null ? String(markPx) : String(row.markPx ?? "").trim() || null,
|
|
1260
|
+
positionValueUsd: String(row.positionValueNotional ?? row.notional ?? "").trim() || null,
|
|
1261
|
+
unrealizedPnlUsd: String(row.unrealizedPnl ?? row.unrealizedPnlUsd ?? "").trim() || null,
|
|
1262
|
+
liquidationPxHuman: String(row.liquidationPx ?? row.liquidationPrice ?? "").trim() || null,
|
|
1263
|
+
leverageLabel,
|
|
1264
|
+
marginUsedUsd: String(row.marginUsed ?? "").trim() || null
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
function arcusParseOpenOrderDisplayRow(raw, markets) {
|
|
1268
|
+
if (!raw || typeof raw !== "object") return null;
|
|
1269
|
+
const row = raw;
|
|
1270
|
+
const orderId = String(row.orderId ?? row.id ?? "").trim();
|
|
1271
|
+
const marketDisplayName = String(row.marketDisplayName ?? "").trim();
|
|
1272
|
+
if (!orderId || !marketDisplayName) return null;
|
|
1273
|
+
const isBuy = parseArcusSideIsBuy(row.side);
|
|
1274
|
+
if (isBuy == null) return null;
|
|
1275
|
+
const stepSize = marketStepSize(markets, marketDisplayName);
|
|
1276
|
+
const step = stepSize ? parsePositiveDecimal(stepSize) : null;
|
|
1277
|
+
let sizeHuman = String(row.sizeHuman ?? row.remainingSize ?? row.size ?? "").trim() || null;
|
|
1278
|
+
if (sizeHuman && step != null) {
|
|
1279
|
+
const asNum = Number(sizeHuman);
|
|
1280
|
+
if (Number.isFinite(asNum) && asNum > 0 && asNum < 1e6) {
|
|
1281
|
+
sizeHuman = formatArcusDisplaySize(asNum * step, stepSize);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
const priceRaw = String(row.price ?? row.limitPx ?? "").trim();
|
|
1285
|
+
return {
|
|
1286
|
+
orderId,
|
|
1287
|
+
marketDisplayName,
|
|
1288
|
+
isBuy,
|
|
1289
|
+
limitPxHuman: priceRaw || null,
|
|
1290
|
+
sizeHuman,
|
|
1291
|
+
reduceOnly: Boolean(row.reduceOnly) || Number(row.reduceOnly) === 1,
|
|
1292
|
+
isTrigger: Boolean(row.isTrigger) || String(row.orderType ?? "").toUpperCase().includes("STOP")
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
function arcusParseAccountSummary(account) {
|
|
1296
|
+
if (!account) {
|
|
1297
|
+
return { freeCollateralUsd: null, equityUsd: null, marginUsedUsd: null };
|
|
1298
|
+
}
|
|
1299
|
+
return {
|
|
1300
|
+
freeCollateralUsd: String(account.freeCollateral ?? "").trim() || null,
|
|
1301
|
+
equityUsd: String(account.equity ?? account.accountValue ?? "").trim() || null,
|
|
1302
|
+
marginUsedUsd: String(account.marginUsed ?? account.totalMarginUsed ?? "").trim() || null
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
function arcusMapPositionDisplayRows(rawPositions, markets) {
|
|
1306
|
+
return rawPositions.map((raw) => arcusParsePositionDisplayRow(raw, markets)).filter((row) => row != null);
|
|
1307
|
+
}
|
|
1308
|
+
function arcusMapOpenOrderDisplayRows(rawOrders, markets) {
|
|
1309
|
+
return rawOrders.map((raw) => arcusParseOpenOrderDisplayRow(raw, markets)).filter((row) => row != null);
|
|
1310
|
+
}
|
|
1311
|
+
init_api();
|
|
1312
|
+
init_constants();
|
|
1313
|
+
var ZERO_ADDRESS2 = "0x0000000000000000000000000000000000000000";
|
|
1314
|
+
async function buildArcusSpotRfqMultisign(args) {
|
|
1315
|
+
const paired = assertArcusPairedKeyGens({
|
|
1316
|
+
secp256k1KeyGen: args.secp256k1KeyGen,
|
|
1317
|
+
ed25519KeyGen: args.ed25519KeyGen
|
|
1318
|
+
});
|
|
1319
|
+
const address = getAddress(args.executorAddress);
|
|
1320
|
+
const { tokens } = await arcusFetchSpotOverview({ chainId: args.chainId });
|
|
1321
|
+
const ticker = args.ticker.trim().toUpperCase();
|
|
1322
|
+
const token = tokens.find((t) => t.ticker.toUpperCase() === ticker);
|
|
1323
|
+
if (!token) throw new Error(`Arcus spot token not found: ${ticker}`);
|
|
1324
|
+
const timestampNs = arcusNanosecondTimestamp();
|
|
1325
|
+
const body = {
|
|
1326
|
+
address: address.toLowerCase(),
|
|
1327
|
+
ticker: token.ticker,
|
|
1328
|
+
side: args.isBuy ? "BUY" : "SELL",
|
|
1329
|
+
size: args.sizeHuman.trim(),
|
|
1330
|
+
tokenContractAddress: token.contractAddress.toLowerCase()
|
|
1331
|
+
};
|
|
1332
|
+
if (args.limitPxHuman?.trim()) {
|
|
1333
|
+
body.limitPrice = args.limitPxHuman.trim();
|
|
1334
|
+
}
|
|
1335
|
+
const canonical = arcusCanonicalJson(body);
|
|
1336
|
+
const action = args.isBuy ? "spotBuy" : "spotSell";
|
|
1337
|
+
const messageUtf8 = `${timestampNs}${action}${canonical}`;
|
|
1338
|
+
return buildPayloadSignEd25519MultisignBody({
|
|
1339
|
+
keyGen: paired.ed25519,
|
|
1340
|
+
purposeText: args.purposeText,
|
|
1341
|
+
destinationChainID: String(args.chainId),
|
|
1342
|
+
destinationAddress: ZERO_ADDRESS2,
|
|
1343
|
+
messageUtf8,
|
|
1344
|
+
billingKeyGenPubKey: paired.secp256k1.pubkeyhex,
|
|
1345
|
+
expiryDate: args.expiryDate,
|
|
1346
|
+
signatureText: `Arcus spot RFQ ${action}`,
|
|
1347
|
+
delivery: {
|
|
1348
|
+
kind: "arcus_spot_rfq",
|
|
1349
|
+
chainId: args.chainId,
|
|
1350
|
+
endpoint: action,
|
|
1351
|
+
timestampNs,
|
|
1352
|
+
scheme: 2,
|
|
1353
|
+
apiKeyPubKeyHex: paired.ed25519.pubkeyhex,
|
|
1354
|
+
body,
|
|
1355
|
+
tokenContractAddress: token.contractAddress,
|
|
1356
|
+
tokenSymbol: token.ticker,
|
|
1357
|
+
tokenName: token.name
|
|
1358
|
+
},
|
|
1359
|
+
audit: {
|
|
1360
|
+
protocol: ARCUS_PROTOCOL_ID,
|
|
1361
|
+
action,
|
|
1362
|
+
marketKind: "spot",
|
|
1363
|
+
ticker: token.ticker,
|
|
1364
|
+
isBuy: args.isBuy,
|
|
1365
|
+
sizeHuman: args.sizeHuman,
|
|
1366
|
+
limitPxHuman: args.limitPxHuman ?? null
|
|
1367
|
+
}
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
async function buildEvmMultisignBodyArcusSpotRfqBatch(args) {
|
|
1371
|
+
return buildArcusSpotRfqMultisign(args);
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
// src/protocols/evm/arcus/bridgeConstants.ts
|
|
1375
|
+
init_constants();
|
|
1376
|
+
var ARCUS_BRIDGE_VAULT_4663 = "0x14b107cf534239c59571b066cB6497a321Da897c";
|
|
1377
|
+
var ARCUS_DEPOSIT_PROXY_4663 = "0xD42c46c7BaD6A54b38395F846b09981CE75fb8e2";
|
|
1378
|
+
var ARCUS_USDG_DECIMALS = 6;
|
|
1379
|
+
var ARCUS_QUOTE_QUANTUMS_PER_USD = 1000000000n;
|
|
1380
|
+
var ARCUS_MIN_WITHDRAW_USD = 1;
|
|
1381
|
+
var ARCUS_PERP_ACCOUNT_INDEX = 0;
|
|
1382
|
+
var ARCUS_SPOT_ACCOUNT_INDEX = 1;
|
|
1383
|
+
function arcusAccountIndexForProduct(product) {
|
|
1384
|
+
return product === "spot" ? ARCUS_SPOT_ACCOUNT_INDEX : ARCUS_PERP_ACCOUNT_INDEX;
|
|
1385
|
+
}
|
|
1386
|
+
function arcusBridgeConfig(chainId) {
|
|
1387
|
+
if (chainId !== 4663) return null;
|
|
1388
|
+
return {
|
|
1389
|
+
chainId,
|
|
1390
|
+
usdg: ARCUS_USDG_ADDRESS_4663,
|
|
1391
|
+
depositProxy: ARCUS_DEPOSIT_PROXY_4663,
|
|
1392
|
+
bridgeVault: ARCUS_BRIDGE_VAULT_4663,
|
|
1393
|
+
usdgDecimals: ARCUS_USDG_DECIMALS
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
var ARCUS_WITHDRAW_EIP712_DOMAIN_NAME = "Arcus Withdraw";
|
|
1397
|
+
var ARCUS_WITHDRAW_EIP712_DOMAIN_VERSION = "1";
|
|
1398
|
+
var ARCUS_WITHDRAW_SIGN_TYPES = {
|
|
1399
|
+
Withdraw: [
|
|
1400
|
+
{ name: "ethereumAddress", type: "address" },
|
|
1401
|
+
{ name: "accountIndex", type: "uint8" },
|
|
1402
|
+
{ name: "amount", type: "uint256" },
|
|
1403
|
+
{ name: "nonce", type: "string" }
|
|
1404
|
+
]
|
|
1405
|
+
};
|
|
1406
|
+
function parseArcusUsdHumanToQuoteQuantums(usdHuman) {
|
|
1407
|
+
const trimmed = usdHuman.trim().replace(/,/g, "");
|
|
1408
|
+
if (!trimmed || !/^\d+(\.\d+)?$/.test(trimmed)) {
|
|
1409
|
+
throw new Error("usdHuman must be a positive decimal string");
|
|
1410
|
+
}
|
|
1411
|
+
const [whole, frac = ""] = trimmed.split(".");
|
|
1412
|
+
const frac9 = (frac + "000000000").slice(0, 9);
|
|
1413
|
+
const quantums = BigInt(whole) * 1000000000n + BigInt(frac9 || "0");
|
|
1414
|
+
if (quantums <= 0n) throw new Error("usdHuman must be greater than zero");
|
|
1415
|
+
return quantums;
|
|
1416
|
+
}
|
|
1417
|
+
function parseArcusUsdHumanToUsdgWei(usdHuman) {
|
|
1418
|
+
const trimmed = usdHuman.trim().replace(/,/g, "");
|
|
1419
|
+
if (!trimmed || !/^\d+(\.\d+)?$/.test(trimmed)) {
|
|
1420
|
+
throw new Error("usdHuman must be a positive decimal string");
|
|
1421
|
+
}
|
|
1422
|
+
const [whole, frac = ""] = trimmed.split(".");
|
|
1423
|
+
const frac6 = (frac + "000000").slice(0, 6);
|
|
1424
|
+
const wei = BigInt(whole) * 1000000n + BigInt(frac6 || "0");
|
|
1425
|
+
if (wei <= 0n) throw new Error("usdHuman must be greater than zero");
|
|
1426
|
+
return wei;
|
|
1427
|
+
}
|
|
1428
|
+
function buildArcusWithdrawTypedData(args) {
|
|
1429
|
+
const cfg = arcusBridgeConfig(args.chainId);
|
|
1430
|
+
if (!cfg) throw new Error("Arcus fund flows are supported on Robinhood Chain (4663) only.");
|
|
1431
|
+
const address = getAddress(args.executorAddress);
|
|
1432
|
+
const amount = parseArcusUsdHumanToQuoteQuantums(args.usdHuman);
|
|
1433
|
+
const nonce = args.nonceNs ?? arcusNanosecondTimestamp();
|
|
1434
|
+
const accountIndex = arcusAccountIndexForProduct(args.product);
|
|
1435
|
+
return {
|
|
1436
|
+
domain: {
|
|
1437
|
+
name: ARCUS_WITHDRAW_EIP712_DOMAIN_NAME,
|
|
1438
|
+
version: ARCUS_WITHDRAW_EIP712_DOMAIN_VERSION,
|
|
1439
|
+
chainId: cfg.chainId,
|
|
1440
|
+
verifyingContract: getAddress(cfg.bridgeVault)
|
|
1441
|
+
},
|
|
1442
|
+
types: ARCUS_WITHDRAW_SIGN_TYPES,
|
|
1443
|
+
primaryType: "Withdraw",
|
|
1444
|
+
message: {
|
|
1445
|
+
ethereumAddress: address,
|
|
1446
|
+
accountIndex,
|
|
1447
|
+
amount,
|
|
1448
|
+
nonce
|
|
1449
|
+
},
|
|
1450
|
+
requestBody: {
|
|
1451
|
+
ethereumAddress: address.toLowerCase(),
|
|
1452
|
+
accountIndex,
|
|
1453
|
+
amount: amount.toString(),
|
|
1454
|
+
nonce
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1458
|
+
function routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimit) {
|
|
1459
|
+
if (chainGasLimit != null && Number.isFinite(chainGasLimit) && chainGasLimit > 0) {
|
|
1460
|
+
return gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit);
|
|
1461
|
+
}
|
|
1462
|
+
return (estimatedGas * 12n + 9n) / 10n;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
// src/chains/evm/buildBatch.ts
|
|
1466
|
+
async function buildEvmMultisignBatch(args) {
|
|
1467
|
+
const { context, steps } = args;
|
|
1468
|
+
const {
|
|
1469
|
+
chainId,
|
|
1470
|
+
rpcUrl,
|
|
1471
|
+
executorAddress,
|
|
1472
|
+
chainDetail,
|
|
1473
|
+
useCustomGas,
|
|
1474
|
+
customGasChainDetails,
|
|
1475
|
+
keyGen,
|
|
1476
|
+
purposeText
|
|
1477
|
+
} = context;
|
|
1478
|
+
if (steps.length === 0) throw new Error("buildEvmMultisignBatch requires at least one step");
|
|
1479
|
+
const ch = defineChain({
|
|
1480
|
+
id: chainId,
|
|
1481
|
+
name: "Destination",
|
|
1482
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
1483
|
+
rpcUrls: { default: { http: [rpcUrl] } }
|
|
1484
|
+
});
|
|
1485
|
+
const publicClient = createPublicClient({ chain: ch, transport: http(rpcUrl) });
|
|
1486
|
+
const feeParams = await fetchChainFeeParams(rpcUrl, chainId);
|
|
1487
|
+
const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
|
|
1488
|
+
const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
|
|
1489
|
+
const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
|
|
1490
|
+
const chainGasLimitRouter = chainDetail?.gasLimit != null && Number.isFinite(Number(chainDetail.gasLimit)) && Number(chainDetail.gasLimit) > 0 ? Number(chainDetail.gasLimit) : void 0;
|
|
1491
|
+
const gasFeeMultiplier = useCustomGas && chainDetail?.gasMultiplier != null ? Number(chainDetail.gasMultiplier) : void 0;
|
|
1492
|
+
const executor = getAddress(executorAddress);
|
|
1493
|
+
const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
|
|
1494
|
+
const legs = [];
|
|
1495
|
+
for (let i = 0; i < steps.length; i++) {
|
|
1496
|
+
const step = steps[i];
|
|
1497
|
+
const currentNonce = baseNonce + i;
|
|
1498
|
+
let estimatedGas;
|
|
1499
|
+
if (args.estimateGasForStep) {
|
|
1500
|
+
estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient, executor });
|
|
1501
|
+
} else {
|
|
1502
|
+
try {
|
|
1503
|
+
estimatedGas = await publicClient.estimateGas({
|
|
1504
|
+
to: step.to,
|
|
1505
|
+
data: step.data,
|
|
1506
|
+
value: step.value,
|
|
1507
|
+
account: executor
|
|
1508
|
+
});
|
|
1509
|
+
} catch {
|
|
1510
|
+
estimatedGas = step.fallbackGas ?? 100000n;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
let gasLimitI;
|
|
1514
|
+
if (args.resolveGasLimit) {
|
|
1515
|
+
gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient });
|
|
1516
|
+
} else if (step.routerSwap) {
|
|
1517
|
+
gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
|
|
1518
|
+
} else {
|
|
1519
|
+
gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
|
|
1520
|
+
}
|
|
1521
|
+
let proposalTxParams;
|
|
1522
|
+
let feeSnapshot;
|
|
1523
|
+
let serialized;
|
|
1524
|
+
if (legacy) {
|
|
1525
|
+
let gasPriceWei = await publicClient.getGasPrice();
|
|
1526
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
1527
|
+
gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1528
|
+
}
|
|
1529
|
+
if (useCustomGas && chainDetail?.gasPrice != null && chainDetail.gasPrice > 0) {
|
|
1530
|
+
const configured = parseGwei(gweiToDecimalString(Number(chainDetail.gasPrice)));
|
|
1531
|
+
if (configured > gasPriceWei) gasPriceWei = configured;
|
|
1532
|
+
}
|
|
1533
|
+
serialized = serializeTransaction({
|
|
1534
|
+
type: "legacy",
|
|
1535
|
+
to: step.to,
|
|
1536
|
+
data: step.data,
|
|
1537
|
+
value: step.value,
|
|
1538
|
+
gas: gasLimitI,
|
|
1539
|
+
gasPrice: gasPriceWei,
|
|
1540
|
+
nonce: currentNonce,
|
|
1541
|
+
chainId
|
|
1542
|
+
});
|
|
1543
|
+
proposalTxParams = {
|
|
1544
|
+
nonce: currentNonce,
|
|
1545
|
+
gasLimit: gasLimitI.toString(),
|
|
1546
|
+
txType: "legacy",
|
|
1547
|
+
gasPrice: gasPriceWei.toString()
|
|
1548
|
+
};
|
|
1549
|
+
feeSnapshot = proposalTxParamsToFeeSnapshot(proposalTxParams);
|
|
1550
|
+
} else {
|
|
1551
|
+
const fetchedBase = feeParams.baseFeeGwei ?? 0;
|
|
1552
|
+
const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
|
|
1553
|
+
const configuredBase = useCustomGas && chainDetail?.baseFee != null ? Number(chainDetail.baseFee) : 0;
|
|
1554
|
+
const configuredPriority = useCustomGas && chainDetail?.priorityFee != null ? Number(chainDetail.priorityFee) : 0;
|
|
1555
|
+
const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
|
|
1556
|
+
const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
|
|
1557
|
+
const baseFeeMultiplierPct = useCustomGas && chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(chainDetail.baseFeeMultiplier)) : 100;
|
|
1558
|
+
const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
|
|
1559
|
+
const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
|
|
1560
|
+
let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : parseGwei("1");
|
|
1561
|
+
let maxFeePerGas = parseGwei(gweiToDecimalString(maxFeePerGasGwei));
|
|
1562
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
1563
|
+
maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1564
|
+
maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1565
|
+
}
|
|
1566
|
+
({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
|
|
1567
|
+
maxFeePerGas,
|
|
1568
|
+
maxPriorityFeePerGas,
|
|
1569
|
+
latestBaseFeeWei
|
|
1570
|
+
));
|
|
1571
|
+
serialized = serializeTransaction({
|
|
1572
|
+
type: "eip1559",
|
|
1573
|
+
to: step.to,
|
|
1574
|
+
data: step.data,
|
|
1575
|
+
value: step.value,
|
|
1576
|
+
gas: gasLimitI,
|
|
1577
|
+
maxFeePerGas,
|
|
1578
|
+
maxPriorityFeePerGas,
|
|
1579
|
+
nonce: currentNonce,
|
|
1580
|
+
chainId
|
|
1581
|
+
});
|
|
1582
|
+
proposalTxParams = {
|
|
1583
|
+
nonce: currentNonce,
|
|
1584
|
+
gasLimit: gasLimitI.toString(),
|
|
1585
|
+
txType: "eip1559",
|
|
1586
|
+
maxFeePerGas: maxFeePerGas.toString(),
|
|
1587
|
+
maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
1588
|
+
};
|
|
1589
|
+
feeSnapshot = i === 0 ? proposalTxParamsToFeeSnapshot(proposalTxParams) : {};
|
|
1590
|
+
}
|
|
1591
|
+
const h = keccak256(serialized);
|
|
1592
|
+
const msgHash = h.startsWith("0x") ? h.slice(2) : h;
|
|
1593
|
+
const batchMetaExtra = args.buildBatchMeta({ step, index: i, gasLimit: gasLimitI });
|
|
1594
|
+
legs.push({
|
|
1595
|
+
msgHash,
|
|
1596
|
+
msgRaw: i === 0 && args.firstMsgRawNo0x != null ? args.firstMsgRawNo0x : serialized,
|
|
1597
|
+
destinationAddress: step.to,
|
|
1598
|
+
signatureText: typeof batchMetaExtra.signatureText === "string" ? batchMetaExtra.signatureText : JSON.stringify(batchMetaExtra.signatureText ?? {}),
|
|
1599
|
+
audit: batchMetaExtra,
|
|
1600
|
+
feeSnapshot: i === 0 ? feeSnapshot : {},
|
|
1601
|
+
proposalTxParams,
|
|
1602
|
+
valueWei: i === 0 ? step.value : void 0
|
|
1603
|
+
});
|
|
1604
|
+
if (i === 0 && args.firstMsgRawNo0x != null) {
|
|
1605
|
+
legs[0].msgRaw = args.firstMsgRawNo0x;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
const extraJSON = {};
|
|
1609
|
+
if (useCustomGas && customGasChainDetails && Object.keys(customGasChainDetails).length > 0) {
|
|
1610
|
+
extraJSON.customGasChainDetails = customGasChainDetails;
|
|
1611
|
+
}
|
|
1612
|
+
const result = finalizeMultisign({
|
|
1613
|
+
keyGen,
|
|
1614
|
+
purposeText,
|
|
1615
|
+
purposeSuffix: args.purposeSuffix,
|
|
1616
|
+
destinationChainID: String(chainId),
|
|
1617
|
+
destinationAddress: args.destinationAddress ?? steps[0].to,
|
|
1618
|
+
legs,
|
|
1619
|
+
extraJSON: Object.keys(extraJSON).length > 0 ? extraJSON : void 0,
|
|
1620
|
+
expiryDate: context.expiryDate
|
|
1621
|
+
});
|
|
1622
|
+
const pv = args.payableValueWei;
|
|
1623
|
+
if (pv != null && pv > 0n) {
|
|
1624
|
+
result.bodyForSign.value = pv.toString();
|
|
1625
|
+
}
|
|
1626
|
+
return result;
|
|
1627
|
+
}
|
|
1628
|
+
var EIP712_SIGN_REQUEST_KIND = "eip712";
|
|
1629
|
+
function msgHashNo0x(digest) {
|
|
1630
|
+
return digest.startsWith("0x") ? digest.slice(2) : digest;
|
|
1631
|
+
}
|
|
1632
|
+
function eip712SignatureText(domain, primaryType) {
|
|
1633
|
+
return JSON.stringify({
|
|
1634
|
+
kind: "EIP-712",
|
|
1635
|
+
...typeof domain.name === "string" ? { name: domain.name } : {},
|
|
1636
|
+
primaryType
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
function jsonStringifyForAudit(value) {
|
|
1640
|
+
return JSON.stringify(value, (_, v) => typeof v === "bigint" ? v.toString() : v);
|
|
1641
|
+
}
|
|
1642
|
+
function eip712MsgRawHex(typedData, delivery) {
|
|
1643
|
+
const envelope = {
|
|
1644
|
+
version: 1,
|
|
1645
|
+
...typedData,
|
|
1646
|
+
delivery
|
|
1647
|
+
};
|
|
1648
|
+
return stringToHex(jsonStringifyForAudit(envelope));
|
|
1649
|
+
}
|
|
1650
|
+
function buildEip712MultisignBody(args) {
|
|
1651
|
+
const { typedData, delivery } = args;
|
|
1652
|
+
const digest = hashTypedData({
|
|
1653
|
+
domain: typedData.domain,
|
|
1654
|
+
types: typedData.types,
|
|
1655
|
+
primaryType: typedData.primaryType,
|
|
1656
|
+
message: typedData.message
|
|
1657
|
+
});
|
|
1658
|
+
const extraJSON = {
|
|
1659
|
+
signRequestKind: EIP712_SIGN_REQUEST_KIND,
|
|
1660
|
+
eip712: {
|
|
1661
|
+
version: 1,
|
|
1662
|
+
digest,
|
|
1663
|
+
domain: typedData.domain,
|
|
1664
|
+
types: typedData.types,
|
|
1665
|
+
primaryType: typedData.primaryType,
|
|
1666
|
+
message: typedData.message
|
|
1667
|
+
},
|
|
1668
|
+
delivery
|
|
1669
|
+
};
|
|
1670
|
+
return finalizeMultisign({
|
|
1671
|
+
keyGen: args.keyGen,
|
|
1672
|
+
purposeText: args.purposeText,
|
|
1673
|
+
purposeSuffix: args.purposeSuffix,
|
|
1674
|
+
destinationChainID: args.destinationChainID,
|
|
1675
|
+
destinationAddress: args.destinationAddress,
|
|
1676
|
+
extraJSON,
|
|
1677
|
+
expiryDate: args.expiryDate,
|
|
1678
|
+
legs: [
|
|
1679
|
+
{
|
|
1680
|
+
msgHash: msgHashNo0x(digest),
|
|
1681
|
+
msgRaw: eip712MsgRawHex(typedData, delivery),
|
|
1682
|
+
destinationAddress: args.destinationAddress,
|
|
1683
|
+
signatureText: eip712SignatureText(typedData.domain, typedData.primaryType),
|
|
1684
|
+
audit: {
|
|
1685
|
+
kind: "EIP712",
|
|
1686
|
+
primaryType: typedData.primaryType,
|
|
1687
|
+
deliveryKind: delivery.kind,
|
|
1688
|
+
...args.audit ?? {}
|
|
1689
|
+
},
|
|
1690
|
+
feeSnapshot: {}
|
|
1691
|
+
}
|
|
1692
|
+
]
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
// src/protocols/evm/arcus/fundMultisign.ts
|
|
1697
|
+
init_support();
|
|
1698
|
+
init_constants();
|
|
1699
|
+
|
|
1700
|
+
// src/protocols/evm/arcus/registration.ts
|
|
1701
|
+
init_api();
|
|
1702
|
+
async function assertArcusAddressRegisteredForDeposit(args) {
|
|
1703
|
+
const address = getAddress(args.executorAddress).toLowerCase();
|
|
1704
|
+
const { apiKeys } = await arcusFetchApiKeys({ chainId: args.chainId, address });
|
|
1705
|
+
if (!Array.isArray(apiKeys) || apiKeys.length === 0) {
|
|
1706
|
+
throw new Error(
|
|
1707
|
+
"Arcus deposit requires a registered API key for this address. Complete Setup (createApiKey) first."
|
|
1708
|
+
);
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
async function arcusAddressHasRegisteredApiKey(args) {
|
|
1712
|
+
try {
|
|
1713
|
+
await assertArcusAddressRegisteredForDeposit({
|
|
1714
|
+
chainId: args.chainId,
|
|
1715
|
+
executorAddress: getAddress(args.executorAddress.trim())
|
|
1716
|
+
});
|
|
1717
|
+
return true;
|
|
1718
|
+
} catch {
|
|
1719
|
+
return false;
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
// src/protocols/evm/arcus/fundMultisign.ts
|
|
1724
|
+
var erc20ApproveAbi = parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
|
|
1725
|
+
var depositProxyAbi = parseAbi([
|
|
1726
|
+
"function initiateDeposit(address owner, uint16 accountIndex, address token, uint256 amount) returns (uint64 inboxIndex)"
|
|
1727
|
+
]);
|
|
1728
|
+
var DEPOSIT_APPROVE_FALLBACK_GAS = 80000n;
|
|
1729
|
+
var DEPOSIT_INITIATE_FALLBACK_GAS = 350000n;
|
|
1730
|
+
async function buildArcusDepositMultisign(args) {
|
|
1731
|
+
if (!isArcusChainSupported(args.chainId)) {
|
|
1732
|
+
throw new Error("chainId must be 4663 (Robinhood Chain)");
|
|
1733
|
+
}
|
|
1734
|
+
await assertArcusAddressRegisteredForDeposit({
|
|
1735
|
+
chainId: args.chainId,
|
|
1736
|
+
executorAddress: getAddress(args.executorAddress)
|
|
1737
|
+
});
|
|
1738
|
+
const cfg = arcusBridgeConfig(args.chainId);
|
|
1739
|
+
if (!cfg) throw new Error("Arcus deposit is not configured for this chain.");
|
|
1740
|
+
const amountWei = parseArcusUsdHumanToUsdgWei(args.usdHuman);
|
|
1741
|
+
const executor = getAddress(args.executorAddress);
|
|
1742
|
+
const accountIndex = args.product === "spot" ? 1 : 0;
|
|
1743
|
+
const usdg = getAddress(cfg.usdg);
|
|
1744
|
+
const depositProxy = getAddress(cfg.depositProxy);
|
|
1745
|
+
const approveData = encodeFunctionData({
|
|
1746
|
+
abi: erc20ApproveAbi,
|
|
1747
|
+
functionName: "approve",
|
|
1748
|
+
args: [depositProxy, amountWei]
|
|
1749
|
+
});
|
|
1750
|
+
const depositData = encodeFunctionData({
|
|
1751
|
+
abi: depositProxyAbi,
|
|
1752
|
+
functionName: "initiateDeposit",
|
|
1753
|
+
args: [executor, accountIndex, usdg, amountWei]
|
|
1754
|
+
});
|
|
1755
|
+
return buildEvmMultisignBatch({
|
|
1756
|
+
context: {
|
|
1757
|
+
keyGen: args.keyGen,
|
|
1758
|
+
chainCategory: "evm",
|
|
1759
|
+
chainId: args.chainId,
|
|
1760
|
+
rpcUrl: args.rpcUrl,
|
|
1761
|
+
chainDetail: args.chainDetail,
|
|
1762
|
+
useCustomGas: args.useCustomGas,
|
|
1763
|
+
customGasChainDetails: args.customGasChainDetails ?? null,
|
|
1764
|
+
executorAddress: executor,
|
|
1765
|
+
purposeText: args.purposeText
|
|
1766
|
+
},
|
|
1767
|
+
steps: [
|
|
1768
|
+
{ to: usdg, data: approveData, value: 0n, fallbackGas: DEPOSIT_APPROVE_FALLBACK_GAS },
|
|
1769
|
+
{ to: depositProxy, data: depositData, value: 0n, fallbackGas: DEPOSIT_INITIATE_FALLBACK_GAS }
|
|
1770
|
+
],
|
|
1771
|
+
destinationAddress: depositProxy,
|
|
1772
|
+
buildBatchMeta: ({ index }) => {
|
|
1773
|
+
if (index === 0) {
|
|
1774
|
+
return {
|
|
1775
|
+
kind: "Arcus",
|
|
1776
|
+
action: "depositApprove",
|
|
1777
|
+
product: args.product,
|
|
1778
|
+
accountIndex,
|
|
1779
|
+
usdg,
|
|
1780
|
+
depositProxy,
|
|
1781
|
+
usdHuman: args.usdHuman,
|
|
1782
|
+
evm: { type: "arcus_deposit_approve", version: 1, chainId: String(args.chainId) }
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
return {
|
|
1786
|
+
kind: "Arcus",
|
|
1787
|
+
action: "deposit",
|
|
1788
|
+
product: args.product,
|
|
1789
|
+
accountIndex,
|
|
1790
|
+
usdg,
|
|
1791
|
+
depositProxy,
|
|
1792
|
+
usdHuman: args.usdHuman,
|
|
1793
|
+
evm: { type: "arcus_deposit_initiate", version: 1, chainId: String(args.chainId) }
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
async function buildArcusWithdrawMultisign(args) {
|
|
1799
|
+
if (!isArcusChainSupported(args.chainId)) {
|
|
1800
|
+
throw new Error("chainId must be 4663 (Robinhood Chain)");
|
|
1801
|
+
}
|
|
1802
|
+
const minQuantums = ARCUS_QUOTE_QUANTUMS_PER_USD * BigInt(ARCUS_MIN_WITHDRAW_USD);
|
|
1803
|
+
const typed = buildArcusWithdrawTypedData({
|
|
1804
|
+
chainId: args.chainId,
|
|
1805
|
+
executorAddress: args.executorAddress,
|
|
1806
|
+
product: args.product,
|
|
1807
|
+
usdHuman: args.usdHuman
|
|
1808
|
+
});
|
|
1809
|
+
if (typed.message.amount < minQuantums) {
|
|
1810
|
+
throw new Error(`Arcus withdraw must be at least ${ARCUS_MIN_WITHDRAW_USD} USD`);
|
|
1811
|
+
}
|
|
1812
|
+
return buildEip712MultisignBody({
|
|
1813
|
+
keyGen: args.keyGen,
|
|
1814
|
+
purposeText: args.purposeText,
|
|
1815
|
+
destinationChainID: String(args.chainId),
|
|
1816
|
+
destinationAddress: getAddress(args.executorAddress),
|
|
1817
|
+
typedData: typed,
|
|
1818
|
+
delivery: {
|
|
1819
|
+
kind: "arcus_withdraw",
|
|
1820
|
+
chainId: args.chainId,
|
|
1821
|
+
product: args.product,
|
|
1822
|
+
accountIndex: typed.message.accountIndex,
|
|
1823
|
+
body: typed.requestBody
|
|
1824
|
+
},
|
|
1825
|
+
audit: {
|
|
1826
|
+
protocol: ARCUS_PROTOCOL_ID,
|
|
1827
|
+
action: "withdraw",
|
|
1828
|
+
product: args.product,
|
|
1829
|
+
usdHuman: args.usdHuman,
|
|
1830
|
+
accountIndex: typed.message.accountIndex,
|
|
1831
|
+
nonce: typed.message.nonce
|
|
1832
|
+
}
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
async function buildEvmMultisignBodyArcusDepositBatch(args) {
|
|
1836
|
+
return buildArcusDepositMultisign(args);
|
|
1837
|
+
}
|
|
1838
|
+
async function buildArcusWithdrawMultisignBody(args) {
|
|
1839
|
+
return buildArcusWithdrawMultisign(args);
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
// src/protocols/evm/arcus/index.ts
|
|
1843
|
+
init_constants();
|
|
1844
|
+
var arcusProtocolModule = {
|
|
1845
|
+
id: ARCUS_PROTOCOL_ID,
|
|
1846
|
+
chainCategory: "evm",
|
|
1847
|
+
isChainSupported(ctx) {
|
|
1848
|
+
if (ctx.chainCategory !== "evm") return false;
|
|
1849
|
+
const chainId = typeof ctx.chainId === "number" ? ctx.chainId : Number(ctx.chainId);
|
|
1850
|
+
return Number.isFinite(chainId) && isArcusChainSupported(chainId);
|
|
1851
|
+
},
|
|
1852
|
+
isTokenSupported(token) {
|
|
1853
|
+
if (token.category !== "evm") return false;
|
|
1854
|
+
return token.kind === "erc20" || token.kind === "native";
|
|
1855
|
+
},
|
|
1856
|
+
actions: [
|
|
1857
|
+
{
|
|
1858
|
+
id: "arcus.createApiKey",
|
|
1859
|
+
protocolId: ARCUS_PROTOCOL_ID,
|
|
1860
|
+
chainCategory: "evm",
|
|
1861
|
+
description: "Register Arcus Ed25519 API key (secp256k1 personal_sign + paired ed25519 KeyGen)",
|
|
1862
|
+
commonParams: ["keyGen", "purposeText"],
|
|
1863
|
+
params: {
|
|
1864
|
+
secp256k1KeyGenId: { type: "string", required: true, description: "Custody secp256k1 KeyGen id" },
|
|
1865
|
+
ed25519KeyGenId: { type: "string", required: true, description: "Paired ed25519 KeyGen id (same GroupId)" },
|
|
1866
|
+
apiWalletName: { type: "string", required: false, description: "Label for the API key" }
|
|
1867
|
+
}
|
|
1868
|
+
},
|
|
1869
|
+
{
|
|
1870
|
+
id: "arcus.placeOrder",
|
|
1871
|
+
protocolId: ARCUS_PROTOCOL_ID,
|
|
1872
|
+
chainCategory: "evm",
|
|
1873
|
+
description: "Place Arcus perp order with optional TP/SL (Ed25519 payload sign)",
|
|
1874
|
+
commonParams: ["keyGen", "purposeText"],
|
|
1875
|
+
params: {
|
|
1876
|
+
secp256k1KeyGenId: { type: "string", required: true, description: "Custody secp256k1 KeyGen id" },
|
|
1877
|
+
ed25519KeyGenId: { type: "string", required: true, description: "Paired ed25519 KeyGen id" },
|
|
1878
|
+
market: { type: "string", required: true, description: "e.g. BTC-USD" },
|
|
1879
|
+
isBuy: { type: "boolean", required: true, description: "true for buy/long" },
|
|
1880
|
+
limitPxHuman: { type: "string", required: true, description: "Entry limit price" },
|
|
1881
|
+
szHuman: { type: "string", required: true, description: "Order size" },
|
|
1882
|
+
takeProfitTriggerPxHuman: { type: "string", required: false, description: "Optional TP trigger" },
|
|
1883
|
+
stopLossTriggerPxHuman: { type: "string", required: false, description: "Optional SL trigger" }
|
|
1884
|
+
}
|
|
1885
|
+
},
|
|
1886
|
+
{
|
|
1887
|
+
id: "arcus.spotRfq",
|
|
1888
|
+
protocolId: ARCUS_PROTOCOL_ID,
|
|
1889
|
+
chainCategory: "evm",
|
|
1890
|
+
description: "Arcus spot Stock Token RFQ buy/sell",
|
|
1891
|
+
commonParams: ["keyGen", "purposeText"],
|
|
1892
|
+
params: {
|
|
1893
|
+
secp256k1KeyGenId: { type: "string", required: true, description: "Custody secp256k1 KeyGen id" },
|
|
1894
|
+
ed25519KeyGenId: { type: "string", required: true, description: "Paired ed25519 KeyGen id" },
|
|
1895
|
+
ticker: { type: "string", required: true, description: "Spot ticker e.g. AAPL" },
|
|
1896
|
+
isBuy: { type: "boolean", required: true, description: "true for buy" },
|
|
1897
|
+
sizeHuman: { type: "string", required: true, description: "Token size" },
|
|
1898
|
+
limitPxHuman: { type: "string", required: false, description: "Optional limit price" }
|
|
1899
|
+
}
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
id: "arcus.close",
|
|
1903
|
+
protocolId: ARCUS_PROTOCOL_ID,
|
|
1904
|
+
chainCategory: "evm",
|
|
1905
|
+
description: "Close Arcus perp position with IoC reduce-only limit order",
|
|
1906
|
+
commonParams: ["keyGen", "purposeText"],
|
|
1907
|
+
params: {
|
|
1908
|
+
secp256k1KeyGenId: { type: "string", required: true, description: "Custody secp256k1 KeyGen id" },
|
|
1909
|
+
ed25519KeyGenId: { type: "string", required: true, description: "Paired ed25519 KeyGen id" },
|
|
1910
|
+
market: { type: "string", required: true, description: "Perp market" },
|
|
1911
|
+
isLong: { type: "boolean", required: true, description: "true when closing a long position" },
|
|
1912
|
+
limitPxHuman: { type: "string", required: true, description: "Limit price for IoC close" },
|
|
1913
|
+
szHuman: { type: "string", required: true, description: "Size to close" }
|
|
1914
|
+
}
|
|
1915
|
+
},
|
|
1916
|
+
{
|
|
1917
|
+
id: "arcus.cancelOrder",
|
|
1918
|
+
protocolId: ARCUS_PROTOCOL_ID,
|
|
1919
|
+
chainCategory: "evm",
|
|
1920
|
+
description: "Cancel Arcus perp order",
|
|
1921
|
+
commonParams: ["keyGen", "purposeText"],
|
|
1922
|
+
params: {
|
|
1923
|
+
secp256k1KeyGenId: { type: "string", required: true, description: "Custody secp256k1 KeyGen id" },
|
|
1924
|
+
ed25519KeyGenId: { type: "string", required: true, description: "Paired ed25519 KeyGen id" },
|
|
1925
|
+
market: { type: "string", required: true, description: "Perp market" },
|
|
1926
|
+
orderId: { type: "string", required: true, description: "Server order id" }
|
|
1927
|
+
}
|
|
1928
|
+
},
|
|
1929
|
+
{
|
|
1930
|
+
id: "arcus.setLeverage",
|
|
1931
|
+
protocolId: ARCUS_PROTOCOL_ID,
|
|
1932
|
+
chainCategory: "evm",
|
|
1933
|
+
description: "Set Arcus perp leverage for a market",
|
|
1934
|
+
commonParams: ["keyGen", "purposeText"],
|
|
1935
|
+
params: {
|
|
1936
|
+
secp256k1KeyGenId: { type: "string", required: true, description: "Custody secp256k1 KeyGen id" },
|
|
1937
|
+
ed25519KeyGenId: { type: "string", required: true, description: "Paired ed25519 KeyGen id" },
|
|
1938
|
+
market: { type: "string", required: true, description: "Perp market" },
|
|
1939
|
+
leverage: { type: "number", required: true, description: "Target leverage" }
|
|
1940
|
+
}
|
|
1941
|
+
},
|
|
1942
|
+
{
|
|
1943
|
+
id: "arcus.deposit",
|
|
1944
|
+
protocolId: ARCUS_PROTOCOL_ID,
|
|
1945
|
+
chainCategory: "evm",
|
|
1946
|
+
description: "Deposit USDG on Robinhood Chain into Arcus perp or spot collateral (approve + initiateDeposit). Requires registered API key.",
|
|
1947
|
+
commonParams: ["keyGen", "purposeText", "useCustomGas"],
|
|
1948
|
+
params: {
|
|
1949
|
+
usdHuman: { type: "string", required: true, description: "USD amount to deposit" },
|
|
1950
|
+
product: { type: "string", required: false, description: "perp (default) or spot" }
|
|
1951
|
+
}
|
|
1952
|
+
},
|
|
1953
|
+
{
|
|
1954
|
+
id: "arcus.withdraw",
|
|
1955
|
+
protocolId: ARCUS_PROTOCOL_ID,
|
|
1956
|
+
chainCategory: "evm",
|
|
1957
|
+
description: "Withdraw USDG collateral from Arcus to wallet (EIP-712 + POST /v1/withdraw)",
|
|
1958
|
+
commonParams: ["keyGen", "purposeText"],
|
|
1959
|
+
params: {
|
|
1960
|
+
usdHuman: { type: "string", required: true, description: "USD amount to withdraw" },
|
|
1961
|
+
product: { type: "string", required: false, description: "perp (default) or spot" }
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
]
|
|
1965
|
+
};
|
|
1966
|
+
registerProtocolModule(arcusProtocolModule);
|
|
1967
|
+
|
|
1968
|
+
export { ARCUS_BRIDGE_VAULT_4663, ARCUS_DEFAULT_CANDLE_COUNT, ARCUS_DEFAULT_LOOKBACK_DAYS, ARCUS_DEFAULT_OHLCV_INTERVAL, ARCUS_DEPOSIT_PROXY_4663, ARCUS_MAX_CANDLE_COUNT, ARCUS_MIN_WITHDRAW_USD, ARCUS_PERP_ACCOUNT_INDEX, ARCUS_PERP_INTERVAL_MS, ARCUS_PERP_MARKET_STATUS_ONLINE, ARCUS_PERP_OHLCV_INTERVALS, ARCUS_PROTOCOL_ID, ARCUS_QUOTE_QUANTUMS_PER_USD, ARCUS_SPOT_ACCOUNT_INDEX, ARCUS_SPOT_INTERVAL_SEC, ARCUS_SPOT_OHLCV_INTERVALS, ARCUS_SUPPORTED_CHAIN_IDS, ARCUS_USDG_ADDRESS_4663, ARCUS_USDG_DECIMALS, ARCUS_WITHDRAW_EIP712_DOMAIN_NAME, ARCUS_WITHDRAW_EIP712_DOMAIN_VERSION, ARCUS_WITHDRAW_SIGN_TYPES, arcusAccountIndexForProduct, arcusAddressHasRegisteredApiKey, arcusApiMetaBaseUrl, arcusAssertPerpMarketOnline, arcusBridgeConfig, arcusCanonicalJson, arcusFetchAccount, arcusFetchAccountLeverages, arcusFetchAccountSummary, arcusFetchAllMids, arcusFetchApiKeys, arcusFetchLeveragesSummary, arcusFetchMarketSnapshotSummary, arcusFetchMarketsSummary, arcusFetchOhlcvSummary, arcusFetchOpenContextSummary, arcusFetchOpenOrders, arcusFetchOpenOrdersSummary, arcusFetchPerpCandles, arcusFetchPerpMarkets, arcusFetchPerpOhlcvRange, arcusFetchPositions, arcusFetchPositionsSummary, arcusFetchSpotBalances, arcusFetchSpotOhlcvRange, arcusFetchSpotOverview, arcusFetchSpotTokenCandles, arcusIsPerpMarketOnline, arcusLookupMidFromMids, arcusMapOpenOrderDisplayRows, arcusMapPerpCandles, arcusMapPositionDisplayRows, arcusMapSpotCandles, arcusMaxLeverageFromInitialMarginFraction, arcusNanosecondTimestamp, arcusOrderClosesPosition, arcusOrderCountsAsPendingClose, arcusParseAccountSummary, arcusParseOpenOrderDisplayRow, arcusParsePositionDisplayRow, arcusPayloadMessageHex, arcusPayloadMsgHashHex, arcusPendingCloseByMarket, arcusPendingCloseForPosition, arcusPerpApiBaseUrl, arcusProtocolModule, arcusResolveEffectiveLeverage, arcusResolvePerpMarketDisplayName, arcusResolvePerpOhlcvWindow, arcusSearchMarketsSummary, arcusSearchPerpMarkets, arcusSearchSpotMarkets, arcusSpotFetchBalancesSummary, arcusSpotFetchMarketsSummary, arcusSpotFetchOhlcvSummary, arcusSpotIndexerBaseUrl, arcusSpotSearchMarketsSummary, assertArcusAddressRegisteredForDeposit, buildArcusCancelOrderMultisign, buildArcusCancelOrderPayload, buildArcusCloseMultisign, buildArcusCreateApiKeyMultisign, buildArcusDepositMultisign, buildArcusPlaceOrderMultisign, buildArcusPlaceOrderPayload, buildArcusSetLeverageMultisign, buildArcusSpotRfqMultisign, buildArcusTpslPayload, buildArcusWithdrawMultisign, buildArcusWithdrawMultisignBody, buildArcusWithdrawTypedData, buildEvmMultisignBodyArcusCancelOrderBatch, buildEvmMultisignBodyArcusCloseBatch, buildEvmMultisignBodyArcusCreateApiKeyBatch, buildEvmMultisignBodyArcusDepositBatch, buildEvmMultisignBodyArcusPlaceOrderBatch, buildEvmMultisignBodyArcusSetLeverageBatch, buildEvmMultisignBodyArcusSpotRfqBatch, formatArcusDisplaySize, formatArcusLeverageLabel, humanPriceToQuantums, humanSizeToQuantums, isArcusChainSupported, parseArcusLeverageLabel, parseArcusUsdHumanToQuoteQuantums, parseArcusUsdHumanToUsdgWei };
|
|
1969
|
+
//# sourceMappingURL=index.js.map
|
|
1970
|
+
//# sourceMappingURL=index.js.map
|