@livo-build/runtime 0.2.5 → 0.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aes.d.ts +4 -0
- package/dist/aes.js +38 -0
- package/dist/auth.test.d.ts +1 -0
- package/dist/auth.test.js +57 -0
- package/dist/cache.d.ts +12 -0
- package/dist/cache.js +27 -0
- package/dist/gate.d.ts +10 -0
- package/dist/gate.js +18 -0
- package/dist/http.d.ts +48 -0
- package/dist/http.js +133 -0
- package/dist/hyperliquid.d.ts +269 -0
- package/dist/hyperliquid.js +194 -0
- package/dist/hyperliquid.test.js +17 -1
- package/dist/index.d.ts +27 -2
- package/dist/index.js +16 -0
- package/dist/nft.d.ts +33 -0
- package/dist/nft.js +72 -0
- package/dist/nft.test.d.ts +1 -0
- package/dist/nft.test.js +44 -0
- package/dist/polymarket.d.ts +12 -0
- package/dist/polymarket.js +19 -6
- package/dist/polymarket.test.js +10 -0
- package/dist/ratelimit.d.ts +19 -0
- package/dist/ratelimit.js +11 -0
- package/dist/reads.d.ts +12 -0
- package/dist/reads.js +36 -0
- package/dist/sessions.d.ts +8 -0
- package/dist/sessions.js +60 -0
- package/dist/signals.d.ts +131 -0
- package/dist/signals.js +146 -0
- package/dist/siwe.d.ts +24 -0
- package/dist/siwe.js +33 -0
- package/dist/sse.test.d.ts +1 -0
- package/dist/sse.test.js +28 -0
- package/dist/telegram.d.ts +31 -0
- package/dist/telegram.js +73 -0
- package/dist/telegramAuth.d.ts +16 -0
- package/dist/telegramAuth.js +108 -0
- package/dist/telegramAuth.test.d.ts +1 -0
- package/dist/telegramAuth.test.js +68 -0
- package/dist/telegramLinks.d.ts +27 -0
- package/dist/telegramLinks.js +78 -0
- package/dist/webhook.d.ts +18 -0
- package/dist/webhook.js +49 -0
- package/dist/webhook.test.d.ts +1 -0
- package/dist/webhook.test.js +46 -0
- package/package.json +14 -4
package/dist/hyperliquid.js
CHANGED
|
@@ -25,6 +25,35 @@ import { keccak_256 } from "@noble/hashes/sha3";
|
|
|
25
25
|
import { localAccount } from "./eip712.js";
|
|
26
26
|
import { privateKeyToAddress } from "./tx.js";
|
|
27
27
|
import { bytesToHex, concatBytes, hexToBytes } from "./hex.js";
|
|
28
|
+
/** Parse a Hyperliquid clearinghouse position into a clean PositionInfo. */
|
|
29
|
+
export function parseHlPosition(p) {
|
|
30
|
+
const szi = Number(p.szi);
|
|
31
|
+
return {
|
|
32
|
+
coin: p.coin,
|
|
33
|
+
size: Math.abs(szi),
|
|
34
|
+
side: szi > 0 ? "long" : szi < 0 ? "short" : "flat",
|
|
35
|
+
entryPx: Number(p.entryPx ?? 0),
|
|
36
|
+
positionValue: Number(p.positionValue ?? 0),
|
|
37
|
+
unrealizedPnl: Number(p.unrealizedPnl ?? 0),
|
|
38
|
+
returnOnEquity: Number(p.returnOnEquity ?? 0),
|
|
39
|
+
leverage: Number(p.leverage?.value ?? 0),
|
|
40
|
+
liquidationPx: p.liquidationPx != null ? Number(p.liquidationPx) : null,
|
|
41
|
+
marginUsed: Number(p.marginUsed ?? 0),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Parse an L2 book into a best bid/offer + mid + spread. */
|
|
45
|
+
export function parseBbo(book) {
|
|
46
|
+
const bidPx = book.levels?.[0]?.[0]?.px;
|
|
47
|
+
const askPx = book.levels?.[1]?.[0]?.px;
|
|
48
|
+
const bid = bidPx != null ? Number(bidPx) : null;
|
|
49
|
+
const ask = askPx != null ? Number(askPx) : null;
|
|
50
|
+
return {
|
|
51
|
+
bid,
|
|
52
|
+
ask,
|
|
53
|
+
mid: bid != null && ask != null ? (bid + ask) / 2 : null,
|
|
54
|
+
spread: bid != null && ask != null ? ask - bid : null,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
28
57
|
function truthy(v) {
|
|
29
58
|
return v === true || v === 1 || v === "1" || v === "true";
|
|
30
59
|
}
|
|
@@ -206,6 +235,47 @@ export class Hyperliquid {
|
|
|
206
235
|
const startTime = opts.startTime ?? endTime - (opts.lookbackMs ?? 24 * 60 * 60 * 1000);
|
|
207
236
|
return this._info.candleSnapshot({ coin, interval, startTime, endTime });
|
|
208
237
|
}
|
|
238
|
+
/** Recent public trades for a coin. */
|
|
239
|
+
async recentTrades(coin) {
|
|
240
|
+
return this._info.recentTrades({ coin });
|
|
241
|
+
}
|
|
242
|
+
/** Best bid/offer + mid + spread for a coin (parsed from the L2 book). */
|
|
243
|
+
async bbo(coin) {
|
|
244
|
+
return parseBbo((await this.book(coin)));
|
|
245
|
+
}
|
|
246
|
+
/** All open positions as clean PositionInfo[] (default: the signer's account). */
|
|
247
|
+
async positionsList(user) {
|
|
248
|
+
const s = (await this.positions(user));
|
|
249
|
+
return (s.assetPositions ?? []).map((ap) => parseHlPosition(ap.position));
|
|
250
|
+
}
|
|
251
|
+
/** A single open position for a coin, or null if flat. */
|
|
252
|
+
async position(coin, user) {
|
|
253
|
+
return (await this.positionsList(user)).find((p) => p.coin === coin) ?? null;
|
|
254
|
+
}
|
|
255
|
+
/** Current hourly funding rate for a coin (fraction, e.g. 0.0000125). */
|
|
256
|
+
async fundingRate(coin) {
|
|
257
|
+
return (await this.assetCtx(coin)).funding;
|
|
258
|
+
}
|
|
259
|
+
/** Historical funding rates for a coin (default: the last 7 days). */
|
|
260
|
+
async fundingHistory(coin, opts = {}) {
|
|
261
|
+
const endTime = opts.endTime ?? Date.now();
|
|
262
|
+
const startTime = opts.startTime ?? endTime - (opts.lookbackMs ?? 7 * 24 * 60 * 60 * 1000);
|
|
263
|
+
return this._info.fundingHistory({ coin, startTime, endTime });
|
|
264
|
+
}
|
|
265
|
+
/** Funding payments paid/received by an account (default signer; last 7 days). */
|
|
266
|
+
async userFunding(opts = {}, user) {
|
|
267
|
+
const endTime = opts.endTime ?? Date.now();
|
|
268
|
+
const startTime = opts.startTime ?? endTime - (opts.lookbackMs ?? 7 * 24 * 60 * 60 * 1000);
|
|
269
|
+
return this._info.userFunding({ user: this.requireUser(user), startTime, endTime });
|
|
270
|
+
}
|
|
271
|
+
/** Predicted next funding rates across venues (no key). */
|
|
272
|
+
async predictedFundings() {
|
|
273
|
+
return this._info.predictedFundings();
|
|
274
|
+
}
|
|
275
|
+
/** Portfolio history (PnL/account-value time series) for an account (default: the signer). */
|
|
276
|
+
async portfolio(user) {
|
|
277
|
+
return this._info.portfolio({ user: this.requireUser(user) });
|
|
278
|
+
}
|
|
209
279
|
// ---- trading (key required) ----
|
|
210
280
|
/** Raw order passthrough (the @nktkas `order` shape) for full control. */
|
|
211
281
|
async order(params) {
|
|
@@ -281,10 +351,134 @@ export class Hyperliquid {
|
|
|
281
351
|
async cancel(coin, oid) {
|
|
282
352
|
return this.exchange.cancel({ cancels: [{ a: await this.assetId(coin), o: oid }] });
|
|
283
353
|
}
|
|
354
|
+
/** Cancel a resting order by its client order id (cloid). */
|
|
355
|
+
async cancelByCloid(coin, cloid) {
|
|
356
|
+
return this.exchange.cancelByCloid({ cancels: [{ asset: await this.assetId(coin), cloid }] });
|
|
357
|
+
}
|
|
358
|
+
/** Cancel ALL resting orders (optionally only for one coin). Returns the count cancelled. */
|
|
359
|
+
async cancelAll(coin) {
|
|
360
|
+
const orders = (await this.openOrders());
|
|
361
|
+
const targets = coin ? orders.filter((o) => o.coin === coin) : orders;
|
|
362
|
+
if (!targets.length)
|
|
363
|
+
return { cancelled: 0 };
|
|
364
|
+
const cancels = await Promise.all(targets.map(async (o) => ({ a: await this.assetId(o.coin), o: o.oid })));
|
|
365
|
+
await this.exchange.cancel({ cancels });
|
|
366
|
+
return { cancelled: targets.length };
|
|
367
|
+
}
|
|
368
|
+
/** Modify a resting limit order in place (new size/price/side). */
|
|
369
|
+
async modifyOrder(oid, coin, isBuy, size, price, opts = {}) {
|
|
370
|
+
const { id, szDecimals } = await this.lookup(coin);
|
|
371
|
+
return this.exchange.modify({
|
|
372
|
+
oid,
|
|
373
|
+
order: {
|
|
374
|
+
a: id,
|
|
375
|
+
b: isBuy,
|
|
376
|
+
p: formatPrice(price),
|
|
377
|
+
s: formatSize(size, szDecimals),
|
|
378
|
+
r: opts.reduceOnly ?? false,
|
|
379
|
+
t: { limit: { tif: opts.tif ?? "Gtc" } },
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Place a trigger order (stop / take-profit). `triggerPx` is the activation price;
|
|
385
|
+
* `tpsl` is "sl" (stop) or "tp". Market trigger by default (fills at market once
|
|
386
|
+
* armed); reduce-only by default (closing). `isBuy` is the side that executes —
|
|
387
|
+
* to protect a LONG use isBuy=false (sell), for a SHORT use isBuy=true (buy).
|
|
388
|
+
*/
|
|
389
|
+
async trigger(coin, isBuy, size, triggerPx, opts) {
|
|
390
|
+
const { id, szDecimals } = await this.lookup(coin);
|
|
391
|
+
return this.exchange.order({
|
|
392
|
+
orders: [
|
|
393
|
+
{
|
|
394
|
+
a: id,
|
|
395
|
+
b: isBuy,
|
|
396
|
+
p: formatPrice(opts.price ?? triggerPx),
|
|
397
|
+
s: formatSize(size, szDecimals),
|
|
398
|
+
r: opts.reduceOnly ?? true,
|
|
399
|
+
t: { trigger: { isMarket: opts.isMarket ?? true, triggerPx: formatPrice(triggerPx), tpsl: opts.tpsl } },
|
|
400
|
+
},
|
|
401
|
+
],
|
|
402
|
+
grouping: "na",
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
/** Stop-loss trigger (reduce-only market by default). See `trigger` for `isBuy`. */
|
|
406
|
+
stopLoss(coin, isBuy, size, triggerPx, opts = {}) {
|
|
407
|
+
return this.trigger(coin, isBuy, size, triggerPx, { ...opts, tpsl: "sl" });
|
|
408
|
+
}
|
|
409
|
+
/** Take-profit trigger (reduce-only market by default). See `trigger` for `isBuy`. */
|
|
410
|
+
takeProfit(coin, isBuy, size, triggerPx, opts = {}) {
|
|
411
|
+
return this.trigger(coin, isBuy, size, triggerPx, { ...opts, tpsl: "tp" });
|
|
412
|
+
}
|
|
413
|
+
/** Dead-man's switch: auto-cancel all orders at `timeMs` (epoch) unless re-armed. Omit to clear. */
|
|
414
|
+
async scheduleCancel(timeMs) {
|
|
415
|
+
return this.exchange.scheduleCancel(timeMs ? { time: timeMs } : {});
|
|
416
|
+
}
|
|
417
|
+
/** Move USDC from spot into the perp wallet (margin top-up; not a withdrawal). */
|
|
418
|
+
async transferToPerp(usd) {
|
|
419
|
+
return this.exchange.usdClassTransfer({ amount: String(usd), toPerp: true });
|
|
420
|
+
}
|
|
421
|
+
/** Move USDC from the perp wallet back to spot. */
|
|
422
|
+
async transferToSpot(usd) {
|
|
423
|
+
return this.exchange.usdClassTransfer({ amount: String(usd), toPerp: false });
|
|
424
|
+
}
|
|
284
425
|
/** Set leverage for a coin (cross by default). */
|
|
285
426
|
async updateLeverage(coin, leverage, opts = {}) {
|
|
286
427
|
return this.exchange.updateLeverage({ asset: await this.assetId(coin), isCross: opts.cross ?? true, leverage });
|
|
287
428
|
}
|
|
429
|
+
/**
|
|
430
|
+
* Place a TWAP order — slice `size` evenly over `minutes` (5–1440) to reduce impact.
|
|
431
|
+
* `randomize` jitters slice timing. Returns the @nktkas response (incl. the TWAP id).
|
|
432
|
+
*/
|
|
433
|
+
async twap(coin, isBuy, size, minutes, opts = {}) {
|
|
434
|
+
const { id, szDecimals } = await this.lookup(coin);
|
|
435
|
+
return this.exchange.twapOrder({
|
|
436
|
+
twap: { a: id, b: isBuy, s: formatSize(size, szDecimals), r: opts.reduceOnly ?? false, m: minutes, t: opts.randomize ?? false },
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
/** Cancel a running TWAP by its id (from the `twap()` response). */
|
|
440
|
+
async cancelTwap(coin, twapId) {
|
|
441
|
+
return this.exchange.twapCancel({ a: await this.assetId(coin), t: twapId });
|
|
442
|
+
}
|
|
443
|
+
// ---- sub-accounts (isolate strategies under one master) ----
|
|
444
|
+
/** Create a named sub-account (1–16 chars). Returns its address. */
|
|
445
|
+
async createSubAccount(name) {
|
|
446
|
+
return this.exchange.createSubAccount({ name });
|
|
447
|
+
}
|
|
448
|
+
/** List sub-accounts for an address (default: the signer). */
|
|
449
|
+
async subAccounts(user) {
|
|
450
|
+
return this._info.subAccounts({ user: this.requireUser(user) });
|
|
451
|
+
}
|
|
452
|
+
/** Move USDC from the master into a sub-account (`usd` in dollars). */
|
|
453
|
+
async transferToSubAccount(subAccount, usd) {
|
|
454
|
+
return this.exchange.subAccountTransfer({ subAccountUser: subAccount, isDeposit: true, usd: Math.round(usd * 1e6) });
|
|
455
|
+
}
|
|
456
|
+
/** Move USDC from a sub-account back to the master (`usd` in dollars). */
|
|
457
|
+
async transferFromSubAccount(subAccount, usd) {
|
|
458
|
+
return this.exchange.subAccountTransfer({ subAccountUser: subAccount, isDeposit: false, usd: Math.round(usd * 1e6) });
|
|
459
|
+
}
|
|
460
|
+
// ---- vaults (deposit into / withdraw from a Hyperliquid vault) ----
|
|
461
|
+
/** Deposit USDC into a vault (`usd` in dollars). */
|
|
462
|
+
async vaultDeposit(vault, usd) {
|
|
463
|
+
return this.exchange.vaultTransfer({ vaultAddress: vault, isDeposit: true, usd: Math.round(usd * 1e6) });
|
|
464
|
+
}
|
|
465
|
+
/** Withdraw USDC from a vault (`usd` in dollars). */
|
|
466
|
+
async vaultWithdraw(vault, usd) {
|
|
467
|
+
return this.exchange.vaultTransfer({ vaultAddress: vault, isDeposit: false, usd: Math.round(usd * 1e6) });
|
|
468
|
+
}
|
|
469
|
+
/** Vault details + your equity in it (public read; pass a user or use the signer). */
|
|
470
|
+
async vaultDetails(vault, user) {
|
|
471
|
+
return this._info.vaultDetails({ vaultAddress: vault, user: (user ?? this.address ?? undefined) });
|
|
472
|
+
}
|
|
473
|
+
// ---- account info ----
|
|
474
|
+
/** Your fee schedule + 14-day volume (default: the signer). */
|
|
475
|
+
async userFees(user) {
|
|
476
|
+
return this._info.userFees({ user: this.requireUser(user) });
|
|
477
|
+
}
|
|
478
|
+
/** Status of a single order by its oid (resting/filled/canceled). */
|
|
479
|
+
async orderStatus(oid, user) {
|
|
480
|
+
return this._info.orderStatus({ user: this.requireUser(user), oid });
|
|
481
|
+
}
|
|
288
482
|
// The @nktkas SymbolConverter resolves asset ids + szDecimals across the main
|
|
289
483
|
// perp dex, spot, AND HIP-3 builder dexes (so "xyz:TSLA" → its global asset id).
|
|
290
484
|
symbols() {
|
package/dist/hyperliquid.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Hyperliquid agent-wallet delegation: per-user agent keys must be deterministic
|
|
2
2
|
// (recomputable, never stored) and the bound client must sign as the agent.
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
|
-
import { Hyperliquid } from "./hyperliquid.js";
|
|
4
|
+
import { Hyperliquid, parseHlPosition, parseBbo } from "./hyperliquid.js";
|
|
5
5
|
import { localAccount } from "./eip712.js";
|
|
6
6
|
const SECRET = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
|
|
7
7
|
const MASTER = "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826";
|
|
@@ -29,3 +29,19 @@ describe("Hyperliquid agent-wallet delegation", () => {
|
|
|
29
29
|
expect(() => bare.agentKey("user-1")).toThrow(/agent secret/i);
|
|
30
30
|
});
|
|
31
31
|
});
|
|
32
|
+
describe("Hyperliquid parsers", () => {
|
|
33
|
+
it("parseHlPosition normalizes a clearinghouse position (side/size/fields)", () => {
|
|
34
|
+
const long = parseHlPosition({ coin: "BTC", szi: "0.5", entryPx: "60000", positionValue: "31000", unrealizedPnl: "1000", returnOnEquity: "0.12", leverage: { value: 10 }, liquidationPx: "40000", marginUsed: "3100" });
|
|
35
|
+
expect(long).toMatchObject({ coin: "BTC", size: 0.5, side: "long", entryPx: 60000, unrealizedPnl: 1000, leverage: 10, liquidationPx: 40000 });
|
|
36
|
+
const short = parseHlPosition({ coin: "ETH", szi: "-2" });
|
|
37
|
+
expect(short.side).toBe("short");
|
|
38
|
+
expect(short.size).toBe(2);
|
|
39
|
+
expect(short.liquidationPx).toBeNull();
|
|
40
|
+
expect(parseHlPosition({ coin: "X", szi: "0" }).side).toBe("flat");
|
|
41
|
+
});
|
|
42
|
+
it("parseBbo derives bid/ask/mid/spread from the L2 book", () => {
|
|
43
|
+
const bbo = parseBbo({ levels: [[{ px: "99.5" }], [{ px: "100.5" }]] });
|
|
44
|
+
expect(bbo).toEqual({ bid: 99.5, ask: 100.5, mid: 100, spread: 1 });
|
|
45
|
+
expect(parseBbo({ levels: [[], []] })).toEqual({ bid: null, ask: null, mid: null, spread: null });
|
|
46
|
+
});
|
|
47
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export { watchLogs } from "./watch.js";
|
|
|
6
6
|
export type { WatchLogsOptions, WatchLogsResult } from "./watch.js";
|
|
7
7
|
export { defineWatcher } from "./watcher.js";
|
|
8
8
|
export type { WatcherEnv, WatcherMatch, WatcherScope, WatcherContext, WatcherDefinition, WatcherModule, SubscribeOptions, Confidence, MatchStatus, } from "./watcher.js";
|
|
9
|
+
export { Signals, signals, DEFAULT_SIGNALS_URL } from "./signals.js";
|
|
10
|
+
export type { SignalsOptions, Market, SignalMatch, Swap, Launch, EngineSnapshot, MarketFilter, } from "./signals.js";
|
|
9
11
|
export { Relayer } from "./relayer.js";
|
|
10
12
|
export { Queue, queue, queueEnvKey } from "./queue.js";
|
|
11
13
|
export type { RelayerOptions } from "./relayer.js";
|
|
@@ -14,9 +16,32 @@ export type { WalletOptions, WalletAccount, WalletSendOptions } from "./wallet.j
|
|
|
14
16
|
export { Indexer, indexer, indexerEnvKey, GraphQLError } from "./indexer.js";
|
|
15
17
|
export type { IndexerOptions } from "./indexer.js";
|
|
16
18
|
export { Telegram } from "./telegram.js";
|
|
17
|
-
export type { TelegramOptions, BotMessage, SendMessageOptions } from "./telegram.js";
|
|
19
|
+
export type { TelegramOptions, BotMessage, SendMessageOptions, ChatJoinRequest } from "./telegram.js";
|
|
20
|
+
export { verifyTelegramInitData, verifyTelegramLoginWidget, parseStartPayload } from "./telegramAuth.js";
|
|
21
|
+
export type { TelegramUser, VerifyOptions } from "./telegramAuth.js";
|
|
22
|
+
export { TelegramLinks, TELEGRAM_LINKS_TABLE } from "./telegramLinks.js";
|
|
23
|
+
export type { TelegramLink } from "./telegramLinks.js";
|
|
24
|
+
export { tokenBalanceOf, meetsGate } from "./gate.js";
|
|
25
|
+
export type { TokenGate } from "./gate.js";
|
|
26
|
+
export { tokenMetadata, ownerOf, tokenURI } from "./reads.js";
|
|
27
|
+
export type { TokenMetadata } from "./reads.js";
|
|
28
|
+
export { resolveUri, fetchTokenMetadata } from "./nft.js";
|
|
29
|
+
export type { NftMetadata, NftAttribute, FetchMetadataOptions } from "./nft.js";
|
|
30
|
+
export { createSiweMessage, verifySiweMessage } from "./siwe.js";
|
|
31
|
+
export type { SiweParams, SiweVerifyResult } from "./siwe.js";
|
|
32
|
+
export { createSession, verifySession } from "./sessions.js";
|
|
33
|
+
export type { SessionOptions } from "./sessions.js";
|
|
34
|
+
export { json, error, withCors, CORS_HEADERS, Router, sse } from "./http.js";
|
|
35
|
+
export type { RouteContext, RouteHandler, SseConnection } from "./http.js";
|
|
36
|
+
export { rateLimit } from "./ratelimit.js";
|
|
37
|
+
export type { KvLike, RateLimitOptions, RateLimitResult } from "./ratelimit.js";
|
|
38
|
+
export { encrypt, decrypt } from "./aes.js";
|
|
39
|
+
export { cached } from "./cache.js";
|
|
40
|
+
export type { CacheOptions } from "./cache.js";
|
|
41
|
+
export { verifyWebhook, signWebhook } from "./webhook.js";
|
|
42
|
+
export type { VerifyWebhookParams, SignatureEncoding } from "./webhook.js";
|
|
18
43
|
export { Hyperliquid } from "./hyperliquid.js";
|
|
19
|
-
export type { HyperliquidOptions, PlaceOrderOptions, MarketOrderOptions, Tif, CandleInterval, CandlesOptions, AssetContext, AccountBalance, } from "./hyperliquid.js";
|
|
44
|
+
export type { HyperliquidOptions, PlaceOrderOptions, MarketOrderOptions, Tif, CandleInterval, CandlesOptions, AssetContext, AccountBalance, PositionInfo, Bbo, } from "./hyperliquid.js";
|
|
20
45
|
export { Polymarket } from "./polymarket.js";
|
|
21
46
|
export type { PolymarketOptions, PolymarketCreds, PlaceOrderParams, Side, OrderType, SignatureType, PriceInterval, PriceHistoryOptions, MarketOutcome, ResolvedMarket, } from "./polymarket.js";
|
|
22
47
|
export { hashTypedData, signTypedData, localAccount } from "./eip712.js";
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,10 @@ export { watchLogs } from "./watch.js";
|
|
|
10
10
|
// defineWatcher — onMatch primitive for Signal Radar watchers (HTTP-triggered,
|
|
11
11
|
// one subscription-addressed match per request) + ctx.subscribe/cancel.
|
|
12
12
|
export { defineWatcher } from "./watcher.js";
|
|
13
|
+
// Signals — READ client for the shared on-chain signals engine (Signal Radar):
|
|
14
|
+
// live markets, recent swaps, fired matches. Zero-config (defaults to the public
|
|
15
|
+
// engine). The push counterpart is defineWatcher above.
|
|
16
|
+
export { Signals, signals, DEFAULT_SIGNALS_URL } from "./signals.js";
|
|
13
17
|
// Relayer — managed signing for bots (custodied RELAYER_PRIVATE_KEY + optional
|
|
14
18
|
// Convex-serialized nonces). A Chain that defaults to the relayer key.
|
|
15
19
|
export { Relayer } from "./relayer.js";
|
|
@@ -23,6 +27,18 @@ export { Wallet, UserWallet } from "./wallet.js";
|
|
|
23
27
|
export { Indexer, indexer, indexerEnvKey, GraphQLError } from "./indexer.js";
|
|
24
28
|
// Telegram — webhook verification + reply plumbing for bots.
|
|
25
29
|
export { Telegram } from "./telegram.js";
|
|
30
|
+
export { verifyTelegramInitData, verifyTelegramLoginWidget, parseStartPayload } from "./telegramAuth.js";
|
|
31
|
+
export { TelegramLinks, TELEGRAM_LINKS_TABLE } from "./telegramLinks.js";
|
|
32
|
+
export { tokenBalanceOf, meetsGate } from "./gate.js";
|
|
33
|
+
export { tokenMetadata, ownerOf, tokenURI } from "./reads.js";
|
|
34
|
+
export { resolveUri, fetchTokenMetadata } from "./nft.js";
|
|
35
|
+
export { createSiweMessage, verifySiweMessage } from "./siwe.js";
|
|
36
|
+
export { createSession, verifySession } from "./sessions.js";
|
|
37
|
+
export { json, error, withCors, CORS_HEADERS, Router, sse } from "./http.js";
|
|
38
|
+
export { rateLimit } from "./ratelimit.js";
|
|
39
|
+
export { encrypt, decrypt } from "./aes.js";
|
|
40
|
+
export { cached } from "./cache.js";
|
|
41
|
+
export { verifyWebhook, signWebhook } from "./webhook.js";
|
|
26
42
|
// Hyperliquid — perps/spot data + trading (wraps @nktkas/hyperliquid; signs with
|
|
27
43
|
// the runtime's EIP-712 signer, no viem/ethers in the bundle).
|
|
28
44
|
export { Hyperliquid } from "./hyperliquid.js";
|
package/dist/nft.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Chain } from "./chain.js";
|
|
2
|
+
export interface NftAttribute {
|
|
3
|
+
trait_type?: string;
|
|
4
|
+
value?: unknown;
|
|
5
|
+
[k: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface NftMetadata {
|
|
8
|
+
name?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
/** Image URL resolved through the gateway (ipfs:// → https). */
|
|
11
|
+
image?: string;
|
|
12
|
+
/** The original, unresolved image value from the metadata document. */
|
|
13
|
+
imageRaw?: string;
|
|
14
|
+
/** animation_url resolved through the gateway (video/audio/3D media). */
|
|
15
|
+
animationUrl?: string;
|
|
16
|
+
attributes?: NftAttribute[];
|
|
17
|
+
/** The full parsed metadata document. */
|
|
18
|
+
raw: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
export interface FetchMetadataOptions {
|
|
21
|
+
/** IPFS gateway base, trailing slash included. Default "https://ipfs.io/ipfs/". */
|
|
22
|
+
gateway?: string;
|
|
23
|
+
/** ERC-1155 reads uri(id) with {id} substitution; default ERC-721 tokenURI(id). */
|
|
24
|
+
standard?: "erc721" | "erc1155";
|
|
25
|
+
}
|
|
26
|
+
/** Resolve an ipfs:// / ar:// URI to an HTTP(S) URL. http(s)/data URIs pass through. */
|
|
27
|
+
export declare function resolveUri(uri: string, gateway?: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Read a token's metadata URI on-chain, resolve + fetch + parse it, and resolve
|
|
30
|
+
* the embedded image/animation URLs. Returns null if the URI read reverts (e.g.
|
|
31
|
+
* unminted). Throws if the metadata document itself can't be fetched/parsed.
|
|
32
|
+
*/
|
|
33
|
+
export declare function fetchTokenMetadata(chain: Chain, token: string, tokenId: bigint, opts?: FetchMetadataOptions): Promise<NftMetadata | null>;
|
package/dist/nft.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// NFT metadata — read a token's metadata URI on-chain, resolve it (ipfs:// →
|
|
2
|
+
// gateway), fetch + parse the JSON, and resolve the embedded media URLs. Works for
|
|
3
|
+
// ERC-721 (tokenURI) and ERC-1155 (uri(id) with {id} substitution). Pairs with the
|
|
4
|
+
// kit's useNFT / <NFTCard>.
|
|
5
|
+
const DEFAULT_GATEWAY = "https://ipfs.io/ipfs/";
|
|
6
|
+
/** Resolve an ipfs:// / ar:// URI to an HTTP(S) URL. http(s)/data URIs pass through. */
|
|
7
|
+
export function resolveUri(uri, gateway = DEFAULT_GATEWAY) {
|
|
8
|
+
if (!uri)
|
|
9
|
+
return uri;
|
|
10
|
+
if (uri.startsWith("ipfs://")) {
|
|
11
|
+
let rest = uri.slice("ipfs://".length);
|
|
12
|
+
if (rest.startsWith("ipfs/"))
|
|
13
|
+
rest = rest.slice("ipfs/".length); // odd ipfs://ipfs/CID form
|
|
14
|
+
return gateway + rest;
|
|
15
|
+
}
|
|
16
|
+
if (uri.startsWith("ar://"))
|
|
17
|
+
return "https://arweave.net/" + uri.slice("ar://".length);
|
|
18
|
+
return uri;
|
|
19
|
+
}
|
|
20
|
+
/** ERC-1155 {id} substitution token: lowercase hex, zero-padded to 64 chars. */
|
|
21
|
+
function erc1155IdHex(tokenId) {
|
|
22
|
+
return tokenId.toString(16).padStart(64, "0");
|
|
23
|
+
}
|
|
24
|
+
async function loadJson(uri) {
|
|
25
|
+
if (uri.startsWith("data:")) {
|
|
26
|
+
const comma = uri.indexOf(",");
|
|
27
|
+
const header = uri.slice(5, comma);
|
|
28
|
+
const body = uri.slice(comma + 1);
|
|
29
|
+
const text = header.includes("base64") ? atob(body) : decodeURIComponent(body);
|
|
30
|
+
return JSON.parse(text);
|
|
31
|
+
}
|
|
32
|
+
const res = await fetch(uri);
|
|
33
|
+
if (!res.ok)
|
|
34
|
+
throw new Error(`metadata fetch ${res.status} for ${uri}`);
|
|
35
|
+
return (await res.json());
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Read a token's metadata URI on-chain, resolve + fetch + parse it, and resolve
|
|
39
|
+
* the embedded image/animation URLs. Returns null if the URI read reverts (e.g.
|
|
40
|
+
* unminted). Throws if the metadata document itself can't be fetched/parsed.
|
|
41
|
+
*/
|
|
42
|
+
export async function fetchTokenMetadata(chain, token, tokenId, opts = {}) {
|
|
43
|
+
const gateway = opts.gateway ?? DEFAULT_GATEWAY;
|
|
44
|
+
const t = token;
|
|
45
|
+
let uri;
|
|
46
|
+
try {
|
|
47
|
+
if (opts.standard === "erc1155") {
|
|
48
|
+
const raw = (await chain.call(t, "uri(uint256)(string)", [tokenId]));
|
|
49
|
+
uri = raw?.replace(/\{id\}/g, erc1155IdHex(tokenId));
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
uri = (await chain.call(t, "tokenURI(uint256)(string)", [tokenId]));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (!uri)
|
|
59
|
+
return null;
|
|
60
|
+
const doc = await loadJson(resolveUri(uri, gateway));
|
|
61
|
+
const imageRaw = (doc.image ?? doc.image_url ?? doc.imageUrl);
|
|
62
|
+
const animationRaw = (doc.animation_url ?? doc.animationUrl);
|
|
63
|
+
return {
|
|
64
|
+
name: doc.name,
|
|
65
|
+
description: doc.description,
|
|
66
|
+
image: imageRaw ? resolveUri(imageRaw, gateway) : undefined,
|
|
67
|
+
imageRaw,
|
|
68
|
+
animationUrl: animationRaw ? resolveUri(animationRaw, gateway) : undefined,
|
|
69
|
+
attributes: doc.attributes ?? undefined,
|
|
70
|
+
raw: doc,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/nft.test.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { resolveUri, fetchTokenMetadata } from "./nft.js";
|
|
3
|
+
describe("resolveUri", () => {
|
|
4
|
+
it("maps ipfs:// (and ipfs://ipfs/) to the gateway, ar:// to arweave, passes http/data through", () => {
|
|
5
|
+
expect(resolveUri("ipfs://bafyCID/1.json")).toBe("https://ipfs.io/ipfs/bafyCID/1.json");
|
|
6
|
+
expect(resolveUri("ipfs://ipfs/bafyCID")).toBe("https://ipfs.io/ipfs/bafyCID");
|
|
7
|
+
expect(resolveUri("ipfs://CID", "https://x.mypinata.cloud/ipfs/")).toBe("https://x.mypinata.cloud/ipfs/CID");
|
|
8
|
+
expect(resolveUri("ar://TXID")).toBe("https://arweave.net/TXID");
|
|
9
|
+
expect(resolveUri("https://example.com/1.json")).toBe("https://example.com/1.json");
|
|
10
|
+
expect(resolveUri("data:application/json,{}")).toBe("data:application/json,{}");
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
// A fake Chain that returns a canned tokenURI/uri string — no network, no RPC.
|
|
14
|
+
function fakeChain(uri) {
|
|
15
|
+
return { call: async () => uri };
|
|
16
|
+
}
|
|
17
|
+
describe("fetchTokenMetadata", () => {
|
|
18
|
+
it("reads tokenURI, parses a base64 data: doc, and resolves the ipfs image", async () => {
|
|
19
|
+
const doc = { name: "Glyph #7", description: "x", image: "ipfs://imgCID/7.png", attributes: [{ trait_type: "Eyes", value: "Laser" }] };
|
|
20
|
+
const dataUri = "data:application/json;base64," + btoa(JSON.stringify(doc));
|
|
21
|
+
const m = await fetchTokenMetadata(fakeChain(dataUri), "0xabc", 7n);
|
|
22
|
+
expect(m?.name).toBe("Glyph #7");
|
|
23
|
+
expect(m?.image).toBe("https://ipfs.io/ipfs/imgCID/7.png");
|
|
24
|
+
expect(m?.imageRaw).toBe("ipfs://imgCID/7.png");
|
|
25
|
+
expect(m?.attributes?.[0]).toEqual({ trait_type: "Eyes", value: "Laser" });
|
|
26
|
+
});
|
|
27
|
+
it("parses a plain (url-encoded) data: doc and image_url fallback", async () => {
|
|
28
|
+
const doc = { name: "B", image_url: "https://cdn/x.png" };
|
|
29
|
+
const dataUri = "data:application/json," + encodeURIComponent(JSON.stringify(doc));
|
|
30
|
+
const m = await fetchTokenMetadata(fakeChain(dataUri), "0xabc", 1n);
|
|
31
|
+
expect(m?.image).toBe("https://cdn/x.png");
|
|
32
|
+
});
|
|
33
|
+
it("substitutes {id} (64-hex padded) for ERC-1155 uris before fetching", async () => {
|
|
34
|
+
// uri(id) returns a template with {id}; we replace it across the whole URI. Here the
|
|
35
|
+
// literal {id} sits in a plain data: body so the substituted value is observable.
|
|
36
|
+
const uri = 'data:application/json,{"name":"id-{id}"}';
|
|
37
|
+
const m = await fetchTokenMetadata(fakeChain(uri), "0xabc", 1n, { standard: "erc1155" });
|
|
38
|
+
expect(m?.name).toBe("id-0000000000000000000000000000000000000000000000000000000000000001");
|
|
39
|
+
});
|
|
40
|
+
it("returns null when the URI read reverts", async () => {
|
|
41
|
+
const reverting = { call: async () => { throw new Error("revert"); } };
|
|
42
|
+
expect(await fetchTokenMetadata(reverting, "0xabc", 1n)).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
});
|
package/dist/polymarket.d.ts
CHANGED
|
@@ -38,6 +38,15 @@ export interface PolymarketOptions {
|
|
|
38
38
|
* reads always work).
|
|
39
39
|
*/
|
|
40
40
|
allowLegacyV1?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Polymarket Builder/Relayer API key that authorizes V2 deposit-wallet deployment
|
|
43
|
+
* + order placement. On Livo this is a PLATFORM credential (one Livo-held builder
|
|
44
|
+
* key serving every project), injected as env.POLYMARKET_BUILDER_API_KEY — projects
|
|
45
|
+
* never set it. Default: env.POLYMARKET_BUILDER_API_KEY. (V2 order placement via
|
|
46
|
+
* @polymarket/client is not wired yet even when present — see
|
|
47
|
+
* docs/POLYMARKET-V2-IMPLEMENTATION.md.)
|
|
48
|
+
*/
|
|
49
|
+
builderApiKey?: string;
|
|
41
50
|
/** Signing key. Default: env.POLYMARKET_PRIVATE_KEY, then env.RELAYER_PRIVATE_KEY. */
|
|
42
51
|
privateKey?: string;
|
|
43
52
|
/** Env var name to read the key from. Default: "POLYMARKET_PRIVATE_KEY". */
|
|
@@ -85,6 +94,7 @@ export declare class Polymarket {
|
|
|
85
94
|
private readonly host;
|
|
86
95
|
private readonly privateKey?;
|
|
87
96
|
private readonly legacyV1;
|
|
97
|
+
private readonly builderApiKey?;
|
|
88
98
|
private readonly contracts;
|
|
89
99
|
private creds?;
|
|
90
100
|
constructor(env: MinimalEnv | undefined, options?: PolymarketOptions);
|
|
@@ -122,6 +132,8 @@ export declare class Polymarket {
|
|
|
122
132
|
positions(user?: string): Promise<unknown>;
|
|
123
133
|
/** Ensure API credentials exist (derive existing, else create). Cached on the instance. */
|
|
124
134
|
ensureCreds(): Promise<PolymarketCreds>;
|
|
135
|
+
/** True once Livo's platform Polymarket builder key is configured (env.POLYMARKET_BUILDER_API_KEY). */
|
|
136
|
+
get builderConfigured(): boolean;
|
|
125
137
|
private assertTradingEnabled;
|
|
126
138
|
private deriveOrCreateApiKey;
|
|
127
139
|
/** Build, sign, and submit an order. Returns the CLOB response JSON. */
|
package/dist/polymarket.js
CHANGED
|
@@ -171,12 +171,16 @@ export class Polymarket {
|
|
|
171
171
|
host;
|
|
172
172
|
privateKey;
|
|
173
173
|
legacyV1;
|
|
174
|
+
builderApiKey;
|
|
174
175
|
contracts;
|
|
175
176
|
creds;
|
|
176
177
|
constructor(env, options = {}) {
|
|
177
178
|
this.chainId = options.chainId ?? Number(env?.POLYMARKET_CHAIN_ID ?? 137);
|
|
178
179
|
this.host = options.host ?? env?.POLYMARKET_CLOB_HOST ?? CLOB_HOST;
|
|
179
180
|
this.legacyV1 = options.allowLegacyV1 ?? (env?.POLYMARKET_ALLOW_LEGACY_V1 === "1" || env?.POLYMARKET_ALLOW_LEGACY_V1 === "true");
|
|
181
|
+
// Platform-injected Livo builder credential (one key for all projects). Read-only here;
|
|
182
|
+
// V2 order placement via @polymarket/client isn't wired yet, so this is detection only.
|
|
183
|
+
this.builderApiKey = options.builderApiKey ?? env?.POLYMARKET_BUILDER_API_KEY;
|
|
180
184
|
this.contracts = this.legacyV1 ? CONTRACTS_V1 : CONTRACTS_V2;
|
|
181
185
|
const keySecret = options.privateKeySecret ?? "POLYMARKET_PRIVATE_KEY";
|
|
182
186
|
this.privateKey =
|
|
@@ -306,15 +310,24 @@ export class Polymarket {
|
|
|
306
310
|
// All trading + L2-auth methods route through ensureCreds, so this one guard covers
|
|
307
311
|
// them. Data reads never call it. V2 order signing isn't correctly implemented yet
|
|
308
312
|
// (see the header note) — gate it rather than submit invalid orders.
|
|
313
|
+
/** True once Livo's platform Polymarket builder key is configured (env.POLYMARKET_BUILDER_API_KEY). */
|
|
314
|
+
get builderConfigured() {
|
|
315
|
+
return Boolean(this.builderApiKey);
|
|
316
|
+
}
|
|
309
317
|
assertTradingEnabled() {
|
|
310
318
|
if (this.legacyV1)
|
|
311
319
|
return; // deprecated V1 path (testnet/legacy)
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
320
|
+
// V2 trading needs BOTH (1) Livo's platform builder key and (2) the @polymarket/client
|
|
321
|
+
// wrap — which isn't wired yet. Message reflects which prerequisite is missing.
|
|
322
|
+
if (this.builderConfigured) {
|
|
323
|
+
throw new Error("Polymarket builder key is configured, but V2 order placement via @polymarket/client isn't wired up in " +
|
|
324
|
+
"this runtime yet (deposit-wallet deploy + EIP-1271 signing) — tracked in docs/POLYMARKET-V2-IMPLEMENTATION.md. " +
|
|
325
|
+
"Market-DATA reads work. { allowLegacyV1: true } enables the deprecated V1 path on testnet.");
|
|
326
|
+
}
|
|
327
|
+
throw new Error("Polymarket V2 trading needs Livo's platform builder key (env.POLYMARKET_BUILDER_API_KEY), which isn't " +
|
|
328
|
+
"configured. V2 (Apr 2026) requires the deposit-wallet flow — a plain EOA maker is rejected — and deploying " +
|
|
329
|
+
"a deposit wallet needs a Polymarket Builder/Relayer credential (one Livo-held key serves all projects; see " +
|
|
330
|
+
"docs/POLYMARKET-V2-IMPLEMENTATION.md). Market-DATA reads work without it. { allowLegacyV1: true } = V1 testnet.");
|
|
318
331
|
}
|
|
319
332
|
async deriveOrCreateApiKey() {
|
|
320
333
|
// Try derive (deterministic) first; fall back to create.
|
package/dist/polymarket.test.js
CHANGED
|
@@ -68,6 +68,16 @@ describe("Polymarket V2 trading gate", () => {
|
|
|
68
68
|
expect(new Polymarket({}).collateralToken).toBe("0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB");
|
|
69
69
|
expect(new Polymarket({}, { allowLegacyV1: true }).collateralToken).toBeUndefined();
|
|
70
70
|
});
|
|
71
|
+
it("detects the platform builder key and adjusts the gate message", async () => {
|
|
72
|
+
// No key configured → message points at the missing platform builder key.
|
|
73
|
+
const noKey = new Polymarket({ POLYMARKET_PRIVATE_KEY: PK });
|
|
74
|
+
expect(noKey.builderConfigured).toBe(false);
|
|
75
|
+
await expect(noKey.placeOrder({ tokenId: "1", side: "BUY", price: 0.5, size: 2, tickSize: "0.01", negRisk: false })).rejects.toThrow(/platform builder key/);
|
|
76
|
+
// Platform key injected (env) → detected; gate now points at the un-wired SDK path.
|
|
77
|
+
const withKey = new Polymarket({ POLYMARKET_PRIVATE_KEY: PK, POLYMARKET_BUILDER_API_KEY: "blder_test" });
|
|
78
|
+
expect(withKey.builderConfigured).toBe(true);
|
|
79
|
+
await expect(withKey.placeOrder({ tokenId: "1", side: "BUY", price: 0.5, size: 2, tickSize: "0.01", negRisk: false })).rejects.toThrow(/isn't wired up/);
|
|
80
|
+
});
|
|
71
81
|
});
|
|
72
82
|
describe("Polymarket data helpers", () => {
|
|
73
83
|
it("resolveMarket maps Gamma clobTokenIds + outcomes to tradable tokens", async () => {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface KvLike {
|
|
2
|
+
get(key: string): Promise<string | null>;
|
|
3
|
+
put(key: string, value: string, options?: {
|
|
4
|
+
expirationTtl?: number;
|
|
5
|
+
}): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export interface RateLimitOptions {
|
|
8
|
+
/** Max requests allowed in the window. */
|
|
9
|
+
limit: number;
|
|
10
|
+
/** Window length in seconds. */
|
|
11
|
+
windowSeconds: number;
|
|
12
|
+
/** Key prefix (default "rl"). */
|
|
13
|
+
prefix?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface RateLimitResult {
|
|
16
|
+
allowed: boolean;
|
|
17
|
+
remaining: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function rateLimit(kv: KvLike, key: string, opts: RateLimitOptions): Promise<RateLimitResult>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// A KV-backed fixed-window rate limiter for bots / api Workers (e.g. per wallet or
|
|
2
|
+
// per Telegram user). Pass the project's env.KV.
|
|
3
|
+
// Increment the counter for `key`; allow until `limit` is reached within the window.
|
|
4
|
+
export async function rateLimit(kv, key, opts) {
|
|
5
|
+
const k = `${opts.prefix ?? "rl"}:${key}`;
|
|
6
|
+
const current = Number((await kv.get(k)) ?? "0");
|
|
7
|
+
if (current >= opts.limit)
|
|
8
|
+
return { allowed: false, remaining: 0 };
|
|
9
|
+
await kv.put(k, String(current + 1), { expirationTtl: opts.windowSeconds });
|
|
10
|
+
return { allowed: true, remaining: Math.max(0, opts.limit - current - 1) };
|
|
11
|
+
}
|
package/dist/reads.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Chain } from "./chain.js";
|
|
2
|
+
export interface TokenMetadata {
|
|
3
|
+
name?: string;
|
|
4
|
+
symbol?: string;
|
|
5
|
+
decimals?: number;
|
|
6
|
+
}
|
|
7
|
+
/** ERC-20 name / symbol / decimals (each best-effort — missing fields stay undefined). */
|
|
8
|
+
export declare function tokenMetadata(chain: Chain, token: string): Promise<TokenMetadata>;
|
|
9
|
+
/** Owner of an ERC-721 token id (or null if the read reverts, e.g. unminted). */
|
|
10
|
+
export declare function ownerOf(chain: Chain, token: string, tokenId: bigint): Promise<string | null>;
|
|
11
|
+
/** ERC-721 tokenURI for a token id (or null on revert). */
|
|
12
|
+
export declare function tokenURI(chain: Chain, token: string, tokenId: bigint): Promise<string | null>;
|