@continuumdao/ctm-mpc-defi 0.2.49 → 0.2.51

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.
Files changed (39) hide show
  1. package/dist/agent/catalog.cjs +1344 -22
  2. package/dist/agent/catalog.cjs.map +1 -1
  3. package/dist/agent/catalog.d.ts +1528 -9
  4. package/dist/agent/catalog.js +1316 -23
  5. package/dist/agent/catalog.js.map +1 -1
  6. package/dist/agent/skills/continuum-dao/SKILL.md +2 -0
  7. package/dist/agent/skills/continuum-dao-marketplace/SKILL.md +58 -0
  8. package/dist/agent/skills/hyperliquidOutcome/SKILL.md +47 -0
  9. package/dist/agent/skills/trueo/SKILL.md +64 -0
  10. package/dist/core/index.cjs +3 -1
  11. package/dist/core/index.cjs.map +1 -1
  12. package/dist/core/index.js +3 -1
  13. package/dist/core/index.js.map +1 -1
  14. package/dist/index.cjs +3 -1
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.js +3 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/protocols/evm/aerodrome/index.cjs.map +1 -1
  19. package/dist/protocols/evm/aerodrome/index.js.map +1 -1
  20. package/dist/protocols/evm/continuum-dao/index.cjs +2274 -59
  21. package/dist/protocols/evm/continuum-dao/index.cjs.map +1 -1
  22. package/dist/protocols/evm/continuum-dao/index.d.ts +690 -2
  23. package/dist/protocols/evm/continuum-dao/index.js +2218 -61
  24. package/dist/protocols/evm/continuum-dao/index.js.map +1 -1
  25. package/dist/protocols/evm/hyperliquidOutcome/index.cjs +1294 -0
  26. package/dist/protocols/evm/hyperliquidOutcome/index.cjs.map +1 -0
  27. package/dist/protocols/evm/hyperliquidOutcome/index.d.ts +465 -0
  28. package/dist/protocols/evm/hyperliquidOutcome/index.js +1247 -0
  29. package/dist/protocols/evm/hyperliquidOutcome/index.js.map +1 -0
  30. package/dist/protocols/evm/pendle/index.cjs +3 -1
  31. package/dist/protocols/evm/pendle/index.cjs.map +1 -1
  32. package/dist/protocols/evm/pendle/index.js +3 -1
  33. package/dist/protocols/evm/pendle/index.js.map +1 -1
  34. package/dist/protocols/evm/trueo/index.cjs +2868 -0
  35. package/dist/protocols/evm/trueo/index.cjs.map +1 -0
  36. package/dist/protocols/evm/trueo/index.d.ts +1295 -0
  37. package/dist/protocols/evm/trueo/index.js +2725 -0
  38. package/dist/protocols/evm/trueo/index.js.map +1 -0
  39. package/package.json +11 -1
@@ -0,0 +1,2725 @@
1
+ import { getAddress, parseAbi, encodeFunctionData, encodeAbiParameters, parseUnits, defineChain, createPublicClient, http, parseGwei, serializeTransaction, keccak256 } from 'viem';
2
+ import { fetchChainFeeParams, gasLimitFromEstimateAndChainConfig, gweiToDecimalString, proposalTxParamsToFeeSnapshot, alignEip1559FeesWithLatestBase, getClientIdFromKeyGenResult } from '@continuumdao/continuum-node-sdk';
3
+
4
+ // src/core/registry.ts
5
+ var modules = [];
6
+ function registerProtocolModule(mod) {
7
+ const existing = modules.findIndex((m) => m.id === mod.id);
8
+ if (existing >= 0) {
9
+ modules[existing] = mod;
10
+ } else {
11
+ modules.push(mod);
12
+ }
13
+ }
14
+ var TRUEO_PROTOCOL_ID = "trueo";
15
+ var PREDICTION_MARKETS_PROTOCOL_ID = "prediction-markets";
16
+ var TRUEO_BASE_CHAIN_ID = 8453;
17
+ var TRUEO_CHAIN_IDS = [TRUEO_BASE_CHAIN_ID];
18
+ var TRUEO_NETWORK = "base-mainnet";
19
+ var TRUEO_API_ORIGIN = "https://api.trueo.com";
20
+ var TRUEO_MARKET_API_PREFIX = "/market";
21
+ function trueoMarketApiPath(suffix) {
22
+ const s = suffix.startsWith("/") ? suffix : `/${suffix}`;
23
+ return `${TRUEO_MARKET_API_PREFIX}${s}`;
24
+ }
25
+ var TRUEO_TYD = getAddress("0xb13CF163d916917d9cD6E836905cA5f12a1dEF4B");
26
+ var TRUEO_TRUE = getAddress("0x21CFCFc3d8F98fC728f48341D10Ad8283F6EB7AB");
27
+ var TRUEO_USDC = getAddress("0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913");
28
+ var TRUEO_TRUTH_MARKET_MANAGER = getAddress("0x61A98Bef11867c69489B91f340fE545eEfc695d7");
29
+ var TRUEO_ORDER_MANAGER = getAddress("0x1cdB1C43eB64e99780EA9d3C7162C23432C59a1A");
30
+ var TRUEO_UNIVERSAL_ROUTER_ADAPTER = getAddress("0x0A1AFc2121B4441127a02DEF72928F61f564034a");
31
+ var TRUEO_TRUTH_MARKET_ADAPTER = getAddress("0x6E08d24AEf78F6d1a97503F64E84fDF65f8125E1");
32
+ var TRUEO_V4_HOOK = getAddress("0x3E83479B2276EcFb7C7181F67b5d092d3511e0C4");
33
+ var TRUEO_PERMIT2 = getAddress("0x000000000022D473030F116dDEE9F6B43aC78BA3");
34
+ var TRUEO_TYD_DECIMALS = 6;
35
+ var TRUEO_OUTCOME_TOKEN_DECIMALS = 18;
36
+ var TRUEO_BET_CLOSE_BUFFER_SECONDS = 5 * 60;
37
+ var TRUEO_PERMIT2_APPROVAL_SECONDS = 30 * 60;
38
+ var TRUEO_BET_ACTIONS = ["swap", "createOrder"];
39
+ function isTrueoChainSupported(chainId) {
40
+ return TRUEO_CHAIN_IDS.includes(chainId);
41
+ }
42
+ function assertTrueoChainId(chainId) {
43
+ if (!isTrueoChainSupported(chainId)) {
44
+ throw new Error(`Trueo is Base-only (chainId ${TRUEO_BASE_CHAIN_ID}).`);
45
+ }
46
+ }
47
+ function isTrueoCoreToken(address) {
48
+ try {
49
+ const n = getAddress(address);
50
+ return n === TRUEO_TYD || n === TRUEO_TRUE;
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
55
+ function normalizeTrueoAddress(address) {
56
+ return getAddress(address);
57
+ }
58
+ var trueoErc20Abi = parseAbi([
59
+ "function allowance(address owner, address spender) view returns (uint256)",
60
+ "function approve(address spender, uint256 amount) returns (bool)",
61
+ "function balanceOf(address account) view returns (uint256)",
62
+ "function decimals() view returns (uint8)",
63
+ "function symbol() view returns (string)"
64
+ ]);
65
+ var trueoMarketAbi = parseAbi([
66
+ "function mint(uint256 paymentTokenAmount)",
67
+ "function burn(uint256 amount)",
68
+ "function redeem(uint256 amount)",
69
+ "function paymentToken() view returns (address)",
70
+ "function yesToken() view returns (address)",
71
+ "function noToken() view returns (address)",
72
+ "function getCurrentStatus() view returns (uint8)",
73
+ "function winningPosition() view returns (uint256)",
74
+ "function paused() view returns (bool)"
75
+ ]);
76
+ var trueoMarketV2Abi = parseAbi([
77
+ "function getPoolKeys() view returns ((address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) yesPoolKey, (address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) noPoolKey)",
78
+ "function getPoolIds() view returns (bytes32 yesPoolId, bytes32 noPoolId)"
79
+ ]);
80
+ var trueoAdapterMintAbi = parseAbi([
81
+ "function mint(address truthMarket, uint256 assetAmount)",
82
+ "function burn(address truthMarket, uint256 amount)",
83
+ "function redeem(address truthMarket, uint256 amount)"
84
+ ]);
85
+ var trueoTydVaultAbi = parseAbi([
86
+ "function asset() view returns (address)",
87
+ "function deposit(uint256 assets, address receiver) returns (uint256 shares)",
88
+ "function redeem(uint256 shares, address receiver, address owner) returns (uint256 assets)"
89
+ ]);
90
+ var trueoUniversalRouterAdapterAbi = parseAbi([
91
+ "function execute(bytes commands, bytes[] inputs, uint256 deadline) payable"
92
+ ]);
93
+ var trueoPermit2ApproveAbi = parseAbi([
94
+ "function approve(address token, address spender, uint160 amount, uint48 expiration)",
95
+ "function allowance(address owner, address token, address spender) view returns (uint160 amount, uint48 expiration, uint48 nonce)"
96
+ ]);
97
+ var trueoOrderManagerAbi = parseAbi([
98
+ "function createOrder((address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) poolKey, uint128 amountIn, int24 tickLower, int24 tickUpper, bool zeroForOne, bool enablePartialFill) returns (uint32 orderId)",
99
+ "function cancelOrder((address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) poolKey, uint32 orderId, uint128 amount0Min, uint128 amount1Min)",
100
+ "function pendingOrder(bytes32 poolId, uint32 orderId) view returns (address owner, bool zeroForOne, int24 tickLower, int24 tickUpper, uint256 liquidity, bool enablePartialFill)"
101
+ ]);
102
+ var trueoOrderManagerStructAbi = parseAbi([
103
+ "function createOrder(((address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) poolKey, uint128 amountIn, int24 tickLower, int24 tickUpper, bool zeroForOne, bool enablePartialFill) params) returns (uint32 orderId)",
104
+ "function cancelOrder(((address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) poolKey, uint32 orderId, uint128 amount0Min, uint128 amount1Min) params)"
105
+ ]);
106
+ var UR_COMMAND_V4_SWAP = 16;
107
+ var V4_ACTION_SWAP_EXACT_IN_SINGLE = 6;
108
+ var V4_ACTION_SETTLE = 11;
109
+ var V4_ACTION_TAKE = 12;
110
+ var poolKeyComponents = [
111
+ { name: "currency0", type: "address" },
112
+ { name: "currency1", type: "address" },
113
+ { name: "fee", type: "uint24" },
114
+ { name: "tickSpacing", type: "int24" },
115
+ { name: "hooks", type: "address" }
116
+ ];
117
+ function encodeErc20Approve(spender, amount) {
118
+ return encodeFunctionData({
119
+ abi: trueoErc20Abi,
120
+ functionName: "approve",
121
+ args: [getAddress(spender), amount]
122
+ });
123
+ }
124
+ function encodePermit2Approve(args) {
125
+ return encodeFunctionData({
126
+ abi: trueoPermit2ApproveAbi,
127
+ functionName: "approve",
128
+ args: [getAddress(args.token), getAddress(args.spender), args.amount, args.expiration]
129
+ });
130
+ }
131
+ function encodeTrueoTydDeposit(assets, receiver) {
132
+ return encodeFunctionData({
133
+ abi: trueoTydVaultAbi,
134
+ functionName: "deposit",
135
+ args: [assets, getAddress(receiver)]
136
+ });
137
+ }
138
+ function encodeTrueoTydRedeem(shares, receiver, owner) {
139
+ return encodeFunctionData({
140
+ abi: trueoTydVaultAbi,
141
+ functionName: "redeem",
142
+ args: [shares, getAddress(receiver), getAddress(owner)]
143
+ });
144
+ }
145
+ function encodeTrueoMint(paymentTokenAmount) {
146
+ return encodeFunctionData({
147
+ abi: trueoMarketAbi,
148
+ functionName: "mint",
149
+ args: [paymentTokenAmount]
150
+ });
151
+ }
152
+ function encodeTrueoBurn(amount) {
153
+ return encodeFunctionData({
154
+ abi: trueoMarketAbi,
155
+ functionName: "burn",
156
+ args: [amount]
157
+ });
158
+ }
159
+ function encodeTrueoRedeem(amount) {
160
+ return encodeFunctionData({
161
+ abi: trueoMarketAbi,
162
+ functionName: "redeem",
163
+ args: [amount]
164
+ });
165
+ }
166
+ function encodeTrueoAdapterMint(market, assetAmount) {
167
+ return encodeFunctionData({
168
+ abi: trueoAdapterMintAbi,
169
+ functionName: "mint",
170
+ args: [getAddress(market), assetAmount]
171
+ });
172
+ }
173
+ function encodeTrueoAdapterBurn(market, amount) {
174
+ return encodeFunctionData({
175
+ abi: trueoAdapterMintAbi,
176
+ functionName: "burn",
177
+ args: [getAddress(market), amount]
178
+ });
179
+ }
180
+ function encodeTrueoAdapterRedeem(market, amount) {
181
+ return encodeFunctionData({
182
+ abi: trueoAdapterMintAbi,
183
+ functionName: "redeem",
184
+ args: [getAddress(market), amount]
185
+ });
186
+ }
187
+ function encodeTrueoV4ExactInInput(args) {
188
+ const poolKey = {
189
+ currency0: getAddress(args.poolKey.currency0),
190
+ currency1: getAddress(args.poolKey.currency1),
191
+ fee: args.poolKey.fee,
192
+ tickSpacing: args.poolKey.tickSpacing,
193
+ hooks: getAddress(args.poolKey.hooks)
194
+ };
195
+ const settleCurrency = args.zeroForOne ? poolKey.currency0 : poolKey.currency1;
196
+ const takeCurrency = args.zeroForOne ? poolKey.currency1 : poolKey.currency0;
197
+ const swapParams = encodeAbiParameters(
198
+ [
199
+ {
200
+ type: "tuple",
201
+ components: [
202
+ { name: "poolKey", type: "tuple", components: [...poolKeyComponents] },
203
+ { name: "zeroForOne", type: "bool" },
204
+ { name: "amountIn", type: "uint128" },
205
+ { name: "amountOutMinimum", type: "uint128" },
206
+ { name: "hookData", type: "bytes" }
207
+ ]
208
+ }
209
+ ],
210
+ [
211
+ {
212
+ poolKey,
213
+ zeroForOne: args.zeroForOne,
214
+ amountIn: args.amountIn,
215
+ amountOutMinimum: args.amountOutMinimum,
216
+ hookData: "0x"
217
+ }
218
+ ]
219
+ );
220
+ const settleParams = encodeAbiParameters(
221
+ [
222
+ { name: "currency", type: "address" },
223
+ { name: "amount", type: "uint256" },
224
+ { name: "payerIsUser", type: "bool" }
225
+ ],
226
+ [settleCurrency, args.amountIn, true]
227
+ );
228
+ const takeParams = encodeAbiParameters(
229
+ [
230
+ { name: "currency", type: "address" },
231
+ { name: "recipient", type: "address" },
232
+ { name: "amount", type: "uint256" }
233
+ ],
234
+ [takeCurrency, getAddress(args.recipient), 0n]
235
+ );
236
+ const actions = `0x${[V4_ACTION_SWAP_EXACT_IN_SINGLE, V4_ACTION_SETTLE, V4_ACTION_TAKE].map((b) => b.toString(16).padStart(2, "0")).join("")}`;
237
+ return encodeAbiParameters(
238
+ [
239
+ { name: "actions", type: "bytes" },
240
+ { name: "params", type: "bytes[]" }
241
+ ],
242
+ [actions, [swapParams, settleParams, takeParams]]
243
+ );
244
+ }
245
+ function encodeTrueoUniversalRouterExecute(args) {
246
+ return encodeFunctionData({
247
+ abi: trueoUniversalRouterAdapterAbi,
248
+ functionName: "execute",
249
+ args: [args.commands, [...args.inputs], args.deadline]
250
+ });
251
+ }
252
+ function encodeTrueoV4ExactInSwap(args) {
253
+ const commands = `0x${UR_COMMAND_V4_SWAP.toString(16).padStart(2, "0")}`;
254
+ const inputs = [
255
+ encodeTrueoV4ExactInInput({
256
+ poolKey: args.poolKey,
257
+ zeroForOne: args.zeroForOne,
258
+ amountIn: args.amountIn,
259
+ amountOutMinimum: args.amountOutMinimum,
260
+ recipient: args.recipient
261
+ })
262
+ ];
263
+ const data = encodeTrueoUniversalRouterExecute({
264
+ commands,
265
+ inputs,
266
+ deadline: args.deadline
267
+ });
268
+ return { commands, inputs, deadline: args.deadline, data, to: TRUEO_UNIVERSAL_ROUTER_ADAPTER };
269
+ }
270
+ function encodeTrueoCreateOrder(args) {
271
+ return encodeFunctionData({
272
+ abi: trueoOrderManagerStructAbi,
273
+ functionName: "createOrder",
274
+ args: [
275
+ {
276
+ poolKey: {
277
+ currency0: getAddress(args.poolKey.currency0),
278
+ currency1: getAddress(args.poolKey.currency1),
279
+ fee: args.poolKey.fee,
280
+ tickSpacing: args.poolKey.tickSpacing,
281
+ hooks: getAddress(args.poolKey.hooks)
282
+ },
283
+ amountIn: args.amountIn,
284
+ tickLower: args.tickLower,
285
+ tickUpper: args.tickUpper,
286
+ zeroForOne: args.zeroForOne,
287
+ enablePartialFill: args.enablePartialFill
288
+ }
289
+ ]
290
+ });
291
+ }
292
+ function encodeTrueoCancelOrder(args) {
293
+ return encodeFunctionData({
294
+ abi: trueoOrderManagerStructAbi,
295
+ functionName: "cancelOrder",
296
+ args: [
297
+ {
298
+ poolKey: {
299
+ currency0: getAddress(args.poolKey.currency0),
300
+ currency1: getAddress(args.poolKey.currency1),
301
+ fee: args.poolKey.fee,
302
+ tickSpacing: args.poolKey.tickSpacing,
303
+ hooks: getAddress(args.poolKey.hooks)
304
+ },
305
+ orderId: args.orderId,
306
+ amount0Min: args.amount0Min ?? 0n,
307
+ amount1Min: args.amount1Min ?? 0n
308
+ }
309
+ ]
310
+ });
311
+ }
312
+ function trueoPermit2Expiration(nowSec = Math.floor(Date.now() / 1e3)) {
313
+ return nowSec + TRUEO_PERMIT2_APPROVAL_SECONDS;
314
+ }
315
+ function trueoSwapDeadline(nowSec = Math.floor(Date.now() / 1e3)) {
316
+ return BigInt(nowSec + TRUEO_PERMIT2_APPROVAL_SECONDS);
317
+ }
318
+
319
+ // src/protocols/evm/trueo/prices.ts
320
+ var Q96 = 2n ** 96n;
321
+ function trueoImpliedPriceFromSqrtX96(sqrtPriceX96, token0Decimals = TRUEO_OUTCOME_TOKEN_DECIMALS, token1Decimals = TRUEO_TYD_DECIMALS) {
322
+ if (sqrtPriceX96 == null) return null;
323
+ let sqrt;
324
+ try {
325
+ sqrt = typeof sqrtPriceX96 === "bigint" ? sqrtPriceX96 : BigInt(String(sqrtPriceX96));
326
+ } catch {
327
+ return null;
328
+ }
329
+ if (sqrt <= 0n) return null;
330
+ const raw = Number(sqrt) / Number(Q96);
331
+ if (!Number.isFinite(raw) || raw <= 0) return null;
332
+ const price = raw * raw * 10 ** (token0Decimals - token1Decimals);
333
+ return Number.isFinite(price) && price > 0 ? price : null;
334
+ }
335
+ var TRUEO_SANE_IMPLIED_PRICE_MAX = 1.05;
336
+ function trueoIsSaneImpliedPrice(price) {
337
+ return price != null && Number.isFinite(price) && price >= 0 && price <= TRUEO_SANE_IMPLIED_PRICE_MAX;
338
+ }
339
+ function trueoSanitizeBinaryPrices(yes, no) {
340
+ const y = trueoIsSaneImpliedPrice(yes) ? yes : null;
341
+ const n = trueoIsSaneImpliedPrice(no) ? no : null;
342
+ if (y != null && n == null) return { yes: y, no: Math.min(1, Math.max(0, 1 - y)) };
343
+ if (n != null && y == null) return { yes: Math.min(1, Math.max(0, 1 - n)), no: n };
344
+ return { yes: y, no: n };
345
+ }
346
+ function trueoPercentLabel(price) {
347
+ if (!trueoIsSaneImpliedPrice(price)) return null;
348
+ const pct = Math.min(100, Math.max(0, price * 100));
349
+ return `${pct.toFixed(1)}%`;
350
+ }
351
+ function trueoCompactVolumeLabel(size) {
352
+ if (size == null || size === "") return "";
353
+ const n = typeof size === "number" ? size : Number(String(size).replace(/,/g, ""));
354
+ if (!Number.isFinite(n) || n < 0) return "";
355
+ if (n >= 1e6) return `${(n / 1e6).toFixed(n >= 1e7 ? 0 : 1)}M`;
356
+ if (n >= 1e5) return `${(n / 1e3).toFixed(0)}k`;
357
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
358
+ if (n >= 10) return n.toFixed(0);
359
+ if (n >= 1) return n.toFixed(1);
360
+ if (n === 0) return "0";
361
+ return n.toFixed(2);
362
+ }
363
+ function trueoExpiresAtIso(endOfTrading) {
364
+ if (!Number.isFinite(endOfTrading) || endOfTrading <= 0) return null;
365
+ return new Date(endOfTrading * 1e3).toISOString();
366
+ }
367
+ function trueoExpiresAtLabel(endOfTrading) {
368
+ if (!Number.isFinite(endOfTrading) || endOfTrading <= 0) return null;
369
+ const d = new Date(endOfTrading * 1e3);
370
+ const y = d.getUTCFullYear();
371
+ const m = String(d.getUTCMonth() + 1).padStart(2, "0");
372
+ const day = String(d.getUTCDate()).padStart(2, "0");
373
+ const hh = String(d.getUTCHours()).padStart(2, "0");
374
+ const mm = String(d.getUTCMinutes()).padStart(2, "0");
375
+ return `${y}-${m}-${day} ${hh}:${mm} UTC`;
376
+ }
377
+ var UTC_MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
378
+ function trueoTradingDeadlineLabel(endOfTrading) {
379
+ if (!Number.isFinite(endOfTrading) || endOfTrading <= 0) return null;
380
+ const d = new Date(endOfTrading * 1e3);
381
+ const day = d.getUTCDate();
382
+ const mon = UTC_MONTHS[d.getUTCMonth()];
383
+ const y = d.getUTCFullYear();
384
+ const hh = String(d.getUTCHours()).padStart(2, "0");
385
+ const mm = String(d.getUTCMinutes()).padStart(2, "0");
386
+ return `${day} ${mon} ${y}, ${hh}:${mm} UTC`;
387
+ }
388
+ function trueoTimeUntilTradingEndsLabel(endOfTrading, nowSec = Math.floor(Date.now() / 1e3)) {
389
+ if (!Number.isFinite(endOfTrading) || endOfTrading <= 0) return null;
390
+ const sec = Math.floor(endOfTrading) - nowSec;
391
+ if (sec <= 0) return "ended";
392
+ const days = Math.floor(sec / 86400);
393
+ const hours = Math.floor(sec % 86400 / 3600);
394
+ if (days >= 60) {
395
+ const months = Math.max(1, Math.round(days / 30));
396
+ return `in ${months} month${months === 1 ? "" : "s"}`;
397
+ }
398
+ if (days >= 2) return `in ${days} days`;
399
+ if (days === 1) return hours > 0 ? `in 1 day ${hours}h` : "in 1 day";
400
+ if (hours >= 2) return `in ${hours} hours`;
401
+ const mins = Math.max(1, Math.floor(sec / 60));
402
+ return `in ${mins} min`;
403
+ }
404
+
405
+ // src/protocols/evm/trueo/marketScope.ts
406
+ var TRUEO_RECENT_MARKET_WINDOW_SECONDS = 14 * 24 * 60 * 60;
407
+ function trueoFinishedAtSec(row, nowSec) {
408
+ if (row.endOfTrading > 0 && nowSec >= row.endOfTrading) return row.endOfTrading;
409
+ if (row.statusUpdatedAt?.trim()) {
410
+ const ms = Date.parse(row.statusUpdatedAt);
411
+ if (Number.isFinite(ms) && ms <= nowSec * 1e3) return Math.floor(ms / 1e3);
412
+ }
413
+ return null;
414
+ }
415
+ function isTrueoMarketLiveOrRecent(row, nowSec = Math.floor(Date.now() / 1e3), recentWindowSec = TRUEO_RECENT_MARKET_WINDOW_SECONDS) {
416
+ if (row.endOfTrading > 0 && nowSec < row.endOfTrading) return true;
417
+ const status = Number(row.status);
418
+ if (status === 0 && row.endOfTrading <= 0) return true;
419
+ const finishedAt = trueoFinishedAtSec(row, nowSec);
420
+ if (finishedAt == null) return false;
421
+ return nowSec - finishedAt <= recentWindowSec;
422
+ }
423
+ function applyTrueoMarketSearchScope(rows, args) {
424
+ if (args?.includeAll === true) return rows;
425
+ return rows.filter((row) => isTrueoMarketLiveOrRecent(row, args?.nowSec));
426
+ }
427
+ function isTrueoMarketTradingOpen(row, nowSec = Math.floor(Date.now() / 1e3)) {
428
+ const status = Number(row.status);
429
+ if (status !== 0) return false;
430
+ if (row.endOfTrading > 0 && nowSec >= row.endOfTrading) return false;
431
+ return true;
432
+ }
433
+ function preferOpenTrueoTitleMatches(rows, args) {
434
+ if (args?.includeAll === true || rows.length === 0) return rows;
435
+ const open = rows.filter((row) => isTrueoMarketTradingOpen(row, args?.nowSec));
436
+ return open.length > 0 ? open : rows;
437
+ }
438
+ function trueoSearchScopeNote(includeAll) {
439
+ return includeAll ? "Includes closed and resolved markets." : "Live and recently finished only (last 14 days). Set includeAll to include older closed markets.";
440
+ }
441
+
442
+ // src/protocols/evm/trueo/marketApi.ts
443
+ function trueoHeaders() {
444
+ return { Accept: "application/json" };
445
+ }
446
+ async function trueoJsonFetch(path, opts) {
447
+ const url = path.startsWith("http") ? path : `${TRUEO_API_ORIGIN}${path}`;
448
+ const res = await fetch(url, { headers: trueoHeaders(), signal: opts?.signal });
449
+ if (!res.ok) {
450
+ throw new Error(`Trueo API ${res.status} for ${path}`);
451
+ }
452
+ return await res.json();
453
+ }
454
+ function asRecord(value) {
455
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
456
+ }
457
+ function asString(value, fallback = "") {
458
+ return value == null ? fallback : String(value);
459
+ }
460
+ function asNumber(value, fallback = 0) {
461
+ const n = typeof value === "number" ? value : Number(value);
462
+ return Number.isFinite(n) ? n : fallback;
463
+ }
464
+ function asNumberOrNull(value) {
465
+ if (value == null || value === "") return null;
466
+ const n = typeof value === "number" ? value : Number(value);
467
+ return Number.isFinite(n) ? n : null;
468
+ }
469
+ function asStringArray(value) {
470
+ if (!Array.isArray(value)) return [];
471
+ return value.map((v) => String(v)).filter(Boolean);
472
+ }
473
+ function normalizeTrueoMarketRow(raw) {
474
+ const r = asRecord(raw) ?? {};
475
+ const address = asString(r.address).toLowerCase();
476
+ const endOfTrading = asNumber(r.endOfTrading, 0);
477
+ const yesSqrt = r.yesPoolSqrtPriceX96 == null ? null : asString(r.yesPoolSqrtPriceX96);
478
+ const noSqrt = r.noPoolSqrtPriceX96 == null ? null : asString(r.noPoolSqrtPriceX96);
479
+ const { yes: yesPrice, no: noPrice } = trueoSanitizeBinaryPrices(
480
+ trueoImpliedPriceFromSqrtX96(yesSqrt),
481
+ trueoImpliedPriceFromSqrtX96(noSqrt)
482
+ );
483
+ return {
484
+ address,
485
+ question: asString(r.question),
486
+ rules: asString(r.rules),
487
+ source: asString(r.source),
488
+ categories: asStringArray(r.categories),
489
+ size: asString(r.size, "0"),
490
+ tvl: asNumberOrNull(r.tvl),
491
+ yesTokenAddress: asString(r.yesTokenAddress).toLowerCase(),
492
+ noTokenAddress: asString(r.noTokenAddress).toLowerCase(),
493
+ yesPoolAddress: asString(r.yesPoolAddress).toLowerCase(),
494
+ noPoolAddress: asString(r.noPoolAddress).toLowerCase(),
495
+ paymentTokenAddress: asString(r.paymentTokenAddress).toLowerCase(),
496
+ paymentTokenSymbol: asString(r.paymentTokenSymbol, "TYD"),
497
+ paymentTokenDecimals: asNumber(r.paymentTokenDecimals, 6),
498
+ endOfTrading,
499
+ status: asNumber(r.status, 0),
500
+ outcome: asNumber(r.outcome, 0),
501
+ statusUpdatedAt: r.statusUpdatedAt == null || r.statusUpdatedAt === "" ? null : asString(r.statusUpdatedAt),
502
+ imageUrl: r.imageUrl == null ? null : asString(r.imageUrl),
503
+ yesPoolTick: asNumberOrNull(r.yesPoolTick),
504
+ noPoolTick: asNumberOrNull(r.noPoolTick),
505
+ yesPoolSqrtPriceX96: yesSqrt,
506
+ noPoolSqrtPriceX96: noSqrt,
507
+ yesPrice,
508
+ noPrice,
509
+ yesPriceLabel: trueoPercentLabel(yesPrice),
510
+ noPriceLabel: trueoPercentLabel(noPrice),
511
+ expiresAt: trueoExpiresAtIso(endOfTrading),
512
+ expiresAtLabel: trueoExpiresAtLabel(endOfTrading),
513
+ chainId: TRUEO_BASE_CHAIN_ID,
514
+ network: asString(r.network, TRUEO_NETWORK)
515
+ };
516
+ }
517
+ var TRUEO_QUERY_STOPWORDS = /* @__PURE__ */ new Set([
518
+ "the",
519
+ "for",
520
+ "any",
521
+ "all",
522
+ "and",
523
+ "search",
524
+ "find",
525
+ "show",
526
+ "list",
527
+ "market",
528
+ "markets",
529
+ "prediction",
530
+ "live",
531
+ "recent",
532
+ "trending",
533
+ "trueo",
534
+ "topic",
535
+ "about"
536
+ ]);
537
+ function trueoQueryTokens(query) {
538
+ const seen = /* @__PURE__ */ new Set();
539
+ const out = [];
540
+ for (const raw of query.toLowerCase().split(/[^a-z0-9]+/)) {
541
+ const t = raw.trim();
542
+ if (t.length < 3 || TRUEO_QUERY_STOPWORDS.has(t) || seen.has(t)) continue;
543
+ seen.add(t);
544
+ out.push(t);
545
+ }
546
+ return out;
547
+ }
548
+ function filterTrueoMarkets(rows, args) {
549
+ const q = args.query?.trim().toLowerCase() ?? "";
550
+ const tokens = q ? trueoQueryTokens(q) : [];
551
+ const category = args.category?.trim().toLowerCase() ?? "";
552
+ return rows.filter((row) => {
553
+ if (category) {
554
+ const hit = row.categories.some((c) => c.toLowerCase() === category || c.toLowerCase().includes(category));
555
+ if (!hit) return false;
556
+ }
557
+ if (!q) return true;
558
+ const hay = [
559
+ row.question,
560
+ row.rules,
561
+ row.source,
562
+ row.address,
563
+ row.yesTokenAddress,
564
+ row.noTokenAddress,
565
+ ...row.categories
566
+ ].join(" ").toLowerCase();
567
+ if (hay.includes(q)) return true;
568
+ return tokens.some((t) => hay.includes(t));
569
+ });
570
+ }
571
+ async function fetchTrueoCategories(opts) {
572
+ const raw = await trueoJsonFetch(trueoMarketApiPath("categories"), opts);
573
+ if (!Array.isArray(raw)) return [];
574
+ return raw.map((item) => {
575
+ const r = asRecord(item);
576
+ if (!r) return null;
577
+ const name = asString(r.name).trim();
578
+ if (!name) return null;
579
+ return { id: asNumber(r.id, 0), name };
580
+ }).filter((c) => c != null);
581
+ }
582
+ var TRUEO_TRENDING_MAX_LIMIT = 10;
583
+ var TRUEO_MARKET_PAGE_SIZE = 10;
584
+ function trueoSearchFetchLimit(requested, includeAll) {
585
+ const cap = 100;
586
+ if (includeAll) return Math.min(requested, cap);
587
+ return Math.min(Math.max(requested * 4, 40), cap);
588
+ }
589
+ async function fetchTrueoTrending(args) {
590
+ const requested = Math.min(Math.max(args?.limit ?? 20, 1), TRUEO_TRENDING_MAX_LIMIT);
591
+ const includeAll = args?.includeAll === true;
592
+ const raw = await trueoJsonFetch(
593
+ `${trueoMarketApiPath(`${TRUEO_NETWORK}/trending`)}?limit=${requested}`,
594
+ args?.opts
595
+ );
596
+ if (!Array.isArray(raw)) return [];
597
+ const rows = applyTrueoMarketSearchScope(raw.map(normalizeTrueoMarketRow), { includeAll });
598
+ return rows.slice(0, requested);
599
+ }
600
+ var TRUEO_TITLE_SEARCH_PAGE_SIZE = 100;
601
+ var TRUEO_TITLE_SEARCH_MAX_PAGES = 16;
602
+ async function searchTrueoMarketsPage(args) {
603
+ const requested = Math.min(Math.max(args.limit ?? TRUEO_MARKET_PAGE_SIZE, 1), 100);
604
+ const includeAll = args.includeAll === true;
605
+ const titleQuery = Boolean(args.query?.trim());
606
+ const pageSize = titleQuery ? TRUEO_TITLE_SEARCH_PAGE_SIZE : trueoSearchFetchLimit(requested, includeAll);
607
+ const maxPages = titleQuery ? TRUEO_TITLE_SEARCH_MAX_PAGES : 3;
608
+ let offset = Math.max(args.offset ?? 0, 0);
609
+ const exclude = new Set((args.excludeAddresses ?? []).map((a) => a.toLowerCase()));
610
+ const collected = [];
611
+ let apiExhausted = false;
612
+ for (let page = 0; page < maxPages && collected.length < requested; page++) {
613
+ const params = new URLSearchParams();
614
+ params.set("limit", String(pageSize));
615
+ params.set("offset", String(offset));
616
+ if (args.category?.trim()) params.set("category", args.category.trim());
617
+ const raw = await trueoJsonFetch(
618
+ `${trueoMarketApiPath(TRUEO_NETWORK)}?${params.toString()}`,
619
+ args.opts
620
+ );
621
+ const pageRows = Array.isArray(raw) ? raw.map(normalizeTrueoMarketRow) : [];
622
+ const scoped = applyTrueoMarketSearchScope(
623
+ filterTrueoMarkets(pageRows, { query: args.query, category: args.category }),
624
+ { includeAll }
625
+ );
626
+ let filled = false;
627
+ for (const row of scoped) {
628
+ if (exclude.has(row.address)) continue;
629
+ exclude.add(row.address);
630
+ collected.push(row);
631
+ const enough = titleQuery && !includeAll ? collected.filter((r) => isTrueoMarketTradingOpen(r)).length >= requested : collected.length >= requested;
632
+ if (enough) {
633
+ filled = true;
634
+ break;
635
+ }
636
+ }
637
+ if (filled) break;
638
+ offset += pageRows.length;
639
+ if (pageRows.length < pageSize) {
640
+ apiExhausted = true;
641
+ break;
642
+ }
643
+ }
644
+ const ranked = titleQuery ? preferOpenTrueoTitleMatches(collected, { includeAll }) : collected;
645
+ const markets = ranked.slice(0, requested);
646
+ return {
647
+ markets,
648
+ hasMore: !apiExhausted,
649
+ nextOffset: offset
650
+ };
651
+ }
652
+ async function searchTrueoMarkets(args) {
653
+ return (await searchTrueoMarketsPage(args)).markets;
654
+ }
655
+ function trueoListUsesTrending(args) {
656
+ if (args.query?.trim() || args.category?.trim()) return false;
657
+ const offset = Math.max(args.offset ?? 0, 0);
658
+ return args.trending === true || offset === 0 && args.trending !== false;
659
+ }
660
+ async function listTrueoMarketsPage(args) {
661
+ const offset = Math.max(args.offset ?? 0, 0);
662
+ const includeAll = args.includeAll === true;
663
+ const useTrending = trueoListUsesTrending(args);
664
+ if (useTrending) {
665
+ const markets = await fetchTrueoTrending({ limit: TRUEO_TRENDING_MAX_LIMIT, includeAll, opts: args.opts });
666
+ if (includeAll || markets.length >= TRUEO_TRENDING_MAX_LIMIT) {
667
+ return { markets, hasMore: true, nextOffset: 0 };
668
+ }
669
+ const fill = await searchTrueoMarketsPage({
670
+ limit: TRUEO_TRENDING_MAX_LIMIT - markets.length,
671
+ includeAll,
672
+ excludeAddresses: [...args.excludeAddresses ?? [], ...markets.map((m) => m.address)],
673
+ opts: args.opts
674
+ });
675
+ return {
676
+ markets: [...markets, ...fill.markets],
677
+ hasMore: fill.hasMore,
678
+ nextOffset: fill.nextOffset
679
+ };
680
+ }
681
+ return searchTrueoMarketsPage({
682
+ query: args.query,
683
+ category: args.category,
684
+ limit: TRUEO_MARKET_PAGE_SIZE,
685
+ offset,
686
+ includeAll,
687
+ excludeAddresses: args.excludeAddresses,
688
+ opts: args.opts
689
+ });
690
+ }
691
+ async function fetchTrueoMarket(args) {
692
+ const address = getAddress(args.marketAddress).toLowerCase();
693
+ const raw = await trueoJsonFetch(trueoMarketApiPath(`${TRUEO_NETWORK}/${address}`), args.opts);
694
+ return normalizeTrueoMarketRow(raw);
695
+ }
696
+ var TRUEO_CHANCE_SERIES_RESOLUTION = "3600";
697
+ var TRUEO_CHANCE_SERIES_LOOKBACK_SECONDS = 14 * 24 * 60 * 60;
698
+ var TRUEO_OHLC_CALENDAR_ALIASES = /* @__PURE__ */ new Set(["1h", "1d", "1w", "all", "hour", "day", "week"]);
699
+ function normalizeTrueoOhlcResolution(raw) {
700
+ const v = raw?.trim() ?? "";
701
+ if (!v || TRUEO_OHLC_CALENDAR_ALIASES.has(v.toLowerCase())) {
702
+ return TRUEO_CHANCE_SERIES_RESOLUTION;
703
+ }
704
+ return v;
705
+ }
706
+ async function fetchTrueoOhlc(args) {
707
+ const poolId = args.poolId.startsWith("0x") ? args.poolId.toLowerCase() : args.poolId;
708
+ const resolution = normalizeTrueoOhlcResolution(args.resolution);
709
+ const raw = await trueoJsonFetch(
710
+ `${trueoMarketApiPath(`${TRUEO_NETWORK}/${poolId}/ohlc`)}?from=${Math.floor(args.from)}&to=${Math.floor(args.to)}&resolution=${encodeURIComponent(resolution)}`,
711
+ args.opts
712
+ );
713
+ if (!Array.isArray(raw)) return [];
714
+ return raw.map((item) => {
715
+ const r = asRecord(item);
716
+ if (!r) return null;
717
+ return {
718
+ timestamp: asNumber(r.timestamp, 0),
719
+ open: asNumber(r.open, 0),
720
+ high: asNumber(r.high, 0),
721
+ low: asNumber(r.low, 0),
722
+ close: asNumber(r.close, 0)
723
+ };
724
+ }).filter((b) => b != null && b.timestamp > 0);
725
+ }
726
+ async function fetchTrueoTransactions(args) {
727
+ const address = getAddress(args.marketAddress).toLowerCase();
728
+ const limit = Math.min(Math.max(args.limit ?? 20, 1), 100);
729
+ const raw = await trueoJsonFetch(
730
+ `${trueoMarketApiPath("transactions")}?marketAddress=${address}&limit=${limit}`,
731
+ args.opts
732
+ );
733
+ return Array.isArray(raw) ? raw : [];
734
+ }
735
+ async function trueoFetchCategoriesSummary() {
736
+ const categories = await fetchTrueoCategories();
737
+ return { categories, chainId: TRUEO_BASE_CHAIN_ID };
738
+ }
739
+ async function trueoFetchTrendingSummary(input = {}) {
740
+ const includeAll = input.includeAll === true;
741
+ const markets = await fetchTrueoTrending({ limit: input.limit, includeAll });
742
+ return {
743
+ markets,
744
+ hasMore: true,
745
+ nextOffset: 0,
746
+ notes: `Trueo trending on Base (max ${TRUEO_TRENDING_MAX_LIMIT}; API does not page trending). More: ctm_trueo_search_markets with offset=0, skip listed addresses. ${trueoSearchScopeNote(includeAll)}`
747
+ };
748
+ }
749
+ async function trueoSearchMarketsSummary(input) {
750
+ const includeAll = input.includeAll === true;
751
+ const page = await searchTrueoMarketsPage({ ...input, includeAll });
752
+ const q = input.query?.trim() ?? "";
753
+ let notes = `Trueo search on Base (page ${TRUEO_MARKET_PAGE_SIZE}). ${page.hasMore ? `More: same tool with offset=${page.nextOffset}.` : "End of results."} ${trueoSearchScopeNote(includeAll)}`;
754
+ if (q && page.markets.length === 0) {
755
+ notes = `No live or recent title match for ${JSON.stringify(q)} after scanning the catalog (vendor search text is ignored). Do not list trending unless the operator asked.`;
756
+ }
757
+ return {
758
+ markets: page.markets,
759
+ hasMore: page.hasMore,
760
+ nextOffset: page.nextOffset,
761
+ notes
762
+ };
763
+ }
764
+ async function trueoFetchMarketSummary(input) {
765
+ return fetchTrueoMarket({ marketAddress: input.marketAddress });
766
+ }
767
+ async function fetchTrueoChanceSeries(args) {
768
+ const to = Math.floor(Date.now() / 1e3);
769
+ const lookback = Math.max(3600, args.lookbackSeconds ?? TRUEO_CHANCE_SERIES_LOOKBACK_SECONDS);
770
+ const bars = await fetchTrueoOhlc({
771
+ poolId: args.poolId,
772
+ from: to - lookback,
773
+ to,
774
+ resolution: TRUEO_CHANCE_SERIES_RESOLUTION,
775
+ opts: args.opts
776
+ });
777
+ return bars.filter((b) => trueoIsSaneImpliedPrice(b.close));
778
+ }
779
+ async function trueoFetchChanceSeriesSummary(input) {
780
+ const resolution = normalizeTrueoOhlcResolution(input.resolution);
781
+ const to = input.to ?? Math.floor(Date.now() / 1e3);
782
+ const from = input.from ?? to - Math.max(1, input.lookbackDays ?? TRUEO_CHANCE_SERIES_LOOKBACK_SECONDS / 86400) * 86400;
783
+ const bars = await fetchTrueoOhlc({
784
+ poolId: input.poolId,
785
+ from,
786
+ to,
787
+ resolution
788
+ });
789
+ return {
790
+ poolId: input.poolId,
791
+ bars,
792
+ from,
793
+ to,
794
+ resolution,
795
+ notes: "Chance samples (not candlesticks). resolution is seconds; default 3600 (hourly). 1H/1D/1W/All coerce to hourly."
796
+ };
797
+ }
798
+ var TRUEO_BET_CLOSE_ERROR = "Market closes within 5 minutes.";
799
+ var TRUEO_BET_EXPIRED_ERROR = "Market expired. Cannot Get Sig or Execute.";
800
+ function trueoMarketStatusLabel(row, nowSec = Math.floor(Date.now() / 1e3)) {
801
+ const status = Number(row.status);
802
+ if (status === 7) return "resolved";
803
+ if (status >= 4) return "closed";
804
+ if (status >= 1) return "resolving";
805
+ if (row.endOfTrading > 0 && nowSec >= row.endOfTrading) return "closed";
806
+ return "open";
807
+ }
808
+ function trueoTradingEndsCopy(row, nowSec = Math.floor(Date.now() / 1e3)) {
809
+ const when = trueoTradingDeadlineLabel(row.endOfTrading);
810
+ if (!when) {
811
+ return trueoMarketStatusLabel(row, nowSec) === "open" ? "Trading end unknown" : "Trading ended";
812
+ }
813
+ const remain = trueoTimeUntilTradingEndsLabel(row.endOfTrading, nowSec);
814
+ return remain && remain !== "ended" ? `Trading ends ${when} \xB7 ${remain}` : `Trading ended ${when}`;
815
+ }
816
+ function trueoResolvedSide(outcome) {
817
+ if (outcome === 1) return "yes";
818
+ if (outcome === 2) return "no";
819
+ return null;
820
+ }
821
+ function statusUpdatedAtLabel(raw) {
822
+ if (!raw?.trim()) return null;
823
+ const ms = Date.parse(raw);
824
+ if (!Number.isFinite(ms)) return null;
825
+ return trueoTradingDeadlineLabel(Math.floor(ms / 1e3));
826
+ }
827
+ function trueoResolutionCopy(row, nowSec = Math.floor(Date.now() / 1e3)) {
828
+ const status = Number(row.status);
829
+ const when = statusUpdatedAtLabel(row.statusUpdatedAt);
830
+ const side = trueoResolvedSide(row.outcome);
831
+ const sideWord = side === "yes" ? "Yes" : side === "no" ? "No" : null;
832
+ if (status === 7) {
833
+ const who = sideWord ?? "unknown outcome";
834
+ return when ? `Resolved ${who} \xB7 ${when}` : `Resolved ${who}`;
835
+ }
836
+ if (status >= 1 && status < 4) {
837
+ return when ? `This market is being resolved \xB7 since ${when}` : "This market is being resolved";
838
+ }
839
+ if (status >= 4) {
840
+ if (sideWord) return when ? `Closed ${sideWord} \xB7 ${when}` : `Closed ${sideWord}`;
841
+ return when ? `Closed without a Yes/No result \xB7 ${when}` : "Closed without a Yes/No result";
842
+ }
843
+ if (row.endOfTrading > 0 && nowSec >= row.endOfTrading) {
844
+ return "This market is being resolved";
845
+ }
846
+ return null;
847
+ }
848
+ function isTrueoBetAction(action) {
849
+ return TRUEO_BET_ACTIONS.includes(action);
850
+ }
851
+ function trueoMarketVotingOpen(row, nowSec = Math.floor(Date.now() / 1e3)) {
852
+ return trueoBetCreateBlockedReason(row, nowSec) == null;
853
+ }
854
+ function trueoBetCreateBlockedReason(row, nowSec = Math.floor(Date.now() / 1e3)) {
855
+ const label = trueoMarketStatusLabel(row, nowSec);
856
+ if (label !== "open") return TRUEO_BET_CLOSE_ERROR;
857
+ if (row.endOfTrading > 0 && nowSec + TRUEO_BET_CLOSE_BUFFER_SECONDS >= row.endOfTrading) {
858
+ return TRUEO_BET_CLOSE_ERROR;
859
+ }
860
+ return null;
861
+ }
862
+ function trueoBetExecuteBlockedReason(row, nowSec = Math.floor(Date.now() / 1e3)) {
863
+ const label = trueoMarketStatusLabel(row, nowSec);
864
+ if (label === "resolved" || label === "closed") return TRUEO_BET_EXPIRED_ERROR;
865
+ if (row.endOfTrading > 0 && nowSec >= row.endOfTrading) return TRUEO_BET_EXPIRED_ERROR;
866
+ return null;
867
+ }
868
+ function assertTrueoBetCanCreate(row, nowSec = Math.floor(Date.now() / 1e3)) {
869
+ const reason = trueoBetCreateBlockedReason(row, nowSec);
870
+ if (reason) throw new Error(reason);
871
+ }
872
+ function assertTrueoBetCanExecute(row, nowSec = Math.floor(Date.now() / 1e3)) {
873
+ const reason = trueoBetExecuteBlockedReason(row, nowSec);
874
+ if (reason) throw new Error(reason);
875
+ }
876
+ async function assertTrueoBetStillOpen(marketAddress) {
877
+ const row = await fetchTrueoMarket({ marketAddress: getAddress(marketAddress) });
878
+ assertTrueoBetCanExecute(row);
879
+ return row;
880
+ }
881
+ function buildTrueoBetExtraJson(args) {
882
+ return {
883
+ protocolId: "trueo",
884
+ trueoAction: args.action,
885
+ marketAddress: getAddress(args.marketAddress),
886
+ endOfTrading: args.endOfTrading
887
+ };
888
+ }
889
+ function parseTrueoBetExtraJson(raw) {
890
+ let obj = null;
891
+ if (typeof raw === "string" && raw.trim()) {
892
+ try {
893
+ const parsed = JSON.parse(raw);
894
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
895
+ obj = parsed;
896
+ }
897
+ } catch {
898
+ return null;
899
+ }
900
+ } else if (raw && typeof raw === "object" && !Array.isArray(raw)) {
901
+ obj = raw;
902
+ }
903
+ if (!obj) return null;
904
+ if (obj.protocolId !== "trueo") return null;
905
+ const action = String(obj.trueoAction ?? "");
906
+ if (!isTrueoBetAction(action)) return null;
907
+ const marketAddress = String(obj.marketAddress ?? "").trim();
908
+ if (!marketAddress) return null;
909
+ const endOfTrading = Number(obj.endOfTrading ?? 0);
910
+ return {
911
+ protocolId: "trueo",
912
+ trueoAction: action,
913
+ marketAddress,
914
+ endOfTrading: Number.isFinite(endOfTrading) ? endOfTrading : 0
915
+ };
916
+ }
917
+ function extraJsonFromSignRequest(detail) {
918
+ return detail?.ExtraJSON ?? detail?.extraJSON ?? null;
919
+ }
920
+ function defiEvmTransport(rpcUrl, opts) {
921
+ const url = rpcUrl.trim();
922
+ return http(url, opts);
923
+ }
924
+
925
+ // src/protocols/evm/trueo/orderbook.ts
926
+ function publicClient(rpcUrl, chainId) {
927
+ const chain = defineChain({
928
+ id: chainId,
929
+ name: "Base",
930
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
931
+ rpcUrls: { default: { http: [rpcUrl] } }
932
+ });
933
+ return createPublicClient({ chain, transport: defiEvmTransport(rpcUrl) });
934
+ }
935
+ async function readTrueoPoolKeys(args) {
936
+ const client2 = publicClient(args.rpcUrl, TRUEO_BASE_CHAIN_ID);
937
+ try {
938
+ const [yes, no] = await client2.readContract({
939
+ address: getAddress(args.marketAddress),
940
+ abi: trueoMarketV2Abi,
941
+ functionName: "getPoolKeys"
942
+ });
943
+ return {
944
+ yes: {
945
+ currency0: yes.currency0,
946
+ currency1: yes.currency1,
947
+ fee: Number(yes.fee),
948
+ tickSpacing: Number(yes.tickSpacing),
949
+ hooks: yes.hooks
950
+ },
951
+ no: {
952
+ currency0: no.currency0,
953
+ currency1: no.currency1,
954
+ fee: Number(no.fee),
955
+ tickSpacing: Number(no.tickSpacing),
956
+ hooks: no.hooks
957
+ }
958
+ };
959
+ } catch {
960
+ return null;
961
+ }
962
+ }
963
+ async function probePendingOrders(args) {
964
+ const client2 = publicClient(args.rpcUrl, TRUEO_BASE_CHAIN_ID);
965
+ const levels = [];
966
+ const ids = [1, 2, 3, 4, 5, 6, 7, 8];
967
+ const reads = await Promise.all(
968
+ ids.map(async (orderId) => {
969
+ try {
970
+ const order = await client2.readContract({
971
+ address: TRUEO_ORDER_MANAGER,
972
+ abi: trueoOrderManagerAbi,
973
+ functionName: "pendingOrder",
974
+ args: [args.poolId, orderId]
975
+ });
976
+ const [owner, zeroForOne, tickLower, , liquidity] = order;
977
+ if (!owner || owner === "0x0000000000000000000000000000000000000000") return null;
978
+ if (liquidity === 0n) return null;
979
+ return {
980
+ tick: Number(tickLower),
981
+ price: null,
982
+ orderId,
983
+ owner,
984
+ zeroForOne: Boolean(zeroForOne)
985
+ };
986
+ } catch {
987
+ return null;
988
+ }
989
+ })
990
+ );
991
+ for (const row of reads) {
992
+ if (row) levels.push(row);
993
+ }
994
+ return levels;
995
+ }
996
+ function recentTradesFromApi(raw, _side, poolId) {
997
+ return raw.map((item) => {
998
+ if (!item || typeof item !== "object") return null;
999
+ const r = item;
1000
+ const swap = r.v4PoolSwapData && typeof r.v4PoolSwapData === "object" ? r.v4PoolSwapData : null;
1001
+ const tradePool = swap ? String(swap.poolId ?? "").toLowerCase() : "";
1002
+ if (tradePool && tradePool !== poolId.toLowerCase()) return null;
1003
+ return {
1004
+ timestamp: r.timestamp == null ? null : String(r.timestamp),
1005
+ price: swap && typeof swap.price === "number" ? swap.price : null,
1006
+ usdValue: r.usdValue == null ? null : String(r.usdValue),
1007
+ poolId: tradePool || null
1008
+ };
1009
+ }).filter((t) => t != null).slice(0, 12);
1010
+ }
1011
+ async function fetchTrueoOrderbook(args) {
1012
+ const market = await fetchTrueoMarket({ marketAddress: args.marketAddress });
1013
+ const side = args.side === "no" ? "no" : "yes";
1014
+ const poolId = side === "yes" ? market.yesPoolAddress : market.noPoolAddress;
1015
+ const impliedPrice = side === "yes" ? market.yesPrice : market.noPrice;
1016
+ const currentTick = side === "yes" ? market.yesPoolTick : market.noPoolTick;
1017
+ const txs = await fetchTrueoTransactions({ marketAddress: market.address, limit: 20 }).catch(() => []);
1018
+ let levels = [];
1019
+ if (args.rpcUrl && poolId.startsWith("0x") && poolId.length === 66) {
1020
+ levels = await probePendingOrders({ rpcUrl: args.rpcUrl, poolId });
1021
+ }
1022
+ return {
1023
+ marketAddress: market.address,
1024
+ side,
1025
+ poolId,
1026
+ currentTick,
1027
+ impliedPrice,
1028
+ impliedPriceLabel: side === "yes" ? market.yesPriceLabel : market.noPriceLabel,
1029
+ levels,
1030
+ recentTrades: recentTradesFromApi(txs, side, poolId),
1031
+ notes: levels.length ? "On-chain OrderManager pending orders plus recent pool trades." : "Depth from live tick / implied price and recent pool trades. Pending OrderManager rows are included when rpcUrl can read them."
1032
+ };
1033
+ }
1034
+ async function trueoFetchOrderbookSummary(input) {
1035
+ return fetchTrueoOrderbook(input);
1036
+ }
1037
+ function outcomeTokensFromMarkets(markets) {
1038
+ const out = /* @__PURE__ */ new Set();
1039
+ for (const m of markets) {
1040
+ if (m.yesTokenAddress) out.add(m.yesTokenAddress.toLowerCase());
1041
+ if (m.noTokenAddress) out.add(m.noTokenAddress.toLowerCase());
1042
+ }
1043
+ return [...out];
1044
+ }
1045
+ function isTrueoOutcomeTokenLabel(symbol, name) {
1046
+ const s = (symbol ?? "").trim().toUpperCase();
1047
+ if (s === "YES" || s === "NO") return true;
1048
+ const n = (name ?? "").trim().toLowerCase();
1049
+ return n.startsWith("trueo yes") || n.startsWith("trueo no");
1050
+ }
1051
+ function clipQuestion(question) {
1052
+ const t = (question ?? "").trim();
1053
+ if (!t) return "Trueo market";
1054
+ return t.length <= 48 ? t : `${t.slice(0, 45).trim()}\u2026`;
1055
+ }
1056
+ function outcomeToken(address, kind, question) {
1057
+ const label = clipQuestion(question);
1058
+ const side = kind === "no" ? "No" : "Yes";
1059
+ return {
1060
+ address: getAddress(address),
1061
+ name: `Trueo ${side} \u2014 ${label}`,
1062
+ symbol: kind === "no" ? "NO" : "YES",
1063
+ decimals: TRUEO_OUTCOME_TOKEN_DECIMALS,
1064
+ kind
1065
+ };
1066
+ }
1067
+ function suggestTrueoTokensToAdd(args) {
1068
+ const tyd = {
1069
+ address: TRUEO_TYD,
1070
+ name: "True Yield Dollar",
1071
+ symbol: "TYD",
1072
+ decimals: TRUEO_TYD_DECIMALS,
1073
+ kind: "tyd"
1074
+ };
1075
+ const usdc = {
1076
+ address: TRUEO_USDC,
1077
+ name: "USD Coin",
1078
+ symbol: "USDC",
1079
+ decimals: 6,
1080
+ kind: "usdc"
1081
+ };
1082
+ if (args.action === "wrapTyd") return [tyd];
1083
+ if (args.action === "unwrapTyd") return [usdc];
1084
+ if (!args.market) return [];
1085
+ try {
1086
+ const yes = outcomeToken(args.market.yesTokenAddress, "yes", args.market.question);
1087
+ const no = outcomeToken(args.market.noTokenAddress, "no", args.market.question);
1088
+ switch (args.action) {
1089
+ case "swap":
1090
+ case "createOrder":
1091
+ return [args.side === "no" ? no : yes];
1092
+ case "mint":
1093
+ return [yes, no];
1094
+ case "burn":
1095
+ case "redeem":
1096
+ return [tyd];
1097
+ default:
1098
+ return [];
1099
+ }
1100
+ } catch {
1101
+ return [];
1102
+ }
1103
+ }
1104
+
1105
+ // src/protocols/evm/hyperliquid/support.ts
1106
+ var HYPERLIQUID_SUPPORTED_CHAIN_IDS = [999, 998];
1107
+ function isHyperliquidChainSupported(chainId) {
1108
+ return HYPERLIQUID_SUPPORTED_CHAIN_IDS.includes(chainId);
1109
+ }
1110
+ function hyperliquidApiBaseUrl(chainId) {
1111
+ return chainId === 998 ? "https://api.hyperliquid-testnet.xyz" : "https://api.hyperliquid.xyz";
1112
+ }
1113
+
1114
+ // src/protocols/evm/hyperliquidOutcome/model.ts
1115
+ function outcomeEncoding(outcome, side) {
1116
+ const o = Math.trunc(outcome);
1117
+ const s = Math.trunc(side);
1118
+ if (!Number.isFinite(o) || o < 0) throw new Error("outcome must be a non-negative integer");
1119
+ if (s !== 0 && s !== 1) throw new Error("side must be 0 or 1");
1120
+ return 10 * o + s;
1121
+ }
1122
+ function outcomeTradeCoin(encoding) {
1123
+ return `#${encoding}`;
1124
+ }
1125
+ function outcomeBalanceCoin(encoding) {
1126
+ return `+${encoding}`;
1127
+ }
1128
+ function outcomeAssetId(encoding) {
1129
+ return 1e8 + encoding;
1130
+ }
1131
+ function outcomeSideName(name, fallback) {
1132
+ const raw = (name ?? "").trim();
1133
+ const stripped = raw.replace(/^template:/i, "").trim();
1134
+ return stripped || fallback;
1135
+ }
1136
+ function parseHyperliquidPipeFields(description) {
1137
+ const out = {};
1138
+ for (const part of (description ?? "").split("|")) {
1139
+ const i = part.indexOf(":");
1140
+ if (i <= 0) continue;
1141
+ const key = part.slice(0, i).trim();
1142
+ const value = part.slice(i + 1).trim();
1143
+ if (key && value) out[key] = value;
1144
+ }
1145
+ return out;
1146
+ }
1147
+ function parseHyperliquidStamp(raw) {
1148
+ const m = /^(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})$/.exec((raw ?? "").trim());
1149
+ if (!m) return null;
1150
+ const ms = Date.parse(`${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:00Z`);
1151
+ return Number.isFinite(ms) ? Math.floor(ms / 1e3) : null;
1152
+ }
1153
+ function formatHyperliquidStamp(unixSec) {
1154
+ if (unixSec == null || !Number.isFinite(unixSec) || unixSec <= 0) return null;
1155
+ const d = new Date(unixSec * 1e3);
1156
+ if (Number.isNaN(d.getTime())) return null;
1157
+ const pad = (n) => String(n).padStart(2, "0");
1158
+ return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())} UTC`;
1159
+ }
1160
+ function questionForOutcome(outcomeId, questions) {
1161
+ for (const q of questions) {
1162
+ if (q.fallbackOutcome === outcomeId) return q;
1163
+ if ((q.namedOutcomes ?? []).includes(outcomeId)) return q;
1164
+ }
1165
+ return void 0;
1166
+ }
1167
+ function formatOutcomeQuestion(outcome, question) {
1168
+ const fields = parseHyperliquidPipeFields(outcome.description);
1169
+ const qFields = parseHyperliquidPipeFields(question?.description);
1170
+ const when = formatHyperliquidStamp(
1171
+ parseHyperliquidStamp(fields.time) ?? parseHyperliquidStamp(qFields.resolutionDeadline)
1172
+ );
1173
+ const whenSuffix = when ? ` at ${when}` : "";
1174
+ if (outcome.name === "template:binaryPrice") {
1175
+ const asset = fields.priceDescription || fields.perp || "the asset";
1176
+ const threshold = fields.threshold ?? "";
1177
+ return `Will ${asset} be above ${threshold}${whenSuffix}?`.replace(/\s+/g, " ").trim();
1178
+ }
1179
+ if (outcome.name === "template:priceTouch") {
1180
+ const asset = fields.priceDescription || fields.perp || "the asset";
1181
+ const target = fields.target ?? "";
1182
+ return `Will ${asset} touch ${target}${when ? ` by ${when}` : ""}?`.replace(/\s+/g, " ").trim();
1183
+ }
1184
+ if (outcome.name === "template:sportsTournamentParticipant") {
1185
+ const participant = fields.participant || "this participant";
1186
+ const competition = qFields.competition;
1187
+ return competition ? `Will ${participant} win ${competition}?` : `Will ${participant} win?`;
1188
+ }
1189
+ const desc = (outcome.description ?? "").trim();
1190
+ if (desc && desc !== "other") return desc;
1191
+ return outcome.name || `Outcome ${outcome.outcome}`;
1192
+ }
1193
+ var STAMP_FIELDS = /* @__PURE__ */ new Set([
1194
+ "time",
1195
+ "dateTime",
1196
+ "resolutionDeadline",
1197
+ "scheduledStart",
1198
+ "scheduledDecision",
1199
+ "expiry",
1200
+ "decisionDeadline"
1201
+ ]);
1202
+ function outcomeTemplateId(name) {
1203
+ return (name ?? "").replace(/^template:/, "").trim();
1204
+ }
1205
+ function outcomeFieldMap(outcome, question) {
1206
+ return {
1207
+ ...parseHyperliquidPipeFields(question?.description),
1208
+ ...parseHyperliquidPipeFields(outcome.description)
1209
+ };
1210
+ }
1211
+ function displayField(key, raw) {
1212
+ if (STAMP_FIELDS.has(key)) {
1213
+ const formatted = formatHyperliquidStamp(parseHyperliquidStamp(raw));
1214
+ if (formatted) return formatted;
1215
+ }
1216
+ return raw.trim();
1217
+ }
1218
+ function fillOutcomeTemplate(text, fields) {
1219
+ return text.replace(/\{([A-Za-z0-9_]+)\}/g, (token, key) => {
1220
+ const raw = fields[key];
1221
+ if (raw == null || !raw.trim()) return token;
1222
+ return displayField(key, raw);
1223
+ });
1224
+ }
1225
+ function stripTemplateMetadata(text) {
1226
+ return (text.split(/\smetadata=/)[0] ?? text).replace(/\bprice price\b/g, "price").trim();
1227
+ }
1228
+ function templateById(templates, id) {
1229
+ return templates?.find((template) => template.id === id);
1230
+ }
1231
+ function parentTemplateId(template) {
1232
+ const role = template?.role;
1233
+ if (!role || typeof role !== "object") return null;
1234
+ const parent = role.questionOutcome?.parent?.trim();
1235
+ return parent || null;
1236
+ }
1237
+ function resolveOutcomeSideLabel(specName, fields, fallback) {
1238
+ const raw = outcomeSideName(specName, fallback);
1239
+ const filled = fillOutcomeTemplate(raw, fields).trim();
1240
+ return filled || fallback;
1241
+ }
1242
+ function recurringRules(outcome, fields) {
1243
+ const when = fields.expiry ? displayField("expiry", fields.expiry) : "expiry";
1244
+ const asset = fields.underlying || "the asset";
1245
+ if (outcome.name === "Recurring" && fields.class === "priceBinary") {
1246
+ const period = fields.period ? ` Period ${fields.period}.` : "";
1247
+ return `Yes if the ${asset} HyperCore mark at ${when} is at or above ${fields.targetPrice ?? "\u2014"}. Otherwise No. The mark is the linear interpolation of the HyperCore mark updates immediately before and after that time.${period}`;
1248
+ }
1249
+ if (outcome.name === "Recurring Fallback") {
1250
+ return `Yes if none of the named ${asset} price buckets contain the mark at ${when}. Otherwise No. Thresholds: ${fields.priceThresholds || "\u2014"}.`;
1251
+ }
1252
+ if (outcome.name === "Recurring Named Outcome") {
1253
+ return `Yes if bucket ${fields.index ?? "\u2014"} is the ${asset} price range that contains the interpolated HyperCore mark at ${when}. Otherwise No. Thresholds: ${fields.priceThresholds || "\u2014"}.`;
1254
+ }
1255
+ return null;
1256
+ }
1257
+ function fallbackRules(outcome, fields) {
1258
+ const id = outcomeTemplateId(outcome.name);
1259
+ const when = fields.time ? displayField("time", fields.time) : "the settlement time";
1260
+ const feed = fields.priceDescription || fields.perp || "the price";
1261
+ if (id === "binaryPrice") {
1262
+ const twap = fields.seconds ? ` Settlement uses the ${fields.seconds}-second TWAP of ${feed} ending at ${when}.` : "";
1263
+ return `Yes if the ${fields.perp || feed} price is above ${fields.threshold ?? "\u2014"} at ${when}. Otherwise No.${twap}`.replace(
1264
+ /\bprice price\b/g,
1265
+ "price"
1266
+ );
1267
+ }
1268
+ if (id === "priceTouch") {
1269
+ return `Yes if the ${fields.perp || feed} price touches ${fields.target ?? "\u2014"} at or before ${when}. Otherwise No. A touch is the ${fields.seconds || "\u2014"}-second TWAP of ${feed} equaling or crossing the target.`;
1270
+ }
1271
+ if (id === "companyIpoConfirmed") {
1272
+ const at = fields.dateTime ? displayField("dateTime", fields.dateTime) : "the deadline";
1273
+ return `Yes if ${fields.company || "the company"}'s first public listing is confirmed at or before ${at}. Otherwise No.`;
1274
+ }
1275
+ if (id === "sportsTournamentParticipant" || id === "sportsContestParticipant2") {
1276
+ return `Yes if ${fields.participant || "this participant"} is the sole winner. Otherwise No.`;
1277
+ }
1278
+ if (id === "sportsContestDraw2") return "Yes if the contest is a draw. Otherwise No.";
1279
+ if (id === "policyRateNoChange") return "Yes if the announced policy value equals the prior value. Otherwise No.";
1280
+ if (id === "policyRateDecrease") return "Yes if the announced policy value is lower than the prior value. Otherwise No.";
1281
+ if (id === "policyRateIncrease") return "Yes if the announced policy value is higher than the prior value. Otherwise No.";
1282
+ if (id === "sportsContestWinner") {
1283
+ const a = fields.shortNameA || fields.participantA || "Side A";
1284
+ const b = fields.shortNameB || fields.participantB || "Side B";
1285
+ return `${a} wins if ${fields.participantA || a} wins the contest. ${b} wins if ${fields.participantB || b} wins. Counted play: ${fields.countedPlay || "the scheduled contest"}.`;
1286
+ }
1287
+ return "";
1288
+ }
1289
+ function formatOutcomeRules(outcome, question, templates) {
1290
+ const fields = outcomeFieldMap(outcome, question);
1291
+ const recurring = recurringRules(outcome, fields);
1292
+ if (recurring) return recurring;
1293
+ const template = templateById(templates, outcomeTemplateId(outcome.name));
1294
+ const ownRaw = template?.description?.trim();
1295
+ const own = ownRaw ? stripTemplateMetadata(fillOutcomeTemplate(ownRaw, fields)) : fallbackRules(outcome, fields);
1296
+ const parentRaw = templateById(templates, parentTemplateId(template) ?? "")?.description?.trim();
1297
+ const parent = parentRaw ? stripTemplateMetadata(fillOutcomeTemplate(parentRaw, fields)) : "";
1298
+ if (own && parent) return `${own}
1299
+
1300
+ ${parent}`;
1301
+ return own;
1302
+ }
1303
+ function outcomeExpiryUnix(outcome, question) {
1304
+ const fields = parseHyperliquidPipeFields(outcome.description);
1305
+ const qFields = parseHyperliquidPipeFields(question?.description);
1306
+ return parseHyperliquidStamp(fields.time) ?? parseHyperliquidStamp(qFields.resolutionDeadline) ?? 0;
1307
+ }
1308
+
1309
+ // src/protocols/evm/hyperliquidOutcome/api.ts
1310
+ var HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID = 999;
1311
+ var META_TTL_MS = 15e3;
1312
+ var metaCache = /* @__PURE__ */ new Map();
1313
+ function outcomeFetchFailure(error) {
1314
+ if (!(error instanceof Error)) return "request failed";
1315
+ const cause = error.cause;
1316
+ if (cause instanceof Error && cause.message.trim()) return cause.message;
1317
+ return error.message;
1318
+ }
1319
+ function outcomeErrorCode(error) {
1320
+ if (!error || typeof error !== "object") return "";
1321
+ if ("code" in error && error.code != null) return String(error.code);
1322
+ if (error instanceof Error && error.cause && typeof error.cause === "object" && "code" in error.cause) {
1323
+ return String(error.cause.code ?? "");
1324
+ }
1325
+ return "";
1326
+ }
1327
+ var OUTCOME_RETRYABLE_CODES = /* @__PURE__ */ new Set([
1328
+ "UND_ERR_CONNECT_TIMEOUT",
1329
+ "UND_ERR_CONNECT",
1330
+ "ECONNRESET",
1331
+ "ECONNREFUSED",
1332
+ "ETIMEDOUT",
1333
+ "ENOTFOUND",
1334
+ "EAI_AGAIN",
1335
+ "EPIPE"
1336
+ ]);
1337
+ function outcomeFetchRetryable(error) {
1338
+ if (error && typeof error === "object" && "outcomeFinal" in error) return false;
1339
+ const code = outcomeErrorCode(error);
1340
+ return OUTCOME_RETRYABLE_CODES.has(code) || error instanceof Error && (error.message === "fetch failed" || error.message === "connect timeout");
1341
+ }
1342
+ var outcomeHttpsAgent = null;
1343
+ async function outcomeNodeModules() {
1344
+ const importer = new Function("specifier", "return import(specifier)");
1345
+ const [http2, https] = await Promise.all([importer("node:http"), importer("node:https")]);
1346
+ return { http: http2, https };
1347
+ }
1348
+ async function outcomeNodePost(urlString, payload) {
1349
+ const { http: http2, https } = await outcomeNodeModules();
1350
+ if (!outcomeHttpsAgent) {
1351
+ outcomeHttpsAgent = new https.Agent({ keepAlive: true, maxSockets: 4, timeout: 2e4 });
1352
+ }
1353
+ const url = new URL(urlString);
1354
+ const lib = url.protocol === "http:" ? http2 : https;
1355
+ return new Promise((resolve, reject) => {
1356
+ const req = lib.request(
1357
+ {
1358
+ protocol: url.protocol,
1359
+ hostname: url.hostname,
1360
+ port: url.port || void 0,
1361
+ path: `${url.pathname}${url.search}`,
1362
+ method: "POST",
1363
+ headers: {
1364
+ "Content-Type": "application/json",
1365
+ "Content-Length": Buffer.byteLength(payload)
1366
+ },
1367
+ agent: url.protocol === "https:" ? outcomeHttpsAgent ?? void 0 : void 0,
1368
+ timeout: 2e4
1369
+ },
1370
+ (res) => {
1371
+ const chunks = [];
1372
+ res.on("data", (chunk) => chunks.push(chunk));
1373
+ res.on("end", () => {
1374
+ resolve({ status: res.statusCode ?? 0, text: Buffer.concat(chunks).toString("utf8") });
1375
+ });
1376
+ }
1377
+ );
1378
+ req.on("timeout", () => {
1379
+ const error = new Error("connect timeout");
1380
+ error.code = "ETIMEDOUT";
1381
+ req.destroy(error);
1382
+ });
1383
+ req.on("error", reject);
1384
+ req.write(payload);
1385
+ req.end();
1386
+ });
1387
+ }
1388
+ async function outcomeFetchPost(url, payload) {
1389
+ const res = await fetch(url, {
1390
+ method: "POST",
1391
+ headers: { "Content-Type": "application/json" },
1392
+ body: payload,
1393
+ cache: "no-store"
1394
+ });
1395
+ return { status: res.status, text: await res.text() };
1396
+ }
1397
+ async function outcomeInfoPost(chainId, body) {
1398
+ if (!isHyperliquidChainSupported(chainId)) {
1399
+ throw new Error(`Hyperliquid outcome markets require chainId 999 or 998 (got ${chainId}).`);
1400
+ }
1401
+ const url = `${hyperliquidApiBaseUrl(chainId)}/info`;
1402
+ const payload = JSON.stringify(body);
1403
+ let last;
1404
+ for (let attempt = 0; attempt < 3; attempt++) {
1405
+ try {
1406
+ const res = typeof window !== "undefined" || process.env.VITEST ? await outcomeFetchPost(url, payload) : await outcomeNodePost(url, payload);
1407
+ if (res.status < 200 || res.status >= 300) {
1408
+ const error = new Error(`Hyperliquid info API failed (${res.status}): ${res.text.slice(0, 200)}`);
1409
+ if (res.status !== 429 && res.status !== 502 && res.status !== 503 && res.status !== 504) {
1410
+ error.outcomeFinal = true;
1411
+ }
1412
+ throw error;
1413
+ }
1414
+ return JSON.parse(res.text);
1415
+ } catch (error) {
1416
+ last = error;
1417
+ if (!outcomeFetchRetryable(error) || attempt === 2) break;
1418
+ await new Promise((resolve) => setTimeout(resolve, 250 * (attempt + 1)));
1419
+ }
1420
+ }
1421
+ throw new Error(`Hyperliquid info API failed: ${outcomeFetchFailure(last)}`);
1422
+ }
1423
+ function priceNumber(raw) {
1424
+ if (raw == null || raw === "") return null;
1425
+ const n = Number.parseFloat(raw);
1426
+ return Number.isFinite(n) ? n : null;
1427
+ }
1428
+ function priceLabel(n) {
1429
+ if (n == null) return null;
1430
+ if (n >= 0 && n <= 1) return `${(n * 100).toFixed(1)}%`;
1431
+ return String(n);
1432
+ }
1433
+ function marketFromMeta(outcome, question, mids, nowSec, templates) {
1434
+ const fields = outcomeFieldMap(outcome, question);
1435
+ const yesEncoding = outcomeEncoding(outcome.outcome, 0);
1436
+ const noEncoding = outcomeEncoding(outcome.outcome, 1);
1437
+ const yesCoin = outcomeTradeCoin(yesEncoding);
1438
+ const noCoin = outcomeTradeCoin(noEncoding);
1439
+ const yesPrice = priceNumber(mids[yesCoin]);
1440
+ const noPrice = priceNumber(mids[noCoin]);
1441
+ const endOfTrading = outcomeExpiryUnix(outcome, question);
1442
+ const closed = endOfTrading > 0 && nowSec >= endOfTrading;
1443
+ const template = outcome.name.replace(/^template:/, "");
1444
+ const categories = [template, outcome.venue].filter((v) => Boolean(v && v.trim()));
1445
+ return {
1446
+ outcome: outcome.outcome,
1447
+ name: outcome.name,
1448
+ description: outcome.description,
1449
+ question: formatOutcomeQuestion(outcome, question),
1450
+ questionId: question?.question ?? null,
1451
+ sideNames: [
1452
+ resolveOutcomeSideLabel(outcome.sideSpecs?.[0]?.name, fields, "Yes"),
1453
+ resolveOutcomeSideLabel(outcome.sideSpecs?.[1]?.name, fields, "No")
1454
+ ],
1455
+ quoteToken: outcome.quoteToken?.trim() || "USDC",
1456
+ venue: outcome.venue ?? "",
1457
+ categories,
1458
+ yesEncoding,
1459
+ noEncoding,
1460
+ yesCoin,
1461
+ noCoin,
1462
+ yesAssetId: outcomeAssetId(yesEncoding),
1463
+ noAssetId: outcomeAssetId(noEncoding),
1464
+ yesBalanceCoin: outcomeBalanceCoin(yesEncoding),
1465
+ noBalanceCoin: outcomeBalanceCoin(noEncoding),
1466
+ yesPrice,
1467
+ noPrice,
1468
+ yesPriceLabel: priceLabel(yesPrice),
1469
+ noPriceLabel: priceLabel(noPrice),
1470
+ expiresAt: formatHyperliquidStamp(endOfTrading > 0 ? endOfTrading : null),
1471
+ endOfTrading,
1472
+ status: closed ? "closed" : "live",
1473
+ tradable: !closed,
1474
+ rules: formatOutcomeRules(outcome, question, templates)
1475
+ };
1476
+ }
1477
+ async function loadOutcomeSnapshot(chainId) {
1478
+ const hit = metaCache.get(chainId);
1479
+ if (hit && Date.now() - hit.at < META_TTL_MS) {
1480
+ return {
1481
+ outcomes: hit.data.outcomes ?? [],
1482
+ questions: hit.data.questions ?? [],
1483
+ mids: hit.mids,
1484
+ templates: hit.templates
1485
+ };
1486
+ }
1487
+ const meta = await outcomeInfoPost(chainId, { type: "outcomeMeta" });
1488
+ let mids = {};
1489
+ try {
1490
+ mids = await outcomeInfoPost(chainId, { type: "allMids" });
1491
+ } catch {
1492
+ mids = {};
1493
+ }
1494
+ let templates = [];
1495
+ try {
1496
+ const raw = await outcomeInfoPost(chainId, { type: "outcomeTemplates" });
1497
+ templates = Array.isArray(raw) ? raw : [];
1498
+ } catch {
1499
+ templates = [];
1500
+ }
1501
+ metaCache.set(chainId, { at: Date.now(), data: meta, mids, templates });
1502
+ return { outcomes: meta.outcomes ?? [], questions: meta.questions ?? [], mids, templates };
1503
+ }
1504
+ function hyperliquidOutcomeMatchesQuery(market, query) {
1505
+ const q = query.trim().toLowerCase();
1506
+ if (!q) return true;
1507
+ const hay = [market.question, market.description, market.name, market.sideNames.join(" "), String(market.outcome)].join(" ").toLowerCase();
1508
+ return hay.includes(q);
1509
+ }
1510
+ async function listHyperliquidOutcomeMarkets(args) {
1511
+ const chainId = args.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
1512
+ const snap = await loadOutcomeSnapshot(chainId);
1513
+ const nowSec = Math.floor(Date.now() / 1e3);
1514
+ const markets = snap.outcomes.map(
1515
+ (outcome) => marketFromMeta(
1516
+ outcome,
1517
+ questionForOutcome(outcome.outcome, snap.questions),
1518
+ snap.mids,
1519
+ nowSec,
1520
+ snap.templates
1521
+ )
1522
+ ).filter((market) => hyperliquidOutcomeMatchesQuery(market, args.query ?? ""));
1523
+ return { markets, questions: snap.questions };
1524
+ }
1525
+ async function getHyperliquidOutcomeMarket(args) {
1526
+ const { markets } = await listHyperliquidOutcomeMarkets({ chainId: args.chainId });
1527
+ const found = markets.find((m) => m.outcome === args.outcomeId);
1528
+ if (!found) throw new Error(`Hyperliquid outcome ${args.outcomeId} is not in outcomeMeta.`);
1529
+ return found;
1530
+ }
1531
+ function normalizeLevels(rows) {
1532
+ return (rows ?? []).map((row) => ({
1533
+ px: String(row.px ?? ""),
1534
+ sz: String(row.sz ?? ""),
1535
+ n: Number(row.n ?? 0)
1536
+ }));
1537
+ }
1538
+ var CHANCE_LOOKBACK_MS = 14 * 24 * 60 * 60 * 1e3;
1539
+ async function fetchHyperliquidOutcomeChanceSeries(args) {
1540
+ const chainId = args.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
1541
+ const endTime = Date.now();
1542
+ const raw = await outcomeInfoPost(chainId, {
1543
+ type: "candleSnapshot",
1544
+ req: {
1545
+ coin: args.coin,
1546
+ interval: "1h",
1547
+ startTime: endTime - CHANCE_LOOKBACK_MS,
1548
+ endTime
1549
+ }
1550
+ });
1551
+ if (!Array.isArray(raw)) return [];
1552
+ return raw.map((bar) => {
1553
+ const timestamp = Number(bar.t);
1554
+ const open = Number(bar.o);
1555
+ const high = Number(bar.h);
1556
+ const low = Number(bar.l);
1557
+ const close = Number(bar.c);
1558
+ if (![timestamp, open, high, low, close].every(Number.isFinite)) return null;
1559
+ return { timestamp, open, high, low, close };
1560
+ }).filter((bar) => bar != null);
1561
+ }
1562
+ async function fetchHyperliquidOutcomeBook(args) {
1563
+ const chainId = args.chainId ?? HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID;
1564
+ const raw = await outcomeInfoPost(chainId, { type: "l2Book", coin: args.coin });
1565
+ if (raw == null || typeof raw !== "object") {
1566
+ return { coin: args.coin, time: null, bids: [], asks: [] };
1567
+ }
1568
+ return {
1569
+ coin: raw.coin ?? args.coin,
1570
+ time: typeof raw.time === "number" ? raw.time : null,
1571
+ bids: normalizeLevels(raw.levels?.[0]),
1572
+ asks: normalizeLevels(raw.levels?.[1])
1573
+ };
1574
+ }
1575
+
1576
+ // src/protocols/evm/hyperliquidOutcome/mcpSummaries.ts
1577
+ function hyperliquidOutcomeToPredictionFields(market, chainId) {
1578
+ return {
1579
+ venue: "hyperliquid",
1580
+ venueMarketId: String(market.outcome),
1581
+ question: market.question,
1582
+ categories: market.categories,
1583
+ yesPrice: market.yesPrice,
1584
+ noPrice: market.noPrice,
1585
+ yesPriceLabel: market.yesPriceLabel,
1586
+ noPriceLabel: market.noPriceLabel,
1587
+ volume: "\u2014",
1588
+ tvl: null,
1589
+ expiresAt: market.expiresAt,
1590
+ expiresAtLabel: market.expiresAt,
1591
+ tradingEndsLabel: market.expiresAt ? `Settles ${market.expiresAt}` : "Settlement time not listed",
1592
+ endOfTrading: market.endOfTrading,
1593
+ status: market.status,
1594
+ outcome: market.outcome,
1595
+ statusUpdatedAt: null,
1596
+ resolutionLabel: null,
1597
+ chainId,
1598
+ paymentSymbol: market.quoteToken,
1599
+ tradable: market.tradable,
1600
+ rules: market.rules,
1601
+ source: market.name
1602
+ };
1603
+ }
1604
+
1605
+ // src/protocols/evm/trueo/predictionSearch.ts
1606
+ var PREDICTION_MARKET_V1_KIND = "continuum/prediction-market/v1";
1607
+ function trueoMarketToPredictionRow(row) {
1608
+ const status = trueoMarketStatusLabel(row);
1609
+ return {
1610
+ venue: "trueo",
1611
+ venueMarketId: row.address,
1612
+ question: row.question,
1613
+ categories: row.categories,
1614
+ yesPrice: row.yesPrice,
1615
+ noPrice: row.noPrice,
1616
+ yesPriceLabel: row.yesPriceLabel,
1617
+ noPriceLabel: row.noPriceLabel,
1618
+ volume: trueoCompactVolumeLabel(row.size),
1619
+ tvl: row.tvl,
1620
+ expiresAt: row.expiresAt,
1621
+ expiresAtLabel: row.expiresAtLabel,
1622
+ tradingEndsLabel: trueoTradingEndsCopy(row),
1623
+ endOfTrading: row.endOfTrading,
1624
+ status,
1625
+ outcome: row.outcome,
1626
+ statusUpdatedAt: row.statusUpdatedAt,
1627
+ resolutionLabel: trueoResolutionCopy(row),
1628
+ chainId: row.chainId,
1629
+ paymentSymbol: row.paymentTokenSymbol,
1630
+ tradable: trueoMarketVotingOpen(row),
1631
+ rules: row.rules,
1632
+ source: row.source,
1633
+ imageUrl: row.imageUrl
1634
+ };
1635
+ }
1636
+ async function searchPredictionMarkets(args) {
1637
+ const venues = (args.venues ?? ["trueo", "hyperliquid"]).map((v) => v.trim().toLowerCase()).filter(Boolean);
1638
+ const wantTrueo = venues.includes("trueo");
1639
+ const wantHl = venues.includes("hyperliquid");
1640
+ if (!wantTrueo && !wantHl) {
1641
+ return {
1642
+ markets: [],
1643
+ notes: "No supported venues in the filter. Use trueo and/or hyperliquid.",
1644
+ hasMore: false,
1645
+ includeAll: false,
1646
+ nextOffset: 0,
1647
+ nextIndexBase: 0
1648
+ };
1649
+ }
1650
+ const includeAll = args.includeAll === true;
1651
+ const offset = Math.max(args.offset ?? 0, 0);
1652
+ const indexBase = Math.max(args.indexBase ?? 0, 0);
1653
+ const limit = args.limit ?? TRUEO_MARKET_PAGE_SIZE;
1654
+ const exclude = new Set((args.excludeAddresses ?? []).map((id) => id.trim().toLowerCase()).filter(Boolean));
1655
+ let hlAll = [];
1656
+ let hlError = "";
1657
+ if (wantHl) {
1658
+ try {
1659
+ const listed = await listHyperliquidOutcomeMarkets({ query: args.query });
1660
+ hlAll = listed.markets.map((market) => hyperliquidOutcomeToPredictionFields(market, HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID)).filter((row) => !exclude.has(row.venueMarketId.toLowerCase()));
1661
+ } catch (error) {
1662
+ hlError = error instanceof Error ? error.message : "Hyperliquid outcome search failed.";
1663
+ }
1664
+ }
1665
+ if (!wantTrueo) {
1666
+ const page2 = hlAll.slice(offset, offset + limit).map((row, i) => ({ ...row, index: indexBase + i + 1 }));
1667
+ const nextOffset = offset + page2.length;
1668
+ const nextIndexBase2 = indexBase + page2.length;
1669
+ const more2 = nextOffset < hlAll.length;
1670
+ return {
1671
+ markets: page2,
1672
+ hasMore: more2,
1673
+ nextOffset,
1674
+ nextIndexBase: nextIndexBase2,
1675
+ includeAll: false,
1676
+ notes: hlError ? hlError : page2.length ? `Hyperliquid outcome markets. ${more2 ? `More: offset=${nextOffset}, indexBase=${nextIndexBase2}.` : "End of results."} Pick #N then get_prediction_market with venue hyperliquid.` : `No Hyperliquid outcome title match for ${JSON.stringify(args.query?.trim() ?? "")}.`
1677
+ };
1678
+ }
1679
+ const hlPrefix = offset === 0 ? hlAll.slice(0, limit) : [];
1680
+ const page = await listTrueoMarketsPage({
1681
+ query: args.query,
1682
+ category: args.category,
1683
+ offset,
1684
+ includeAll,
1685
+ trending: args.trending,
1686
+ excludeAddresses: args.excludeAddresses
1687
+ });
1688
+ const trueoRows = page.markets.map(trueoMarketToPredictionRow).filter((row) => !exclude.has(row.venueMarketId.toLowerCase())).slice(0, limit);
1689
+ const markets = [...hlPrefix, ...trueoRows].map((row, i) => ({ ...row, index: indexBase + i + 1 }));
1690
+ const nextIndexBase = indexBase + markets.length;
1691
+ const scope = trueoSearchScopeNote(includeAll);
1692
+ const q = args.query?.trim() ?? "";
1693
+ const hlNote = hlPrefix.length > 0 ? `Included ${hlPrefix.length} Hyperliquid outcome match(es) on this page (venue hyperliquid).` : hlError;
1694
+ if (q && markets.length === 0) {
1695
+ return {
1696
+ markets,
1697
+ hasMore: page.hasMore,
1698
+ nextOffset: page.nextOffset,
1699
+ nextIndexBase,
1700
+ includeAll,
1701
+ notes: `No live or recent title match for ${JSON.stringify(q)} after scanning Trueo${wantHl ? " and Hyperliquid" : ""}. Do not list trending unless the operator asked. ${scope} ${hlError}`.trim()
1702
+ };
1703
+ }
1704
+ const more = page.hasMore ? `More Trueo: search_prediction_markets with offset=${page.nextOffset}, trending=false, indexBase=${nextIndexBase}, excludeAddresses from this page's venueMarketIds.` : "End of Trueo results.";
1705
+ return {
1706
+ markets,
1707
+ hasMore: page.hasMore,
1708
+ nextOffset: page.nextOffset,
1709
+ nextIndexBase,
1710
+ includeAll,
1711
+ notes: `${hlNote} ${scope} ${more} Pick #N then get_prediction_market.`.replace(/\s+/g, " ").trim()
1712
+ };
1713
+ }
1714
+ function buildPredictionMarketEnvelope(market) {
1715
+ return { kind: PREDICTION_MARKET_V1_KIND, market };
1716
+ }
1717
+ async function searchPredictionMarketsSummary(input) {
1718
+ return searchPredictionMarkets(input);
1719
+ }
1720
+ function trueoHoldingTokenAddressesFromRegistry(tokens) {
1721
+ const out = [];
1722
+ const seen = /* @__PURE__ */ new Set();
1723
+ for (const t of tokens) {
1724
+ const addr = String(t.contractAddress ?? "").trim().toLowerCase();
1725
+ if (!addr || seen.has(addr)) continue;
1726
+ if (!isTrueoOutcomeTokenLabel(t.symbol, t.name)) continue;
1727
+ seen.add(addr);
1728
+ out.push(addr);
1729
+ }
1730
+ return out;
1731
+ }
1732
+ async function listPredictionMarketsForHoldings(args) {
1733
+ const marketIds = new Set(
1734
+ (args.marketAddresses ?? []).map((a) => a.trim().toLowerCase()).filter((a) => a.startsWith("0x"))
1735
+ );
1736
+ const tokens = (args.tokenAddresses ?? []).map((a) => a.trim().toLowerCase()).filter((a) => a.startsWith("0x"));
1737
+ for (const token of tokens) {
1738
+ try {
1739
+ const page = await searchTrueoMarketsPage({
1740
+ query: token,
1741
+ includeAll: true,
1742
+ limit: TRUEO_MARKET_PAGE_SIZE
1743
+ });
1744
+ for (const row of page.markets) {
1745
+ if (row.yesTokenAddress === token || row.noTokenAddress === token || row.address === token) {
1746
+ marketIds.add(row.address);
1747
+ }
1748
+ }
1749
+ } catch {
1750
+ }
1751
+ }
1752
+ const markets = [];
1753
+ let i = 0;
1754
+ for (const id of marketIds) {
1755
+ try {
1756
+ const row = await fetchTrueoMarket({ marketAddress: id });
1757
+ markets.push({ ...trueoMarketToPredictionRow(row), index: ++i });
1758
+ } catch {
1759
+ }
1760
+ }
1761
+ const notes = markets.length === 0 ? "No matching holdings. Pass YES/NO token addresses from get_token_registry (or add_to_token_registry after a bet) and/or marketAddresses from prior bets." : `Live status for ${markets.length} market(s) you hold or named. Pick #N then get_prediction_market.`;
1762
+ return {
1763
+ markets,
1764
+ notes,
1765
+ hasMore: false,
1766
+ nextOffset: 0,
1767
+ nextIndexBase: markets.length,
1768
+ includeAll: true
1769
+ };
1770
+ }
1771
+ async function listMyPredictionMarketsSummary(input) {
1772
+ return listPredictionMarketsForHoldings(input);
1773
+ }
1774
+ async function getPredictionMarketSummary(input) {
1775
+ const venue = (input.venue ?? "trueo").trim().toLowerCase();
1776
+ if (venue === "hyperliquid") {
1777
+ const outcomeId = Number.parseInt(input.venueMarketId, 10);
1778
+ if (!Number.isFinite(outcomeId)) {
1779
+ throw new Error("Hyperliquid venueMarketId must be the outcome id.");
1780
+ }
1781
+ const market2 = await getHyperliquidOutcomeMarket({ outcomeId });
1782
+ const [yesBook, noBook] = await Promise.all([
1783
+ fetchHyperliquidOutcomeBook({ coin: market2.yesCoin }),
1784
+ fetchHyperliquidOutcomeBook({ coin: market2.noCoin })
1785
+ ]);
1786
+ let ohlc2 = [];
1787
+ try {
1788
+ ohlc2 = await fetchHyperliquidOutcomeChanceSeries({ coin: market2.yesCoin });
1789
+ } catch {
1790
+ ohlc2 = [];
1791
+ }
1792
+ const row2 = {
1793
+ ...hyperliquidOutcomeToPredictionFields(market2, HYPERLIQUID_OUTCOME_MAINNET_CHAIN_ID),
1794
+ yesBook,
1795
+ noBook,
1796
+ ohlc: ohlc2
1797
+ };
1798
+ return {
1799
+ kind: PREDICTION_MARKET_V1_KIND,
1800
+ ...row2,
1801
+ envelope: buildPredictionMarketEnvelope(row2)
1802
+ };
1803
+ }
1804
+ if (venue !== "trueo") {
1805
+ throw new Error(`Venue "${venue}" is not supported. Use trueo or hyperliquid.`);
1806
+ }
1807
+ const row = await fetchTrueoMarket({ marketAddress: input.venueMarketId });
1808
+ let ohlc = [];
1809
+ let orderbook = null;
1810
+ try {
1811
+ ohlc = await fetchTrueoChanceSeries({
1812
+ poolId: row.yesPoolAddress
1813
+ });
1814
+ } catch {
1815
+ ohlc = [];
1816
+ }
1817
+ try {
1818
+ orderbook = await fetchTrueoOrderbook({ marketAddress: row.address, side: "yes" });
1819
+ } catch {
1820
+ orderbook = null;
1821
+ }
1822
+ const market = {
1823
+ ...trueoMarketToPredictionRow(row),
1824
+ rules: row.rules,
1825
+ source: row.source,
1826
+ yesTokenAddress: row.yesTokenAddress,
1827
+ noTokenAddress: row.noTokenAddress,
1828
+ yesPoolAddress: row.yesPoolAddress,
1829
+ noPoolAddress: row.noPoolAddress,
1830
+ paymentTokenAddress: row.paymentTokenAddress,
1831
+ endOfTrading: row.endOfTrading,
1832
+ ohlc,
1833
+ orderbook
1834
+ };
1835
+ return {
1836
+ kind: PREDICTION_MARKET_V1_KIND,
1837
+ ...market,
1838
+ envelope: buildPredictionMarketEnvelope(market)
1839
+ };
1840
+ }
1841
+
1842
+ // src/protocols/evm/trueo/purpose.ts
1843
+ function buildTrueoPurposePrefill(args) {
1844
+ const chainId = args.chainId ?? TRUEO_BASE_CHAIN_ID;
1845
+ const q = args.question?.trim() ? ` on ${args.question.trim()}` : "";
1846
+ const amt = args.amountHuman?.trim() ?? "";
1847
+ const sym = args.paymentSymbol?.trim() || "TYD";
1848
+ const side = args.side === "no" ? "No" : "Yes";
1849
+ switch (args.action) {
1850
+ case "wrapTyd":
1851
+ return `Trueo: mint ${amt || ""} TYD from USDC on Base (chainId ${chainId})`.replace(/\s+/g, " ").trim();
1852
+ case "unwrapTyd":
1853
+ return `Trueo: unwrap ${amt || ""} TYD to USDC on Base (chainId ${chainId})`.replace(/\s+/g, " ").trim();
1854
+ case "mint":
1855
+ return `Trueo: mint ${amt || "YES+NO"} ${sym}${q} on Base (chainId ${chainId})`;
1856
+ case "burn":
1857
+ return `Trueo: burn ${amt || "YES+NO"}${q} on Base (chainId ${chainId})`;
1858
+ case "redeem":
1859
+ return `Trueo: redeem ${amt || "winning tokens"}${q} on Base (chainId ${chainId})`;
1860
+ case "swap":
1861
+ return `Trueo: buy ${side} ${amt || ""} ${sym}${q} on Base (chainId ${chainId})`.replace(/\s+/g, " ").trim();
1862
+ case "createOrder":
1863
+ return `Trueo: limit ${side} ${amt || ""} ${sym}${q} on Base (chainId ${chainId})`.replace(/\s+/g, " ").trim();
1864
+ case "cancelOrder":
1865
+ return `Trueo: cancel order${q} on Base (chainId ${chainId})`;
1866
+ default:
1867
+ return `Trueo${q} on Base (chainId ${chainId})`;
1868
+ }
1869
+ }
1870
+
1871
+ // src/core/expiry.ts
1872
+ var DEFAULT_DEFI_MULTISIGN_EXPIRY_SECONDS = 30 * 60;
1873
+ var DEFI_PROTOCOLS_WITH_30MIN_EXPIRY = /* @__PURE__ */ new Set([
1874
+ "uniswap-v4",
1875
+ "gmx",
1876
+ "hyperliquid",
1877
+ "arcus",
1878
+ "curve-dao",
1879
+ "aerodrome",
1880
+ "pendle",
1881
+ "derive",
1882
+ "trueo",
1883
+ "hyperliquidOutcome"
1884
+ ]);
1885
+ function defiMultisignExpiryUnixSeconds(protocolId, explicitExpiryDate) {
1886
+ if (typeof explicitExpiryDate === "number" && Number.isFinite(explicitExpiryDate) && explicitExpiryDate > 0) {
1887
+ return Math.floor(explicitExpiryDate);
1888
+ }
1889
+ if (DEFI_PROTOCOLS_WITH_30MIN_EXPIRY.has(protocolId)) {
1890
+ return Math.floor(Date.now() / 1e3) + DEFAULT_DEFI_MULTISIGN_EXPIRY_SECONDS;
1891
+ }
1892
+ return void 0;
1893
+ }
1894
+
1895
+ // src/core/purpose.ts
1896
+ function mergePurposeText(purposeText, purposeSuffix) {
1897
+ const t = (purposeText ?? "").trim();
1898
+ const suffix = (purposeSuffix ?? "").trim();
1899
+ if (!suffix) return t;
1900
+ return t ? `${t}
1901
+
1902
+ ${suffix}` : suffix;
1903
+ }
1904
+
1905
+ // src/core/envelope.ts
1906
+ function finalizeMultisign(input) {
1907
+ const { keyGen, destinationChainID, legs } = input;
1908
+ if (legs.length === 0) {
1909
+ throw new Error("finalizeMultisign requires at least one leg");
1910
+ }
1911
+ const ph = (keyGen.pubkeyhex ?? "").trim();
1912
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
1913
+ const keyList = keyGen.keylist ?? [];
1914
+ const clientId = getClientIdFromKeyGenResult(keyGen);
1915
+ const first = legs[0];
1916
+ const messageHashes = legs.map((l) => l.msgHash);
1917
+ const messageRawBatch = legs.map((l) => l.msgRaw);
1918
+ const batchMeta = legs.map((l) => ({
1919
+ destinationAddress: l.destinationAddress,
1920
+ signatureText: l.signatureText,
1921
+ ...l.audit
1922
+ }));
1923
+ const proposalTxParams = legs.map((l) => l.proposalTxParams).filter((p) => p != null && typeof p === "object");
1924
+ const extraPayload = {
1925
+ batchMeta,
1926
+ ...input.extraJSON ?? {}
1927
+ };
1928
+ const extraJSON = JSON.stringify(extraPayload, (_, v) => typeof v === "bigint" ? v.toString() : v);
1929
+ const bodyForSign = {
1930
+ keyList,
1931
+ pubKey: ph,
1932
+ msgHash: messageHashes[0],
1933
+ msgRaw: first.msgRaw,
1934
+ destinationChainID,
1935
+ destinationAddress: input.destinationAddress ?? first.destinationAddress,
1936
+ extraJSON,
1937
+ signatureText: first.signatureText,
1938
+ purpose: mergePurposeText(input.purposeText, input.purposeSuffix),
1939
+ ...first.feeSnapshot
1940
+ };
1941
+ if (legs.length > 1) {
1942
+ bodyForSign.messageHashes = messageHashes;
1943
+ bodyForSign.messageRawBatch = messageRawBatch;
1944
+ }
1945
+ if (proposalTxParams.length > 0) {
1946
+ bodyForSign.proposalTxParams = proposalTxParams;
1947
+ }
1948
+ const valueWei = first.valueWei;
1949
+ if (valueWei != null && valueWei > 0n) {
1950
+ bodyForSign.value = valueWei.toString();
1951
+ }
1952
+ if (clientId) bodyForSign.clientId = clientId;
1953
+ if (input.expiryDate != null && input.expiryDate > 0) {
1954
+ bodyForSign.expiryDate = Math.floor(input.expiryDate);
1955
+ }
1956
+ return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
1957
+ }
1958
+ function routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimit) {
1959
+ if (chainGasLimit != null && Number.isFinite(chainGasLimit) && chainGasLimit > 0) {
1960
+ return gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit);
1961
+ }
1962
+ return (estimatedGas * 12n + 9n) / 10n;
1963
+ }
1964
+
1965
+ // src/chains/evm/buildBatch.ts
1966
+ async function buildEvmMultisignBatch(args) {
1967
+ const { context, steps } = args;
1968
+ const {
1969
+ chainId,
1970
+ rpcUrl,
1971
+ executorAddress,
1972
+ chainDetail,
1973
+ useCustomGas,
1974
+ customGasChainDetails,
1975
+ keyGen,
1976
+ purposeText
1977
+ } = context;
1978
+ if (steps.length === 0) throw new Error("buildEvmMultisignBatch requires at least one step");
1979
+ const ch = defineChain({
1980
+ id: chainId,
1981
+ name: "Destination",
1982
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
1983
+ rpcUrls: { default: { http: [rpcUrl] } }
1984
+ });
1985
+ const publicClient2 = createPublicClient({ chain: ch, transport: defiEvmTransport(rpcUrl) });
1986
+ const feeParams = await fetchChainFeeParams(rpcUrl, chainId);
1987
+ const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
1988
+ const latestBaseFeeWei = !legacy ? (await publicClient2.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
1989
+ const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
1990
+ const chainGasLimitRouter = chainDetail?.gasLimit != null && Number.isFinite(Number(chainDetail.gasLimit)) && Number(chainDetail.gasLimit) > 0 ? Number(chainDetail.gasLimit) : void 0;
1991
+ const gasFeeMultiplier = useCustomGas && chainDetail?.gasMultiplier != null ? Number(chainDetail.gasMultiplier) : void 0;
1992
+ const executor = getAddress(executorAddress);
1993
+ const baseNonce = await publicClient2.getTransactionCount({ address: executor, blockTag: "pending" });
1994
+ const legs = [];
1995
+ for (let i = 0; i < steps.length; i++) {
1996
+ const step = steps[i];
1997
+ const currentNonce = baseNonce + i;
1998
+ let estimatedGas;
1999
+ if (args.estimateGasForStep) {
2000
+ estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient: publicClient2, executor });
2001
+ } else {
2002
+ try {
2003
+ estimatedGas = await publicClient2.estimateGas({
2004
+ to: step.to,
2005
+ data: step.data,
2006
+ value: step.value,
2007
+ account: executor
2008
+ });
2009
+ } catch {
2010
+ estimatedGas = step.fallbackGas ?? 100000n;
2011
+ }
2012
+ }
2013
+ let gasLimitI;
2014
+ if (args.resolveGasLimit) {
2015
+ gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient: publicClient2 });
2016
+ } else if (step.routerSwap) {
2017
+ gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
2018
+ } else {
2019
+ gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
2020
+ }
2021
+ let proposalTxParams;
2022
+ let feeSnapshot;
2023
+ let serialized;
2024
+ if (legacy) {
2025
+ let gasPriceWei = await publicClient2.getGasPrice();
2026
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
2027
+ gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
2028
+ }
2029
+ if (useCustomGas && chainDetail?.gasPrice != null && chainDetail.gasPrice > 0) {
2030
+ const configured = parseGwei(gweiToDecimalString(Number(chainDetail.gasPrice)));
2031
+ if (configured > gasPriceWei) gasPriceWei = configured;
2032
+ }
2033
+ serialized = serializeTransaction({
2034
+ type: "legacy",
2035
+ to: step.to,
2036
+ data: step.data,
2037
+ value: step.value,
2038
+ gas: gasLimitI,
2039
+ gasPrice: gasPriceWei,
2040
+ nonce: currentNonce,
2041
+ chainId
2042
+ });
2043
+ proposalTxParams = {
2044
+ nonce: currentNonce,
2045
+ gasLimit: gasLimitI.toString(),
2046
+ txType: "legacy",
2047
+ gasPrice: gasPriceWei.toString()
2048
+ };
2049
+ feeSnapshot = proposalTxParamsToFeeSnapshot(proposalTxParams);
2050
+ } else {
2051
+ const fetchedBase = feeParams.baseFeeGwei ?? 0;
2052
+ const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
2053
+ const configuredBase = useCustomGas && chainDetail?.baseFee != null ? Number(chainDetail.baseFee) : 0;
2054
+ const configuredPriority = useCustomGas && chainDetail?.priorityFee != null ? Number(chainDetail.priorityFee) : 0;
2055
+ const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
2056
+ const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
2057
+ const baseFeeMultiplierPct = useCustomGas && chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(chainDetail.baseFeeMultiplier)) : 100;
2058
+ const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
2059
+ const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
2060
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : parseGwei("1");
2061
+ let maxFeePerGas = parseGwei(gweiToDecimalString(maxFeePerGasGwei));
2062
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
2063
+ maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
2064
+ maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
2065
+ }
2066
+ ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
2067
+ maxFeePerGas,
2068
+ maxPriorityFeePerGas,
2069
+ latestBaseFeeWei
2070
+ ));
2071
+ serialized = serializeTransaction({
2072
+ type: "eip1559",
2073
+ to: step.to,
2074
+ data: step.data,
2075
+ value: step.value,
2076
+ gas: gasLimitI,
2077
+ maxFeePerGas,
2078
+ maxPriorityFeePerGas,
2079
+ nonce: currentNonce,
2080
+ chainId
2081
+ });
2082
+ proposalTxParams = {
2083
+ nonce: currentNonce,
2084
+ gasLimit: gasLimitI.toString(),
2085
+ txType: "eip1559",
2086
+ maxFeePerGas: maxFeePerGas.toString(),
2087
+ maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
2088
+ };
2089
+ feeSnapshot = i === 0 ? proposalTxParamsToFeeSnapshot(proposalTxParams) : {};
2090
+ }
2091
+ const h = keccak256(serialized);
2092
+ const msgHash = h.startsWith("0x") ? h.slice(2) : h;
2093
+ const batchMetaExtra = args.buildBatchMeta({ step, index: i, gasLimit: gasLimitI });
2094
+ legs.push({
2095
+ msgHash,
2096
+ msgRaw: i === 0 && args.firstMsgRawNo0x != null ? args.firstMsgRawNo0x : serialized,
2097
+ destinationAddress: step.to,
2098
+ signatureText: typeof batchMetaExtra.signatureText === "string" ? batchMetaExtra.signatureText : JSON.stringify(batchMetaExtra.signatureText ?? {}),
2099
+ audit: batchMetaExtra,
2100
+ feeSnapshot: i === 0 ? feeSnapshot : {},
2101
+ proposalTxParams,
2102
+ valueWei: i === 0 ? step.value : void 0
2103
+ });
2104
+ if (i === 0 && args.firstMsgRawNo0x != null) {
2105
+ legs[0].msgRaw = args.firstMsgRawNo0x;
2106
+ }
2107
+ }
2108
+ const extraJSON = {};
2109
+ if (useCustomGas && customGasChainDetails && Object.keys(customGasChainDetails).length > 0) {
2110
+ extraJSON.customGasChainDetails = customGasChainDetails;
2111
+ }
2112
+ const result = finalizeMultisign({
2113
+ keyGen,
2114
+ purposeText,
2115
+ purposeSuffix: args.purposeSuffix,
2116
+ destinationChainID: String(chainId),
2117
+ destinationAddress: args.destinationAddress ?? steps[0].to,
2118
+ legs,
2119
+ extraJSON: Object.keys(extraJSON).length > 0 ? extraJSON : void 0,
2120
+ expiryDate: context.expiryDate
2121
+ });
2122
+ const pv = args.payableValueWei;
2123
+ if (pv != null && pv > 0n) {
2124
+ result.bodyForSign.value = pv.toString();
2125
+ }
2126
+ return result;
2127
+ }
2128
+
2129
+ // src/protocols/evm/trueo/multisign.ts
2130
+ var TRUEO_APPROVE_FALLBACK_GAS = 80000n;
2131
+ var TRUEO_MINT_FALLBACK_GAS = 450000n;
2132
+ var TRUEO_TYD_VAULT_FALLBACK_GAS = 350000n;
2133
+ var TRUEO_SWAP_FALLBACK_GAS = 650000n;
2134
+ var TRUEO_ORDER_FALLBACK_GAS = 520000n;
2135
+ function client(rpcUrl) {
2136
+ const chain = defineChain({
2137
+ id: TRUEO_BASE_CHAIN_ID,
2138
+ name: "Base",
2139
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
2140
+ rpcUrls: { default: { http: [rpcUrl] } }
2141
+ });
2142
+ return createPublicClient({ chain, transport: defiEvmTransport(rpcUrl) });
2143
+ }
2144
+ async function allowance(args) {
2145
+ return client(args.rpcUrl).readContract({
2146
+ address: args.token,
2147
+ abi: trueoErc20Abi,
2148
+ functionName: "allowance",
2149
+ args: [args.owner, args.spender]
2150
+ });
2151
+ }
2152
+ async function pushApproveIfNeeded(args) {
2153
+ const current = await allowance({
2154
+ rpcUrl: args.rpcUrl,
2155
+ token: args.token,
2156
+ owner: args.owner,
2157
+ spender: args.spender
2158
+ });
2159
+ if (current >= args.amount) return;
2160
+ if (current > 0n) {
2161
+ args.steps.push({
2162
+ kind: "approve_zero",
2163
+ to: args.token,
2164
+ data: encodeErc20Approve(args.spender, 0n),
2165
+ value: 0n,
2166
+ fallbackGas: TRUEO_APPROVE_FALLBACK_GAS
2167
+ });
2168
+ }
2169
+ args.steps.push({
2170
+ kind: "approve",
2171
+ to: args.token,
2172
+ data: encodeErc20Approve(args.spender, args.amount),
2173
+ value: 0n,
2174
+ fallbackGas: TRUEO_APPROVE_FALLBACK_GAS
2175
+ });
2176
+ }
2177
+ function mergeExtraJson(result, extra) {
2178
+ const body = result.bodyForSign;
2179
+ const existing = typeof body.extraJSON === "string" ? JSON.parse(body.extraJSON) : body.extraJSON ?? {};
2180
+ body.extraJSON = JSON.stringify({ ...existing, ...extra });
2181
+ }
2182
+ async function finalizeTrueo(args) {
2183
+ const expiryDate = defiMultisignExpiryUnixSeconds(TRUEO_PROTOCOL_ID, args.expiryDate);
2184
+ const first = args.steps[0];
2185
+ if (!first) throw new Error("Trueo: no transaction steps.");
2186
+ const firstDataNo0x = first.data.startsWith("0x") ? first.data.slice(2) : first.data;
2187
+ const result = await buildEvmMultisignBatch({
2188
+ context: {
2189
+ chainCategory: "evm",
2190
+ keyGen: args.keyGen,
2191
+ purposeText: args.purposeText,
2192
+ chainId: TRUEO_BASE_CHAIN_ID,
2193
+ rpcUrl: args.rpcUrl,
2194
+ executorAddress: args.executorAddress,
2195
+ chainDetail: args.chainDetail,
2196
+ useCustomGas: args.useCustomGas,
2197
+ customGasChainDetails: args.customGasChainDetails,
2198
+ expiryDate
2199
+ },
2200
+ steps: args.steps,
2201
+ purposeSuffix: args.purposeSuffix,
2202
+ firstMsgRawNo0x: firstDataNo0x,
2203
+ destinationAddress: first.to,
2204
+ buildBatchMeta: ({ step, index }) => {
2205
+ const s = step;
2206
+ return {
2207
+ signatureText: JSON.stringify({
2208
+ kind: "Trueo",
2209
+ name: s.kind,
2210
+ index,
2211
+ chainId: TRUEO_BASE_CHAIN_ID
2212
+ }),
2213
+ evm: { type: `trueo_${s.kind}`, version: 1, chainId: String(TRUEO_BASE_CHAIN_ID) }
2214
+ };
2215
+ }
2216
+ });
2217
+ if (args.betExtra) mergeExtraJson(result, args.betExtra);
2218
+ return result;
2219
+ }
2220
+ function parseAmount(human, decimals) {
2221
+ const amt = parseUnits(String(human).trim(), decimals);
2222
+ if (amt <= 0n) throw new Error("Amount must be greater than zero.");
2223
+ return amt;
2224
+ }
2225
+ async function assertTrueoTydVaultUnderlying(rpcUrl) {
2226
+ const underlying = await client(rpcUrl).readContract({
2227
+ address: TRUEO_TYD,
2228
+ abi: trueoTydVaultAbi,
2229
+ functionName: "asset"
2230
+ });
2231
+ if (getAddress(underlying) !== TRUEO_USDC) {
2232
+ throw new Error("TYD vault underlying is not Base USDC.");
2233
+ }
2234
+ }
2235
+ async function buildEvmMultisignBodyTrueoWrapTyd(args) {
2236
+ assertTrueoChainId(args.chainId);
2237
+ const executor = getAddress(args.executorAddress);
2238
+ const amount = parseAmount(args.amountHuman, 6);
2239
+ await assertTrueoTydVaultUnderlying(args.rpcUrl);
2240
+ const steps = [];
2241
+ await pushApproveIfNeeded({
2242
+ steps,
2243
+ rpcUrl: args.rpcUrl,
2244
+ token: TRUEO_USDC,
2245
+ owner: executor,
2246
+ spender: TRUEO_TYD,
2247
+ amount
2248
+ });
2249
+ steps.push({
2250
+ kind: "wrap_tyd",
2251
+ to: TRUEO_TYD,
2252
+ data: encodeTrueoTydDeposit(amount, executor),
2253
+ value: 0n,
2254
+ fallbackGas: TRUEO_TYD_VAULT_FALLBACK_GAS
2255
+ });
2256
+ const purposeText = args.purposeText.trim() || buildTrueoPurposePrefill({ action: "wrapTyd", amountHuman: args.amountHuman, paymentSymbol: "USDC" });
2257
+ return finalizeTrueo({
2258
+ ...args,
2259
+ executorAddress: executor,
2260
+ purposeText,
2261
+ purposeSuffix: "Trueo: mint TYD from USDC (Yearn ERC-4626 deposit).",
2262
+ steps
2263
+ });
2264
+ }
2265
+ async function buildEvmMultisignBodyTrueoUnwrapTyd(args) {
2266
+ assertTrueoChainId(args.chainId);
2267
+ const executor = getAddress(args.executorAddress);
2268
+ const amount = parseAmount(args.amountHuman, TRUEO_TYD_DECIMALS);
2269
+ await assertTrueoTydVaultUnderlying(args.rpcUrl);
2270
+ const steps = [
2271
+ {
2272
+ kind: "unwrap_tyd",
2273
+ to: TRUEO_TYD,
2274
+ data: encodeTrueoTydRedeem(amount, executor, executor),
2275
+ value: 0n,
2276
+ fallbackGas: TRUEO_TYD_VAULT_FALLBACK_GAS
2277
+ }
2278
+ ];
2279
+ const purposeText = args.purposeText.trim() || buildTrueoPurposePrefill({ action: "unwrapTyd", amountHuman: args.amountHuman, paymentSymbol: "TYD" });
2280
+ return finalizeTrueo({
2281
+ ...args,
2282
+ executorAddress: executor,
2283
+ purposeText,
2284
+ purposeSuffix: "Trueo: unwrap TYD to USDC (Yearn ERC-4626 redeem).",
2285
+ steps
2286
+ });
2287
+ }
2288
+ async function buildEvmMultisignBodyTrueoMint(args) {
2289
+ assertTrueoChainId(args.chainId);
2290
+ const executor = getAddress(args.executorAddress);
2291
+ const market = getAddress(args.marketAddress);
2292
+ const paymentKind = args.paymentKind === "usdc" ? "usdc" : "tyd";
2293
+ const decimals = paymentKind === "usdc" ? 6 : TRUEO_TYD_DECIMALS;
2294
+ const amount = parseAmount(args.amountHuman, decimals);
2295
+ const row = await fetchTrueoMarket({ marketAddress: market });
2296
+ const steps = [];
2297
+ if (paymentKind === "usdc") {
2298
+ await pushApproveIfNeeded({
2299
+ steps,
2300
+ rpcUrl: args.rpcUrl,
2301
+ token: TRUEO_USDC,
2302
+ owner: executor,
2303
+ spender: TRUEO_TRUTH_MARKET_ADAPTER,
2304
+ amount
2305
+ });
2306
+ steps.push({
2307
+ kind: "adapter_mint",
2308
+ to: TRUEO_TRUTH_MARKET_ADAPTER,
2309
+ data: encodeTrueoAdapterMint(market, amount),
2310
+ value: 0n,
2311
+ fallbackGas: TRUEO_MINT_FALLBACK_GAS
2312
+ });
2313
+ } else {
2314
+ await pushApproveIfNeeded({
2315
+ steps,
2316
+ rpcUrl: args.rpcUrl,
2317
+ token: TRUEO_TYD,
2318
+ owner: executor,
2319
+ spender: market,
2320
+ amount
2321
+ });
2322
+ steps.push({
2323
+ kind: "mint",
2324
+ to: market,
2325
+ data: encodeTrueoMint(amount),
2326
+ value: 0n,
2327
+ fallbackGas: TRUEO_MINT_FALLBACK_GAS
2328
+ });
2329
+ }
2330
+ const purposeText = args.purposeText.trim() || buildTrueoPurposePrefill({
2331
+ action: "mint",
2332
+ amountHuman: args.amountHuman,
2333
+ paymentSymbol: paymentKind === "usdc" ? "USDC" : "TYD",
2334
+ question: row.question
2335
+ });
2336
+ return finalizeTrueo({
2337
+ ...args,
2338
+ executorAddress: executor,
2339
+ purposeText,
2340
+ purposeSuffix: `Trueo: mint YES+NO on ${row.question || market}.`,
2341
+ steps
2342
+ });
2343
+ }
2344
+ async function buildEvmMultisignBodyTrueoBurn(args) {
2345
+ assertTrueoChainId(args.chainId);
2346
+ const executor = getAddress(args.executorAddress);
2347
+ const market = getAddress(args.marketAddress);
2348
+ const row = await fetchTrueoMarket({ marketAddress: market });
2349
+ const amount = parseAmount(args.amountHuman, 18);
2350
+ const steps = [];
2351
+ const paymentKind = args.paymentKind === "usdc" ? "usdc" : "tyd";
2352
+ if (paymentKind === "usdc") {
2353
+ await pushApproveIfNeeded({
2354
+ steps,
2355
+ rpcUrl: args.rpcUrl,
2356
+ token: getAddress(row.yesTokenAddress),
2357
+ owner: executor,
2358
+ spender: TRUEO_TRUTH_MARKET_ADAPTER,
2359
+ amount
2360
+ });
2361
+ await pushApproveIfNeeded({
2362
+ steps,
2363
+ rpcUrl: args.rpcUrl,
2364
+ token: getAddress(row.noTokenAddress),
2365
+ owner: executor,
2366
+ spender: TRUEO_TRUTH_MARKET_ADAPTER,
2367
+ amount
2368
+ });
2369
+ steps.push({
2370
+ kind: "adapter_burn",
2371
+ to: TRUEO_TRUTH_MARKET_ADAPTER,
2372
+ data: encodeTrueoAdapterBurn(market, amount),
2373
+ value: 0n,
2374
+ fallbackGas: TRUEO_MINT_FALLBACK_GAS
2375
+ });
2376
+ } else {
2377
+ await pushApproveIfNeeded({
2378
+ steps,
2379
+ rpcUrl: args.rpcUrl,
2380
+ token: getAddress(row.yesTokenAddress),
2381
+ owner: executor,
2382
+ spender: market,
2383
+ amount
2384
+ });
2385
+ await pushApproveIfNeeded({
2386
+ steps,
2387
+ rpcUrl: args.rpcUrl,
2388
+ token: getAddress(row.noTokenAddress),
2389
+ owner: executor,
2390
+ spender: market,
2391
+ amount
2392
+ });
2393
+ steps.push({
2394
+ kind: "burn",
2395
+ to: market,
2396
+ data: encodeTrueoBurn(amount),
2397
+ value: 0n,
2398
+ fallbackGas: TRUEO_MINT_FALLBACK_GAS
2399
+ });
2400
+ }
2401
+ const purposeText = args.purposeText.trim() || buildTrueoPurposePrefill({ action: "burn", amountHuman: args.amountHuman, question: row.question });
2402
+ return finalizeTrueo({
2403
+ ...args,
2404
+ executorAddress: executor,
2405
+ purposeText,
2406
+ purposeSuffix: `Trueo: burn YES+NO on ${row.question || market}.`,
2407
+ steps
2408
+ });
2409
+ }
2410
+ async function buildEvmMultisignBodyTrueoRedeem(args) {
2411
+ assertTrueoChainId(args.chainId);
2412
+ const executor = getAddress(args.executorAddress);
2413
+ const market = getAddress(args.marketAddress);
2414
+ const row = await fetchTrueoMarket({ marketAddress: market });
2415
+ const amount = parseAmount(args.amountHuman, 18);
2416
+ const steps = [];
2417
+ const paymentKind = args.paymentKind === "usdc" ? "usdc" : "tyd";
2418
+ const winning = row.yesTokenAddress;
2419
+ if (paymentKind === "usdc") {
2420
+ await pushApproveIfNeeded({
2421
+ steps,
2422
+ rpcUrl: args.rpcUrl,
2423
+ token: getAddress(winning),
2424
+ owner: executor,
2425
+ spender: TRUEO_TRUTH_MARKET_ADAPTER,
2426
+ amount
2427
+ });
2428
+ await pushApproveIfNeeded({
2429
+ steps,
2430
+ rpcUrl: args.rpcUrl,
2431
+ token: getAddress(row.noTokenAddress),
2432
+ owner: executor,
2433
+ spender: TRUEO_TRUTH_MARKET_ADAPTER,
2434
+ amount
2435
+ });
2436
+ steps.push({
2437
+ kind: "adapter_redeem",
2438
+ to: TRUEO_TRUTH_MARKET_ADAPTER,
2439
+ data: encodeTrueoAdapterRedeem(market, amount),
2440
+ value: 0n,
2441
+ fallbackGas: TRUEO_MINT_FALLBACK_GAS
2442
+ });
2443
+ } else {
2444
+ await pushApproveIfNeeded({
2445
+ steps,
2446
+ rpcUrl: args.rpcUrl,
2447
+ token: getAddress(winning),
2448
+ owner: executor,
2449
+ spender: market,
2450
+ amount
2451
+ });
2452
+ await pushApproveIfNeeded({
2453
+ steps,
2454
+ rpcUrl: args.rpcUrl,
2455
+ token: getAddress(row.noTokenAddress),
2456
+ owner: executor,
2457
+ spender: market,
2458
+ amount
2459
+ });
2460
+ steps.push({
2461
+ kind: "redeem",
2462
+ to: market,
2463
+ data: encodeTrueoRedeem(amount),
2464
+ value: 0n,
2465
+ fallbackGas: TRUEO_MINT_FALLBACK_GAS
2466
+ });
2467
+ }
2468
+ const purposeText = args.purposeText.trim() || buildTrueoPurposePrefill({ action: "redeem", amountHuman: args.amountHuman, question: row.question });
2469
+ return finalizeTrueo({
2470
+ ...args,
2471
+ executorAddress: executor,
2472
+ purposeText,
2473
+ purposeSuffix: `Trueo: redeem on ${row.question || market}.`,
2474
+ steps
2475
+ });
2476
+ }
2477
+ function zeroForOneBuyOutcome(poolKey, tyd) {
2478
+ return getAddress(poolKey.currency0) === getAddress(tyd);
2479
+ }
2480
+ async function loadBetMarketAndPool(args) {
2481
+ const row = await fetchTrueoMarket({ marketAddress: args.marketAddress });
2482
+ assertTrueoBetCanCreate(row);
2483
+ const keys = await readTrueoPoolKeys({ rpcUrl: args.rpcUrl, marketAddress: getAddress(args.marketAddress) });
2484
+ if (!keys) throw new Error("Could not read Trueo V2 pool keys. Confirm the market is Uniswap v4.");
2485
+ return { row, poolKey: args.side === "no" ? keys.no : keys.yes };
2486
+ }
2487
+ async function buildEvmMultisignBodyTrueoSwap(args) {
2488
+ assertTrueoChainId(args.chainId);
2489
+ const executor = getAddress(args.executorAddress);
2490
+ const { row, poolKey } = await loadBetMarketAndPool({
2491
+ rpcUrl: args.rpcUrl,
2492
+ marketAddress: args.marketAddress,
2493
+ side: args.side
2494
+ });
2495
+ const amount = parseAmount(args.amountHuman, TRUEO_TYD_DECIMALS);
2496
+ const amountOutMin = args.amountOutMinimumHuman?.trim() ? parseUnits(args.amountOutMinimumHuman.trim(), 18) : 0n;
2497
+ const steps = [];
2498
+ await pushApproveIfNeeded({
2499
+ steps,
2500
+ rpcUrl: args.rpcUrl,
2501
+ token: TRUEO_TYD,
2502
+ owner: executor,
2503
+ spender: TRUEO_PERMIT2,
2504
+ amount
2505
+ });
2506
+ steps.push({
2507
+ kind: "permit2_approve",
2508
+ to: TRUEO_PERMIT2,
2509
+ data: encodePermit2Approve({
2510
+ token: TRUEO_TYD,
2511
+ spender: TRUEO_UNIVERSAL_ROUTER_ADAPTER,
2512
+ amount,
2513
+ expiration: trueoPermit2Expiration()
2514
+ }),
2515
+ value: 0n,
2516
+ fallbackGas: TRUEO_APPROVE_FALLBACK_GAS
2517
+ });
2518
+ const encoded = encodeTrueoV4ExactInSwap({
2519
+ poolKey,
2520
+ zeroForOne: zeroForOneBuyOutcome(poolKey, TRUEO_TYD),
2521
+ amountIn: amount,
2522
+ amountOutMinimum: amountOutMin,
2523
+ recipient: executor,
2524
+ deadline: trueoSwapDeadline()
2525
+ });
2526
+ steps.push({
2527
+ kind: "swap",
2528
+ to: encoded.to,
2529
+ data: encoded.data,
2530
+ value: 0n,
2531
+ fallbackGas: TRUEO_SWAP_FALLBACK_GAS,
2532
+ routerSwap: true
2533
+ });
2534
+ const purposeText = args.purposeText.trim() || buildTrueoPurposePrefill({
2535
+ action: "swap",
2536
+ side: args.side,
2537
+ amountHuman: args.amountHuman,
2538
+ paymentSymbol: "TYD",
2539
+ question: row.question
2540
+ });
2541
+ return finalizeTrueo({
2542
+ ...args,
2543
+ executorAddress: executor,
2544
+ purposeText,
2545
+ purposeSuffix: `Trueo: buy ${args.side === "no" ? "No" : "Yes"} ${args.amountHuman} TYD.`,
2546
+ steps,
2547
+ betExtra: buildTrueoBetExtraJson({
2548
+ action: "swap",
2549
+ marketAddress: row.address,
2550
+ endOfTrading: row.endOfTrading
2551
+ })
2552
+ });
2553
+ }
2554
+ async function buildEvmMultisignBodyTrueoCreateOrder(args) {
2555
+ assertTrueoChainId(args.chainId);
2556
+ const executor = getAddress(args.executorAddress);
2557
+ const { row, poolKey } = await loadBetMarketAndPool({
2558
+ rpcUrl: args.rpcUrl,
2559
+ marketAddress: args.marketAddress,
2560
+ side: args.side
2561
+ });
2562
+ const amount = parseAmount(args.amountHuman, TRUEO_TYD_DECIMALS);
2563
+ const steps = [];
2564
+ await pushApproveIfNeeded({
2565
+ steps,
2566
+ rpcUrl: args.rpcUrl,
2567
+ token: TRUEO_TYD,
2568
+ owner: executor,
2569
+ spender: TRUEO_PERMIT2,
2570
+ amount
2571
+ });
2572
+ steps.push({
2573
+ kind: "permit2_approve",
2574
+ to: TRUEO_PERMIT2,
2575
+ data: encodePermit2Approve({
2576
+ token: TRUEO_TYD,
2577
+ spender: TRUEO_ORDER_MANAGER,
2578
+ amount,
2579
+ expiration: trueoPermit2Expiration()
2580
+ }),
2581
+ value: 0n,
2582
+ fallbackGas: TRUEO_APPROVE_FALLBACK_GAS
2583
+ });
2584
+ steps.push({
2585
+ kind: "createOrder",
2586
+ to: TRUEO_ORDER_MANAGER,
2587
+ data: encodeTrueoCreateOrder({
2588
+ poolKey,
2589
+ amountIn: amount,
2590
+ tickLower: args.tickLower,
2591
+ tickUpper: args.tickUpper,
2592
+ zeroForOne: zeroForOneBuyOutcome(poolKey, TRUEO_TYD),
2593
+ enablePartialFill: args.enablePartialFill !== false
2594
+ }),
2595
+ value: 0n,
2596
+ fallbackGas: TRUEO_ORDER_FALLBACK_GAS
2597
+ });
2598
+ const purposeText = args.purposeText.trim() || buildTrueoPurposePrefill({
2599
+ action: "createOrder",
2600
+ side: args.side,
2601
+ amountHuman: args.amountHuman,
2602
+ question: row.question
2603
+ });
2604
+ return finalizeTrueo({
2605
+ ...args,
2606
+ executorAddress: executor,
2607
+ purposeText,
2608
+ purposeSuffix: `Trueo: limit ${args.side === "no" ? "No" : "Yes"} ${args.amountHuman} TYD.`,
2609
+ steps,
2610
+ betExtra: buildTrueoBetExtraJson({
2611
+ action: "createOrder",
2612
+ marketAddress: row.address,
2613
+ endOfTrading: row.endOfTrading
2614
+ })
2615
+ });
2616
+ }
2617
+ async function buildEvmMultisignBodyTrueoCancelOrder(args) {
2618
+ assertTrueoChainId(args.chainId);
2619
+ const executor = getAddress(args.executorAddress);
2620
+ const row = await fetchTrueoMarket({ marketAddress: args.marketAddress });
2621
+ const keys = await readTrueoPoolKeys({ rpcUrl: args.rpcUrl, marketAddress: getAddress(args.marketAddress) });
2622
+ if (!keys) throw new Error("Could not read Trueo V2 pool keys.");
2623
+ const poolKey = args.side === "no" ? keys.no : keys.yes;
2624
+ const steps = [
2625
+ {
2626
+ kind: "cancelOrder",
2627
+ to: TRUEO_ORDER_MANAGER,
2628
+ data: encodeTrueoCancelOrder({ poolKey, orderId: args.orderId }),
2629
+ value: 0n,
2630
+ fallbackGas: TRUEO_ORDER_FALLBACK_GAS
2631
+ }
2632
+ ];
2633
+ const purposeText = args.purposeText.trim() || buildTrueoPurposePrefill({ action: "cancelOrder", question: row.question });
2634
+ return finalizeTrueo({
2635
+ ...args,
2636
+ executorAddress: executor,
2637
+ purposeText,
2638
+ purposeSuffix: `Trueo: cancel order ${args.orderId}.`,
2639
+ steps
2640
+ });
2641
+ }
2642
+
2643
+ // src/protocols/evm/trueo/index.ts
2644
+ var trueoProtocolModule = {
2645
+ id: TRUEO_PROTOCOL_ID,
2646
+ chainCategory: "evm",
2647
+ isChainSupported(ctx) {
2648
+ const id = typeof ctx.chainId === "string" ? Number(ctx.chainId) : ctx.chainId;
2649
+ return ctx.chainCategory === "evm" && isTrueoChainSupported(id);
2650
+ },
2651
+ isTokenSupported(token) {
2652
+ return token.category === "evm" && (token.kind === "erc20" || token.kind === "ctmerc20");
2653
+ },
2654
+ actions: [
2655
+ {
2656
+ id: "trueo.wrap-tyd",
2657
+ protocolId: TRUEO_PROTOCOL_ID,
2658
+ chainCategory: "evm",
2659
+ description: "Mint TYD by depositing Base USDC into the Yearn ERC-4626 vault",
2660
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2661
+ params: {}
2662
+ },
2663
+ {
2664
+ id: "trueo.unwrap-tyd",
2665
+ protocolId: TRUEO_PROTOCOL_ID,
2666
+ chainCategory: "evm",
2667
+ description: "Redeem TYD shares back to Base USDC",
2668
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2669
+ params: {}
2670
+ },
2671
+ {
2672
+ id: "trueo.mint",
2673
+ protocolId: TRUEO_PROTOCOL_ID,
2674
+ chainCategory: "evm",
2675
+ description: "Mint YES+NO from TYD (or USDC via adapter)",
2676
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2677
+ params: {}
2678
+ },
2679
+ {
2680
+ id: "trueo.burn",
2681
+ protocolId: TRUEO_PROTOCOL_ID,
2682
+ chainCategory: "evm",
2683
+ description: "Burn equal YES+NO back to TYD",
2684
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2685
+ params: {}
2686
+ },
2687
+ {
2688
+ id: "trueo.redeem",
2689
+ protocolId: TRUEO_PROTOCOL_ID,
2690
+ chainCategory: "evm",
2691
+ description: "Redeem winning outcome tokens after resolution",
2692
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2693
+ params: {}
2694
+ },
2695
+ {
2696
+ id: "trueo.swap",
2697
+ protocolId: TRUEO_PROTOCOL_ID,
2698
+ chainCategory: "evm",
2699
+ description: "Buy Yes or No with TYD via UniversalRouterAdapter V4 swap",
2700
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2701
+ params: {}
2702
+ },
2703
+ {
2704
+ id: "trueo.create-order",
2705
+ protocolId: TRUEO_PROTOCOL_ID,
2706
+ chainCategory: "evm",
2707
+ description: "Create an on-chain OrderManager limit order",
2708
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2709
+ params: {}
2710
+ },
2711
+ {
2712
+ id: "trueo.cancel-order",
2713
+ protocolId: TRUEO_PROTOCOL_ID,
2714
+ chainCategory: "evm",
2715
+ description: "Cancel an on-chain OrderManager order",
2716
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2717
+ params: {}
2718
+ }
2719
+ ]
2720
+ };
2721
+ registerProtocolModule(trueoProtocolModule);
2722
+
2723
+ export { PREDICTION_MARKETS_PROTOCOL_ID, PREDICTION_MARKET_V1_KIND, TRUEO_API_ORIGIN, TRUEO_APPROVE_FALLBACK_GAS, TRUEO_BASE_CHAIN_ID, TRUEO_BET_ACTIONS, TRUEO_BET_CLOSE_BUFFER_SECONDS, TRUEO_BET_CLOSE_ERROR, TRUEO_BET_EXPIRED_ERROR, TRUEO_CHAIN_IDS, TRUEO_CHANCE_SERIES_LOOKBACK_SECONDS, TRUEO_CHANCE_SERIES_RESOLUTION, TRUEO_MARKET_API_PREFIX, TRUEO_MARKET_PAGE_SIZE, TRUEO_MINT_FALLBACK_GAS, TRUEO_NETWORK, TRUEO_ORDER_FALLBACK_GAS, TRUEO_ORDER_MANAGER, TRUEO_OUTCOME_TOKEN_DECIMALS, TRUEO_PERMIT2, TRUEO_PERMIT2_APPROVAL_SECONDS, TRUEO_PROTOCOL_ID, TRUEO_RECENT_MARKET_WINDOW_SECONDS, TRUEO_SANE_IMPLIED_PRICE_MAX, TRUEO_SWAP_FALLBACK_GAS, TRUEO_TRENDING_MAX_LIMIT, TRUEO_TRUE, TRUEO_TRUTH_MARKET_ADAPTER, TRUEO_TRUTH_MARKET_MANAGER, TRUEO_TYD, TRUEO_TYD_DECIMALS, TRUEO_TYD_VAULT_FALLBACK_GAS, TRUEO_UNIVERSAL_ROUTER_ADAPTER, TRUEO_USDC, TRUEO_V4_HOOK, UR_COMMAND_V4_SWAP, V4_ACTION_SETTLE, V4_ACTION_SWAP_EXACT_IN_SINGLE, V4_ACTION_TAKE, applyTrueoMarketSearchScope, assertTrueoBetCanCreate, assertTrueoBetCanExecute, assertTrueoBetStillOpen, assertTrueoChainId, buildEvmMultisignBodyTrueoBurn, buildEvmMultisignBodyTrueoCancelOrder, buildEvmMultisignBodyTrueoCreateOrder, buildEvmMultisignBodyTrueoMint, buildEvmMultisignBodyTrueoRedeem, buildEvmMultisignBodyTrueoSwap, buildEvmMultisignBodyTrueoUnwrapTyd, buildEvmMultisignBodyTrueoWrapTyd, buildPredictionMarketEnvelope, buildTrueoBetExtraJson, buildTrueoPurposePrefill, encodeErc20Approve, encodePermit2Approve, encodeTrueoAdapterBurn, encodeTrueoAdapterMint, encodeTrueoAdapterRedeem, encodeTrueoBurn, encodeTrueoCancelOrder, encodeTrueoCreateOrder, encodeTrueoMint, encodeTrueoRedeem, encodeTrueoTydDeposit, encodeTrueoTydRedeem, encodeTrueoUniversalRouterExecute, encodeTrueoV4ExactInInput, encodeTrueoV4ExactInSwap, extraJsonFromSignRequest, fetchTrueoCategories, fetchTrueoChanceSeries, fetchTrueoMarket, fetchTrueoOhlc, fetchTrueoOrderbook, fetchTrueoTransactions, fetchTrueoTrending, filterTrueoMarkets, getPredictionMarketSummary, isTrueoBetAction, isTrueoChainSupported, isTrueoCoreToken, isTrueoMarketLiveOrRecent, isTrueoMarketTradingOpen, isTrueoOutcomeTokenLabel, listMyPredictionMarketsSummary, listPredictionMarketsForHoldings, listTrueoMarketsPage, normalizeTrueoAddress, normalizeTrueoMarketRow, normalizeTrueoOhlcResolution, outcomeTokensFromMarkets, parseTrueoBetExtraJson, preferOpenTrueoTitleMatches, readTrueoPoolKeys, searchPredictionMarkets, searchPredictionMarketsSummary, searchTrueoMarkets, searchTrueoMarketsPage, suggestTrueoTokensToAdd, trueoAdapterMintAbi, trueoBetCreateBlockedReason, trueoBetExecuteBlockedReason, trueoCompactVolumeLabel, trueoErc20Abi, trueoExpiresAtIso, trueoExpiresAtLabel, trueoFetchCategoriesSummary, trueoFetchChanceSeriesSummary, trueoFetchMarketSummary, trueoFetchOrderbookSummary, trueoFetchTrendingSummary, trueoHoldingTokenAddressesFromRegistry, trueoImpliedPriceFromSqrtX96, trueoIsSaneImpliedPrice, trueoJsonFetch, trueoListUsesTrending, trueoMarketAbi, trueoMarketApiPath, trueoMarketStatusLabel, trueoMarketToPredictionRow, trueoMarketV2Abi, trueoMarketVotingOpen, trueoOrderManagerAbi, trueoOrderManagerStructAbi, trueoPercentLabel, trueoPermit2ApproveAbi, trueoPermit2Expiration, trueoProtocolModule, trueoQueryTokens, trueoResolutionCopy, trueoResolvedSide, trueoSanitizeBinaryPrices, trueoSearchMarketsSummary, trueoSearchScopeNote, trueoSwapDeadline, trueoTimeUntilTradingEndsLabel, trueoTradingDeadlineLabel, trueoTradingEndsCopy, trueoTydVaultAbi, trueoUniversalRouterAdapterAbi };
2724
+ //# sourceMappingURL=index.js.map
2725
+ //# sourceMappingURL=index.js.map