lovewebconnection 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/.env.example +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1937 -2
  4. package/dist/alert-queue.d.ts +72 -0
  5. package/dist/alert-queue.js +155 -0
  6. package/dist/alert-store.d.ts +29 -0
  7. package/dist/alert-store.js +75 -0
  8. package/dist/balances.d.ts +221 -0
  9. package/dist/balances.js +1232 -0
  10. package/dist/cache.d.ts +37 -0
  11. package/dist/cache.js +128 -0
  12. package/dist/call.d.ts +46 -0
  13. package/dist/call.js +377 -0
  14. package/dist/chain.d.ts +97 -0
  15. package/dist/chain.js +419 -0
  16. package/dist/cli.d.ts +2 -0
  17. package/dist/cli.js +1011 -0
  18. package/dist/compare-history.d.ts +82 -0
  19. package/dist/compare-history.js +340 -0
  20. package/dist/compare.d.ts +98 -0
  21. package/dist/compare.js +492 -0
  22. package/dist/contract.d.ts +107 -0
  23. package/dist/contract.js +162 -0
  24. package/dist/default-abi.d.ts +53 -0
  25. package/dist/default-abi.js +85 -0
  26. package/dist/history.d.ts +83 -0
  27. package/dist/history.js +422 -0
  28. package/dist/index.d.ts +54 -0
  29. package/dist/index.js +27 -0
  30. package/dist/market-page.d.ts +8 -0
  31. package/dist/market-page.js +554 -0
  32. package/dist/market-server.d.ts +46 -0
  33. package/dist/market-server.js +473 -0
  34. package/dist/market.d.ts +323 -0
  35. package/dist/market.js +835 -0
  36. package/dist/metadata-cache.d.ts +72 -0
  37. package/dist/metadata-cache.js +197 -0
  38. package/dist/metrics.d.ts +64 -0
  39. package/dist/metrics.js +138 -0
  40. package/dist/plot.d.ts +60 -0
  41. package/dist/plot.js +317 -0
  42. package/dist/presets-history.d.ts +372 -0
  43. package/dist/presets-history.js +1243 -0
  44. package/dist/presets-summary.d.ts +17 -0
  45. package/dist/presets-summary.js +55 -0
  46. package/dist/presets.d.ts +59 -0
  47. package/dist/presets.js +181 -0
  48. package/dist/prices.d.ts +63 -0
  49. package/dist/prices.js +154 -0
  50. package/dist/rotate.d.ts +19 -0
  51. package/dist/rotate.js +34 -0
  52. package/dist/scan.d.ts +81 -0
  53. package/dist/scan.js +501 -0
  54. package/dist/sessions.d.ts +104 -0
  55. package/dist/sessions.js +515 -0
  56. package/dist/signatures.d.ts +13 -0
  57. package/dist/signatures.js +43 -0
  58. package/dist/tokenfile.d.ts +50 -0
  59. package/dist/tokenfile.js +218 -0
  60. package/dist/views.d.ts +63 -0
  61. package/dist/views.js +415 -0
  62. package/dist/watch.d.ts +306 -0
  63. package/dist/watch.js +1121 -0
  64. package/dist/webhook.d.ts +17 -0
  65. package/dist/webhook.js +35 -0
  66. package/examples/erc20.abi.json +57 -0
  67. package/examples/xlayer.tokens.json +4 -0
  68. package/package.json +77 -4
@@ -0,0 +1,162 @@
1
+ import { ChainMismatchError } from "./chain.js";
2
+ async function callRead(client, contract, functionName, args) {
3
+ const result = await client.readContract({
4
+ address: contract.address,
5
+ abi: contract.abi,
6
+ functionName,
7
+ args,
8
+ });
9
+ return result;
10
+ }
11
+ /**
12
+ * ABI-typed read: functionName and args are checked against the ABI, and the
13
+ * return type is derived from the function's outputs.
14
+ */
15
+ export async function readTyped(client, contract, functionName, ...args) {
16
+ return callRead(client, contract, functionName, args);
17
+ }
18
+ /** Bind a client and contract once, then call typed view functions directly. */
19
+ export function createReader(client, contract) {
20
+ return (functionName, ...args) => callRead(client, contract, functionName, args);
21
+ }
22
+ export class ContractCallError extends Error {
23
+ address;
24
+ constructor(message, address) {
25
+ super(message);
26
+ this.name = "ContractCallError";
27
+ this.address = address;
28
+ }
29
+ }
30
+ /** Canonical Multicall3 deployment, present on most EVM chains. */
31
+ export const MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
32
+ export const multicall3Abi = [
33
+ {
34
+ type: "function",
35
+ name: "getChainId",
36
+ stateMutability: "view",
37
+ inputs: [],
38
+ outputs: [{ name: "chainid", type: "uint256" }],
39
+ },
40
+ {
41
+ type: "function",
42
+ name: "getBlockNumber",
43
+ stateMutability: "view",
44
+ inputs: [],
45
+ outputs: [{ name: "blockNumber", type: "uint256" }],
46
+ },
47
+ {
48
+ type: "function",
49
+ name: "getCurrentBlockTimestamp",
50
+ stateMutability: "view",
51
+ inputs: [],
52
+ outputs: [{ name: "timestamp", type: "uint256" }],
53
+ },
54
+ ];
55
+ /** Multicall3 batching surface: aggregate3 plus the native-balance helper. */
56
+ export const multicall3BatchAbi = [
57
+ {
58
+ type: "function",
59
+ name: "aggregate3",
60
+ // aggregate3 is payable on-chain, but this kit only ever reaches it through
61
+ // eth_call, so it is declared view to stay inside the typed read surface.
62
+ stateMutability: "view",
63
+ inputs: [
64
+ {
65
+ name: "calls",
66
+ type: "tuple[]",
67
+ components: [
68
+ { name: "target", type: "address" },
69
+ { name: "allowFailure", type: "bool" },
70
+ { name: "callData", type: "bytes" },
71
+ ],
72
+ },
73
+ ],
74
+ outputs: [
75
+ {
76
+ name: "returnData",
77
+ type: "tuple[]",
78
+ components: [
79
+ { name: "success", type: "bool" },
80
+ { name: "returnData", type: "bytes" },
81
+ ],
82
+ },
83
+ ],
84
+ },
85
+ {
86
+ type: "function",
87
+ name: "getEthBalance",
88
+ stateMutability: "view",
89
+ inputs: [{ name: "addr", type: "address" }],
90
+ outputs: [{ name: "balance", type: "uint256" }],
91
+ },
92
+ ];
93
+ /**
94
+ * Example call against the configured chain: three view functions on Multicall3.
95
+ * These are self-verifying — getChainId() must agree with LWC_CHAIN_ID.
96
+ */
97
+ export async function readExampleCall(client) {
98
+ const code = await client.getCode({ address: MULTICALL3_ADDRESS });
99
+ if (!code || code === "0x" || code === "0x0") {
100
+ throw new ContractCallError(`No contract deployed at ${MULTICALL3_ADDRESS} on this chain; Multicall3 is missing.`, MULTICALL3_ADDRESS);
101
+ }
102
+ const contract = { address: MULTICALL3_ADDRESS, abi: multicall3Abi };
103
+ const [chainId, blockNumber, blockTimestamp] = await Promise.all([
104
+ readTyped(client, contract, "getChainId"),
105
+ readTyped(client, contract, "getBlockNumber"),
106
+ readTyped(client, contract, "getCurrentBlockTimestamp"),
107
+ ]);
108
+ return { contract: MULTICALL3_ADDRESS, chainId, blockNumber, blockTimestamp };
109
+ }
110
+ /** Cross-check the contract's answer against the configured chain id. */
111
+ export function assertContractChain(result, config) {
112
+ const reported = Number(result.chainId);
113
+ if (reported !== config.chainId) {
114
+ throw new ChainMismatchError(config.chainId, reported, "Multicall3 getChainId()");
115
+ }
116
+ }
117
+ function describeAge(seconds) {
118
+ if (seconds < 60)
119
+ return `${seconds}s`;
120
+ if (seconds < 3600)
121
+ return `${Math.floor(seconds / 60)}m`;
122
+ if (seconds < 86_400)
123
+ return `${Math.floor(seconds / 3600)}h`;
124
+ return `${Math.floor(seconds / 86_400)}d`;
125
+ }
126
+ export function formatExampleCall(result, config, latencyMs) {
127
+ const reported = Number(result.chainId);
128
+ const matches = reported === config.chainId;
129
+ const age = Math.max(0, Math.floor(Date.now() / 1000) - Number(result.blockTimestamp));
130
+ const rows = [
131
+ [
132
+ "getChainId()",
133
+ `${reported} ${matches ? "matches LWC_CHAIN_ID" : `MISMATCH, expected ${config.chainId}`}`,
134
+ ],
135
+ ["getBlockNumber()", `${result.blockNumber}`],
136
+ ["getCurrentBlockTimestamp()", `${result.blockTimestamp} (${describeAge(age)} old)`],
137
+ ];
138
+ const width = Math.max(...rows.map(([label]) => label.length));
139
+ return [
140
+ "lwc — example contract call",
141
+ "",
142
+ ` contract Multicall3 (${result.contract})`,
143
+ ` chain ${config.name} (${config.chainId})`,
144
+ ` latency ${latencyMs} ms`,
145
+ "",
146
+ ...rows.map(([label, value]) => ` ${label.padEnd(width)} ${value}`),
147
+ ].join("\n");
148
+ }
149
+ export function toExampleCallJson(result, config, latencyMs) {
150
+ return {
151
+ ok: true,
152
+ contract: { name: "Multicall3", address: result.contract },
153
+ chain: { id: config.chainId, name: config.name },
154
+ latencyMs,
155
+ chainIdMatches: Number(result.chainId) === config.chainId,
156
+ calls: {
157
+ getChainId: result.chainId.toString(),
158
+ getBlockNumber: result.blockNumber.toString(),
159
+ getCurrentBlockTimestamp: result.blockTimestamp.toString(),
160
+ },
161
+ };
162
+ }
@@ -0,0 +1,53 @@
1
+ /** Shown instead of a file path when the built-in ABI is used. */
2
+ export declare const DEFAULT_ABI_LABEL = "built-in ERC-20";
3
+ /**
4
+ * Standard ERC-20 surface, including the write functions so that asking for
5
+ * `transfer` gets the "only view and pure" error rather than "no function".
6
+ * Used whenever `--abi` is omitted, so plain token reads need no file.
7
+ */
8
+ export declare const DEFAULT_ERC20_ABI: ({
9
+ type: "function";
10
+ name: string;
11
+ stateMutability: "view";
12
+ inputs: never[];
13
+ outputs: {
14
+ name: string;
15
+ type: "string";
16
+ }[];
17
+ } | {
18
+ type: "function";
19
+ name: string;
20
+ stateMutability: "view";
21
+ inputs: never[];
22
+ outputs: {
23
+ name: string;
24
+ type: "uint8";
25
+ }[];
26
+ } | {
27
+ type: "function";
28
+ name: string;
29
+ stateMutability: "view";
30
+ inputs: {
31
+ name: string;
32
+ type: "address";
33
+ }[];
34
+ outputs: {
35
+ name: string;
36
+ type: "uint256";
37
+ }[];
38
+ } | {
39
+ type: "function";
40
+ name: string;
41
+ stateMutability: "nonpayable";
42
+ inputs: ({
43
+ name: string;
44
+ type: "address";
45
+ } | {
46
+ name: string;
47
+ type: "uint256";
48
+ })[];
49
+ outputs: {
50
+ name: string;
51
+ type: "bool";
52
+ }[];
53
+ })[];
@@ -0,0 +1,85 @@
1
+ /** Shown instead of a file path when the built-in ABI is used. */
2
+ export const DEFAULT_ABI_LABEL = "built-in ERC-20";
3
+ /**
4
+ * Standard ERC-20 surface, including the write functions so that asking for
5
+ * `transfer` gets the "only view and pure" error rather than "no function".
6
+ * Used whenever `--abi` is omitted, so plain token reads need no file.
7
+ */
8
+ export const DEFAULT_ERC20_ABI = [
9
+ {
10
+ type: "function",
11
+ name: "name",
12
+ stateMutability: "view",
13
+ inputs: [],
14
+ outputs: [{ name: "", type: "string" }],
15
+ },
16
+ {
17
+ type: "function",
18
+ name: "symbol",
19
+ stateMutability: "view",
20
+ inputs: [],
21
+ outputs: [{ name: "", type: "string" }],
22
+ },
23
+ {
24
+ type: "function",
25
+ name: "decimals",
26
+ stateMutability: "view",
27
+ inputs: [],
28
+ outputs: [{ name: "", type: "uint8" }],
29
+ },
30
+ {
31
+ type: "function",
32
+ name: "totalSupply",
33
+ stateMutability: "view",
34
+ inputs: [],
35
+ outputs: [{ name: "", type: "uint256" }],
36
+ },
37
+ {
38
+ type: "function",
39
+ name: "balanceOf",
40
+ stateMutability: "view",
41
+ inputs: [{ name: "account", type: "address" }],
42
+ outputs: [{ name: "", type: "uint256" }],
43
+ },
44
+ {
45
+ type: "function",
46
+ name: "allowance",
47
+ stateMutability: "view",
48
+ inputs: [
49
+ { name: "owner", type: "address" },
50
+ { name: "spender", type: "address" },
51
+ ],
52
+ outputs: [{ name: "remaining", type: "uint256" }],
53
+ },
54
+ {
55
+ type: "function",
56
+ name: "transfer",
57
+ stateMutability: "nonpayable",
58
+ inputs: [
59
+ { name: "to", type: "address" },
60
+ { name: "amount", type: "uint256" },
61
+ ],
62
+ outputs: [{ name: "", type: "bool" }],
63
+ },
64
+ {
65
+ type: "function",
66
+ name: "approve",
67
+ stateMutability: "nonpayable",
68
+ inputs: [
69
+ { name: "spender", type: "address" },
70
+ { name: "amount", type: "uint256" },
71
+ ],
72
+ outputs: [{ name: "", type: "bool" }],
73
+ },
74
+ {
75
+ type: "function",
76
+ name: "transferFrom",
77
+ stateMutability: "nonpayable",
78
+ inputs: [
79
+ { name: "from", type: "address" },
80
+ { name: "to", type: "address" },
81
+ { name: "amount", type: "uint256" },
82
+ ],
83
+ outputs: [{ name: "", type: "bool" }],
84
+ },
85
+ ];
@@ -0,0 +1,83 @@
1
+ import type { Address } from "viem";
2
+ export declare class HistoryArgError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export interface HistoryArgs {
6
+ /** Watch log to read. */
7
+ path: string;
8
+ /** Where to write CSV, "-" for stdout, or null for the table. */
9
+ csvPath: string | null;
10
+ /** Only rows from this session id. */
11
+ session: string | null;
12
+ /** Pivot to one column per asset instead of one row per asset. */
13
+ wide: boolean;
14
+ /** Add a USD value column per asset to the wide CSV. */
15
+ usd: boolean;
16
+ /** Emit only the USD-valued asset columns, dropping the raw balances. */
17
+ usdOnly: boolean;
18
+ }
19
+ export declare function parseHistoryArgs(argv: string[]): HistoryArgs;
20
+ export declare function readHistoryLog(path: string): string;
21
+ export interface HistoryRow {
22
+ session: string;
23
+ /** Epoch milliseconds of the portfolio sample. */
24
+ at: number;
25
+ blockNumber: bigint | null;
26
+ asset: "native" | "token";
27
+ /** Token address, or null for the native balance. */
28
+ address: Address | null;
29
+ symbol: string;
30
+ label: string | null;
31
+ decimals: number | null;
32
+ balance: bigint | null;
33
+ /** Change since the previous sample; null for the first one. */
34
+ delta: bigint | null;
35
+ /** USD value at the sample's prices; null when the asset was not priced. */
36
+ usd: number | null;
37
+ /** Total of the priced holdings for this sample; null when nothing was priced. */
38
+ usdTotal: number | null;
39
+ }
40
+ export interface HistoryReport {
41
+ path: string;
42
+ rows: HistoryRow[];
43
+ /** Sessions that contributed rows, in log order. */
44
+ sessions: string[];
45
+ /** Portfolio samples that carried an error and no balances. */
46
+ skippedSamples: number;
47
+ /** Lines that were not JSON objects with a session id. */
48
+ skippedLines: number;
49
+ }
50
+ /**
51
+ * Turn the `portfolio` events of a watch NDJSON log into one row per asset per
52
+ * sample. A log holds every event type, so anything else is ignored; an errored
53
+ * sample has no balances and is counted instead of emitting blank rows.
54
+ */
55
+ export declare function extractBalanceHistory(text: string, options?: {
56
+ session?: string | null;
57
+ }): Omit<HistoryReport, "path">;
58
+ export declare const HISTORY_CSV_HEADER: readonly ["session", "at", "block", "asset", "address", "symbol", "label", "decimals", "balance", "delta", "usd", "usd_total"];
59
+ /**
60
+ * Long-format CSV: one row per asset per sample, with an ISO timestamp, the
61
+ * human-readable balance, and the change since the previous sample. Long format
62
+ * keeps the columns stable as tokens come and go, so a chart can group by
63
+ * `symbol` or `address`.
64
+ */
65
+ export declare function historyCsv(report: Pick<HistoryReport, "rows">): string;
66
+ /**
67
+ * Wide-format CSV: one row per portfolio sample and one column per asset, for
68
+ * tools that expect a single time series per column. `session`, `at`, and
69
+ * `block` stay first so samples from different runs remain distinguishable; an
70
+ * asset missing from a sample leaves its cell empty rather than repeating it.
71
+ * `usd_total` closes every row. With `options.usd`, a `SYMBOL (USD)` column
72
+ * follows each asset column and uses the values priced by `watch --usd`. With
73
+ * `options.usdOnly`, the raw balance columns are dropped and only those
74
+ * `SYMBOL (USD)` columns remain, for a purely value-based series.
75
+ */
76
+ export declare function historyWideCsv(report: Pick<HistoryReport, "rows">, options?: {
77
+ usd?: boolean;
78
+ usdOnly?: boolean;
79
+ }): string;
80
+ /** Write the CSV, creating parent directories; a derived file is overwritten. */
81
+ export declare function saveHistoryCsv(path: string, csv: string): void;
82
+ export declare function formatHistory(report: HistoryReport): string;
83
+ export declare function toHistoryJson(report: HistoryReport): Record<string, unknown>;