chain-insights 0.73.2 → 0.73.3
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/cli.cjs +2 -2
- package/dist/cli.mjs +2 -2
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/mcp-proxy.cjs +1 -1
- package/dist/mcp-proxy.mjs +1 -1
- package/dist/{topup-CfFe1bGS.mjs → topup-C-Tczr2k.mjs} +2 -2
- package/dist/{topup-CfFe1bGS.mjs.map → topup-C-Tczr2k.mjs.map} +1 -1
- package/dist/{topup--nWtDnWP.cjs → topup-Do_4EAld.cjs} +1 -1
- package/dist/{topup-server-BkGM8P99.cjs → topup-server-B1cdQDFT.cjs} +1 -1
- package/dist/{topup-server-RQ6-c92F.mjs → topup-server-CSF5EjNt.mjs} +60 -69
- package/dist/topup-server-CSF5EjNt.mjs.map +1 -0
- package/dist/{topup-server-C58eWflv.cjs → topup-server-DH3qVMvO.cjs} +59 -68
- package/dist/{topup-server-B0VKHslr.mjs → topup-server-qdruzcre.mjs} +2 -2
- package/dist/{topup-server-B0VKHslr.mjs.map → topup-server-qdruzcre.mjs.map} +1 -1
- package/package.json +1 -1
- package/dist/topup-server-RQ6-c92F.mjs.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -122,7 +122,7 @@ function fail(err) {
|
|
|
122
122
|
function registerSubscriptionCommands(program, command) {
|
|
123
123
|
program.command("subscription").description("Buy and check the Chain Insights subscription, paid in $CIA on Robinhood Chain").addCommand(command("topup").description("Show the wallet address and QR code to fund with $CIA and ETH on Robinhood Chain").option("--no-open", "Print the top-up page URL without opening a browser").option("--json", "Print machine-readable top-up facts").action(async (opts) => {
|
|
124
124
|
try {
|
|
125
|
-
const { runSubscriptionTopup } = await Promise.resolve().then(() => require("./topup
|
|
125
|
+
const { runSubscriptionTopup } = await Promise.resolve().then(() => require("./topup-Do_4EAld.cjs"));
|
|
126
126
|
await runSubscriptionTopup(opts);
|
|
127
127
|
} catch (err) {
|
|
128
128
|
fail(err);
|
|
@@ -592,7 +592,7 @@ program.command("wallet").description("Manage the local Base USDC payment wallet
|
|
|
592
592
|
})).addCommand(createCliCommand("topup").description("Open a local browser page to top up the payment wallet").option("--no-open", "Print the top-up URL without opening a browser").option("--json", "Print machine-readable top-up metadata").action(async (opts) => {
|
|
593
593
|
try {
|
|
594
594
|
const { assertWalletKeyNotPublic, buildTopupInfo, getWalletAccount } = await Promise.resolve().then(() => require("./tools-Cmnq7yip.cjs")).then((n) => n.tools_exports);
|
|
595
|
-
const { startTopupServer } = await Promise.resolve().then(() => require("./topup-server-
|
|
595
|
+
const { startTopupServer } = await Promise.resolve().then(() => require("./topup-server-B1cdQDFT.cjs")).then((n) => n.topup_server_exports);
|
|
596
596
|
const account = await getWalletAccount();
|
|
597
597
|
assertWalletKeyNotPublic(account);
|
|
598
598
|
const url = await startTopupServer(account);
|
package/dist/cli.mjs
CHANGED
|
@@ -120,7 +120,7 @@ function fail(err) {
|
|
|
120
120
|
function registerSubscriptionCommands(program, command) {
|
|
121
121
|
program.command("subscription").description("Buy and check the Chain Insights subscription, paid in $CIA on Robinhood Chain").addCommand(command("topup").description("Show the wallet address and QR code to fund with $CIA and ETH on Robinhood Chain").option("--no-open", "Print the top-up page URL without opening a browser").option("--json", "Print machine-readable top-up facts").action(async (opts) => {
|
|
122
122
|
try {
|
|
123
|
-
const { runSubscriptionTopup } = await import("./topup-
|
|
123
|
+
const { runSubscriptionTopup } = await import("./topup-C-Tczr2k.mjs");
|
|
124
124
|
await runSubscriptionTopup(opts);
|
|
125
125
|
} catch (err) {
|
|
126
126
|
fail(err);
|
|
@@ -590,7 +590,7 @@ program.command("wallet").description("Manage the local Base USDC payment wallet
|
|
|
590
590
|
})).addCommand(createCliCommand("topup").description("Open a local browser page to top up the payment wallet").option("--no-open", "Print the top-up URL without opening a browser").option("--json", "Print machine-readable top-up metadata").action(async (opts) => {
|
|
591
591
|
try {
|
|
592
592
|
const { assertWalletKeyNotPublic, buildTopupInfo, getWalletAccount } = await import("./tools-D9ep9E7Q.mjs").then((n) => n.p);
|
|
593
|
-
const { startTopupServer } = await import("./topup-server-
|
|
593
|
+
const { startTopupServer } = await import("./topup-server-qdruzcre.mjs").then((n) => n.r);
|
|
594
594
|
const account = await getWalletAccount();
|
|
595
595
|
assertWalletKeyNotPublic(account);
|
|
596
596
|
const url = await startTopupServer(account);
|
package/dist/index.cjs
CHANGED
|
@@ -3,8 +3,8 @@ const require_config = require("./config-BxtMoo9c.cjs");
|
|
|
3
3
|
const require_wallet = require("./wallet-CgIAfdxL.cjs");
|
|
4
4
|
const require_create = require("./create-DdggkF04.cjs");
|
|
5
5
|
const require_tools = require("./tools-Cmnq7yip.cjs");
|
|
6
|
-
const require_topup_server = require("./topup-server-
|
|
7
|
-
const require_topup_server$1 = require("./topup-server-
|
|
6
|
+
const require_topup_server = require("./topup-server-DH3qVMvO.cjs");
|
|
7
|
+
const require_topup_server$1 = require("./topup-server-B1cdQDFT.cjs");
|
|
8
8
|
const require_client = require("./client-C9uwHnBS.cjs");
|
|
9
9
|
exports.buildTopupInfo = require_tools.buildTopupInfo;
|
|
10
10
|
exports.createMcpFetchClient = require_client.createMcpFetchClient;
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/config/schema.ts","../src/config/index.ts","../src/wallet/index.ts","../src/wallet/create.ts","../src/wallet/tools.ts","../src/wallet/mcp-proxy/topup-server.ts","../src/wallet/topup-server.ts","../src/mcp/client.ts"],"mappings":";;;cA4Da,cAAY,EAAA;;;;;;;;;;;;;;;;GAcvB,EAAA,KAAA;KAEU,qBAAqB,EAAE,aAAa;;;wBC3B1B,cAAc,QAAQ;wBAMtB,WAAW,SAAS,QAAQ,sBAAsB;wBAsDlD,oBAAoB;;;wBCnF1B,0BAA0B,gBAAgB;wBAO1C,4BAA4B,qBAAqB;;;;;;;;wBAW3C,WAAW,qBAAqB;UAwBrC;;EAEf;;;;;;;;wBAuCoB,oBACpB,oBACA,UAAS,6BACR,QAAQ;;;;;;;wBAkEW,cAAc;;;;;wBAwCd,sBAAsB;;;UCnN3B;EACf;EACA;;wBAGc,4BAA4B;wBAI5B,wBAAwB;wBAQxB,0BACd,oBACA,UAAS;;;cCXE;cACA;UAgDI;EACf,SAAS;EACT,YAAY;;EAEZ;;UAGe;EACf;EACA;EACA,iBAAiB;EACjB;EACA,uBAAuB;EACvB;;UAoDe;EACf,SAAS;EACT;EACA;EACA,iBAAiB;EACjB;EACA;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA;IACE,QAAQ;;;;;;;;wBASU,oBAAoB,QAAQ;wBAoB5B,eACpB,SAAS,kBACT,8BACC;wBAsBmB,cACpB,SAAS,kBACT,8BACC;wBAuQa,oBACd,iBACA,qBACA;wBAaoB,uBACpB,UAAU,uBACT,QAAQ;wBAyBK,0BAA0B,QAAQ;wBAK5B,qBAAqB,UAAU,uBAAuB;wBAI5D,eAAe,iBAAiB,oBAAoB;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/config/schema.ts","../src/config/index.ts","../src/wallet/index.ts","../src/wallet/create.ts","../src/wallet/tools.ts","../src/wallet/mcp-proxy/topup-server.ts","../src/wallet/topup-server.ts","../src/mcp/client.ts"],"mappings":";;;cA4Da,cAAY,EAAA;;;;;;;;;;;;;;;;GAcvB,EAAA,KAAA;KAEU,qBAAqB,EAAE,aAAa;;;wBC3B1B,cAAc,QAAQ;wBAMtB,WAAW,SAAS,QAAQ,sBAAsB;wBAsDlD,oBAAoB;;;wBCnF1B,0BAA0B,gBAAgB;wBAO1C,4BAA4B,qBAAqB;;;;;;;;wBAW3C,WAAW,qBAAqB;UAwBrC;;EAEf;;;;;;;;wBAuCoB,oBACpB,oBACA,UAAS,6BACR,QAAQ;;;;;;;wBAkEW,cAAc;;;;;wBAwCd,sBAAsB;;;UCnN3B;EACf;EACA;;wBAGc,4BAA4B;wBAI5B,wBAAwB;wBAQxB,0BACd,oBACA,UAAS;;;cCXE;cACA;UAgDI;EACf,SAAS;EACT,YAAY;;EAEZ;;UAGe;EACf;EACA;EACA,iBAAiB;EACjB;EACA,uBAAuB;EACvB;;UAoDe;EACf,SAAS;EACT;EACA;EACA,iBAAiB;EACjB;EACA;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA;IACE,QAAQ;;;;;;;;wBASU,oBAAoB,QAAQ;wBAoB5B,eACpB,SAAS,kBACT,8BACC;wBAsBmB,cACpB,SAAS,kBACT,8BACC;wBAuQa,oBACd,iBACA,qBACA;wBAaoB,uBACpB,UAAU,uBACT,QAAQ;wBAyBK,0BAA0B,QAAQ;wBAK5B,qBAAqB,UAAU,uBAAuB;wBAI5D,eAAe,iBAAiB,oBAAoB;;;wBCxUpD,qBAAqB,4BAA4B;;;wBCnFjD;wBAcM,iBAAiB,SAAS,gCAAgC;;;;;;;;;;;;;;;wBCkKhE,qBAAqB,2BAA2B,4BAAkB"}
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/config/schema.ts","../src/config/index.ts","../src/wallet/index.ts","../src/wallet/create.ts","../src/wallet/tools.ts","../src/wallet/mcp-proxy/topup-server.ts","../src/wallet/topup-server.ts","../src/mcp/client.ts"],"mappings":";;;cA4Da,cAAY,EAAA;;;;;;;;;;;;;;;;GAcvB,EAAA,KAAA;KAEU,qBAAqB,EAAE,aAAa;;;wBC3B1B,cAAc,QAAQ;wBAMtB,WAAW,SAAS,QAAQ,sBAAsB;wBAsDlD,oBAAoB;;;wBCnF1B,0BAA0B,gBAAgB;wBAO1C,4BAA4B,qBAAqB;;;;;;;;wBAW3C,WAAW,qBAAqB;UAwBrC;;EAEf;;;;;;;;wBAuCoB,oBACpB,oBACA,UAAS,6BACR,QAAQ;;;;;;;wBAkEW,cAAc;;;;;wBAwCd,sBAAsB;;;UCnN3B;EACf;EACA;;wBAGc,4BAA4B;wBAI5B,wBAAwB;wBAQxB,0BACd,oBACA,UAAS;;;cCXE;cACA;UAgDI;EACf,SAAS;EACT,YAAY;;EAEZ;;UAGe;EACf;EACA;EACA,iBAAiB;EACjB;EACA,uBAAuB;EACvB;;UAoDe;EACf,SAAS;EACT;EACA;EACA,iBAAiB;EACjB;EACA;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA;IACE,QAAQ;;;;;;;;wBASU,oBAAoB,QAAQ;wBAoB5B,eACpB,SAAS,kBACT,8BACC;wBAsBmB,cACpB,SAAS,kBACT,8BACC;wBAuQa,oBACd,iBACA,qBACA;wBAaoB,uBACpB,UAAU,uBACT,QAAQ;wBAyBK,0BAA0B,QAAQ;wBAK5B,qBAAqB,UAAU,uBAAuB;wBAI5D,eAAe,iBAAiB,oBAAoB;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/config/schema.ts","../src/config/index.ts","../src/wallet/index.ts","../src/wallet/create.ts","../src/wallet/tools.ts","../src/wallet/mcp-proxy/topup-server.ts","../src/wallet/topup-server.ts","../src/mcp/client.ts"],"mappings":";;;cA4Da,cAAY,EAAA;;;;;;;;;;;;;;;;GAcvB,EAAA,KAAA;KAEU,qBAAqB,EAAE,aAAa;;;wBC3B1B,cAAc,QAAQ;wBAMtB,WAAW,SAAS,QAAQ,sBAAsB;wBAsDlD,oBAAoB;;;wBCnF1B,0BAA0B,gBAAgB;wBAO1C,4BAA4B,qBAAqB;;;;;;;;wBAW3C,WAAW,qBAAqB;UAwBrC;;EAEf;;;;;;;;wBAuCoB,oBACpB,oBACA,UAAS,6BACR,QAAQ;;;;;;;wBAkEW,cAAc;;;;;wBAwCd,sBAAsB;;;UCnN3B;EACf;EACA;;wBAGc,4BAA4B;wBAI5B,wBAAwB;wBAQxB,0BACd,oBACA,UAAS;;;cCXE;cACA;UAgDI;EACf,SAAS;EACT,YAAY;;EAEZ;;UAGe;EACf;EACA;EACA,iBAAiB;EACjB;EACA,uBAAuB;EACvB;;UAoDe;EACf,SAAS;EACT;EACA;EACA,iBAAiB;EACjB;EACA;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA;IACE,QAAQ;;;;;;;;wBASU,oBAAoB,QAAQ;wBAoB5B,eACpB,SAAS,kBACT,8BACC;wBAsBmB,cACpB,SAAS,kBACT,8BACC;wBAuQa,oBACd,iBACA,qBACA;wBAaoB,uBACpB,UAAU,uBACT,QAAQ;wBAyBK,0BAA0B,QAAQ;wBAK5B,qBAAqB,UAAU,uBAAuB;wBAI5D,eAAe,iBAAiB,oBAAoB;;;wBCxUpD,qBAAqB,4BAA4B;;;wBCnFjD;wBAcM,iBAAiB,SAAS,gCAAgC;;;;;;;;;;;;;;;wBCkKhE,qBAAqB,2BAA2B,4BAAkB"}
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { i as saveConfig, n as loadConfig, r as resetConfigCache } from "./confi
|
|
|
2
2
|
import { a as setWalletPrivateKey, i as normalizeWalletPrivateKey, n as encryptKey, o as walletAddressFromPrivateKey, r as isWalletConfigured, t as decryptKey } from "./wallet-QTo5YCFR.mjs";
|
|
3
3
|
import { i as isWalletBackupConfirmed, n as formatWalletBackupWarning, r as generateWalletPrivateKey } from "./create-CpfZq2fx.mjs";
|
|
4
4
|
import { a as formatWalletBalanceResult, c as getWalletAccount, i as formatWalletBalance, l as getWalletBalanceResult, o as getBalanceEth, r as buildTopupInfo, s as getBalanceUsdc, u as getWalletBalanceText } from "./tools-D9ep9E7Q.mjs";
|
|
5
|
-
import { n as generateArtifactHtml } from "./topup-server-
|
|
6
|
-
import { n as startTopupServer, t as getTopupUrl } from "./topup-server-
|
|
5
|
+
import { n as generateArtifactHtml } from "./topup-server-CSF5EjNt.mjs";
|
|
6
|
+
import { n as startTopupServer, t as getTopupUrl } from "./topup-server-qdruzcre.mjs";
|
|
7
7
|
import { i as createMcpFetchClient } from "./client-7qOfAdpH.mjs";
|
|
8
8
|
export { buildTopupInfo, createMcpFetchClient, decryptKey, encryptKey, formatWalletBackupWarning, formatWalletBalance, formatWalletBalanceResult, generateArtifactHtml, generateWalletPrivateKey, getBalanceEth, getBalanceUsdc, getTopupUrl, getWalletAccount, getWalletBalanceResult, getWalletBalanceText, isWalletBackupConfirmed, isWalletConfigured, loadConfig, normalizeWalletPrivateKey, resetConfigCache, saveConfig, setWalletPrivateKey, startTopupServer, walletAddressFromPrivateKey };
|
package/dist/mcp-proxy.cjs
CHANGED
|
@@ -1833,7 +1833,7 @@ async function createProxy() {
|
|
|
1833
1833
|
};
|
|
1834
1834
|
server.registerTool(tool.name, toolConfig, handler);
|
|
1835
1835
|
}
|
|
1836
|
-
const { registerTopupView, TOPUP_VIEW_URI } = await Promise.resolve().then(() => require("./topup-server-
|
|
1836
|
+
const { registerTopupView, TOPUP_VIEW_URI } = await Promise.resolve().then(() => require("./topup-server-DH3qVMvO.cjs")).then((n) => n.topup_server_exports);
|
|
1837
1837
|
await registerTopupView(server, { walletAddress: localTopupWalletAddress });
|
|
1838
1838
|
server.registerResource("Chain Insights view", CLAUDE_VIEW_URI, {
|
|
1839
1839
|
title: "Chain Insights view",
|
package/dist/mcp-proxy.mjs
CHANGED
|
@@ -1829,7 +1829,7 @@ async function createProxy() {
|
|
|
1829
1829
|
};
|
|
1830
1830
|
server.registerTool(tool.name, toolConfig, handler);
|
|
1831
1831
|
}
|
|
1832
|
-
const { registerTopupView, TOPUP_VIEW_URI } = await import("./topup-server-
|
|
1832
|
+
const { registerTopupView, TOPUP_VIEW_URI } = await import("./topup-server-CSF5EjNt.mjs").then((n) => n.a);
|
|
1833
1833
|
await registerTopupView(server, { walletAddress: localTopupWalletAddress });
|
|
1834
1834
|
server.registerResource("Chain Insights view", CLAUDE_VIEW_URI, {
|
|
1835
1835
|
title: "Chain Insights view",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as startTopupServer, o as qrTerminalText, t as SUBSCRIPTION_TOPUP_TEXT } from "./topup-server-
|
|
1
|
+
import { i as startTopupServer, o as qrTerminalText, t as SUBSCRIPTION_TOPUP_TEXT } from "./topup-server-CSF5EjNt.mjs";
|
|
2
2
|
import { a as ROBINHOOD_NETWORK_DISPLAY, n as CIA_TOKEN_ADDRESS, r as ROBINHOOD_CHAIN_ID, t as CIA_SYMBOL } from "./robinhood-CCTMagwM.mjs";
|
|
3
3
|
//#region src/subscription/topup.ts
|
|
4
4
|
function subscriptionTopupFacts(walletAddress, topupUrl) {
|
|
@@ -42,4 +42,4 @@ async function runSubscriptionTopup(opts) {
|
|
|
42
42
|
//#endregion
|
|
43
43
|
export { runSubscriptionTopup };
|
|
44
44
|
|
|
45
|
-
//# sourceMappingURL=topup-
|
|
45
|
+
//# sourceMappingURL=topup-C-Tczr2k.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"topup-
|
|
1
|
+
{"version":3,"file":"topup-C-Tczr2k.mjs","names":[],"sources":["../src/subscription/topup.ts"],"sourcesContent":["import { qrTerminalText } from '../wallet/mcp-proxy/qr.js'\nimport { SUBSCRIPTION_TOPUP_TEXT, startTopupServer } from '../wallet/mcp-proxy/topup-server.js'\nimport {\n CIA_SYMBOL,\n CIA_TOKEN_ADDRESS,\n ROBINHOOD_CHAIN_ID,\n ROBINHOOD_NETWORK_DISPLAY,\n} from '../wallet/robinhood.js'\n\nexport interface SubscriptionTopupFacts {\n wallet_address: string\n network: typeof ROBINHOOD_NETWORK_DISPLAY\n chain_id: typeof ROBINHOOD_CHAIN_ID\n token: typeof CIA_SYMBOL\n token_contract: typeof CIA_TOKEN_ADDRESS\n gas_token: 'ETH'\n topup_url?: string\n}\n\nexport function subscriptionTopupFacts(walletAddress: string, topupUrl?: string): SubscriptionTopupFacts {\n return {\n wallet_address: walletAddress,\n network: ROBINHOOD_NETWORK_DISPLAY,\n chain_id: ROBINHOOD_CHAIN_ID,\n token: CIA_SYMBOL,\n token_contract: CIA_TOKEN_ADDRESS,\n gas_token: 'ETH',\n ...(topupUrl ? { topup_url: topupUrl } : {}),\n }\n}\n\n/** The terminal text of `cia subscription topup`: facts, then the QR code. */\nexport function subscriptionTopupText(facts: SubscriptionTopupFacts): string {\n return [\n ...(facts.topup_url ? [`Top-up page: ${facts.topup_url}`] : []),\n `Wallet: ${facts.wallet_address}`,\n `Network: ${facts.network} (chain ID ${facts.chain_id})`,\n `Token: ${facts.token} ${facts.token_contract}, and a little ETH for gas`,\n '',\n qrTerminalText(facts.wallet_address),\n '',\n SUBSCRIPTION_TOPUP_TEXT,\n ].join('\\n')\n}\n\n/** `cia subscription topup`: the user's own wallet, never the payment address. */\nexport async function runSubscriptionTopup(opts: { open?: boolean; json?: boolean }): Promise<void> {\n const { assertWalletKeyNotPublic, getWalletAccount } = await import('../wallet/tools.js')\n const account = await getWalletAccount()\n assertWalletKeyNotPublic(account)\n const url = await startTopupServer(account.address, 'robinhood-cia')\n const facts = subscriptionTopupFacts(account.address, url)\n console.log(opts.json ? JSON.stringify(facts, null, 2) : subscriptionTopupText(facts))\n if (!opts.json) console.log('Press Ctrl+C to stop the top-up page.')\n if (opts.open !== false) {\n const open = (await import('open')).default\n await open(url)\n }\n}\n"],"mappings":";;;AAmBA,SAAgB,uBAAuB,eAAuB,UAA2C;CACvG,OAAO;EACL,gBAAgB;EAChB,SAAS;EACT,UAAU;EACV,OAAO;EACP,gBAAgB;EAChB,WAAW;EACX,GAAI,WAAW,EAAE,WAAW,SAAS,IAAI,CAAC;CAC5C;AACF;;AAGA,SAAgB,sBAAsB,OAAuC;CAC3E,OAAO;EACL,GAAI,MAAM,YAAY,CAAC,gBAAgB,MAAM,WAAW,IAAI,CAAC;EAC7D,gBAAgB,MAAM;EACtB,gBAAgB,MAAM,QAAQ,aAAa,MAAM,SAAS;EAC1D,gBAAgB,MAAM,MAAM,GAAG,MAAM,eAAe;EACpD;EACA,eAAe,MAAM,cAAc;EACnC;EACA;CACF,CAAC,CAAC,KAAK,IAAI;AACb;;AAGA,eAAsB,qBAAqB,MAAyD;CAClG,MAAM,EAAE,0BAA0B,qBAAqB,MAAM,OAAO,uBAAqB,CAAA,MAAA,MAAA,EAAA,CAAA;CACzF,MAAM,UAAU,MAAM,iBAAiB;CACvC,yBAAyB,OAAO;CAChC,MAAM,MAAM,MAAM,iBAAiB,QAAQ,SAAS,eAAe;CACnE,MAAM,QAAQ,uBAAuB,QAAQ,SAAS,GAAG;CACzD,QAAQ,IAAI,KAAK,OAAO,KAAK,UAAU,OAAO,MAAM,CAAC,IAAI,sBAAsB,KAAK,CAAC;CACrF,IAAI,CAAC,KAAK,MAAM,QAAQ,IAAI,uCAAuC;CACnE,IAAI,KAAK,SAAS,OAAO;EACvB,MAAM,QAAQ,MAAM,OAAO,QAAA,CAAS;EACpC,MAAM,KAAK,GAAG;CAChB;AACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_topup_server = require("./topup-server-
|
|
1
|
+
const require_topup_server = require("./topup-server-DH3qVMvO.cjs");
|
|
2
2
|
const require_robinhood = require("./robinhood-ChiT69sc.cjs");
|
|
3
3
|
//#region src/subscription/topup.ts
|
|
4
4
|
function subscriptionTopupFacts(walletAddress, topupUrl) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
|
|
2
|
-
const require_topup_server = require("./topup-server-
|
|
2
|
+
const require_topup_server = require("./topup-server-DH3qVMvO.cjs");
|
|
3
3
|
let viem = require("viem");
|
|
4
4
|
let node_http = require("node:http");
|
|
5
5
|
//#region src/wallet/topup-server.ts
|
|
@@ -301,6 +301,9 @@ var topup_server_exports = /* @__PURE__ */ __exportAll({
|
|
|
301
301
|
topupViewHtml: () => topupViewHtml
|
|
302
302
|
});
|
|
303
303
|
const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
304
|
+
const BASE_CHAIN_ID = "0x2105";
|
|
305
|
+
/** Robinhood Chain, chain ID 4663. */
|
|
306
|
+
const ROBINHOOD_CHAIN_ID_HEX = "0x1237";
|
|
304
307
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
305
308
|
function loadAsset(name) {
|
|
306
309
|
const paths = [join(__dirname, "assets", name), join(__dirname, "..", "src", "assets", name)];
|
|
@@ -626,77 +629,49 @@ function selectAndCopy() {
|
|
|
626
629
|
</body>
|
|
627
630
|
</html>`;
|
|
628
631
|
}
|
|
629
|
-
/** The text of
|
|
632
|
+
/** The text of `cia subscription topup` in the terminal. */
|
|
630
633
|
const SUBSCRIPTION_TOPUP_TEXT = "Send $CIA and a little ETH on Robinhood Chain to your own wallet. Then run `cia subscription buy --days N`.";
|
|
631
634
|
const TOPUP_PAGE_TEXT = {
|
|
632
635
|
"base-usdc": {
|
|
633
636
|
title: "Chain Insights — Fund Wallet",
|
|
634
637
|
subtitle: "Fund your wallet to use blockchain intelligence tools",
|
|
635
638
|
badge: "Base Network · USDC",
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
label: "Current balance",
|
|
639
|
-
currency: "USDC",
|
|
640
|
-
field: "balance_usdc",
|
|
641
|
-
decimals: 2
|
|
642
|
-
}, {
|
|
643
|
-
id: "gasBalance",
|
|
644
|
-
label: "Gas balance",
|
|
645
|
-
currency: "ETH",
|
|
646
|
-
field: "balance_eth",
|
|
647
|
-
decimals: 6
|
|
648
|
-
}],
|
|
639
|
+
currency: "USDC",
|
|
640
|
+
balanceField: "balance_usdc",
|
|
649
641
|
gasNote: "Base ETH is used for one-time payment setup gas.",
|
|
650
|
-
info: "Send Base USDC to the wallet address above."
|
|
642
|
+
info: "Send Base USDC to the wallet address above.",
|
|
643
|
+
tokenAddress: USDC_ADDRESS,
|
|
644
|
+
chainIdHex: BASE_CHAIN_ID
|
|
651
645
|
},
|
|
652
646
|
"robinhood-cia": {
|
|
653
|
-
title: "Chain Insights — Fund
|
|
654
|
-
subtitle: "Fund your wallet
|
|
647
|
+
title: "Chain Insights — Fund Subscription Wallet",
|
|
648
|
+
subtitle: "Fund your wallet to buy a Chain Insights subscription",
|
|
655
649
|
badge: "Robinhood Chain · $CIA",
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
}, {
|
|
663
|
-
id: "gasBalance",
|
|
664
|
-
label: "Gas balance",
|
|
665
|
-
currency: "ETH",
|
|
666
|
-
field: "balance_eth",
|
|
667
|
-
decimals: 6
|
|
668
|
-
}],
|
|
669
|
-
gasNote: "Robinhood Chain ETH pays the gas of the two buy transactions.",
|
|
670
|
-
info: `Send \$CIA and a little ETH on Robinhood Chain to your own wallet. Then run <code>cia subscription buy --days N</code>.<br>Robinhood Chain (chain ID 4663) · $CIA <code>${CIA_TOKEN_ADDRESS}</code><br>This page shows your own address only. It signs nothing and sends nothing.`
|
|
650
|
+
currency: "$CIA",
|
|
651
|
+
balanceField: "balance_cia",
|
|
652
|
+
gasNote: "Robinhood Chain ETH is used for the buy transaction gas.",
|
|
653
|
+
info: "Send $CIA and Robinhood Chain ETH to the wallet address above.",
|
|
654
|
+
tokenAddress: CIA_TOKEN_ADDRESS,
|
|
655
|
+
chainIdHex: ROBINHOOD_CHAIN_ID_HEX
|
|
671
656
|
}
|
|
672
657
|
};
|
|
673
658
|
/**
|
|
674
|
-
* The one local top-up page: the wallet address, its QR code
|
|
675
|
-
*
|
|
676
|
-
*
|
|
659
|
+
* The one local top-up page: the wallet address, its QR code, the network
|
|
660
|
+
* and the balances. It names only the user's own wallet, never where a
|
|
661
|
+
* payment goes.
|
|
677
662
|
*/
|
|
678
663
|
function generateTopupPage(walletAddressInput, rail = "base-usdc") {
|
|
664
|
+
const text = TOPUP_PAGE_TEXT[rail];
|
|
679
665
|
const walletAddress = assertWalletAddress(walletAddressInput);
|
|
680
666
|
const safeWalletAddress = escapeHtml(walletAddress);
|
|
681
667
|
const walletAddressJson = jsonForScript(walletAddress);
|
|
682
|
-
const text = TOPUP_PAGE_TEXT[rail];
|
|
683
|
-
const qrSvg = generateQrSvg(walletAddress, { cellSize: 5 });
|
|
684
|
-
const balanceRows = text.balances.map((row) => ` <div class="balance-row">
|
|
685
|
-
<span class="balance-label">${row.label}</span>
|
|
686
|
-
<span class="balance-value" id="${row.id}">—<span class="currency">${row.currency}</span></span>
|
|
687
|
-
</div>`).join("\n");
|
|
688
|
-
const balancesJson = JSON.stringify(text.balances.map(({ id, currency, field, decimals }) => ({
|
|
689
|
-
id,
|
|
690
|
-
currency,
|
|
691
|
-
field,
|
|
692
|
-
decimals
|
|
693
|
-
})));
|
|
694
668
|
return `<!DOCTYPE html>
|
|
695
669
|
<html lang="en">
|
|
696
670
|
<head>
|
|
697
671
|
<meta charset="utf-8">
|
|
698
672
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
699
673
|
<title>${text.title}</title>
|
|
674
|
+
<script src="https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js"><\/script>
|
|
700
675
|
<style>
|
|
701
676
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
702
677
|
|
|
@@ -748,7 +723,7 @@ function generateTopupPage(walletAddressInput, rail = "base-usdc") {
|
|
|
748
723
|
margin-bottom: 24px;
|
|
749
724
|
}
|
|
750
725
|
|
|
751
|
-
.qr-container
|
|
726
|
+
.qr-container canvas, .qr-container img {
|
|
752
727
|
border-radius: 12px;
|
|
753
728
|
background: #fff;
|
|
754
729
|
padding: 12px;
|
|
@@ -903,12 +878,6 @@ function generateTopupPage(walletAddressInput, rail = "base-usdc") {
|
|
|
903
878
|
color: rgba(255, 255, 255, 0.3);
|
|
904
879
|
line-height: 1.6;
|
|
905
880
|
}
|
|
906
|
-
|
|
907
|
-
.info code {
|
|
908
|
-
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
909
|
-
color: #f2dda6;
|
|
910
|
-
word-break: break-all;
|
|
911
|
-
}
|
|
912
881
|
</style>
|
|
913
882
|
</head>
|
|
914
883
|
<body>
|
|
@@ -919,7 +888,7 @@ function generateTopupPage(walletAddressInput, rail = "base-usdc") {
|
|
|
919
888
|
</div>
|
|
920
889
|
|
|
921
890
|
<div class="card">
|
|
922
|
-
<div class="qr-container" id="qr"
|
|
891
|
+
<div class="qr-container" id="qr"></div>
|
|
923
892
|
|
|
924
893
|
<div class="address-box">
|
|
925
894
|
<code id="address">${safeWalletAddress}</code>
|
|
@@ -931,7 +900,14 @@ function generateTopupPage(walletAddressInput, rail = "base-usdc") {
|
|
|
931
900
|
${text.badge}
|
|
932
901
|
</div>
|
|
933
902
|
|
|
934
|
-
|
|
903
|
+
<div class="balance-row">
|
|
904
|
+
<span class="balance-label">Current balance</span>
|
|
905
|
+
<span class="balance-value" id="balance">—<span class="currency">${text.currency}</span></span>
|
|
906
|
+
</div>
|
|
907
|
+
<div class="balance-row">
|
|
908
|
+
<span class="balance-label">Gas balance</span>
|
|
909
|
+
<span class="balance-value" id="gasBalance">—<span class="currency">ETH</span></span>
|
|
910
|
+
</div>
|
|
935
911
|
<p class="gas-note">${text.gasNote}</p>
|
|
936
912
|
|
|
937
913
|
<div class="status" id="status"></div>
|
|
@@ -942,14 +918,27 @@ ${balanceRows}
|
|
|
942
918
|
|
|
943
919
|
<script>
|
|
944
920
|
const WALLET = ${walletAddressJson};
|
|
945
|
-
const
|
|
921
|
+
const USDC = '${text.tokenAddress}';
|
|
922
|
+
const CHAIN_ID = '${text.chainIdHex}';
|
|
923
|
+
|
|
924
|
+
// QR code
|
|
925
|
+
(function() {
|
|
926
|
+
var qr = qrcode(0, 'M');
|
|
927
|
+
qr.addData(WALLET);
|
|
928
|
+
qr.make();
|
|
929
|
+
document.getElementById('qr').innerHTML = qr.createSvgTag(5, 0);
|
|
930
|
+
var svg = document.querySelector('#qr svg');
|
|
931
|
+
if (svg) { svg.style.borderRadius = '12px'; svg.style.background = '#fff'; svg.style.padding = '12px'; }
|
|
932
|
+
})();
|
|
946
933
|
|
|
947
934
|
// Copy address — fallback for sandboxed iframes where navigator.clipboard is blocked
|
|
948
935
|
function copyAddress() {
|
|
949
936
|
var ok = false;
|
|
937
|
+
// Try modern clipboard API first
|
|
950
938
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
951
939
|
navigator.clipboard.writeText(WALLET).then(function() { ok = true; }).catch(function() {});
|
|
952
940
|
}
|
|
941
|
+
// Fallback: hidden textarea + execCommand
|
|
953
942
|
if (!ok) {
|
|
954
943
|
var ta = document.createElement('textarea');
|
|
955
944
|
ta.value = WALLET;
|
|
@@ -966,18 +955,20 @@ function copyAddress() {
|
|
|
966
955
|
setTimeout(function() { btn.classList.remove('copied'); btn.innerHTML = '⎘'; }, 2000);
|
|
967
956
|
}
|
|
968
957
|
|
|
969
|
-
|
|
970
|
-
document.getElementById(row.id).innerHTML = value + '<span class="currency">' + row.currency + '</span>';
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
// Fetch balances
|
|
958
|
+
// Fetch balance
|
|
974
959
|
async function fetchBalance() {
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
960
|
+
try {
|
|
961
|
+
var resp = await fetch('/api/balance');
|
|
962
|
+
var json = await resp.json();
|
|
963
|
+
if (json.${text.balanceField} === 'unknown') throw new Error('balance unavailable');
|
|
964
|
+
var balance = Number(json.${text.balanceField} || 0).toFixed(2);
|
|
965
|
+
document.getElementById('balance').innerHTML = balance + '<span class="currency">${text.currency}</span>';
|
|
966
|
+
var gasBalance = json.balance_eth === 'unknown' ? '—' : Number(json.balance_eth || 0).toFixed(6);
|
|
967
|
+
document.getElementById('gasBalance').innerHTML = gasBalance + '<span class="currency">ETH</span>';
|
|
968
|
+
} catch(e) {
|
|
969
|
+
document.getElementById('balance').innerHTML = '—<span class="currency">${text.currency}</span>';
|
|
970
|
+
document.getElementById('gasBalance').innerHTML = '—<span class="currency">ETH</span>';
|
|
971
|
+
}
|
|
981
972
|
}
|
|
982
973
|
fetchBalance();
|
|
983
974
|
setInterval(fetchBalance, 15000);
|
|
@@ -1426,4 +1417,4 @@ async function registerTopupView(server, deps) {
|
|
|
1426
1417
|
//#endregion
|
|
1427
1418
|
export { topup_server_exports as a, startTopupServer as i, generateArtifactHtml as n, qrTerminalText as o, getTopupUrl as r, SUBSCRIPTION_TOPUP_TEXT as t };
|
|
1428
1419
|
|
|
1429
|
-
//# sourceMappingURL=topup-server-
|
|
1420
|
+
//# sourceMappingURL=topup-server-CSF5EjNt.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topup-server-CSF5EjNt.mjs","names":["USDC_ADDRESS"],"sources":["../src/wallet/mcp-proxy/qr.ts","../src/wallet/mcp-proxy/tools.ts","../src/wallet/mcp-proxy/topup-server.ts"],"sourcesContent":["/**\n * Minimal QR code generator. No external dependencies.\n *\n * Byte mode, error correction level L, versions 1 to 4 (single block), mask 0.\n * Version 3 holds a 42-character EVM address; version 4 holds up to 78 bytes.\n *\n * `qrModules` is self-contained on purpose: the top-up view inlines its source\n * (`qrModules.toString()`) and draws the same QR code inside the Claude view,\n * so the function must not reference anything outside its own body.\n */\n\n/**\n * The QR code modules for `text`: a square matrix, 1 for a dark module and 0\n * for a light one, without the quiet zone. Throws when `text` is longer than\n * 78 UTF-8 bytes.\n */\nexport function qrModules(text: string): number[][] {\n // Level L, single-block versions: [version, total codewords, EC codewords].\n const versions = [\n [1, 26, 7],\n [2, 44, 10],\n [3, 70, 15],\n [4, 100, 20],\n ]\n // Format information for level L and mask 0, BCH-coded and masked.\n const formatBits = 0x77c4\n const bytes = Array.from(new TextEncoder().encode(text))\n\n let version = 0\n let totalCodewords = 0\n let ecCodewords = 0\n for (const entry of versions) {\n const dataCodewords = entry[1] - entry[2]\n if (4 + 8 + bytes.length * 8 <= dataCodewords * 8) {\n version = entry[0]\n totalCodewords = entry[1]\n ecCodewords = entry[2]\n break\n }\n }\n if (version === 0) throw new Error('QR text is too long: at most 78 bytes')\n\n const dataCodewords = totalCodewords - ecCodewords\n const size = version * 4 + 17\n\n // Data bits: mode, count, bytes, terminator, byte padding, pad codewords.\n const bits: number[] = []\n const pushBits = (value: number, length: number) => {\n for (let i = length - 1; i >= 0; i--) bits.push((value >>> i) & 1)\n }\n pushBits(0b0100, 4)\n pushBits(bytes.length, 8)\n for (const value of bytes) pushBits(value, 8)\n const capacity = dataCodewords * 8\n pushBits(0, Math.min(4, capacity - bits.length))\n while (bits.length % 8 !== 0) bits.push(0)\n for (let pad = 0xec; bits.length < capacity; pad ^= 0xec ^ 0x11) pushBits(pad, 8)\n const data: number[] = []\n for (let i = 0; i < bits.length; i += 8) {\n let value = 0\n for (let j = 0; j < 8; j++) value = (value << 1) | bits[i + j]\n data.push(value)\n }\n\n // Reed-Solomon error correction over GF(256), polynomial 0x11d.\n const exp: number[] = []\n const log: number[] = []\n for (let i = 0, x = 1; i < 255; i++) {\n exp[i] = x\n log[x] = i\n x <<= 1\n if (x & 0x100) x ^= 0x11d\n }\n const multiply = (a: number, b: number) => (a === 0 || b === 0 ? 0 : exp[(log[a] + log[b]) % 255])\n let generator = [1]\n for (let i = 0; i < ecCodewords; i++) {\n const next = new Array(generator.length + 1).fill(0)\n for (let j = 0; j < generator.length; j++) {\n next[j] ^= generator[j]\n next[j + 1] ^= multiply(generator[j], exp[i])\n }\n generator = next\n }\n const remainder = [...data, ...new Array(ecCodewords).fill(0)]\n for (let i = 0; i < data.length; i++) {\n const factor = remainder[i]\n if (factor === 0) continue\n for (let j = 0; j < generator.length; j++) remainder[i + j] ^= multiply(generator[j], factor)\n }\n const codewords = [...data, ...remainder.slice(data.length)]\n\n // Function patterns. `reserved` marks every module data may not use.\n const modules: number[][] = []\n const reserved: boolean[][] = []\n for (let row = 0; row < size; row++) {\n modules.push(new Array(size).fill(0))\n reserved.push(new Array(size).fill(false))\n }\n const setFunction = (row: number, col: number, dark: boolean) => {\n modules[row][col] = dark ? 1 : 0\n reserved[row][col] = true\n }\n for (let i = 0; i < size; i++) {\n setFunction(6, i, i % 2 === 0)\n setFunction(i, 6, i % 2 === 0)\n }\n const finder = (centerRow: number, centerCol: number) => {\n for (let dr = -4; dr <= 4; dr++) {\n for (let dc = -4; dc <= 4; dc++) {\n const row = centerRow + dr\n const col = centerCol + dc\n if (row < 0 || row >= size || col < 0 || col >= size) continue\n const distance = Math.max(Math.abs(dr), Math.abs(dc))\n setFunction(row, col, distance !== 2 && distance !== 4)\n }\n }\n }\n finder(3, 3)\n finder(3, size - 4)\n finder(size - 4, 3)\n if (version >= 2) {\n const center = size - 7\n for (let dr = -2; dr <= 2; dr++) {\n for (let dc = -2; dc <= 2; dc++) {\n setFunction(center + dr, center + dc, Math.max(Math.abs(dr), Math.abs(dc)) !== 1)\n }\n }\n }\n const formatBit = (i: number) => ((formatBits >>> i) & 1) === 1\n for (let i = 0; i <= 5; i++) setFunction(i, 8, formatBit(i))\n setFunction(7, 8, formatBit(6))\n setFunction(8, 8, formatBit(7))\n setFunction(8, 7, formatBit(8))\n for (let i = 9; i < 15; i++) setFunction(8, 14 - i, formatBit(i))\n for (let i = 0; i < 8; i++) setFunction(8, size - 1 - i, formatBit(i))\n for (let i = 8; i < 15; i++) setFunction(size - 15 + i, 8, formatBit(i))\n setFunction(size - 8, 8, true)\n\n // Data in the zigzag order, then mask 0 on every data module.\n let bitIndex = 0\n for (let right = size - 1; right >= 1; right -= 2) {\n if (right === 6) right = 5\n const upward = ((right + 1) & 2) === 0\n for (let step = 0; step < size; step++) {\n const row = upward ? size - 1 - step : step\n for (let j = 0; j < 2; j++) {\n const col = right - j\n if (reserved[row][col]) continue\n if (bitIndex < codewords.length * 8) {\n modules[row][col] = (codewords[bitIndex >>> 3] >>> (7 - (bitIndex & 7))) & 1\n bitIndex++\n }\n if ((row + col) % 2 === 0) modules[row][col] ^= 1\n }\n }\n }\n return modules\n}\n\nexport interface QrOptions {\n cellSize?: number\n fgColor?: string\n bgColor?: string\n finderColor?: string\n logoBase64?: string // data URI for center logo\n logoWidth?: number // logo width in modules (default: 7)\n logoHeight?: number // logo height in modules (default: 5)\n}\n\nexport function generateQrSvg(text: string, opts: QrOptions | number = 4): string {\n // Backward compat: accept bare cellSize number\n const options: QrOptions = typeof opts === 'number' ? { cellSize: opts } : opts\n const cellSize = options.cellSize ?? 4\n const fgColor = options.fgColor ?? '#000'\n const bgColor = options.bgColor ?? '#fff'\n const finderColor = options.finderColor ?? fgColor\n\n const matrix = qrModules(text)\n const size = matrix.length\n\n // Logo exclusion zone (center of QR)\n const logoW = options.logoWidth ?? 7\n const logoH = options.logoHeight ?? 5\n const logoStartC = Math.floor((size - logoW) / 2)\n const logoStartR = Math.floor((size - logoH) / 2)\n const hasLogo = !!options.logoBase64\n\n const svgSize = size * cellSize\n let svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"${svgSize}\" height=\"${svgSize}\" viewBox=\"0 0 ${svgSize} ${svgSize}\">`\n svg += `<rect width=\"${svgSize}\" height=\"${svgSize}\" fill=\"${bgColor}\" rx=\"4\"/>`\n\n const isFinderModule = (r: number, c: number): boolean =>\n (r < 7 && c < 7) || (r < 7 && c >= size - 7) || (r >= size - 7 && c < 7)\n\n for (let r = 0; r < size; r++) {\n for (let c = 0; c < size; c++) {\n if (\n hasLogo &&\n r >= logoStartR &&\n r < logoStartR + logoH &&\n c >= logoStartC &&\n c < logoStartC + logoW\n ) {\n continue\n }\n if (matrix[r][c] === 1) {\n const color = isFinderModule(r, c) ? finderColor : fgColor\n svg += `<rect x=\"${c * cellSize}\" y=\"${r * cellSize}\" width=\"${cellSize}\" height=\"${cellSize}\" fill=\"${color}\" rx=\"0.5\"/>`\n }\n }\n }\n\n if (hasLogo && options.logoBase64) {\n const lx = logoStartC * cellSize\n const ly = logoStartR * cellSize\n const lw = logoW * cellSize\n const lh = logoH * cellSize\n svg += `<rect x=\"${lx - 1}\" y=\"${ly - 1}\" width=\"${lw + 2}\" height=\"${lh + 2}\" fill=\"${bgColor}\" rx=\"3\"/>`\n svg += `<image x=\"${lx + 2}\" y=\"${ly + 2}\" width=\"${lw - 4}\" height=\"${lh - 4}\" href=\"${options.logoBase64}\" xlink:href=\"${options.logoBase64}\" preserveAspectRatio=\"xMidYMid meet\"/>`\n }\n\n svg += '</svg>'\n return svg\n}\n\nexport interface QrTerminalOptions {\n /** Quiet zone in modules on each side (default 4, the QR standard). */\n quietZone?: number\n /** Draw light modules as blocks instead of dark ones, for a light-on-dark read. */\n invert?: boolean\n}\n\n/**\n * The QR code of `text` as terminal text. Each character covers two module\n * rows with half blocks (▀ ▄ █ and space), so the code keeps its square shape.\n * A block is a dark module; the quiet zone is light.\n */\nexport function qrTerminalText(text: string, options: QrTerminalOptions = {}): string {\n const quiet = options.quietZone ?? 4\n const modules = qrModules(text)\n const size = modules.length + quiet * 2\n const dark = (row: number, col: number): boolean => {\n const r = row - quiet\n const c = col - quiet\n const on = r >= 0 && c >= 0 && r < modules.length && c < modules.length && modules[r][c] === 1\n return options.invert ? !on : on\n }\n const lines: string[] = []\n for (let row = 0; row < size; row += 2) {\n let line = ''\n for (let col = 0; col < size; col++) {\n const top = dark(row, col)\n const bottom = row + 1 < size ? dark(row + 1, col) : options.invert === true\n line += top && bottom ? '█' : top ? '▀' : bottom ? '▄' : ' '\n }\n lines.push(line)\n }\n return lines.join('\\n')\n}\n","import type { WalletData } from './types.js'\nimport { createPublicClient, http, formatEther, formatUnits } from 'viem'\nimport { base } from 'viem/chains'\n\nconst USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as const\nconst DEFAULT_BASE_RPC_URL = 'https://mainnet.base.org'\nconst PUBLIC_BASE_RPC_URLS = [\n DEFAULT_BASE_RPC_URL,\n 'https://base-rpc.publicnode.com',\n 'https://base.drpc.org',\n 'https://1rpc.io/base',\n] as const\nconst USDC_ABI = [\n {\n name: 'balanceOf',\n type: 'function',\n stateMutability: 'view',\n inputs: [{ name: 'account', type: 'address' }],\n outputs: [{ name: '', type: 'uint256' }],\n },\n] as const\n\nfunction walletAddress(wallet: WalletData | string): `0x${string}` {\n return (typeof wallet === 'string' ? wallet : wallet.address) as `0x${string}`\n}\n\nexport async function getBalanceUsdc(wallet: WalletData | string): Promise<string> {\n const envRpcUrl = process.env.BASE_RPC_URL\n const rpcUrls = [\n ...(envRpcUrl ? [envRpcUrl] : []),\n ...PUBLIC_BASE_RPC_URLS.filter((url) => url !== envRpcUrl),\n ]\n\n for (const rpcUrl of rpcUrls) {\n try {\n const client = createPublicClient({ chain: base, transport: http(rpcUrl) })\n const balance = await client.readContract({\n address: USDC_ADDRESS,\n abi: USDC_ABI,\n functionName: 'balanceOf',\n args: [walletAddress(wallet)],\n })\n return formatUnits(balance, 6)\n } catch {\n // Try the next public Base RPC endpoint.\n }\n }\n\n return 'unknown'\n}\n\nexport async function getBalanceEth(wallet: WalletData | string): Promise<string> {\n const envRpcUrl = process.env.BASE_RPC_URL\n const rpcUrls = [\n ...(envRpcUrl ? [envRpcUrl] : []),\n ...PUBLIC_BASE_RPC_URLS.filter((url) => url !== envRpcUrl),\n ]\n\n for (const rpcUrl of rpcUrls) {\n try {\n const client = createPublicClient({ chain: base, transport: http(rpcUrl) })\n const balance = await client.getBalance({ address: walletAddress(wallet) })\n return formatEther(balance)\n } catch {\n // Try the next public Base RPC endpoint.\n }\n }\n\n return 'unknown'\n}\n\nexport async function getBalance(wallet: WalletData): Promise<string> {\n const [balanceUsdc, balanceEth] = await Promise.all([\n getBalanceUsdc(wallet),\n getBalanceEth(wallet),\n ])\n return [\n `Payment wallet: ${wallet.address}`,\n `USDC on Base: ${balanceUsdc}`,\n `Gas on Base: ${balanceEth} ETH`,\n `Payment network: Base`,\n `Base ETH is used only for one-time payment setup gas.`,\n ]\n .filter(Boolean)\n .join('\\n')\n}\n\nexport function getTopupInfo(wallet: WalletData): string {\n return JSON.stringify({\n wallet_address: wallet.address,\n network: 'Base (Chain ID 8453)',\n token: 'USDC',\n contract: USDC_ADDRESS,\n instructions: [`Send USDC on Base network to: ${wallet.address}`],\n })\n}\n","import { createServer, type Server } from 'node:http'\nimport { readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { isAddress } from 'viem'\nimport type { WalletData } from './types.js'\nimport { generateQrSvg, qrModules } from './qr.js'\nimport { getBalanceEth, getBalanceUsdc } from './tools.js'\nimport {\n CIA_SYMBOL,\n CIA_TOKEN_ADDRESS,\n ROBINHOOD_CHAIN_ID,\n ROBINHOOD_NETWORK_DISPLAY,\n getCiaBalance,\n getRobinhoodEthBalance,\n} from '../robinhood.js'\nimport {\n SUBSCRIPTION_TOPUP_DESCRIPTION,\n SUBSCRIPTION_TOPUP_TOOL,\n WALLET_TOPUP_DESCRIPTION,\n} from '../../subscription/rules.js'\n\nconst USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'\nconst BASE_CHAIN_ID = '0x2105'\n/** Robinhood Chain, chain ID 4663. */\nconst ROBINHOOD_CHAIN_ID_HEX = '0x1237'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n// Pre-load assets — try dist/assets first (installed), then src/assets (dev)\nfunction loadAsset(name: string): Buffer {\n const paths = [\n join(__dirname, 'assets', name), // dist/assets/ (global install)\n join(__dirname, '..', 'src', 'assets', name), // src/assets/ (dev)\n ]\n for (const p of paths) {\n try {\n return readFileSync(p)\n } catch {\n /* try next */\n }\n }\n console.error(`[chain-insights] Warning: asset ${name} not found`)\n return Buffer.alloc(0)\n}\n\nconst logoPng = loadAsset('logo.png')\nconst bgPatternPng = loadAsset('bg-pattern.png')\n\n/**\n * The top-up rails. base-usdc funds pay-per-call queries with USDC on Base;\n * robinhood-cia funds the subscription with $CIA and ETH on Robinhood Chain.\n * One local server per rail.\n */\nexport type TopupRail = 'base-usdc' | 'robinhood-cia'\n\nconst servers = new Map<TopupRail, { server: Server; port: number }>()\n\nfunction assertWalletAddress(wallet: WalletData | string): string {\n const walletAddress = typeof wallet === 'string' ? wallet : wallet.address\n if (!isAddress(walletAddress)) {\n throw new Error('Wallet address must be a valid 0x-prefixed 20-byte EVM address')\n }\n return walletAddress\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''')\n}\n\nfunction jsonForScript(value: string): string {\n return JSON.stringify(value)\n .replaceAll('<', '\\\\u003c')\n .replaceAll('>', '\\\\u003e')\n .replaceAll('&', '\\\\u0026')\n}\n\nexport function getTopupUrl(rail: TopupRail = 'base-usdc'): string | null {\n const running = servers.get(rail)\n return running ? `http://localhost:${running.port}` : null\n}\n\nasync function railBalance(rail: TopupRail, walletAddress: string): Promise<Record<string, string>> {\n if (rail === 'robinhood-cia') {\n const [balanceCia, balanceEth] = await Promise.all([\n getCiaBalance(walletAddress),\n getRobinhoodEthBalance(walletAddress),\n ])\n return { balance_cia: balanceCia, balance_eth: balanceEth }\n }\n const [balanceUsdc, balanceEth] = await Promise.all([\n getBalanceUsdc(walletAddress),\n getBalanceEth(walletAddress),\n ])\n return { balance_usdc: balanceUsdc, balance_eth: balanceEth }\n}\n\nconst UNKNOWN_BALANCE: Record<TopupRail, Record<string, string>> = {\n 'base-usdc': { balance_usdc: 'unknown', balance_eth: 'unknown' },\n 'robinhood-cia': { balance_cia: 'unknown', balance_eth: 'unknown' },\n}\n\nexport async function startTopupServer(\n wallet: WalletData | string,\n rail: TopupRail = 'base-usdc'\n): Promise<string> {\n const walletAddress = assertWalletAddress(wallet)\n\n const running = getTopupUrl(rail)\n if (running) {\n return running\n }\n\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n if (req.url === '/api/wallet') {\n res.writeHead(200, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n })\n res.end(JSON.stringify({ address: walletAddress }))\n return\n }\n\n if (req.url === '/api/balance') {\n railBalance(rail, walletAddress)\n .then((balance) => {\n res.writeHead(200, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n })\n res.end(JSON.stringify(balance))\n })\n .catch(() => {\n res.writeHead(200, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n })\n res.end(JSON.stringify(UNKNOWN_BALANCE[rail]))\n })\n return\n }\n\n if (req.url === '/assets/logo.png') {\n res.writeHead(200, {\n 'Content-Type': 'image/png',\n 'Cache-Control': 'public, max-age=86400',\n 'Access-Control-Allow-Origin': '*',\n })\n res.end(logoPng)\n return\n }\n\n if (req.url === '/assets/bg-pattern.png') {\n res.writeHead(200, {\n 'Content-Type': 'image/png',\n 'Cache-Control': 'public, max-age=86400',\n 'Access-Control-Allow-Origin': '*',\n })\n res.end(bgPatternPng)\n return\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(rail === 'robinhood-cia' ? generateSubscriptionPage(walletAddress) : generatePage(walletAddress))\n })\n\n server.listen(0, '127.0.0.1', () => {\n const addr = server.address()\n if (addr && typeof addr === 'object') {\n servers.set(rail, { server, port: addr.port })\n const url = `http://localhost:${addr.port}`\n console.error(`[chain-insights] Topup server running at ${url}`)\n resolve(url)\n } else {\n reject(new Error('Failed to start topup server'))\n }\n })\n\n server.on('error', reject)\n })\n}\n\nexport function generateArtifactHtml(walletAddressInput: string, topupUrl: string): string {\n const walletAddress = assertWalletAddress(walletAddressInput)\n const safeWalletAddress = escapeHtml(walletAddress)\n const safeTopupUrl = escapeHtml(topupUrl)\n const topupUrlJson = jsonForScript(topupUrl)\n\n const LOGO_B64 =\n 'data:image/png;base64,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'\n\n // Generate QR code SVG server-side — standard B&W, 2x size\n const qrSvg = generateQrSvg(walletAddress, { cellSize: 8 })\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;\n background: rgba(10, 12, 17, 1) url('${safeTopupUrl}/assets/bg-pattern.png') top left / contain no-repeat;\n color: rgba(255, 255, 255, 0.9);\n display: flex;\n justify-content: center;\n padding: 24px;\n line-height: 1.3;\n }\n .card {\n max-width: 400px;\n width: 100%;\n background: rgba(19, 19, 24, 1);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 12px;\n padding: 28px 24px;\n text-align: center;\n }\n .logo { margin-bottom: 8px; }\n .logo img { height: 24px; }\n .subtitle { color: rgba(255, 255, 255, 0.5); font-size: 13px; margin-bottom: 20px; }\n .qr { margin-bottom: 16px; }\n .qr svg { border-radius: 12px; background: #fff; padding: 10px; }\n .address-wrap {\n position: relative;\n margin-bottom: 16px;\n }\n .address {\n width: 100%;\n background: rgba(10, 12, 17, 1);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 8px;\n padding: 10px 14px;\n font-family: 'SF Mono', 'Fira Code', monospace;\n font-size: 11px; color: #f2dda6; word-break: break-all;\n cursor: pointer; text-align: center;\n transition: border-color 0.3s linear;\n -webkit-user-select: all; user-select: all;\n outline: none;\n }\n .address:focus { border-color: #ae9d71; }\n .address-hint {\n font-size: 10px; color: rgba(255,255,255,0.3);\n text-align: center; margin-top: 4px;\n }\n .badge {\n display: inline-flex; align-items: center; gap: 6px;\n background: rgba(255, 255, 255, 0.04);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 20px; padding: 5px 12px;\n font-size: 11px; color: rgba(255, 255, 255, 0.5); margin-bottom: 20px;\n }\n .badge .dot { width: 7px; height: 7px; border-radius: 50%; background: #f2dda6; }\n .balance-line {\n font-size: 13px;\n color: rgba(255, 255, 255, 0.5);\n margin-bottom: 20px;\n line-height: 1.4;\n }\n .balance-line .amount {\n color: #4feb69;\n font-weight: 600;\n }\n .balance-line .gas {\n color: #f2dda6;\n font-weight: 600;\n }\n @keyframes flash { 0%{opacity:0.4} 100%{opacity:1} }\n .balance-line.flash .amount { animation: flash 1s ease-out; }\n .hint {\n margin-top: 14px; font-size: 12px;\n color: rgba(255, 255, 255, 0.3);\n line-height: 1.5;\n }\n</style>\n</head>\n<body>\n<div class=\"card\">\n <div class=\"logo\"><img src=\"${safeTopupUrl}/assets/logo.png\" alt=\"Chain Insights\"></div>\n <p class=\"subtitle\">Fund your wallet with USDC on Base</p>\n <div class=\"qr\">${qrSvg}</div>\n <div class=\"address-wrap\">\n <div class=\"address\" id=\"addr\" onclick=\"selectAndCopy()\">${safeWalletAddress}</div>\n <div class=\"address-hint\" id=\"addrHint\">Click to copy address</div>\n </div>\n <div class=\"badge\"><span class=\"dot\"></span>Base Network · USDC</div>\n <p class=\"balance-line\" id=\"balLine\">Current balance: <span class=\"amount\" id=\"bal\">--</span> USDC<br>Gas balance: <span class=\"gas\" id=\"gas\">--</span> ETH<br>Base ETH is used for one-time payment setup gas.</p>\n</div>\n<script>\n// MCP Apps protocol handshake for the wallet top-up page.\n(function() {\n var initId = 1;\n\n // Listen for host messages\n window.addEventListener('message', function(event) {\n var data = event.data;\n if (!data || data.jsonrpc !== '2.0') return;\n\n // Initialize response\n if (data.id === initId && data.result) {\n // Send initialized notification\n window.parent.postMessage({\n jsonrpc: '2.0',\n method: 'ui/notifications/initialized',\n params: {}\n }, '*');\n\n // Send initial size\n var rect = document.documentElement.getBoundingClientRect();\n window.parent.postMessage({\n jsonrpc: '2.0',\n method: 'ui/notifications/size-changed',\n params: { width: Math.ceil(rect.width), height: Math.ceil(rect.height) }\n }, '*');\n }\n\n // Respond to pings\n if (data.method === 'ping' && data.id != null) {\n window.parent.postMessage({\n jsonrpc: '2.0',\n id: data.id,\n result: {}\n }, '*');\n }\n });\n\n // Send initialize request (must match App class protocol)\n window.parent.postMessage({\n jsonrpc: '2.0',\n id: initId,\n method: 'ui/initialize',\n params: {\n appInfo: { name: 'Chain Insights Topup', version: '1.0.0' },\n appCapabilities: {},\n protocolVersion: '2026-01-26'\n }\n }, '*');\n})();\n\n// Live balance polling\nvar lastBal = null;\nvar TOPUP_URL = ${topupUrlJson};\nfunction fetchBal() {\n fetch(TOPUP_URL + '/api/balance')\n .then(function(r) { return r.json(); })\n .then(function(d) {\n var el = document.getElementById('bal');\n var gas = document.getElementById('gas');\n var line = document.getElementById('balLine');\n var val = parseFloat(d.balance_usdc || '0').toFixed(2);\n var gasVal = d.balance_eth === 'unknown' ? '--' : parseFloat(d.balance_eth || '0').toFixed(6);\n if (d.balance_usdc === 'unknown') { el.textContent = '--'; gas.textContent = gasVal; return; }\n el.textContent = val;\n gas.textContent = gasVal;\n if (lastBal !== null && val !== lastBal) {\n line.classList.remove('flash');\n void line.offsetWidth;\n line.classList.add('flash');\n }\n lastBal = val;\n })\n .catch(function() {});\n}\nfetchBal();\nsetInterval(fetchBal, 10000);\n\nfunction selectAndCopy() {\n var addr = document.getElementById('addr');\n var hint = document.getElementById('addrHint');\n // Select the text\n var r = document.createRange();\n r.selectNodeContents(addr);\n var s = window.getSelection();\n s.removeAllRanges();\n s.addRange(r);\n // Try every clipboard method available\n var copied = false;\n try { copied = document.execCommand('copy'); } catch(e) {}\n if (!copied && navigator.clipboard && navigator.clipboard.writeText) {\n navigator.clipboard.writeText(addr.textContent).then(function() {\n hint.textContent = 'Copied!'; hint.style.color = '#4feb69';\n setTimeout(function() { hint.textContent = 'Click to copy address'; hint.style.color = ''; }, 2000);\n }).catch(function() {});\n }\n if (copied) {\n hint.textContent = 'Copied!'; hint.style.color = '#4feb69';\n setTimeout(function() { hint.textContent = 'Click to copy address'; hint.style.color = ''; }, 2000);\n } else {\n hint.textContent = 'Selected — press Ctrl+C'; hint.style.color = '#f2dda6';\n setTimeout(function() { hint.textContent = 'Click to copy address'; hint.style.color = ''; }, 3000);\n }\n}\n<\\/script>\n</body>\n</html>`\n}\n\n/**\n * The text of the one top-up page, per rail. The page is the Base wallet\n * page, unchanged (aphex5, 2026-10-28: \"wallet topup is your baseline\"): the\n * layout, the style and the script never fork by rail, only these strings do.\n */\ninterface TopupPageText {\n title: string\n subtitle: string\n badge: string\n currency: string\n /** The /api/balance field of the main balance. */\n balanceField: string\n gasNote: string\n info: string\n tokenAddress: string\n chainIdHex: string\n}\n\n/** The text of `cia subscription topup` in the terminal. */\nexport const SUBSCRIPTION_TOPUP_TEXT =\n 'Send $CIA and a little ETH on Robinhood Chain to your own wallet. Then run `cia subscription buy --days N`.'\n\nconst TOPUP_PAGE_TEXT: Record<TopupRail, TopupPageText> = {\n 'base-usdc': {\n title: 'Chain Insights — Fund Wallet',\n subtitle: 'Fund your wallet to use blockchain intelligence tools',\n badge: 'Base Network · USDC',\n currency: 'USDC',\n balanceField: 'balance_usdc',\n gasNote: 'Base ETH is used for one-time payment setup gas.',\n info: 'Send Base USDC to the wallet address above.',\n tokenAddress: USDC_ADDRESS,\n chainIdHex: BASE_CHAIN_ID,\n },\n 'robinhood-cia': {\n title: 'Chain Insights — Fund Subscription Wallet',\n subtitle: 'Fund your wallet to buy a Chain Insights subscription',\n badge: 'Robinhood Chain · $CIA',\n currency: '$CIA',\n balanceField: 'balance_cia',\n gasNote: 'Robinhood Chain ETH is used for the buy transaction gas.',\n info: 'Send $CIA and Robinhood Chain ETH to the wallet address above.',\n tokenAddress: CIA_TOKEN_ADDRESS,\n chainIdHex: ROBINHOOD_CHAIN_ID_HEX,\n },\n}\n\n/**\n * The one local top-up page: the wallet address, its QR code, the network\n * and the balances. It names only the user's own wallet, never where a\n * payment goes.\n */\nexport function generateTopupPage(walletAddressInput: string, rail: TopupRail = 'base-usdc'): string {\n const text = TOPUP_PAGE_TEXT[rail]\n const walletAddress = assertWalletAddress(walletAddressInput)\n const safeWalletAddress = escapeHtml(walletAddress)\n const walletAddressJson = jsonForScript(walletAddress)\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${text.title}</title>\n<script src=\"https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js\"></script>\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n\n body {\n font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;\n background: rgba(10, 12, 17, 1);\n color: rgba(255, 255, 255, 0.9);\n min-height: 100vh;\n display: flex;\n justify-content: center;\n align-items: center;\n line-height: 1.3;\n }\n\n .container {\n max-width: 480px;\n width: 100%;\n padding: 24px;\n }\n\n .logo {\n text-align: center;\n margin-bottom: 32px;\n }\n\n .logo h1 {\n font-size: 24px;\n font-weight: 600;\n color: #f2dda6;\n letter-spacing: -0.5px;\n }\n\n .logo p {\n color: rgba(255, 255, 255, 0.5);\n font-size: 14px;\n margin-top: 4px;\n }\n\n .card {\n background: rgba(19, 19, 24, 1);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 12px;\n padding: 32px 24px;\n }\n\n .qr-container {\n display: flex;\n justify-content: center;\n margin-bottom: 24px;\n }\n\n .qr-container canvas, .qr-container img {\n border-radius: 12px;\n background: #fff;\n padding: 12px;\n }\n\n .address-box {\n background: rgba(10, 12, 17, 1);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 8px;\n padding: 12px 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 24px;\n }\n\n .address-box code {\n font-family: 'SF Mono', 'Fira Code', monospace;\n font-size: 12px;\n color: #f2dda6;\n word-break: break-all;\n flex: 1;\n }\n\n .copy-btn {\n background: none;\n border: none;\n color: rgba(255, 255, 255, 0.4);\n cursor: pointer;\n padding: 4px;\n font-size: 18px;\n transition: color 0.3s linear;\n }\n\n .copy-btn:hover { color: #f2dda6; }\n .copy-btn.copied { color: #4feb69; }\n\n .network-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: rgba(255, 255, 255, 0.04);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 20px;\n padding: 6px 12px;\n font-size: 12px;\n color: rgba(255, 255, 255, 0.5);\n margin-bottom: 24px;\n }\n\n .network-badge .dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #f2dda6;\n }\n\n .balance-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 0;\n border-top: 1px solid rgba(255, 255, 255, 0.06);\n margin-bottom: 16px;\n }\n\n .balance-label { color: rgba(255, 255, 255, 0.5); font-size: 14px; }\n\n .balance-value {\n font-size: 20px;\n font-weight: 600;\n color: rgba(255, 255, 255, 0.9);\n }\n\n .balance-value .currency {\n font-size: 14px;\n color: rgba(255, 255, 255, 0.5);\n font-weight: 400;\n margin-left: 4px;\n }\n\n .gas-note {\n margin-top: -8px;\n margin-bottom: 16px;\n color: rgba(255,255,255,0.45);\n font-size: 12px;\n line-height: 1.4;\n text-align: left;\n }\n\n .metamask-btn {\n width: 100%;\n padding: 14px;\n border: none;\n border-radius: 10px;\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n transition: all 0.2s;\n background: #f6851b;\n color: #fff;\n }\n\n .metamask-btn:hover { background: #e2761b; transform: translateY(-1px); }\n .metamask-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }\n\n .metamask-btn svg { width: 22px; height: 22px; }\n\n .amount-input {\n width: 100%;\n padding: 12px 16px;\n background: #0a0c11;\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 8px;\n color: rgba(255, 255, 255, 0.9);\n font-size: 16px;\n margin-bottom: 16px;\n outline: none;\n transition: border-color 0.2s;\n }\n\n .amount-input:focus { border-color: #f2dda6; }\n\n .amount-label {\n font-size: 13px;\n color: rgba(255, 255, 255, 0.5);\n margin-bottom: 6px;\n display: block;\n }\n\n .status {\n text-align: center;\n padding: 12px;\n border-radius: 8px;\n font-size: 14px;\n margin-top: 16px;\n display: none;\n }\n\n .status.success { display: block; background: #0a2e1a; color: #4feb69; border: 1px solid #1a4a2e; }\n .status.error { display: block; background: #2e0a0a; color: #eb4f4f; border: 1px solid #4a1a1a; }\n .status.pending { display: block; background: #1a1a0a; color: #f2dda6; border: 1px solid #4a4a1a; }\n\n .info {\n text-align: center;\n margin-top: 24px;\n font-size: 12px;\n color: rgba(255, 255, 255, 0.3);\n line-height: 1.6;\n }\n</style>\n</head>\n<body>\n<div class=\"container\">\n <div class=\"logo\">\n <h1>Chain Insights</h1>\n <p>${text.subtitle}</p>\n </div>\n\n <div class=\"card\">\n <div class=\"qr-container\" id=\"qr\"></div>\n\n <div class=\"address-box\">\n <code id=\"address\">${safeWalletAddress}</code>\n <button class=\"copy-btn\" onclick=\"copyAddress()\" id=\"copyBtn\" title=\"Copy address\">⎘</button>\n </div>\n\n <div class=\"network-badge\">\n <span class=\"dot\"></span>\n ${text.badge}\n </div>\n\n <div class=\"balance-row\">\n <span class=\"balance-label\">Current balance</span>\n <span class=\"balance-value\" id=\"balance\">—<span class=\"currency\">${text.currency}</span></span>\n </div>\n <div class=\"balance-row\">\n <span class=\"balance-label\">Gas balance</span>\n <span class=\"balance-value\" id=\"gasBalance\">—<span class=\"currency\">ETH</span></span>\n </div>\n <p class=\"gas-note\">${text.gasNote}</p>\n\n <div class=\"status\" id=\"status\"></div>\n </div>\n\n <p class=\"info\">${text.info}</p>\n</div>\n\n<script>\nconst WALLET = ${walletAddressJson};\nconst USDC = '${text.tokenAddress}';\nconst CHAIN_ID = '${text.chainIdHex}';\n\n// QR code\n(function() {\n var qr = qrcode(0, 'M');\n qr.addData(WALLET);\n qr.make();\n document.getElementById('qr').innerHTML = qr.createSvgTag(5, 0);\n var svg = document.querySelector('#qr svg');\n if (svg) { svg.style.borderRadius = '12px'; svg.style.background = '#fff'; svg.style.padding = '12px'; }\n})();\n\n// Copy address — fallback for sandboxed iframes where navigator.clipboard is blocked\nfunction copyAddress() {\n var ok = false;\n // Try modern clipboard API first\n if (navigator.clipboard && navigator.clipboard.writeText) {\n navigator.clipboard.writeText(WALLET).then(function() { ok = true; }).catch(function() {});\n }\n // Fallback: hidden textarea + execCommand\n if (!ok) {\n var ta = document.createElement('textarea');\n ta.value = WALLET;\n ta.style.position = 'fixed';\n ta.style.left = '-9999px';\n document.body.appendChild(ta);\n ta.select();\n try { document.execCommand('copy'); } catch(e) {}\n document.body.removeChild(ta);\n }\n var btn = document.getElementById('copyBtn');\n btn.classList.add('copied');\n btn.innerHTML = '✓';\n setTimeout(function() { btn.classList.remove('copied'); btn.innerHTML = '⎘'; }, 2000);\n}\n\n// Fetch balance\nasync function fetchBalance() {\n try {\n var resp = await fetch('/api/balance');\n var json = await resp.json();\n if (json.${text.balanceField} === 'unknown') throw new Error('balance unavailable');\n var balance = Number(json.${text.balanceField} || 0).toFixed(2);\n document.getElementById('balance').innerHTML = balance + '<span class=\"currency\">${text.currency}</span>';\n var gasBalance = json.balance_eth === 'unknown' ? '—' : Number(json.balance_eth || 0).toFixed(6);\n document.getElementById('gasBalance').innerHTML = gasBalance + '<span class=\"currency\">ETH</span>';\n } catch(e) {\n document.getElementById('balance').innerHTML = '—<span class=\"currency\">${text.currency}</span>';\n document.getElementById('gasBalance').innerHTML = '—<span class=\"currency\">ETH</span>';\n }\n}\nfetchBalance();\nsetInterval(fetchBalance, 15000);\n\n</script>\n</body>\n</html>`\n}\n\n/** The Base USDC page. */\nfunction generatePage(walletAddressInput: string): string {\n return generateTopupPage(walletAddressInput, 'base-usdc')\n}\n\n/** The robinhood-cia page: the same page, the subscription text. */\nexport function generateSubscriptionPage(walletAddressInput: string): string {\n return generateTopupPage(walletAddressInput, 'robinhood-cia')\n}\n\n// ---------------------------------------------------------------------------\n// The local top-up view: an MCP App drawn by Claude Desktop next to the\n// wallet_topup answer of the local proxy. It shows the wallet address, its QR\n// code, Base Mainnet and USDC. It fetches nothing, signs nothing and sends\n// nothing: the page holds no wallet code, only an address to read.\n// The hosted connector never serves it (ruling 2026-10-05).\n// ---------------------------------------------------------------------------\n\nexport const TOPUP_TOOL_NAME = 'wallet_topup'\nexport const TOPUP_VIEW_URI = 'ui://chain-insights/topup'\nexport const TOPUP_NETWORK_DISPLAY = 'Base Mainnet'\nconst TOPUP_CHAIN_ID = 8453\nconst EXT_APPS_GLOBAL = '__chainInsightsExtApps'\nconst NO_WALLET_TEXT =\n 'No local payment wallet exists yet. Run `cia wallet create` to make one, then ask to top up again.'\n\nexport interface TopupViewFacts {\n wallet_address: string\n network: 'Base'\n network_display: typeof TOPUP_NETWORK_DISPLAY\n chain_id: typeof TOPUP_CHAIN_ID\n token: 'USDC'\n token_contract: typeof USDC_ADDRESS\n}\n\nexport interface TopupToolResult {\n [key: string]: unknown\n content: Array<{ type: 'text'; text: string }>\n structuredContent: {\n schema: 'chain-insights.result.v1'\n tool: typeof TOPUP_TOOL_NAME\n hint: string | null\n facts: { wallet_configured: boolean; topup: TopupViewFacts | null }\n }\n isError: false\n}\n\n/**\n * The wallet_topup answer. The text is complete on its own, for hosts that\n * draw no view. `walletAddress` null means no local wallet exists.\n */\nexport function topupToolResult(walletAddress: string | null): TopupToolResult {\n if (walletAddress === null) {\n return {\n content: [{ type: 'text', text: NO_WALLET_TEXT }],\n structuredContent: {\n schema: 'chain-insights.result.v1',\n tool: TOPUP_TOOL_NAME,\n hint: 'Run `cia wallet create`.',\n facts: { wallet_configured: false, topup: null },\n },\n isError: false,\n }\n }\n const address = assertWalletAddress(walletAddress)\n const topup: TopupViewFacts = {\n wallet_address: address,\n network: 'Base',\n network_display: TOPUP_NETWORK_DISPLAY,\n chain_id: TOPUP_CHAIN_ID,\n token: 'USDC',\n token_contract: USDC_ADDRESS,\n }\n return {\n content: [\n {\n type: 'text',\n text: [\n `Payment wallet: ${address}`,\n `Network: ${TOPUP_NETWORK_DISPLAY} (chain ID ${TOPUP_CHAIN_ID})`,\n `Token: USDC (contract ${USDC_ADDRESS})`,\n `To top up, send USDC on ${TOPUP_NETWORK_DISPLAY} to the wallet address. Chain Insights makes no payment from here.`,\n ].join('\\n'),\n },\n ],\n structuredContent: {\n schema: 'chain-insights.result.v1',\n tool: TOPUP_TOOL_NAME,\n hint: null,\n facts: { wallet_configured: true, topup },\n },\n isError: false,\n }\n}\n\nexport interface SubscriptionTopupViewFacts {\n wallet_address: string\n network: typeof ROBINHOOD_NETWORK_DISPLAY\n network_display: typeof ROBINHOOD_NETWORK_DISPLAY\n chain_id: typeof ROBINHOOD_CHAIN_ID\n token: typeof CIA_SYMBOL\n token_contract: typeof CIA_TOKEN_ADDRESS\n gas_token: 'ETH'\n cia_balance: string\n eth_balance: string\n}\n\nexport interface SubscriptionTopupToolResult {\n [key: string]: unknown\n content: Array<{ type: 'text'; text: string }>\n structuredContent: {\n schema: 'chain-insights.result.v1'\n tool: typeof SUBSCRIPTION_TOPUP_TOOL\n hint: string | null\n facts: { wallet_configured: boolean; topup: SubscriptionTopupViewFacts | null }\n }\n isError: false\n}\n\n/** The rule the subscription_topup answer ends with. */\nexport const SUBSCRIPTION_TOPUP_RULE =\n 'Fund this wallet. The user buys days in a terminal with `cia subscription buy --days N`. An agent cannot buy. Never send $CIA by hand to any other address: it is not credited.'\n\n/**\n * The subscription_topup answer: the local wallet on Robinhood Chain, $CIA,\n * the balances ('unknown' when no RPC answers). It names the user's own\n * wallet only. `walletAddress` null means no local wallet exists.\n */\nexport function subscriptionTopupToolResult(\n walletAddress: string | null,\n balances: { cia: string; eth: string } = { cia: 'unknown', eth: 'unknown' }\n): SubscriptionTopupToolResult {\n if (walletAddress === null) {\n return {\n content: [{ type: 'text', text: 'No local wallet exists yet. Run `cia wallet create` to make one, then ask to top up again.' }],\n structuredContent: {\n schema: 'chain-insights.result.v1',\n tool: SUBSCRIPTION_TOPUP_TOOL,\n hint: 'Run `cia wallet create`.',\n facts: { wallet_configured: false, topup: null },\n },\n isError: false,\n }\n }\n const address = assertWalletAddress(walletAddress)\n const topup: SubscriptionTopupViewFacts = {\n wallet_address: address,\n network: ROBINHOOD_NETWORK_DISPLAY,\n network_display: ROBINHOOD_NETWORK_DISPLAY,\n chain_id: ROBINHOOD_CHAIN_ID,\n token: CIA_SYMBOL,\n token_contract: CIA_TOKEN_ADDRESS,\n gas_token: 'ETH',\n cia_balance: balances.cia,\n eth_balance: balances.eth,\n }\n return {\n content: [\n {\n type: 'text',\n text: [\n `Subscription wallet: ${address}`,\n `Network: ${ROBINHOOD_NETWORK_DISPLAY} (chain ID ${ROBINHOOD_CHAIN_ID})`,\n `Token: ${CIA_SYMBOL} (contract ${CIA_TOKEN_ADDRESS}), with ETH for gas`,\n `Balance: ${balances.cia} ${CIA_SYMBOL}, ${balances.eth} ETH`,\n SUBSCRIPTION_TOPUP_RULE,\n ].join('\\n'),\n },\n ],\n structuredContent: {\n schema: 'chain-insights.result.v1',\n tool: SUBSCRIPTION_TOPUP_TOOL,\n hint: null,\n facts: { wallet_configured: true, topup },\n },\n isError: false,\n }\n}\n\nlet extAppsScript: string | null = null\n\n/**\n * The browser build of the ext-apps App client (the same 1.x pin as\n * the Chain Insights UI package), turned from an ES module into a script that publishes\n * its exports on globalThis, so the view stays one self-contained file.\n */\nexport function extAppsBrowserScript(): string {\n if (extAppsScript !== null) return extAppsScript\n const require = createRequire(import.meta.url)\n const source = readFileSync(\n require.resolve('@modelcontextprotocol/ext-apps/app-with-deps'),\n 'utf8'\n )\n const match = /export\\s*\\{([^}]*)\\}\\s*;?\\s*(?:\\/\\/# sourceMappingURL=\\S*\\s*)?$/.exec(source)\n if (!match) throw new Error('The ext-apps app bundle does not end with one export list')\n const fields = match[1]\n .split(',')\n .map((part) => part.trim())\n .filter(Boolean)\n .map((part) => {\n const [local, exported] = part.split(/\\s+as\\s+/)\n return `${JSON.stringify(exported ?? local)}:${local}`\n })\n extAppsScript = `${source.slice(0, match.index)}\\nglobalThis.${EXT_APPS_GLOBAL}={${fields.join(',')}};\\n`\n extAppsScript = extAppsScript.replaceAll('</script', '<\\\\/script')\n return extAppsScript\n}\n\nconst TOPUP_VIEW_STYLE = `\n:root {\n color-scheme: light;\n --ci-text: var(--color-text-primary, #1d1b16);\n --ci-muted: var(--color-text-secondary, #5c5647);\n --ci-surface: var(--color-background-secondary, #f7f4ec);\n --ci-border: var(--color-border-primary, #d9d2c0);\n --ci-accent: #8a6a12;\n --ci-on-accent: #ffffff;\n --ci-qr-dark: #000000;\n --ci-qr-light: #ffffff;\n --ci-font: var(--font-sans, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif);\n --ci-mono: var(--font-mono, ui-monospace, 'SF Mono', Menlo, Consolas, monospace);\n}\nhtml[data-theme='dark'] {\n color-scheme: dark;\n --ci-text: var(--color-text-primary, #f2eee4);\n --ci-muted: var(--color-text-secondary, #b8b09c);\n --ci-surface: var(--color-background-secondary, #1f1d19);\n --ci-border: var(--color-border-primary, #3d392f);\n --ci-accent: #e3c46a;\n --ci-on-accent: #1d1b16;\n}\n* { box-sizing: border-box; margin: 0; padding: 0; }\nhtml, body { background: transparent; }\nbody { font-family: var(--ci-font); color: var(--ci-text); font-size: 14px; line-height: 1.45; padding: 4px; }\n.card { max-width: 420px; margin: 0 auto; padding: 20px; border: 1px solid var(--ci-border); border-radius: 12px; background: var(--ci-surface); }\nh1 { font-size: 16px; font-weight: 600; margin-bottom: 4px; }\n.lead, .note, .muted { color: var(--ci-muted); }\n.lead { margin-bottom: 16px; }\n.qr-tile { display: flex; justify-content: center; margin-bottom: 16px; }\n.qr { width: 200px; height: 200px; border-radius: 8px; }\n.qr-dark { fill: var(--ci-qr-dark); }\n.qr-light { fill: var(--ci-qr-light); }\ndl { display: grid; grid-template-columns: max-content 1fr; gap: 6px 12px; margin-bottom: 16px; }\ndt { color: var(--ci-muted); }\ndd { overflow-wrap: anywhere; }\ncode { font-family: var(--ci-mono); font-size: 12px; }\n.address { display: block; margin-bottom: 6px; }\nbutton { font: inherit; font-size: 13px; padding: 4px 12px; border-radius: 6px; border: 0; background: var(--ci-accent); color: var(--ci-on-accent); cursor: pointer; }\nbutton:focus-visible { outline: 2px solid var(--ci-accent); outline-offset: 2px; }\n.copy-state { margin-left: 8px; font-size: 12px; color: var(--ci-muted); }\n.skeleton { height: 14px; border-radius: 4px; background: var(--ci-border); margin-bottom: 10px; }\n.skeleton.short { width: 60%; }\n`\n\nconst TOPUP_VIEW_SCRIPT = `\n(function () {\n var extApps = globalThis.${EXT_APPS_GLOBAL};\n var qrModules = __QR_MODULES__;\n var root = document.getElementById('topup');\n var ADDRESS = /^0x[0-9a-fA-F]{40}$/;\n\n function el(tag, attrs, children) {\n var node = document.createElement(tag);\n Object.keys(attrs || {}).forEach(function (key) {\n if (key === 'text') node.textContent = attrs[key];\n else node.setAttribute(key, attrs[key]);\n });\n (children || []).forEach(function (child) {\n node.append(typeof child === 'string' ? document.createTextNode(child) : child);\n });\n return node;\n }\n\n function qrSvg(address) {\n var modules = qrModules(address);\n var quiet = 4;\n var total = modules.length + quiet * 2;\n var ns = 'http://www.w3.org/2000/svg';\n var svg = document.createElementNS(ns, 'svg');\n svg.setAttribute('class', 'qr');\n svg.setAttribute('viewBox', '0 0 ' + total + ' ' + total);\n svg.setAttribute('shape-rendering', 'crispEdges');\n svg.setAttribute('role', 'img');\n svg.setAttribute('aria-label', 'QR code of the wallet address ' + address);\n var light = document.createElementNS(ns, 'rect');\n light.setAttribute('class', 'qr-light');\n light.setAttribute('width', String(total));\n light.setAttribute('height', String(total));\n svg.appendChild(light);\n var d = '';\n for (var row = 0; row < modules.length; row++) {\n for (var col = 0; col < modules.length; col++) {\n if (modules[row][col] === 1) d += 'M' + (col + quiet) + ' ' + (row + quiet) + 'h1v1h-1z';\n }\n }\n var dark = document.createElementNS(ns, 'path');\n dark.setAttribute('class', 'qr-dark');\n dark.setAttribute('d', d);\n svg.appendChild(dark);\n return svg;\n }\n\n function show(children) {\n root.replaceChildren(el('div', { class: 'card' }, children));\n }\n\n function resultText(result) {\n return ((result && result.content) || [])\n .filter(function (item) { return item && item.type === 'text' && typeof item.text === 'string'; })\n .map(function (item) { return item.text; })\n .join('\\\\n');\n }\n\n function copyButton(address, target) {\n var state = el('span', { class: 'copy-state', 'aria-live': 'polite' });\n var button = el('button', { type: 'button', text: 'Copy address' });\n button.addEventListener('click', function () {\n function selectIt(message) {\n var range = document.createRange();\n range.selectNodeContents(target);\n var selection = window.getSelection();\n selection.removeAllRanges();\n selection.addRange(range);\n state.textContent = message;\n }\n if (navigator.clipboard && navigator.clipboard.writeText) {\n navigator.clipboard.writeText(address).then(\n function () { state.textContent = 'Copied'; },\n function () { selectIt('Selected. Press Ctrl+C to copy.'); }\n );\n } else {\n selectIt('Selected. Press Ctrl+C to copy.');\n }\n });\n return el('div', {}, [button, state]);\n }\n\n function render(result) {\n var structured = result && result.structuredContent;\n var facts = structured && (structured.tool === '${TOPUP_TOOL_NAME}' || structured.tool === '${SUBSCRIPTION_TOPUP_TOOL}') ? structured.facts : null;\n if (!result || result.isError || !facts) {\n show([el('h1', { text: 'Wallet top-up' }), el('p', { class: 'muted', text: resultText(result) || 'The wallet details did not arrive.' })]);\n return;\n }\n if (!facts.topup) {\n show([\n el('h1', { text: 'No payment wallet yet' }),\n el('p', { class: 'lead' }, ['Create one with ', el('code', { text: 'cia wallet create' }), ', then ask to top up again.'])\n ]);\n return;\n }\n var topup = facts.topup;\n if (!ADDRESS.test(topup.wallet_address)) {\n show([el('h1', { text: 'Wallet top-up' }), el('p', { class: 'muted', text: 'The wallet address is not a valid 0x address.' })]);\n return;\n }\n var address = el('code', { class: 'address', text: topup.wallet_address });\n show([\n el('h1', { text: 'Top up your Chain Insights wallet' }),\n el('p', { class: 'lead', text: 'Send ' + topup.token + ' on ' + topup.network_display + ' to this address.' }),\n el('div', { class: 'qr-tile' }, [qrSvg(topup.wallet_address)]),\n el('dl', {}, [\n el('dt', { text: 'Network' }), el('dd', { text: topup.network_display + ' (chain ID ' + topup.chain_id + ')' }),\n el('dt', { text: 'Token' }), el('dd', { text: topup.token }),\n el('dt', { text: 'Token contract' }), el('dd', {}, [el('code', { text: topup.token_contract })]),\n el('dt', { text: 'Wallet' }), el('dd', {}, [address, copyButton(topup.wallet_address, address)])\n ]),\n el('p', { class: 'note', text: 'This view shows an address only. It signs nothing and sends nothing.' })\n ]);\n }\n\n function applyContext(context) {\n if (!context) return;\n if (context.theme === 'light' || context.theme === 'dark') extApps.applyDocumentTheme(context.theme);\n var styles = context.styles;\n if (styles && styles.variables) extApps.applyHostStyleVariables(styles.variables);\n if (styles && styles.css && styles.css.fonts) extApps.applyHostFonts(styles.css.fonts);\n }\n\n // Until the host names its theme, follow the system; the host's theme wins.\n if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {\n document.documentElement.setAttribute('data-theme', 'dark');\n }\n\n var app = new extApps.App({ name: 'chain-insights-topup', version: '1.0.0' }, {});\n app.ontoolresult = function (result) { render(result); };\n app.onhostcontextchanged = function (change) { applyContext(change); };\n app.onteardown = function () { return {}; };\n app.connect(new extApps.PostMessageTransport(window.parent, window.parent)).then(\n function () { applyContext(app.getHostContext()); },\n function () {\n show([el('h1', { text: 'Wallet top-up' }), el('p', { class: 'muted', text: 'This view opens inside Claude, next to the ${TOPUP_TOOL_NAME} answer.' })]);\n }\n );\n})();\n`\n\n/**\n * The QR encoder as browser source: an expression that evaluates to\n * `qrModules`. A transform that keeps function names (esbuild keepNames, as\n * tsx runs it) wraps inner functions in `__name(...)`; the wrapper gives that\n * helper a do-nothing body so the source runs anywhere.\n */\nexport function qrModulesSource(): string {\n return `(function () { var __name = function (target) { return target; }; return (${qrModules.toString()}); })()`\n}\n\n/** The one self-contained HTML file of ui://chain-insights/topup. */\nexport function topupViewHtml(): string {\n const script = TOPUP_VIEW_SCRIPT.replace('__QR_MODULES__', () => qrModulesSource())\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Wallet top-up</title>\n<style>${TOPUP_VIEW_STYLE}</style>\n</head>\n<body>\n<main id=\"topup\" aria-live=\"polite\"><div class=\"card\"><div class=\"skeleton\"></div><div class=\"skeleton short\"></div><p class=\"muted\">Waiting for the wallet details.</p></div></main>\n<script type=\"module\">${extAppsBrowserScript()}</script>\n<script type=\"module\">${script.replaceAll('</script', '<\\\\/script')}</script>\n</body>\n</html>`\n}\n\n/** The parts of McpServer the top-up view registers on. */\nexport type TopupViewServer = Pick<\n import('@modelcontextprotocol/sdk/server/mcp.js').McpServer,\n 'registerTool' | 'registerResource'\n>\n\nexport interface TopupViewDependencies {\n /** The local wallet address, or null when no wallet exists. Throws when it cannot be read. */\n walletAddress: () => Promise<string | null>\n /** The $CIA and ETH balances on Robinhood Chain, 'unknown' when no RPC answers. */\n robinhoodBalances?: (address: string) => Promise<{ cia: string; eth: string }>\n}\n\nasync function readRobinhoodBalances(address: string): Promise<{ cia: string; eth: string }> {\n const [cia, eth] = await Promise.all([getCiaBalance(address), getRobinhoodEthBalance(address)])\n return { cia, eth }\n}\n\n/**\n * Register the wallet_topup tool and its view on the local proxy. The tool\n * reads the local wallet address and nothing else.\n */\nexport async function registerTopupView(\n server: TopupViewServer,\n deps: TopupViewDependencies\n): Promise<void> {\n const { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } =\n await import('@modelcontextprotocol/ext-apps/server')\n const uiMeta = { prefersBorder: false, csp: { resourceDomains: ['https://assets.claude.ai'] } }\n\n registerAppTool(\n server,\n TOPUP_TOOL_NAME,\n {\n title: 'Top Up Wallet',\n description: WALLET_TOPUP_DESCRIPTION,\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n },\n _meta: { ui: { resourceUri: TOPUP_VIEW_URI } },\n },\n async () => {\n try {\n return topupToolResult(await deps.walletAddress())\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: `Top-up failed: ${(err as Error).message}` }],\n isError: true,\n }\n }\n }\n )\n\n registerAppTool(\n server,\n SUBSCRIPTION_TOPUP_TOOL,\n {\n title: 'Top Up Subscription Wallet',\n description: SUBSCRIPTION_TOPUP_DESCRIPTION,\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n // It reads the balances on Robinhood Chain.\n openWorldHint: true,\n },\n _meta: { ui: { resourceUri: TOPUP_VIEW_URI } },\n },\n async () => {\n try {\n const address = await deps.walletAddress()\n if (address === null) return subscriptionTopupToolResult(null)\n const balances = await (deps.robinhoodBalances ?? readRobinhoodBalances)(address)\n return subscriptionTopupToolResult(address, balances)\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: `Top-up failed: ${(err as Error).message}` }],\n isError: true,\n }\n }\n }\n )\n\n registerAppResource(\n server,\n 'Chain Insights wallet top-up',\n TOPUP_VIEW_URI,\n {\n description:\n 'The local wallet address and its QR code: on Base Mainnet with USDC, or on Robinhood Chain with $CIA.',\n _meta: { ui: uiMeta },\n },\n async () => ({\n contents: [\n {\n uri: TOPUP_VIEW_URI,\n mimeType: RESOURCE_MIME_TYPE,\n text: topupViewHtml(),\n _meta: { ui: uiMeta },\n },\n ],\n })\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,SAAgB,UAAU,MAA0B;CAElD,MAAM,WAAW;EACf;GAAC;GAAG;GAAI;EAAC;EACT;GAAC;GAAG;GAAI;EAAE;EACV;GAAC;GAAG;GAAI;EAAE;EACV;GAAC;GAAG;GAAK;EAAE;CACb;CAEA,MAAM,aAAa;CACnB,MAAM,QAAQ,MAAM,KAAK,IAAI,YAAY,CAAC,CAAC,OAAO,IAAI,CAAC;CAEvD,IAAI,UAAU;CACd,IAAI,iBAAiB;CACrB,IAAI,cAAc;CAClB,KAAK,MAAM,SAAS,UAAU;EAC5B,MAAM,gBAAgB,MAAM,KAAK,MAAM;EACvC,IAAI,KAAQ,MAAM,SAAS,KAAK,gBAAgB,GAAG;GACjD,UAAU,MAAM;GAChB,iBAAiB,MAAM;GACvB,cAAc,MAAM;GACpB;EACF;CACF;CACA,IAAI,YAAY,GAAG,MAAM,IAAI,MAAM,uCAAuC;CAE1E,MAAM,gBAAgB,iBAAiB;CACvC,MAAM,OAAO,UAAU,IAAI;CAG3B,MAAM,OAAiB,CAAC;CACxB,MAAM,YAAY,OAAe,WAAmB;EAClD,KAAK,IAAI,IAAI,SAAS,GAAG,KAAK,GAAG,KAAK,KAAK,KAAM,UAAU,IAAK,CAAC;CACnE;CACA,SAAS,GAAQ,CAAC;CAClB,SAAS,MAAM,QAAQ,CAAC;CACxB,KAAK,MAAM,SAAS,OAAO,SAAS,OAAO,CAAC;CAC5C,MAAM,WAAW,gBAAgB;CACjC,SAAS,GAAG,KAAK,IAAI,GAAG,WAAW,KAAK,MAAM,CAAC;CAC/C,OAAO,KAAK,SAAS,MAAM,GAAG,KAAK,KAAK,CAAC;CACzC,KAAK,IAAI,MAAM,KAAM,KAAK,SAAS,UAAU,OAAO,KAAa,SAAS,KAAK,CAAC;CAChF,MAAM,OAAiB,CAAC;CACxB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;EACvC,IAAI,QAAQ;EACZ,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK,QAAS,SAAS,IAAK,KAAK,IAAI;EAC5D,KAAK,KAAK,KAAK;CACjB;CAGA,MAAM,MAAgB,CAAC;CACvB,MAAM,MAAgB,CAAC;CACvB,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IAAI,KAAK,KAAK;EACnC,IAAI,KAAK;EACT,IAAI,KAAK;EACT,MAAM;EACN,IAAI,IAAI,KAAO,KAAK;CACtB;CACA,MAAM,YAAY,GAAW,MAAe,MAAM,KAAK,MAAM,IAAI,IAAI,KAAK,IAAI,KAAK,IAAI,MAAM;CAC7F,IAAI,YAAY,CAAC,CAAC;CAClB,KAAK,IAAI,IAAI,GAAG,IAAI,aAAa,KAAK;EACpC,MAAM,OAAO,IAAI,MAAM,UAAU,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;EACnD,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;GACzC,KAAK,MAAM,UAAU;GACrB,KAAK,IAAI,MAAM,SAAS,UAAU,IAAI,IAAI,EAAE;EAC9C;EACA,YAAY;CACd;CACA,MAAM,YAAY,CAAC,GAAG,MAAM,GAAG,IAAI,MAAM,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC;CAC7D,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,SAAS,UAAU;EACzB,IAAI,WAAW,GAAG;EAClB,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,UAAU,IAAI,MAAM,SAAS,UAAU,IAAI,MAAM;CAC9F;CACA,MAAM,YAAY,CAAC,GAAG,MAAM,GAAG,UAAU,MAAM,KAAK,MAAM,CAAC;CAG3D,MAAM,UAAsB,CAAC;CAC7B,MAAM,WAAwB,CAAC;CAC/B,KAAK,IAAI,MAAM,GAAG,MAAM,MAAM,OAAO;EACnC,QAAQ,KAAK,IAAI,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;EACpC,SAAS,KAAK,IAAI,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC;CAC3C;CACA,MAAM,eAAe,KAAa,KAAa,SAAkB;EAC/D,QAAQ,IAAI,CAAC,OAAO,OAAO,IAAI;EAC/B,SAAS,IAAI,CAAC,OAAO;CACvB;CACA,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,KAAK;EAC7B,YAAY,GAAG,GAAG,IAAI,MAAM,CAAC;EAC7B,YAAY,GAAG,GAAG,IAAI,MAAM,CAAC;CAC/B;CACA,MAAM,UAAU,WAAmB,cAAsB;EACvD,KAAK,IAAI,KAAK,IAAI,MAAM,GAAG,MACzB,KAAK,IAAI,KAAK,IAAI,MAAM,GAAG,MAAM;GAC/B,MAAM,MAAM,YAAY;GACxB,MAAM,MAAM,YAAY;GACxB,IAAI,MAAM,KAAK,OAAO,QAAQ,MAAM,KAAK,OAAO,MAAM;GACtD,MAAM,WAAW,KAAK,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,IAAI,EAAE,CAAC;GACpD,YAAY,KAAK,KAAK,aAAa,KAAK,aAAa,CAAC;EACxD;CAEJ;CACA,OAAO,GAAG,CAAC;CACX,OAAO,GAAG,OAAO,CAAC;CAClB,OAAO,OAAO,GAAG,CAAC;CAClB,IAAI,WAAW,GAAG;EAChB,MAAM,SAAS,OAAO;EACtB,KAAK,IAAI,KAAK,IAAI,MAAM,GAAG,MACzB,KAAK,IAAI,KAAK,IAAI,MAAM,GAAG,MACzB,YAAY,SAAS,IAAI,SAAS,IAAI,KAAK,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC;CAGtF;CACA,MAAM,aAAa,OAAgB,eAAe,IAAK,OAAO;CAC9D,KAAK,IAAI,IAAI,GAAG,KAAK,GAAG,KAAK,YAAY,GAAG,GAAG,UAAU,CAAC,CAAC;CAC3D,YAAY,GAAG,GAAG,UAAU,CAAC,CAAC;CAC9B,YAAY,GAAG,GAAG,UAAU,CAAC,CAAC;CAC9B,YAAY,GAAG,GAAG,UAAU,CAAC,CAAC;CAC9B,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,YAAY,GAAG,KAAK,GAAG,UAAU,CAAC,CAAC;CAChE,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK,YAAY,GAAG,OAAO,IAAI,GAAG,UAAU,CAAC,CAAC;CACrE,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,YAAY,OAAO,KAAK,GAAG,GAAG,UAAU,CAAC,CAAC;CACvE,YAAY,OAAO,GAAG,GAAG,IAAI;CAG7B,IAAI,WAAW;CACf,KAAK,IAAI,QAAQ,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG;EACjD,IAAI,UAAU,GAAG,QAAQ;EACzB,MAAM,UAAW,QAAQ,IAAK,OAAO;EACrC,KAAK,IAAI,OAAO,GAAG,OAAO,MAAM,QAAQ;GACtC,MAAM,MAAM,SAAS,OAAO,IAAI,OAAO;GACvC,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;IAC1B,MAAM,MAAM,QAAQ;IACpB,IAAI,SAAS,IAAI,CAAC,MAAM;IACxB,IAAI,WAAW,UAAU,SAAS,GAAG;KACnC,QAAQ,IAAI,CAAC,OAAQ,UAAU,aAAa,OAAQ,KAAK,WAAW,KAAO;KAC3E;IACF;IACA,KAAK,MAAM,OAAO,MAAM,GAAG,QAAQ,IAAI,CAAC,QAAQ;GAClD;EACF;CACF;CACA,OAAO;AACT;AAYA,SAAgB,cAAc,MAAc,OAA2B,GAAW;CAEhF,MAAM,UAAqB,OAAO,SAAS,WAAW,EAAE,UAAU,KAAK,IAAI;CAC3E,MAAM,WAAW,QAAQ,YAAY;CACrC,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,UAAU,QAAQ,WAAW;CACnC,MAAM,cAAc,QAAQ,eAAe;CAE3C,MAAM,SAAS,UAAU,IAAI;CAC7B,MAAM,OAAO,OAAO;CAGpB,MAAM,QAAQ,QAAQ,aAAa;CACnC,MAAM,QAAQ,QAAQ,cAAc;CACpC,MAAM,aAAa,KAAK,OAAO,OAAO,SAAS,CAAC;CAChD,MAAM,aAAa,KAAK,OAAO,OAAO,SAAS,CAAC;CAChD,MAAM,UAAU,CAAC,CAAC,QAAQ;CAE1B,MAAM,UAAU,OAAO;CACvB,IAAI,MAAM,6FAA6F,QAAQ,YAAY,QAAQ,iBAAiB,QAAQ,GAAG,QAAQ;CACvK,OAAO,gBAAgB,QAAQ,YAAY,QAAQ,UAAU,QAAQ;CAErE,MAAM,kBAAkB,GAAW,MAChC,IAAI,KAAK,IAAI,KAAO,IAAI,KAAK,KAAK,OAAO,KAAO,KAAK,OAAO,KAAK,IAAI;CAExE,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,KACxB,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,KAAK;EAC7B,IACE,WACA,KAAK,cACL,IAAI,aAAa,SACjB,KAAK,cACL,IAAI,aAAa,OAEjB;EAEF,IAAI,OAAO,EAAE,CAAC,OAAO,GAAG;GACtB,MAAM,QAAQ,eAAe,GAAG,CAAC,IAAI,cAAc;GACnD,OAAO,YAAY,IAAI,SAAS,OAAO,IAAI,SAAS,WAAW,SAAS,YAAY,SAAS,UAAU,MAAM;EAC/G;CACF;CAGF,IAAI,WAAW,QAAQ,YAAY;EACjC,MAAM,KAAK,aAAa;EACxB,MAAM,KAAK,aAAa;EACxB,MAAM,KAAK,QAAQ;EACnB,MAAM,KAAK,QAAQ;EACnB,OAAO,YAAY,KAAK,EAAE,OAAO,KAAK,EAAE,WAAW,KAAK,EAAE,YAAY,KAAK,EAAE,UAAU,QAAQ;EAC/F,OAAO,aAAa,KAAK,EAAE,OAAO,KAAK,EAAE,WAAW,KAAK,EAAE,YAAY,KAAK,EAAE,UAAU,QAAQ,WAAW,gBAAgB,QAAQ,WAAW;CAChJ;CAEA,OAAO;CACP,OAAO;AACT;;;;;;AAcA,SAAgB,eAAe,MAAc,UAA6B,CAAC,GAAW;CACpF,MAAM,QAAQ,QAAQ,aAAa;CACnC,MAAM,UAAU,UAAU,IAAI;CAC9B,MAAM,OAAO,QAAQ,SAAS,QAAQ;CACtC,MAAM,QAAQ,KAAa,QAAyB;EAClD,MAAM,IAAI,MAAM;EAChB,MAAM,IAAI,MAAM;EAChB,MAAM,KAAK,KAAK,KAAK,KAAK,KAAK,IAAI,QAAQ,UAAU,IAAI,QAAQ,UAAU,QAAQ,EAAE,CAAC,OAAO;EAC7F,OAAO,QAAQ,SAAS,CAAC,KAAK;CAChC;CACA,MAAM,QAAkB,CAAC;CACzB,KAAK,IAAI,MAAM,GAAG,MAAM,MAAM,OAAO,GAAG;EACtC,IAAI,OAAO;EACX,KAAK,IAAI,MAAM,GAAG,MAAM,MAAM,OAAO;GACnC,MAAM,MAAM,KAAK,KAAK,GAAG;GACzB,MAAM,SAAS,MAAM,IAAI,OAAO,KAAK,MAAM,GAAG,GAAG,IAAI,QAAQ,WAAW;GACxE,QAAQ,OAAO,SAAS,MAAM,MAAM,MAAM,SAAS,MAAM;EAC3D;EACA,MAAM,KAAK,IAAI;CACjB;CACA,OAAO,MAAM,KAAK,IAAI;AACxB;;;AC9PA,MAAMA,iBAAe;AAErB,MAAM,uBAAuB;CAC3B;CACA;CACA;CACA;AACF;AACA,MAAM,WAAW,CACf;CACE,MAAM;CACN,MAAM;CACN,iBAAiB;CACjB,QAAQ,CAAC;EAAE,MAAM;EAAW,MAAM;CAAU,CAAC;CAC7C,SAAS,CAAC;EAAE,MAAM;EAAI,MAAM;CAAU,CAAC;AACzC,CACF;AAEA,SAAS,cAAc,QAA4C;CACjE,OAAQ,OAAO,WAAW,WAAW,SAAS,OAAO;AACvD;AAEA,eAAsB,eAAe,QAA8C;CACjF,MAAM,YAAY,QAAQ,IAAI;CAC9B,MAAM,UAAU,CACd,GAAI,YAAY,CAAC,SAAS,IAAI,CAAC,GAC/B,GAAG,qBAAqB,QAAQ,QAAQ,QAAQ,SAAS,CAC3D;CAEA,KAAK,MAAM,UAAU,SACnB,IAAI;EAEF,MAAM,UAAU,MADD,mBAAmB;GAAE,OAAO;GAAM,WAAW,KAAK,MAAM;EAAE,CAC9C,CAAC,CAAC,aAAa;GACxC,SAASA;GACT,KAAK;GACL,cAAc;GACd,MAAM,CAAC,cAAc,MAAM,CAAC;EAC9B,CAAC;EACD,OAAO,YAAY,SAAS,CAAC;CAC/B,QAAQ,CAER;CAGF,OAAO;AACT;AAEA,eAAsB,cAAc,QAA8C;CAChF,MAAM,YAAY,QAAQ,IAAI;CAC9B,MAAM,UAAU,CACd,GAAI,YAAY,CAAC,SAAS,IAAI,CAAC,GAC/B,GAAG,qBAAqB,QAAQ,QAAQ,QAAQ,SAAS,CAC3D;CAEA,KAAK,MAAM,UAAU,SACnB,IAAI;EAEF,MAAM,UAAU,MADD,mBAAmB;GAAE,OAAO;GAAM,WAAW,KAAK,MAAM;EAAE,CAC9C,CAAC,CAAC,WAAW,EAAE,SAAS,cAAc,MAAM,EAAE,CAAC;EAC1E,OAAO,YAAY,OAAO;CAC5B,QAAQ,CAER;CAGF,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;AC9CA,MAAM,eAAe;AACrB,MAAM,gBAAgB;;AAEtB,MAAM,yBAAyB;AAE/B,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAGxD,SAAS,UAAU,MAAsB;CACvC,MAAM,QAAQ,CACZ,KAAK,WAAW,UAAU,IAAI,GAC9B,KAAK,WAAW,MAAM,OAAO,UAAU,IAAI,CAC7C;CACA,KAAK,MAAM,KAAK,OACd,IAAI;EACF,OAAO,aAAa,CAAC;CACvB,QAAQ,CAER;CAEF,QAAQ,MAAM,mCAAmC,KAAK,WAAW;CACjE,OAAO,OAAO,MAAM,CAAC;AACvB;AAEA,MAAM,UAAU,UAAU,UAAU;AACpC,MAAM,eAAe,UAAU,gBAAgB;AAS/C,MAAM,0BAAU,IAAI,IAAiD;AAErE,SAAS,oBAAoB,QAAqC;CAChE,MAAM,gBAAgB,OAAO,WAAW,WAAW,SAAS,OAAO;CACnE,IAAI,CAAC,UAAU,aAAa,GAC1B,MAAM,IAAI,MAAM,gEAAgE;CAElF,OAAO;AACT;AAEA,SAAS,WAAW,OAAuB;CACzC,OAAO,MACJ,WAAW,KAAK,OAAO,CAAC,CACxB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,MAAK,QAAQ,CAAC,CACzB,WAAW,KAAK,OAAO;AAC5B;AAEA,SAAS,cAAc,OAAuB;CAC5C,OAAO,KAAK,UAAU,KAAK,CAAC,CACzB,WAAW,KAAK,SAAS,CAAC,CAC1B,WAAW,KAAK,SAAS,CAAC,CAC1B,WAAW,KAAK,SAAS;AAC9B;AAEA,SAAgB,YAAY,OAAkB,aAA4B;CACxE,MAAM,UAAU,QAAQ,IAAI,IAAI;CAChC,OAAO,UAAU,oBAAoB,QAAQ,SAAS;AACxD;AAEA,eAAe,YAAY,MAAiB,eAAwD;CAClG,IAAI,SAAS,iBAAiB;EAC5B,MAAM,CAAC,YAAY,cAAc,MAAM,QAAQ,IAAI,CACjD,cAAc,aAAa,GAC3B,uBAAuB,aAAa,CACtC,CAAC;EACD,OAAO;GAAE,aAAa;GAAY,aAAa;EAAW;CAC5D;CACA,MAAM,CAAC,aAAa,cAAc,MAAM,QAAQ,IAAI,CAClD,eAAe,aAAa,GAC5B,cAAc,aAAa,CAC7B,CAAC;CACD,OAAO;EAAE,cAAc;EAAa,aAAa;CAAW;AAC9D;AAEA,MAAM,kBAA6D;CACjE,aAAa;EAAE,cAAc;EAAW,aAAa;CAAU;CAC/D,iBAAiB;EAAE,aAAa;EAAW,aAAa;CAAU;AACpE;AAEA,eAAsB,iBACpB,QACA,OAAkB,aACD;CACjB,MAAM,gBAAgB,oBAAoB,MAAM;CAEhD,MAAM,UAAU,YAAY,IAAI;CAChC,IAAI,SACF,OAAO;CAGT,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,SAAS,cAAc,KAAK,QAAQ;GACxC,IAAI,IAAI,QAAQ,eAAe;IAC7B,IAAI,UAAU,KAAK;KACjB,gBAAgB;KAChB,+BAA+B;IACjC,CAAC;IACD,IAAI,IAAI,KAAK,UAAU,EAAE,SAAS,cAAc,CAAC,CAAC;IAClD;GACF;GAEA,IAAI,IAAI,QAAQ,gBAAgB;IAC9B,YAAY,MAAM,aAAa,CAAC,CAC7B,MAAM,YAAY;KACjB,IAAI,UAAU,KAAK;MACjB,gBAAgB;MAChB,+BAA+B;KACjC,CAAC;KACD,IAAI,IAAI,KAAK,UAAU,OAAO,CAAC;IACjC,CAAC,CAAC,CACD,YAAY;KACX,IAAI,UAAU,KAAK;MACjB,gBAAgB;MAChB,+BAA+B;KACjC,CAAC;KACD,IAAI,IAAI,KAAK,UAAU,gBAAgB,KAAK,CAAC;IAC/C,CAAC;IACH;GACF;GAEA,IAAI,IAAI,QAAQ,oBAAoB;IAClC,IAAI,UAAU,KAAK;KACjB,gBAAgB;KAChB,iBAAiB;KACjB,+BAA+B;IACjC,CAAC;IACD,IAAI,IAAI,OAAO;IACf;GACF;GAEA,IAAI,IAAI,QAAQ,0BAA0B;IACxC,IAAI,UAAU,KAAK;KACjB,gBAAgB;KAChB,iBAAiB;KACjB,+BAA+B;IACjC,CAAC;IACD,IAAI,IAAI,YAAY;IACpB;GACF;GAEA,IAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;GAClD,IAAI,IAAI,SAAS,kBAAkB,yBAAyB,aAAa,IAAI,aAAa,aAAa,CAAC;EAC1G,CAAC;EAED,OAAO,OAAO,GAAG,mBAAmB;GAClC,MAAM,OAAO,OAAO,QAAQ;GAC5B,IAAI,QAAQ,OAAO,SAAS,UAAU;IACpC,QAAQ,IAAI,MAAM;KAAE;KAAQ,MAAM,KAAK;IAAK,CAAC;IAC7C,MAAM,MAAM,oBAAoB,KAAK;IACrC,QAAQ,MAAM,4CAA4C,KAAK;IAC/D,QAAQ,GAAG;GACb,OACE,uBAAO,IAAI,MAAM,8BAA8B,CAAC;EAEpD,CAAC;EAED,OAAO,GAAG,SAAS,MAAM;CAC3B,CAAC;AACH;AAEA,SAAgB,qBAAqB,oBAA4B,UAA0B;CACzF,MAAM,gBAAgB,oBAAoB,kBAAkB;CAC5D,MAAM,oBAAoB,WAAW,aAAa;CAClD,MAAM,eAAe,WAAW,QAAQ;CACxC,MAAM,eAAe,cAAc,QAAQ;CAQ3C,OAAO;;;;;;;;;2CASkC,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCA4ExB,aAAa;;oBAvF7B,cAAc,eAAe,EAAE,UAAU,EAAE,CAyFnC,EAAE;;+DAEqC,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA2D/D,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsD/B;;AAqBA,MAAa,0BACX;AAEF,MAAM,kBAAoD;CACxD,aAAa;EACX,OAAO;EACP,UAAU;EACV,OAAO;EACP,UAAU;EACV,cAAc;EACd,SAAS;EACT,MAAM;EACN,cAAc;EACd,YAAY;CACd;CACA,iBAAiB;EACf,OAAO;EACP,UAAU;EACV,OAAO;EACP,UAAU;EACV,cAAc;EACd,SAAS;EACT,MAAM;EACN,cAAc;EACd,YAAY;CACd;AACF;;;;;;AAOA,SAAgB,kBAAkB,oBAA4B,OAAkB,aAAqB;CACnG,MAAM,OAAO,gBAAgB;CAC7B,MAAM,gBAAgB,oBAAoB,kBAAkB;CAC5D,MAAM,oBAAoB,WAAW,aAAa;CAClD,MAAM,oBAAoB,cAAc,aAAa;CAErD,OAAO;;;;;SAKA,KAAK,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SAsNX,KAAK,SAAS;;;;;;;2BAOI,kBAAkB;;;;;;QAMrC,KAAK,MAAM;;;;;yEAKsD,KAAK,SAAS;;;;;;0BAM7D,KAAK,QAAQ;;;;;oBAKnB,KAAK,KAAK;;;;iBAIb,kBAAkB;gBACnB,KAAK,aAAa;oBACd,KAAK,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAyCrB,KAAK,aAAa;gCACD,KAAK,aAAa;uFACqC,KAAK,SAAS;;;;8EAIvB,KAAK,SAAS;;;;;;;;;;AAU5F;;AAGA,SAAS,aAAa,oBAAoC;CACxD,OAAO,kBAAkB,oBAAoB,WAAW;AAC1D;;AAGA,SAAgB,yBAAyB,oBAAoC;CAC3E,OAAO,kBAAkB,oBAAoB,eAAe;AAC9D;AAUA,MAAa,kBAAkB;AAC/B,MAAa,iBAAiB;AAC9B,MAAa,wBAAwB;AACrC,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AACxB,MAAM,iBACJ;;;;;AA2BF,SAAgB,gBAAgB,eAA+C;CAC7E,IAAI,kBAAkB,MACpB,OAAO;EACL,SAAS,CAAC;GAAE,MAAM;GAAQ,MAAM;EAAe,CAAC;EAChD,mBAAmB;GACjB,QAAQ;GACR,MAAM;GACN,MAAM;GACN,OAAO;IAAE,mBAAmB;IAAO,OAAO;GAAK;EACjD;EACA,SAAS;CACX;CAEF,MAAM,UAAU,oBAAoB,aAAa;CACjD,MAAM,QAAwB;EAC5B,gBAAgB;EAChB,SAAS;EACT,iBAAiB;EACjB,UAAU;EACV,OAAO;EACP,gBAAgB;CAClB;CACA,OAAO;EACL,SAAS,CACP;GACE,MAAM;GACN,MAAM;IACJ,mBAAmB;IACnB,YAAY,sBAAsB,aAAa,eAAe;IAC9D,yBAAyB,aAAa;IACtC,2BAA2B,sBAAsB;GACnD,CAAC,CAAC,KAAK,IAAI;EACb,CACF;EACA,mBAAmB;GACjB,QAAQ;GACR,MAAM;GACN,MAAM;GACN,OAAO;IAAE,mBAAmB;IAAM;GAAM;EAC1C;EACA,SAAS;CACX;AACF;;AA2BA,MAAa,0BACX;;;;;;AAOF,SAAgB,4BACd,eACA,WAAyC;CAAE,KAAK;CAAW,KAAK;AAAU,GAC7C;CAC7B,IAAI,kBAAkB,MACpB,OAAO;EACL,SAAS,CAAC;GAAE,MAAM;GAAQ,MAAM;EAA6F,CAAC;EAC9H,mBAAmB;GACjB,QAAQ;GACR,MAAM;GACN,MAAM;GACN,OAAO;IAAE,mBAAmB;IAAO,OAAO;GAAK;EACjD;EACA,SAAS;CACX;CAEF,MAAM,UAAU,oBAAoB,aAAa;CACjD,MAAM,QAAoC;EACxC,gBAAgB;EAChB,SAAS;EACT,iBAAiB;EACjB,UAAU;EACV,OAAO;EACP,gBAAgB;EAChB,WAAW;EACX,aAAa,SAAS;EACtB,aAAa,SAAS;CACxB;CACA,OAAO;EACL,SAAS,CACP;GACE,MAAM;GACN,MAAM;IACJ,wBAAwB;IACxB,YAAY,0BAA0B,aAAa,mBAAmB;IACtE,UAAU,WAAW,aAAa,kBAAkB;IACpD,YAAY,SAAS,IAAI,GAAG,WAAW,IAAI,SAAS,IAAI;IACxD;GACF,CAAC,CAAC,KAAK,IAAI;EACb,CACF;EACA,mBAAmB;GACjB,QAAQ;GACR,MAAM;GACN,MAAM;GACN,OAAO;IAAE,mBAAmB;IAAM;GAAM;EAC1C;EACA,SAAS;CACX;AACF;AAEA,IAAI,gBAA+B;;;;;;AAOnC,SAAgB,uBAA+B;CAC7C,IAAI,kBAAkB,MAAM,OAAO;CACnC,MAAM,UAAU,cAAc,YAAY,GAAG;CAC7C,MAAM,SAAS,aACb,QAAQ,QAAQ,8CAA8C,GAC9D,MACF;CACA,MAAM,QAAQ,kEAAkE,KAAK,MAAM;CAC3F,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,2DAA2D;CACvF,MAAM,SAAS,MAAM,EAAE,CACpB,MAAM,GAAG,CAAC,CACV,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAC1B,OAAO,OAAO,CAAC,CACf,KAAK,SAAS;EACb,MAAM,CAAC,OAAO,YAAY,KAAK,MAAM,UAAU;EAC/C,OAAO,GAAG,KAAK,UAAU,YAAY,KAAK,EAAE,GAAG;CACjD,CAAC;CACH,gBAAgB,GAAG,OAAO,MAAM,GAAG,MAAM,KAAK,EAAE,eAAe,gBAAgB,IAAI,OAAO,KAAK,GAAG,EAAE;CACpG,gBAAgB,cAAc,WAAW,aAAY,YAAY;CACjE,OAAO;AACT;AAEA,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CzB,MAAM,oBAAoB;;6BAEG,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sDAmFS,gBAAgB,4BAA4B,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+HAoDK,gBAAgB;;;;;;;;;;;AAY/I,SAAgB,kBAA0B;CACxC,OAAO,6EAA6E,UAAU,SAAS,EAAE;AAC3G;;AAGA,SAAgB,gBAAwB;CACtC,MAAM,SAAS,kBAAkB,QAAQ,wBAAwB,gBAAgB,CAAC;CAClF,OAAO;;;;;;SAMA,iBAAiB;;;;wBAIF,qBAAqB,EAAE;wBACvB,OAAO,WAAW,aAAY,YAAY,EAAE;;;AAGpE;AAeA,eAAe,sBAAsB,SAAwD;CAC3F,MAAM,CAAC,KAAK,OAAO,MAAM,QAAQ,IAAI,CAAC,cAAc,OAAO,GAAG,uBAAuB,OAAO,CAAC,CAAC;CAC9F,OAAO;EAAE;EAAK;CAAI;AACpB;;;;;AAMA,eAAsB,kBACpB,QACA,MACe;CACf,MAAM,EAAE,qBAAqB,iBAAiB,uBAC5C,MAAM,OAAO;CACf,MAAM,SAAS;EAAE,eAAe;EAAO,KAAK,EAAE,iBAAiB,CAAC,0BAA0B,EAAE;CAAE;CAE9F,gBACE,QACA,iBACA;EACE,OAAO;EACP,aAAa;EACb,aAAa;GACX,cAAc;GACd,iBAAiB;GACjB,gBAAgB;GAChB,eAAe;EACjB;EACA,OAAO,EAAE,IAAI,EAAE,aAAa,eAAe,EAAE;CAC/C,GACA,YAAY;EACV,IAAI;GACF,OAAO,gBAAgB,MAAM,KAAK,cAAc,CAAC;EACnD,SAAS,KAAK;GACZ,OAAO;IACL,SAAS,CAAC;KAAE,MAAM;KAAiB,MAAM,kBAAmB,IAAc;IAAU,CAAC;IACrF,SAAS;GACX;EACF;CACF,CACF;CAEA,gBACE,QACA,yBACA;EACE,OAAO;EACP,aAAa;EACb,aAAa;GACX,cAAc;GACd,iBAAiB;GACjB,gBAAgB;GAEhB,eAAe;EACjB;EACA,OAAO,EAAE,IAAI,EAAE,aAAa,eAAe,EAAE;CAC/C,GACA,YAAY;EACV,IAAI;GACF,MAAM,UAAU,MAAM,KAAK,cAAc;GACzC,IAAI,YAAY,MAAM,OAAO,4BAA4B,IAAI;GAE7D,OAAO,4BAA4B,SAAS,OADpB,KAAK,qBAAqB,sBAAA,CAAuB,OAAO,CAC5B;EACtD,SAAS,KAAK;GACZ,OAAO;IACL,SAAS,CAAC;KAAE,MAAM;KAAiB,MAAM,kBAAmB,IAAc;IAAU,CAAC;IACrF,SAAS;GACX;EACF;CACF,CACF;CAEA,oBACE,QACA,gCACA,gBACA;EACE,aACE;EACF,OAAO,EAAE,IAAI,OAAO;CACtB,GACA,aAAa,EACX,UAAU,CACR;EACE,KAAK;EACL,UAAU;EACV,MAAM,cAAc;EACpB,OAAO,EAAE,IAAI,OAAO;CACtB,CACF,EACF,EACF;AACF"}
|
|
@@ -301,6 +301,9 @@ var topup_server_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll(
|
|
|
301
301
|
topupViewHtml: () => topupViewHtml
|
|
302
302
|
});
|
|
303
303
|
const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
304
|
+
const BASE_CHAIN_ID = "0x2105";
|
|
305
|
+
/** Robinhood Chain, chain ID 4663. */
|
|
306
|
+
const ROBINHOOD_CHAIN_ID_HEX = "0x1237";
|
|
304
307
|
const __dirname$1 = (0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
305
308
|
function loadAsset(name) {
|
|
306
309
|
const paths = [(0, node_path.join)(__dirname$1, "assets", name), (0, node_path.join)(__dirname$1, "..", "src", "assets", name)];
|
|
@@ -626,77 +629,49 @@ function selectAndCopy() {
|
|
|
626
629
|
</body>
|
|
627
630
|
</html>`;
|
|
628
631
|
}
|
|
629
|
-
/** The text of
|
|
632
|
+
/** The text of `cia subscription topup` in the terminal. */
|
|
630
633
|
const SUBSCRIPTION_TOPUP_TEXT = "Send $CIA and a little ETH on Robinhood Chain to your own wallet. Then run `cia subscription buy --days N`.";
|
|
631
634
|
const TOPUP_PAGE_TEXT = {
|
|
632
635
|
"base-usdc": {
|
|
633
636
|
title: "Chain Insights — Fund Wallet",
|
|
634
637
|
subtitle: "Fund your wallet to use blockchain intelligence tools",
|
|
635
638
|
badge: "Base Network · USDC",
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
label: "Current balance",
|
|
639
|
-
currency: "USDC",
|
|
640
|
-
field: "balance_usdc",
|
|
641
|
-
decimals: 2
|
|
642
|
-
}, {
|
|
643
|
-
id: "gasBalance",
|
|
644
|
-
label: "Gas balance",
|
|
645
|
-
currency: "ETH",
|
|
646
|
-
field: "balance_eth",
|
|
647
|
-
decimals: 6
|
|
648
|
-
}],
|
|
639
|
+
currency: "USDC",
|
|
640
|
+
balanceField: "balance_usdc",
|
|
649
641
|
gasNote: "Base ETH is used for one-time payment setup gas.",
|
|
650
|
-
info: "Send Base USDC to the wallet address above."
|
|
642
|
+
info: "Send Base USDC to the wallet address above.",
|
|
643
|
+
tokenAddress: USDC_ADDRESS,
|
|
644
|
+
chainIdHex: BASE_CHAIN_ID
|
|
651
645
|
},
|
|
652
646
|
"robinhood-cia": {
|
|
653
|
-
title: "Chain Insights — Fund
|
|
654
|
-
subtitle: "Fund your wallet
|
|
647
|
+
title: "Chain Insights — Fund Subscription Wallet",
|
|
648
|
+
subtitle: "Fund your wallet to buy a Chain Insights subscription",
|
|
655
649
|
badge: "Robinhood Chain · $CIA",
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
}, {
|
|
663
|
-
id: "gasBalance",
|
|
664
|
-
label: "Gas balance",
|
|
665
|
-
currency: "ETH",
|
|
666
|
-
field: "balance_eth",
|
|
667
|
-
decimals: 6
|
|
668
|
-
}],
|
|
669
|
-
gasNote: "Robinhood Chain ETH pays the gas of the two buy transactions.",
|
|
670
|
-
info: `Send \$CIA and a little ETH on Robinhood Chain to your own wallet. Then run <code>cia subscription buy --days N</code>.<br>Robinhood Chain (chain ID 4663) · $CIA <code>${require_robinhood.CIA_TOKEN_ADDRESS}</code><br>This page shows your own address only. It signs nothing and sends nothing.`
|
|
650
|
+
currency: "$CIA",
|
|
651
|
+
balanceField: "balance_cia",
|
|
652
|
+
gasNote: "Robinhood Chain ETH is used for the buy transaction gas.",
|
|
653
|
+
info: "Send $CIA and Robinhood Chain ETH to the wallet address above.",
|
|
654
|
+
tokenAddress: require_robinhood.CIA_TOKEN_ADDRESS,
|
|
655
|
+
chainIdHex: ROBINHOOD_CHAIN_ID_HEX
|
|
671
656
|
}
|
|
672
657
|
};
|
|
673
658
|
/**
|
|
674
|
-
* The one local top-up page: the wallet address, its QR code
|
|
675
|
-
*
|
|
676
|
-
*
|
|
659
|
+
* The one local top-up page: the wallet address, its QR code, the network
|
|
660
|
+
* and the balances. It names only the user's own wallet, never where a
|
|
661
|
+
* payment goes.
|
|
677
662
|
*/
|
|
678
663
|
function generateTopupPage(walletAddressInput, rail = "base-usdc") {
|
|
664
|
+
const text = TOPUP_PAGE_TEXT[rail];
|
|
679
665
|
const walletAddress = assertWalletAddress(walletAddressInput);
|
|
680
666
|
const safeWalletAddress = escapeHtml(walletAddress);
|
|
681
667
|
const walletAddressJson = jsonForScript(walletAddress);
|
|
682
|
-
const text = TOPUP_PAGE_TEXT[rail];
|
|
683
|
-
const qrSvg = generateQrSvg(walletAddress, { cellSize: 5 });
|
|
684
|
-
const balanceRows = text.balances.map((row) => ` <div class="balance-row">
|
|
685
|
-
<span class="balance-label">${row.label}</span>
|
|
686
|
-
<span class="balance-value" id="${row.id}">—<span class="currency">${row.currency}</span></span>
|
|
687
|
-
</div>`).join("\n");
|
|
688
|
-
const balancesJson = JSON.stringify(text.balances.map(({ id, currency, field, decimals }) => ({
|
|
689
|
-
id,
|
|
690
|
-
currency,
|
|
691
|
-
field,
|
|
692
|
-
decimals
|
|
693
|
-
})));
|
|
694
668
|
return `<!DOCTYPE html>
|
|
695
669
|
<html lang="en">
|
|
696
670
|
<head>
|
|
697
671
|
<meta charset="utf-8">
|
|
698
672
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
699
673
|
<title>${text.title}</title>
|
|
674
|
+
<script src="https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js"><\/script>
|
|
700
675
|
<style>
|
|
701
676
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
702
677
|
|
|
@@ -748,7 +723,7 @@ function generateTopupPage(walletAddressInput, rail = "base-usdc") {
|
|
|
748
723
|
margin-bottom: 24px;
|
|
749
724
|
}
|
|
750
725
|
|
|
751
|
-
.qr-container
|
|
726
|
+
.qr-container canvas, .qr-container img {
|
|
752
727
|
border-radius: 12px;
|
|
753
728
|
background: #fff;
|
|
754
729
|
padding: 12px;
|
|
@@ -903,12 +878,6 @@ function generateTopupPage(walletAddressInput, rail = "base-usdc") {
|
|
|
903
878
|
color: rgba(255, 255, 255, 0.3);
|
|
904
879
|
line-height: 1.6;
|
|
905
880
|
}
|
|
906
|
-
|
|
907
|
-
.info code {
|
|
908
|
-
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
909
|
-
color: #f2dda6;
|
|
910
|
-
word-break: break-all;
|
|
911
|
-
}
|
|
912
881
|
</style>
|
|
913
882
|
</head>
|
|
914
883
|
<body>
|
|
@@ -919,7 +888,7 @@ function generateTopupPage(walletAddressInput, rail = "base-usdc") {
|
|
|
919
888
|
</div>
|
|
920
889
|
|
|
921
890
|
<div class="card">
|
|
922
|
-
<div class="qr-container" id="qr"
|
|
891
|
+
<div class="qr-container" id="qr"></div>
|
|
923
892
|
|
|
924
893
|
<div class="address-box">
|
|
925
894
|
<code id="address">${safeWalletAddress}</code>
|
|
@@ -931,7 +900,14 @@ function generateTopupPage(walletAddressInput, rail = "base-usdc") {
|
|
|
931
900
|
${text.badge}
|
|
932
901
|
</div>
|
|
933
902
|
|
|
934
|
-
|
|
903
|
+
<div class="balance-row">
|
|
904
|
+
<span class="balance-label">Current balance</span>
|
|
905
|
+
<span class="balance-value" id="balance">—<span class="currency">${text.currency}</span></span>
|
|
906
|
+
</div>
|
|
907
|
+
<div class="balance-row">
|
|
908
|
+
<span class="balance-label">Gas balance</span>
|
|
909
|
+
<span class="balance-value" id="gasBalance">—<span class="currency">ETH</span></span>
|
|
910
|
+
</div>
|
|
935
911
|
<p class="gas-note">${text.gasNote}</p>
|
|
936
912
|
|
|
937
913
|
<div class="status" id="status"></div>
|
|
@@ -942,14 +918,27 @@ ${balanceRows}
|
|
|
942
918
|
|
|
943
919
|
<script>
|
|
944
920
|
const WALLET = ${walletAddressJson};
|
|
945
|
-
const
|
|
921
|
+
const USDC = '${text.tokenAddress}';
|
|
922
|
+
const CHAIN_ID = '${text.chainIdHex}';
|
|
923
|
+
|
|
924
|
+
// QR code
|
|
925
|
+
(function() {
|
|
926
|
+
var qr = qrcode(0, 'M');
|
|
927
|
+
qr.addData(WALLET);
|
|
928
|
+
qr.make();
|
|
929
|
+
document.getElementById('qr').innerHTML = qr.createSvgTag(5, 0);
|
|
930
|
+
var svg = document.querySelector('#qr svg');
|
|
931
|
+
if (svg) { svg.style.borderRadius = '12px'; svg.style.background = '#fff'; svg.style.padding = '12px'; }
|
|
932
|
+
})();
|
|
946
933
|
|
|
947
934
|
// Copy address — fallback for sandboxed iframes where navigator.clipboard is blocked
|
|
948
935
|
function copyAddress() {
|
|
949
936
|
var ok = false;
|
|
937
|
+
// Try modern clipboard API first
|
|
950
938
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
951
939
|
navigator.clipboard.writeText(WALLET).then(function() { ok = true; }).catch(function() {});
|
|
952
940
|
}
|
|
941
|
+
// Fallback: hidden textarea + execCommand
|
|
953
942
|
if (!ok) {
|
|
954
943
|
var ta = document.createElement('textarea');
|
|
955
944
|
ta.value = WALLET;
|
|
@@ -966,18 +955,20 @@ function copyAddress() {
|
|
|
966
955
|
setTimeout(function() { btn.classList.remove('copied'); btn.innerHTML = '⎘'; }, 2000);
|
|
967
956
|
}
|
|
968
957
|
|
|
969
|
-
|
|
970
|
-
document.getElementById(row.id).innerHTML = value + '<span class="currency">' + row.currency + '</span>';
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
// Fetch balances
|
|
958
|
+
// Fetch balance
|
|
974
959
|
async function fetchBalance() {
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
960
|
+
try {
|
|
961
|
+
var resp = await fetch('/api/balance');
|
|
962
|
+
var json = await resp.json();
|
|
963
|
+
if (json.${text.balanceField} === 'unknown') throw new Error('balance unavailable');
|
|
964
|
+
var balance = Number(json.${text.balanceField} || 0).toFixed(2);
|
|
965
|
+
document.getElementById('balance').innerHTML = balance + '<span class="currency">${text.currency}</span>';
|
|
966
|
+
var gasBalance = json.balance_eth === 'unknown' ? '—' : Number(json.balance_eth || 0).toFixed(6);
|
|
967
|
+
document.getElementById('gasBalance').innerHTML = gasBalance + '<span class="currency">ETH</span>';
|
|
968
|
+
} catch(e) {
|
|
969
|
+
document.getElementById('balance').innerHTML = '—<span class="currency">${text.currency}</span>';
|
|
970
|
+
document.getElementById('gasBalance').innerHTML = '—<span class="currency">ETH</span>';
|
|
971
|
+
}
|
|
981
972
|
}
|
|
982
973
|
fetchBalance();
|
|
983
974
|
setInterval(fetchBalance, 15000);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
|
|
2
|
-
import { i as startTopupServer$1, n as generateArtifactHtml, r as getTopupUrl$1 } from "./topup-server-
|
|
2
|
+
import { i as startTopupServer$1, n as generateArtifactHtml, r as getTopupUrl$1 } from "./topup-server-CSF5EjNt.mjs";
|
|
3
3
|
import { isAddress } from "viem";
|
|
4
4
|
import { createServer } from "node:http";
|
|
5
5
|
//#region src/wallet/topup-server.ts
|
|
@@ -128,4 +128,4 @@ async function startTopupServer(account) {
|
|
|
128
128
|
//#endregion
|
|
129
129
|
export { startTopupServer as n, topup_server_exports as r, getTopupUrl as t };
|
|
130
130
|
|
|
131
|
-
//# sourceMappingURL=topup-server-
|
|
131
|
+
//# sourceMappingURL=topup-server-qdruzcre.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"topup-server-
|
|
1
|
+
{"version":3,"file":"topup-server-qdruzcre.mjs","names":["getCopiedTopupUrl","startCopiedTopupServer"],"sources":["../src/wallet/topup-server.ts"],"sourcesContent":["import {\n generateArtifactHtml,\n getTopupUrl as getCopiedTopupUrl,\n startTopupServer as startCopiedTopupServer,\n} from './mcp-proxy/topup-server.js'\nimport { createServer, type Server, type ServerResponse } from 'node:http'\nimport { isAddress } from 'viem'\nimport type { PaymentWalletAccount } from './tools.js'\n\ninterface ArtifactServerState {\n address: string\n assetServerUrl: string\n server: Server\n url: string\n}\n\nlet artifactServerState: ArtifactServerState | null = null\nconst REQUEST_TARGET_BASE = 'http://localhost'\n\nclass ProxyRequestError extends Error {\n status: number\n\n constructor(status: number, message: string) {\n super(message)\n this.name = 'ProxyRequestError'\n this.status = status\n }\n}\n\ninterface NormalizedProxyTarget {\n pathAndSearch: string\n pathname: string\n}\n\nfunction toWalletAddress(account: PaymentWalletAccount | string): string {\n const address = typeof account === 'string' ? account : account.address\n if (!isAddress(address)) {\n throw new Error('Wallet address must be a valid 0x-prefixed 20-byte EVM address')\n }\n return address\n}\n\nfunction send(\n res: ServerResponse,\n status: number,\n body: string | Buffer,\n contentType: string\n): void {\n res.writeHead(status, {\n 'content-type': contentType,\n 'cache-control': 'no-store',\n 'access-control-allow-origin': '*',\n })\n res.end(body)\n}\n\nfunction normalizeProxyTarget(reqUrl: string): NormalizedProxyTarget {\n if (!reqUrl.startsWith('/') || reqUrl.startsWith('//')) {\n throw new ProxyRequestError(400, 'Proxy request target must be an origin-form path')\n }\n\n const parsed = new URL(reqUrl, REQUEST_TARGET_BASE)\n if (parsed.origin !== REQUEST_TARGET_BASE) {\n throw new ProxyRequestError(400, 'Absolute and protocol-relative proxy targets are not allowed')\n }\n\n let decodedPathname: string\n try {\n decodedPathname = decodeURIComponent(parsed.pathname)\n } catch {\n throw new ProxyRequestError(400, 'Proxy request target contains invalid encoding')\n }\n\n if (decodedPathname.startsWith('//') || /^\\/[a-z][a-z0-9+.-]*:\\/\\//i.test(decodedPathname)) {\n throw new ProxyRequestError(400, 'Encoded host override targets are not allowed')\n }\n\n return {\n pathAndSearch: `${parsed.pathname}${parsed.search}`,\n pathname: parsed.pathname,\n }\n}\n\nasync function proxyToCopiedServer(\n proxyTarget: NormalizedProxyTarget,\n res: ServerResponse,\n assetServerUrl: string\n): Promise<void> {\n const allowedOrigin = new URL(assetServerUrl).origin\n const upstreamUrl = new URL(proxyTarget.pathAndSearch, assetServerUrl)\n if (upstreamUrl.origin !== allowedOrigin) {\n throw new ProxyRequestError(403, 'Upstream origin is not allowed')\n }\n\n const upstream = await fetch(upstreamUrl)\n const contentType = upstream.headers.get('content-type') ?? 'application/octet-stream'\n const body = Buffer.from(await upstream.arrayBuffer())\n send(res, upstream.status, body, contentType)\n}\n\nexport { generateArtifactHtml }\n\nexport function getTopupArtifactUrl(): string | null {\n return artifactServerState?.url ?? null\n}\n\nexport function getTopupUrl(): string | null {\n return getTopupArtifactUrl() ?? getCopiedTopupUrl()\n}\n\nexport async function stopTopupServer(): Promise<void> {\n if (!artifactServerState) {\n return\n }\n\n const { server } = artifactServerState\n artifactServerState = null\n await new Promise<void>((resolve) => server.close(() => resolve()))\n}\n\nexport async function startTopupServer(account: PaymentWalletAccount | string): Promise<string> {\n const walletAddress = toWalletAddress(account)\n\n if (\n artifactServerState &&\n artifactServerState.address.toLowerCase() === walletAddress.toLowerCase()\n ) {\n return artifactServerState.url\n }\n\n const assetServerUrl = await startCopiedTopupServer(walletAddress)\n\n if (artifactServerState) {\n await stopTopupServer()\n }\n\n const server = createServer((req, res) => {\n const reqUrl = req.url ?? '/'\n let proxyTarget: NormalizedProxyTarget\n try {\n proxyTarget = normalizeProxyTarget(reqUrl)\n } catch (err) {\n if (err instanceof ProxyRequestError) {\n send(\n res,\n err.status,\n JSON.stringify({ error: err.message }) + '\\n',\n 'application/json; charset=utf-8'\n )\n return\n }\n send(\n res,\n 400,\n JSON.stringify({ error: 'Invalid request target' }) + '\\n',\n 'application/json; charset=utf-8'\n )\n return\n }\n const { pathname } = proxyTarget\n\n if (pathname === '/' || pathname === '/index.html') {\n const artifactUrl = artifactServerState?.url ?? assetServerUrl\n send(res, 200, generateArtifactHtml(walletAddress, artifactUrl), 'text/html; charset=utf-8')\n return\n }\n\n if (pathname.startsWith('/assets/') || pathname.startsWith('/api/')) {\n void proxyToCopiedServer(proxyTarget, res, assetServerUrl).catch((err) => {\n if (err instanceof ProxyRequestError) {\n send(\n res,\n err.status,\n JSON.stringify({ error: err.message }) + '\\n',\n 'application/json; charset=utf-8'\n )\n return\n }\n send(\n res,\n 502,\n JSON.stringify({ error: (err as Error).message }) + '\\n',\n 'application/json; charset=utf-8'\n )\n })\n return\n }\n\n send(res, 404, JSON.stringify({ error: 'Not found' }) + '\\n', 'application/json; charset=utf-8')\n })\n\n const url = await new Promise<string>((resolve, reject) => {\n server.once('error', reject)\n server.listen(0, '127.0.0.1', () => {\n const addressInfo = server.address()\n if (!addressInfo || typeof addressInfo === 'string') {\n reject(new Error('Failed to start topup artifact server'))\n return\n }\n resolve(`http://localhost:${addressInfo.port}`)\n })\n })\n\n artifactServerState = {\n address: walletAddress,\n assetServerUrl,\n server,\n url,\n }\n\n return url\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"topup-server-RQ6-c92F.mjs","names":["USDC_ADDRESS"],"sources":["../src/wallet/mcp-proxy/qr.ts","../src/wallet/mcp-proxy/tools.ts","../src/wallet/mcp-proxy/topup-server.ts"],"sourcesContent":["/**\n * Minimal QR code generator. No external dependencies.\n *\n * Byte mode, error correction level L, versions 1 to 4 (single block), mask 0.\n * Version 3 holds a 42-character EVM address; version 4 holds up to 78 bytes.\n *\n * `qrModules` is self-contained on purpose: the top-up view inlines its source\n * (`qrModules.toString()`) and draws the same QR code inside the Claude view,\n * so the function must not reference anything outside its own body.\n */\n\n/**\n * The QR code modules for `text`: a square matrix, 1 for a dark module and 0\n * for a light one, without the quiet zone. Throws when `text` is longer than\n * 78 UTF-8 bytes.\n */\nexport function qrModules(text: string): number[][] {\n // Level L, single-block versions: [version, total codewords, EC codewords].\n const versions = [\n [1, 26, 7],\n [2, 44, 10],\n [3, 70, 15],\n [4, 100, 20],\n ]\n // Format information for level L and mask 0, BCH-coded and masked.\n const formatBits = 0x77c4\n const bytes = Array.from(new TextEncoder().encode(text))\n\n let version = 0\n let totalCodewords = 0\n let ecCodewords = 0\n for (const entry of versions) {\n const dataCodewords = entry[1] - entry[2]\n if (4 + 8 + bytes.length * 8 <= dataCodewords * 8) {\n version = entry[0]\n totalCodewords = entry[1]\n ecCodewords = entry[2]\n break\n }\n }\n if (version === 0) throw new Error('QR text is too long: at most 78 bytes')\n\n const dataCodewords = totalCodewords - ecCodewords\n const size = version * 4 + 17\n\n // Data bits: mode, count, bytes, terminator, byte padding, pad codewords.\n const bits: number[] = []\n const pushBits = (value: number, length: number) => {\n for (let i = length - 1; i >= 0; i--) bits.push((value >>> i) & 1)\n }\n pushBits(0b0100, 4)\n pushBits(bytes.length, 8)\n for (const value of bytes) pushBits(value, 8)\n const capacity = dataCodewords * 8\n pushBits(0, Math.min(4, capacity - bits.length))\n while (bits.length % 8 !== 0) bits.push(0)\n for (let pad = 0xec; bits.length < capacity; pad ^= 0xec ^ 0x11) pushBits(pad, 8)\n const data: number[] = []\n for (let i = 0; i < bits.length; i += 8) {\n let value = 0\n for (let j = 0; j < 8; j++) value = (value << 1) | bits[i + j]\n data.push(value)\n }\n\n // Reed-Solomon error correction over GF(256), polynomial 0x11d.\n const exp: number[] = []\n const log: number[] = []\n for (let i = 0, x = 1; i < 255; i++) {\n exp[i] = x\n log[x] = i\n x <<= 1\n if (x & 0x100) x ^= 0x11d\n }\n const multiply = (a: number, b: number) => (a === 0 || b === 0 ? 0 : exp[(log[a] + log[b]) % 255])\n let generator = [1]\n for (let i = 0; i < ecCodewords; i++) {\n const next = new Array(generator.length + 1).fill(0)\n for (let j = 0; j < generator.length; j++) {\n next[j] ^= generator[j]\n next[j + 1] ^= multiply(generator[j], exp[i])\n }\n generator = next\n }\n const remainder = [...data, ...new Array(ecCodewords).fill(0)]\n for (let i = 0; i < data.length; i++) {\n const factor = remainder[i]\n if (factor === 0) continue\n for (let j = 0; j < generator.length; j++) remainder[i + j] ^= multiply(generator[j], factor)\n }\n const codewords = [...data, ...remainder.slice(data.length)]\n\n // Function patterns. `reserved` marks every module data may not use.\n const modules: number[][] = []\n const reserved: boolean[][] = []\n for (let row = 0; row < size; row++) {\n modules.push(new Array(size).fill(0))\n reserved.push(new Array(size).fill(false))\n }\n const setFunction = (row: number, col: number, dark: boolean) => {\n modules[row][col] = dark ? 1 : 0\n reserved[row][col] = true\n }\n for (let i = 0; i < size; i++) {\n setFunction(6, i, i % 2 === 0)\n setFunction(i, 6, i % 2 === 0)\n }\n const finder = (centerRow: number, centerCol: number) => {\n for (let dr = -4; dr <= 4; dr++) {\n for (let dc = -4; dc <= 4; dc++) {\n const row = centerRow + dr\n const col = centerCol + dc\n if (row < 0 || row >= size || col < 0 || col >= size) continue\n const distance = Math.max(Math.abs(dr), Math.abs(dc))\n setFunction(row, col, distance !== 2 && distance !== 4)\n }\n }\n }\n finder(3, 3)\n finder(3, size - 4)\n finder(size - 4, 3)\n if (version >= 2) {\n const center = size - 7\n for (let dr = -2; dr <= 2; dr++) {\n for (let dc = -2; dc <= 2; dc++) {\n setFunction(center + dr, center + dc, Math.max(Math.abs(dr), Math.abs(dc)) !== 1)\n }\n }\n }\n const formatBit = (i: number) => ((formatBits >>> i) & 1) === 1\n for (let i = 0; i <= 5; i++) setFunction(i, 8, formatBit(i))\n setFunction(7, 8, formatBit(6))\n setFunction(8, 8, formatBit(7))\n setFunction(8, 7, formatBit(8))\n for (let i = 9; i < 15; i++) setFunction(8, 14 - i, formatBit(i))\n for (let i = 0; i < 8; i++) setFunction(8, size - 1 - i, formatBit(i))\n for (let i = 8; i < 15; i++) setFunction(size - 15 + i, 8, formatBit(i))\n setFunction(size - 8, 8, true)\n\n // Data in the zigzag order, then mask 0 on every data module.\n let bitIndex = 0\n for (let right = size - 1; right >= 1; right -= 2) {\n if (right === 6) right = 5\n const upward = ((right + 1) & 2) === 0\n for (let step = 0; step < size; step++) {\n const row = upward ? size - 1 - step : step\n for (let j = 0; j < 2; j++) {\n const col = right - j\n if (reserved[row][col]) continue\n if (bitIndex < codewords.length * 8) {\n modules[row][col] = (codewords[bitIndex >>> 3] >>> (7 - (bitIndex & 7))) & 1\n bitIndex++\n }\n if ((row + col) % 2 === 0) modules[row][col] ^= 1\n }\n }\n }\n return modules\n}\n\nexport interface QrOptions {\n cellSize?: number\n fgColor?: string\n bgColor?: string\n finderColor?: string\n logoBase64?: string // data URI for center logo\n logoWidth?: number // logo width in modules (default: 7)\n logoHeight?: number // logo height in modules (default: 5)\n}\n\nexport function generateQrSvg(text: string, opts: QrOptions | number = 4): string {\n // Backward compat: accept bare cellSize number\n const options: QrOptions = typeof opts === 'number' ? { cellSize: opts } : opts\n const cellSize = options.cellSize ?? 4\n const fgColor = options.fgColor ?? '#000'\n const bgColor = options.bgColor ?? '#fff'\n const finderColor = options.finderColor ?? fgColor\n\n const matrix = qrModules(text)\n const size = matrix.length\n\n // Logo exclusion zone (center of QR)\n const logoW = options.logoWidth ?? 7\n const logoH = options.logoHeight ?? 5\n const logoStartC = Math.floor((size - logoW) / 2)\n const logoStartR = Math.floor((size - logoH) / 2)\n const hasLogo = !!options.logoBase64\n\n const svgSize = size * cellSize\n let svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"${svgSize}\" height=\"${svgSize}\" viewBox=\"0 0 ${svgSize} ${svgSize}\">`\n svg += `<rect width=\"${svgSize}\" height=\"${svgSize}\" fill=\"${bgColor}\" rx=\"4\"/>`\n\n const isFinderModule = (r: number, c: number): boolean =>\n (r < 7 && c < 7) || (r < 7 && c >= size - 7) || (r >= size - 7 && c < 7)\n\n for (let r = 0; r < size; r++) {\n for (let c = 0; c < size; c++) {\n if (\n hasLogo &&\n r >= logoStartR &&\n r < logoStartR + logoH &&\n c >= logoStartC &&\n c < logoStartC + logoW\n ) {\n continue\n }\n if (matrix[r][c] === 1) {\n const color = isFinderModule(r, c) ? finderColor : fgColor\n svg += `<rect x=\"${c * cellSize}\" y=\"${r * cellSize}\" width=\"${cellSize}\" height=\"${cellSize}\" fill=\"${color}\" rx=\"0.5\"/>`\n }\n }\n }\n\n if (hasLogo && options.logoBase64) {\n const lx = logoStartC * cellSize\n const ly = logoStartR * cellSize\n const lw = logoW * cellSize\n const lh = logoH * cellSize\n svg += `<rect x=\"${lx - 1}\" y=\"${ly - 1}\" width=\"${lw + 2}\" height=\"${lh + 2}\" fill=\"${bgColor}\" rx=\"3\"/>`\n svg += `<image x=\"${lx + 2}\" y=\"${ly + 2}\" width=\"${lw - 4}\" height=\"${lh - 4}\" href=\"${options.logoBase64}\" xlink:href=\"${options.logoBase64}\" preserveAspectRatio=\"xMidYMid meet\"/>`\n }\n\n svg += '</svg>'\n return svg\n}\n\nexport interface QrTerminalOptions {\n /** Quiet zone in modules on each side (default 4, the QR standard). */\n quietZone?: number\n /** Draw light modules as blocks instead of dark ones, for a light-on-dark read. */\n invert?: boolean\n}\n\n/**\n * The QR code of `text` as terminal text. Each character covers two module\n * rows with half blocks (▀ ▄ █ and space), so the code keeps its square shape.\n * A block is a dark module; the quiet zone is light.\n */\nexport function qrTerminalText(text: string, options: QrTerminalOptions = {}): string {\n const quiet = options.quietZone ?? 4\n const modules = qrModules(text)\n const size = modules.length + quiet * 2\n const dark = (row: number, col: number): boolean => {\n const r = row - quiet\n const c = col - quiet\n const on = r >= 0 && c >= 0 && r < modules.length && c < modules.length && modules[r][c] === 1\n return options.invert ? !on : on\n }\n const lines: string[] = []\n for (let row = 0; row < size; row += 2) {\n let line = ''\n for (let col = 0; col < size; col++) {\n const top = dark(row, col)\n const bottom = row + 1 < size ? dark(row + 1, col) : options.invert === true\n line += top && bottom ? '█' : top ? '▀' : bottom ? '▄' : ' '\n }\n lines.push(line)\n }\n return lines.join('\\n')\n}\n","import type { WalletData } from './types.js'\nimport { createPublicClient, http, formatEther, formatUnits } from 'viem'\nimport { base } from 'viem/chains'\n\nconst USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as const\nconst DEFAULT_BASE_RPC_URL = 'https://mainnet.base.org'\nconst PUBLIC_BASE_RPC_URLS = [\n DEFAULT_BASE_RPC_URL,\n 'https://base-rpc.publicnode.com',\n 'https://base.drpc.org',\n 'https://1rpc.io/base',\n] as const\nconst USDC_ABI = [\n {\n name: 'balanceOf',\n type: 'function',\n stateMutability: 'view',\n inputs: [{ name: 'account', type: 'address' }],\n outputs: [{ name: '', type: 'uint256' }],\n },\n] as const\n\nfunction walletAddress(wallet: WalletData | string): `0x${string}` {\n return (typeof wallet === 'string' ? wallet : wallet.address) as `0x${string}`\n}\n\nexport async function getBalanceUsdc(wallet: WalletData | string): Promise<string> {\n const envRpcUrl = process.env.BASE_RPC_URL\n const rpcUrls = [\n ...(envRpcUrl ? [envRpcUrl] : []),\n ...PUBLIC_BASE_RPC_URLS.filter((url) => url !== envRpcUrl),\n ]\n\n for (const rpcUrl of rpcUrls) {\n try {\n const client = createPublicClient({ chain: base, transport: http(rpcUrl) })\n const balance = await client.readContract({\n address: USDC_ADDRESS,\n abi: USDC_ABI,\n functionName: 'balanceOf',\n args: [walletAddress(wallet)],\n })\n return formatUnits(balance, 6)\n } catch {\n // Try the next public Base RPC endpoint.\n }\n }\n\n return 'unknown'\n}\n\nexport async function getBalanceEth(wallet: WalletData | string): Promise<string> {\n const envRpcUrl = process.env.BASE_RPC_URL\n const rpcUrls = [\n ...(envRpcUrl ? [envRpcUrl] : []),\n ...PUBLIC_BASE_RPC_URLS.filter((url) => url !== envRpcUrl),\n ]\n\n for (const rpcUrl of rpcUrls) {\n try {\n const client = createPublicClient({ chain: base, transport: http(rpcUrl) })\n const balance = await client.getBalance({ address: walletAddress(wallet) })\n return formatEther(balance)\n } catch {\n // Try the next public Base RPC endpoint.\n }\n }\n\n return 'unknown'\n}\n\nexport async function getBalance(wallet: WalletData): Promise<string> {\n const [balanceUsdc, balanceEth] = await Promise.all([\n getBalanceUsdc(wallet),\n getBalanceEth(wallet),\n ])\n return [\n `Payment wallet: ${wallet.address}`,\n `USDC on Base: ${balanceUsdc}`,\n `Gas on Base: ${balanceEth} ETH`,\n `Payment network: Base`,\n `Base ETH is used only for one-time payment setup gas.`,\n ]\n .filter(Boolean)\n .join('\\n')\n}\n\nexport function getTopupInfo(wallet: WalletData): string {\n return JSON.stringify({\n wallet_address: wallet.address,\n network: 'Base (Chain ID 8453)',\n token: 'USDC',\n contract: USDC_ADDRESS,\n instructions: [`Send USDC on Base network to: ${wallet.address}`],\n })\n}\n","import { createServer, type Server } from 'node:http'\nimport { readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { isAddress } from 'viem'\nimport type { WalletData } from './types.js'\nimport { generateQrSvg, qrModules } from './qr.js'\nimport { getBalanceEth, getBalanceUsdc } from './tools.js'\nimport {\n CIA_SYMBOL,\n CIA_TOKEN_ADDRESS,\n ROBINHOOD_CHAIN_ID,\n ROBINHOOD_NETWORK_DISPLAY,\n getCiaBalance,\n getRobinhoodEthBalance,\n} from '../robinhood.js'\nimport {\n SUBSCRIPTION_TOPUP_DESCRIPTION,\n SUBSCRIPTION_TOPUP_TOOL,\n WALLET_TOPUP_DESCRIPTION,\n} from '../../subscription/rules.js'\n\nconst USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n// Pre-load assets — try dist/assets first (installed), then src/assets (dev)\nfunction loadAsset(name: string): Buffer {\n const paths = [\n join(__dirname, 'assets', name), // dist/assets/ (global install)\n join(__dirname, '..', 'src', 'assets', name), // src/assets/ (dev)\n ]\n for (const p of paths) {\n try {\n return readFileSync(p)\n } catch {\n /* try next */\n }\n }\n console.error(`[chain-insights] Warning: asset ${name} not found`)\n return Buffer.alloc(0)\n}\n\nconst logoPng = loadAsset('logo.png')\nconst bgPatternPng = loadAsset('bg-pattern.png')\n\n/**\n * The top-up rails. base-usdc funds pay-per-call queries with USDC on Base;\n * robinhood-cia funds the subscription with $CIA and ETH on Robinhood Chain.\n * One local server per rail.\n */\nexport type TopupRail = 'base-usdc' | 'robinhood-cia'\n\nconst servers = new Map<TopupRail, { server: Server; port: number }>()\n\nfunction assertWalletAddress(wallet: WalletData | string): string {\n const walletAddress = typeof wallet === 'string' ? wallet : wallet.address\n if (!isAddress(walletAddress)) {\n throw new Error('Wallet address must be a valid 0x-prefixed 20-byte EVM address')\n }\n return walletAddress\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''')\n}\n\nfunction jsonForScript(value: string): string {\n return JSON.stringify(value)\n .replaceAll('<', '\\\\u003c')\n .replaceAll('>', '\\\\u003e')\n .replaceAll('&', '\\\\u0026')\n}\n\nexport function getTopupUrl(rail: TopupRail = 'base-usdc'): string | null {\n const running = servers.get(rail)\n return running ? `http://localhost:${running.port}` : null\n}\n\nasync function railBalance(rail: TopupRail, walletAddress: string): Promise<Record<string, string>> {\n if (rail === 'robinhood-cia') {\n const [balanceCia, balanceEth] = await Promise.all([\n getCiaBalance(walletAddress),\n getRobinhoodEthBalance(walletAddress),\n ])\n return { balance_cia: balanceCia, balance_eth: balanceEth }\n }\n const [balanceUsdc, balanceEth] = await Promise.all([\n getBalanceUsdc(walletAddress),\n getBalanceEth(walletAddress),\n ])\n return { balance_usdc: balanceUsdc, balance_eth: balanceEth }\n}\n\nconst UNKNOWN_BALANCE: Record<TopupRail, Record<string, string>> = {\n 'base-usdc': { balance_usdc: 'unknown', balance_eth: 'unknown' },\n 'robinhood-cia': { balance_cia: 'unknown', balance_eth: 'unknown' },\n}\n\nexport async function startTopupServer(\n wallet: WalletData | string,\n rail: TopupRail = 'base-usdc'\n): Promise<string> {\n const walletAddress = assertWalletAddress(wallet)\n\n const running = getTopupUrl(rail)\n if (running) {\n return running\n }\n\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n if (req.url === '/api/wallet') {\n res.writeHead(200, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n })\n res.end(JSON.stringify({ address: walletAddress }))\n return\n }\n\n if (req.url === '/api/balance') {\n railBalance(rail, walletAddress)\n .then((balance) => {\n res.writeHead(200, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n })\n res.end(JSON.stringify(balance))\n })\n .catch(() => {\n res.writeHead(200, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n })\n res.end(JSON.stringify(UNKNOWN_BALANCE[rail]))\n })\n return\n }\n\n if (req.url === '/assets/logo.png') {\n res.writeHead(200, {\n 'Content-Type': 'image/png',\n 'Cache-Control': 'public, max-age=86400',\n 'Access-Control-Allow-Origin': '*',\n })\n res.end(logoPng)\n return\n }\n\n if (req.url === '/assets/bg-pattern.png') {\n res.writeHead(200, {\n 'Content-Type': 'image/png',\n 'Cache-Control': 'public, max-age=86400',\n 'Access-Control-Allow-Origin': '*',\n })\n res.end(bgPatternPng)\n return\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(rail === 'robinhood-cia' ? generateSubscriptionPage(walletAddress) : generatePage(walletAddress))\n })\n\n server.listen(0, '127.0.0.1', () => {\n const addr = server.address()\n if (addr && typeof addr === 'object') {\n servers.set(rail, { server, port: addr.port })\n const url = `http://localhost:${addr.port}`\n console.error(`[chain-insights] Topup server running at ${url}`)\n resolve(url)\n } else {\n reject(new Error('Failed to start topup server'))\n }\n })\n\n server.on('error', reject)\n })\n}\n\nexport function generateArtifactHtml(walletAddressInput: string, topupUrl: string): string {\n const walletAddress = assertWalletAddress(walletAddressInput)\n const safeWalletAddress = escapeHtml(walletAddress)\n const safeTopupUrl = escapeHtml(topupUrl)\n const topupUrlJson = jsonForScript(topupUrl)\n\n const LOGO_B64 =\n 'data:image/png;base64,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'\n\n // Generate QR code SVG server-side — standard B&W, 2x size\n const qrSvg = generateQrSvg(walletAddress, { cellSize: 8 })\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;\n background: rgba(10, 12, 17, 1) url('${safeTopupUrl}/assets/bg-pattern.png') top left / contain no-repeat;\n color: rgba(255, 255, 255, 0.9);\n display: flex;\n justify-content: center;\n padding: 24px;\n line-height: 1.3;\n }\n .card {\n max-width: 400px;\n width: 100%;\n background: rgba(19, 19, 24, 1);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 12px;\n padding: 28px 24px;\n text-align: center;\n }\n .logo { margin-bottom: 8px; }\n .logo img { height: 24px; }\n .subtitle { color: rgba(255, 255, 255, 0.5); font-size: 13px; margin-bottom: 20px; }\n .qr { margin-bottom: 16px; }\n .qr svg { border-radius: 12px; background: #fff; padding: 10px; }\n .address-wrap {\n position: relative;\n margin-bottom: 16px;\n }\n .address {\n width: 100%;\n background: rgba(10, 12, 17, 1);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 8px;\n padding: 10px 14px;\n font-family: 'SF Mono', 'Fira Code', monospace;\n font-size: 11px; color: #f2dda6; word-break: break-all;\n cursor: pointer; text-align: center;\n transition: border-color 0.3s linear;\n -webkit-user-select: all; user-select: all;\n outline: none;\n }\n .address:focus { border-color: #ae9d71; }\n .address-hint {\n font-size: 10px; color: rgba(255,255,255,0.3);\n text-align: center; margin-top: 4px;\n }\n .badge {\n display: inline-flex; align-items: center; gap: 6px;\n background: rgba(255, 255, 255, 0.04);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 20px; padding: 5px 12px;\n font-size: 11px; color: rgba(255, 255, 255, 0.5); margin-bottom: 20px;\n }\n .badge .dot { width: 7px; height: 7px; border-radius: 50%; background: #f2dda6; }\n .balance-line {\n font-size: 13px;\n color: rgba(255, 255, 255, 0.5);\n margin-bottom: 20px;\n line-height: 1.4;\n }\n .balance-line .amount {\n color: #4feb69;\n font-weight: 600;\n }\n .balance-line .gas {\n color: #f2dda6;\n font-weight: 600;\n }\n @keyframes flash { 0%{opacity:0.4} 100%{opacity:1} }\n .balance-line.flash .amount { animation: flash 1s ease-out; }\n .hint {\n margin-top: 14px; font-size: 12px;\n color: rgba(255, 255, 255, 0.3);\n line-height: 1.5;\n }\n</style>\n</head>\n<body>\n<div class=\"card\">\n <div class=\"logo\"><img src=\"${safeTopupUrl}/assets/logo.png\" alt=\"Chain Insights\"></div>\n <p class=\"subtitle\">Fund your wallet with USDC on Base</p>\n <div class=\"qr\">${qrSvg}</div>\n <div class=\"address-wrap\">\n <div class=\"address\" id=\"addr\" onclick=\"selectAndCopy()\">${safeWalletAddress}</div>\n <div class=\"address-hint\" id=\"addrHint\">Click to copy address</div>\n </div>\n <div class=\"badge\"><span class=\"dot\"></span>Base Network · USDC</div>\n <p class=\"balance-line\" id=\"balLine\">Current balance: <span class=\"amount\" id=\"bal\">--</span> USDC<br>Gas balance: <span class=\"gas\" id=\"gas\">--</span> ETH<br>Base ETH is used for one-time payment setup gas.</p>\n</div>\n<script>\n// MCP Apps protocol handshake for the wallet top-up page.\n(function() {\n var initId = 1;\n\n // Listen for host messages\n window.addEventListener('message', function(event) {\n var data = event.data;\n if (!data || data.jsonrpc !== '2.0') return;\n\n // Initialize response\n if (data.id === initId && data.result) {\n // Send initialized notification\n window.parent.postMessage({\n jsonrpc: '2.0',\n method: 'ui/notifications/initialized',\n params: {}\n }, '*');\n\n // Send initial size\n var rect = document.documentElement.getBoundingClientRect();\n window.parent.postMessage({\n jsonrpc: '2.0',\n method: 'ui/notifications/size-changed',\n params: { width: Math.ceil(rect.width), height: Math.ceil(rect.height) }\n }, '*');\n }\n\n // Respond to pings\n if (data.method === 'ping' && data.id != null) {\n window.parent.postMessage({\n jsonrpc: '2.0',\n id: data.id,\n result: {}\n }, '*');\n }\n });\n\n // Send initialize request (must match App class protocol)\n window.parent.postMessage({\n jsonrpc: '2.0',\n id: initId,\n method: 'ui/initialize',\n params: {\n appInfo: { name: 'Chain Insights Topup', version: '1.0.0' },\n appCapabilities: {},\n protocolVersion: '2026-01-26'\n }\n }, '*');\n})();\n\n// Live balance polling\nvar lastBal = null;\nvar TOPUP_URL = ${topupUrlJson};\nfunction fetchBal() {\n fetch(TOPUP_URL + '/api/balance')\n .then(function(r) { return r.json(); })\n .then(function(d) {\n var el = document.getElementById('bal');\n var gas = document.getElementById('gas');\n var line = document.getElementById('balLine');\n var val = parseFloat(d.balance_usdc || '0').toFixed(2);\n var gasVal = d.balance_eth === 'unknown' ? '--' : parseFloat(d.balance_eth || '0').toFixed(6);\n if (d.balance_usdc === 'unknown') { el.textContent = '--'; gas.textContent = gasVal; return; }\n el.textContent = val;\n gas.textContent = gasVal;\n if (lastBal !== null && val !== lastBal) {\n line.classList.remove('flash');\n void line.offsetWidth;\n line.classList.add('flash');\n }\n lastBal = val;\n })\n .catch(function() {});\n}\nfetchBal();\nsetInterval(fetchBal, 10000);\n\nfunction selectAndCopy() {\n var addr = document.getElementById('addr');\n var hint = document.getElementById('addrHint');\n // Select the text\n var r = document.createRange();\n r.selectNodeContents(addr);\n var s = window.getSelection();\n s.removeAllRanges();\n s.addRange(r);\n // Try every clipboard method available\n var copied = false;\n try { copied = document.execCommand('copy'); } catch(e) {}\n if (!copied && navigator.clipboard && navigator.clipboard.writeText) {\n navigator.clipboard.writeText(addr.textContent).then(function() {\n hint.textContent = 'Copied!'; hint.style.color = '#4feb69';\n setTimeout(function() { hint.textContent = 'Click to copy address'; hint.style.color = ''; }, 2000);\n }).catch(function() {});\n }\n if (copied) {\n hint.textContent = 'Copied!'; hint.style.color = '#4feb69';\n setTimeout(function() { hint.textContent = 'Click to copy address'; hint.style.color = ''; }, 2000);\n } else {\n hint.textContent = 'Selected — press Ctrl+C'; hint.style.color = '#f2dda6';\n setTimeout(function() { hint.textContent = 'Click to copy address'; hint.style.color = ''; }, 3000);\n }\n}\n<\\/script>\n</body>\n</html>`\n}\n\n/** One balance row of the top-up page: the /api/balance field it shows. */\ninterface TopupBalanceRow {\n id: string\n label: string\n currency: string\n field: string\n decimals: number\n}\n\n/**\n * The text of the one top-up page, per rail. The page is the same for both\n * rails (aphex5, 2026-10-28: \"identical, just different text\"): the layout,\n * the style and the script never fork by rail, only these strings do.\n */\ninterface TopupPageText {\n title: string\n subtitle: string\n badge: string\n balances: TopupBalanceRow[]\n gasNote: string\n /** HTML: constant text only, never a value from outside. */\n info: string\n}\n\n/** The text of the subscription top-up page and of `cia subscription topup`. */\nexport const SUBSCRIPTION_TOPUP_TEXT =\n 'Send $CIA and a little ETH on Robinhood Chain to your own wallet. Then run `cia subscription buy --days N`.'\n\nconst TOPUP_PAGE_TEXT: Record<TopupRail, TopupPageText> = {\n 'base-usdc': {\n title: 'Chain Insights — Fund Wallet',\n subtitle: 'Fund your wallet to use blockchain intelligence tools',\n badge: 'Base Network · USDC',\n balances: [\n { id: 'balance', label: 'Current balance', currency: 'USDC', field: 'balance_usdc', decimals: 2 },\n { id: 'gasBalance', label: 'Gas balance', currency: 'ETH', field: 'balance_eth', decimals: 6 },\n ],\n gasNote: 'Base ETH is used for one-time payment setup gas.',\n info: 'Send Base USDC to the wallet address above.',\n },\n 'robinhood-cia': {\n title: 'Chain Insights — Fund the subscription wallet',\n subtitle: 'Fund your wallet for the Chain Insights subscription',\n badge: 'Robinhood Chain · $CIA',\n balances: [\n { id: 'balance', label: 'Current balance', currency: '$CIA', field: 'balance_cia', decimals: 2 },\n { id: 'gasBalance', label: 'Gas balance', currency: 'ETH', field: 'balance_eth', decimals: 6 },\n ],\n gasNote: 'Robinhood Chain ETH pays the gas of the two buy transactions.',\n info:\n 'Send $CIA and a little ETH on Robinhood Chain to your own wallet. Then run <code>cia subscription buy --days N</code>.' +\n `<br>Robinhood Chain (chain ID 4663) · $CIA <code>${CIA_TOKEN_ADDRESS}</code>` +\n '<br>This page shows your own address only. It signs nothing and sends nothing.',\n },\n}\n\n/**\n * The one local top-up page: the wallet address, its QR code (drawn here, no\n * script from outside), the network and the balances. It names only the\n * user's own wallet, never where a payment goes.\n */\nexport function generateTopupPage(walletAddressInput: string, rail: TopupRail = 'base-usdc'): string {\n const walletAddress = assertWalletAddress(walletAddressInput)\n const safeWalletAddress = escapeHtml(walletAddress)\n const walletAddressJson = jsonForScript(walletAddress)\n const text = TOPUP_PAGE_TEXT[rail]\n const qrSvg = generateQrSvg(walletAddress, { cellSize: 5 })\n const balanceRows = text.balances\n .map(\n (row) => ` <div class=\"balance-row\">\n <span class=\"balance-label\">${row.label}</span>\n <span class=\"balance-value\" id=\"${row.id}\">—<span class=\"currency\">${row.currency}</span></span>\n </div>`\n )\n .join('\\n')\n const balancesJson = JSON.stringify(text.balances.map(({ id, currency, field, decimals }) => ({ id, currency, field, decimals })))\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${text.title}</title>\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n\n body {\n font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;\n background: rgba(10, 12, 17, 1);\n color: rgba(255, 255, 255, 0.9);\n min-height: 100vh;\n display: flex;\n justify-content: center;\n align-items: center;\n line-height: 1.3;\n }\n\n .container {\n max-width: 480px;\n width: 100%;\n padding: 24px;\n }\n\n .logo {\n text-align: center;\n margin-bottom: 32px;\n }\n\n .logo h1 {\n font-size: 24px;\n font-weight: 600;\n color: #f2dda6;\n letter-spacing: -0.5px;\n }\n\n .logo p {\n color: rgba(255, 255, 255, 0.5);\n font-size: 14px;\n margin-top: 4px;\n }\n\n .card {\n background: rgba(19, 19, 24, 1);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 12px;\n padding: 32px 24px;\n }\n\n .qr-container {\n display: flex;\n justify-content: center;\n margin-bottom: 24px;\n }\n\n .qr-container svg, .qr-container canvas, .qr-container img {\n border-radius: 12px;\n background: #fff;\n padding: 12px;\n }\n\n .address-box {\n background: rgba(10, 12, 17, 1);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 8px;\n padding: 12px 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 24px;\n }\n\n .address-box code {\n font-family: 'SF Mono', 'Fira Code', monospace;\n font-size: 12px;\n color: #f2dda6;\n word-break: break-all;\n flex: 1;\n }\n\n .copy-btn {\n background: none;\n border: none;\n color: rgba(255, 255, 255, 0.4);\n cursor: pointer;\n padding: 4px;\n font-size: 18px;\n transition: color 0.3s linear;\n }\n\n .copy-btn:hover { color: #f2dda6; }\n .copy-btn.copied { color: #4feb69; }\n\n .network-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: rgba(255, 255, 255, 0.04);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 20px;\n padding: 6px 12px;\n font-size: 12px;\n color: rgba(255, 255, 255, 0.5);\n margin-bottom: 24px;\n }\n\n .network-badge .dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #f2dda6;\n }\n\n .balance-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 0;\n border-top: 1px solid rgba(255, 255, 255, 0.06);\n margin-bottom: 16px;\n }\n\n .balance-label { color: rgba(255, 255, 255, 0.5); font-size: 14px; }\n\n .balance-value {\n font-size: 20px;\n font-weight: 600;\n color: rgba(255, 255, 255, 0.9);\n }\n\n .balance-value .currency {\n font-size: 14px;\n color: rgba(255, 255, 255, 0.5);\n font-weight: 400;\n margin-left: 4px;\n }\n\n .gas-note {\n margin-top: -8px;\n margin-bottom: 16px;\n color: rgba(255,255,255,0.45);\n font-size: 12px;\n line-height: 1.4;\n text-align: left;\n }\n\n .metamask-btn {\n width: 100%;\n padding: 14px;\n border: none;\n border-radius: 10px;\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n transition: all 0.2s;\n background: #f6851b;\n color: #fff;\n }\n\n .metamask-btn:hover { background: #e2761b; transform: translateY(-1px); }\n .metamask-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }\n\n .metamask-btn svg { width: 22px; height: 22px; }\n\n .amount-input {\n width: 100%;\n padding: 12px 16px;\n background: #0a0c11;\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 8px;\n color: rgba(255, 255, 255, 0.9);\n font-size: 16px;\n margin-bottom: 16px;\n outline: none;\n transition: border-color 0.2s;\n }\n\n .amount-input:focus { border-color: #f2dda6; }\n\n .amount-label {\n font-size: 13px;\n color: rgba(255, 255, 255, 0.5);\n margin-bottom: 6px;\n display: block;\n }\n\n .status {\n text-align: center;\n padding: 12px;\n border-radius: 8px;\n font-size: 14px;\n margin-top: 16px;\n display: none;\n }\n\n .status.success { display: block; background: #0a2e1a; color: #4feb69; border: 1px solid #1a4a2e; }\n .status.error { display: block; background: #2e0a0a; color: #eb4f4f; border: 1px solid #4a1a1a; }\n .status.pending { display: block; background: #1a1a0a; color: #f2dda6; border: 1px solid #4a4a1a; }\n\n .info {\n text-align: center;\n margin-top: 24px;\n font-size: 12px;\n color: rgba(255, 255, 255, 0.3);\n line-height: 1.6;\n }\n\n .info code {\n font-family: 'SF Mono', 'Fira Code', monospace;\n color: #f2dda6;\n word-break: break-all;\n }\n</style>\n</head>\n<body>\n<div class=\"container\">\n <div class=\"logo\">\n <h1>Chain Insights</h1>\n <p>${text.subtitle}</p>\n </div>\n\n <div class=\"card\">\n <div class=\"qr-container\" id=\"qr\">${qrSvg}</div>\n\n <div class=\"address-box\">\n <code id=\"address\">${safeWalletAddress}</code>\n <button class=\"copy-btn\" onclick=\"copyAddress()\" id=\"copyBtn\" title=\"Copy address\">⎘</button>\n </div>\n\n <div class=\"network-badge\">\n <span class=\"dot\"></span>\n ${text.badge}\n </div>\n\n${balanceRows}\n <p class=\"gas-note\">${text.gasNote}</p>\n\n <div class=\"status\" id=\"status\"></div>\n </div>\n\n <p class=\"info\">${text.info}</p>\n</div>\n\n<script>\nconst WALLET = ${walletAddressJson};\nconst BALANCES = ${balancesJson};\n\n// Copy address — fallback for sandboxed iframes where navigator.clipboard is blocked\nfunction copyAddress() {\n var ok = false;\n if (navigator.clipboard && navigator.clipboard.writeText) {\n navigator.clipboard.writeText(WALLET).then(function() { ok = true; }).catch(function() {});\n }\n if (!ok) {\n var ta = document.createElement('textarea');\n ta.value = WALLET;\n ta.style.position = 'fixed';\n ta.style.left = '-9999px';\n document.body.appendChild(ta);\n ta.select();\n try { document.execCommand('copy'); } catch(e) {}\n document.body.removeChild(ta);\n }\n var btn = document.getElementById('copyBtn');\n btn.classList.add('copied');\n btn.innerHTML = '✓';\n setTimeout(function() { btn.classList.remove('copied'); btn.innerHTML = '⎘'; }, 2000);\n}\n\nfunction showBalance(row, value) {\n document.getElementById(row.id).innerHTML = value + '<span class=\"currency\">' + row.currency + '</span>';\n}\n\n// Fetch balances\nasync function fetchBalance() {\n var json = {};\n try { json = await (await fetch('/api/balance')).json(); } catch(e) {}\n BALANCES.forEach(function(row) {\n var raw = json[row.field];\n showBalance(row, raw === undefined || raw === 'unknown' ? '—' : Number(raw || 0).toFixed(row.decimals));\n });\n}\nfetchBalance();\nsetInterval(fetchBalance, 15000);\n\n</script>\n</body>\n</html>`\n}\n\n/** The Base USDC page. */\nfunction generatePage(walletAddressInput: string): string {\n return generateTopupPage(walletAddressInput, 'base-usdc')\n}\n\n/** The robinhood-cia page: the same page, the subscription text. */\nexport function generateSubscriptionPage(walletAddressInput: string): string {\n return generateTopupPage(walletAddressInput, 'robinhood-cia')\n}\n\n// ---------------------------------------------------------------------------\n// The local top-up view: an MCP App drawn by Claude Desktop next to the\n// wallet_topup answer of the local proxy. It shows the wallet address, its QR\n// code, Base Mainnet and USDC. It fetches nothing, signs nothing and sends\n// nothing: the page holds no wallet code, only an address to read.\n// The hosted connector never serves it (ruling 2026-10-05).\n// ---------------------------------------------------------------------------\n\nexport const TOPUP_TOOL_NAME = 'wallet_topup'\nexport const TOPUP_VIEW_URI = 'ui://chain-insights/topup'\nexport const TOPUP_NETWORK_DISPLAY = 'Base Mainnet'\nconst TOPUP_CHAIN_ID = 8453\nconst EXT_APPS_GLOBAL = '__chainInsightsExtApps'\nconst NO_WALLET_TEXT =\n 'No local payment wallet exists yet. Run `cia wallet create` to make one, then ask to top up again.'\n\nexport interface TopupViewFacts {\n wallet_address: string\n network: 'Base'\n network_display: typeof TOPUP_NETWORK_DISPLAY\n chain_id: typeof TOPUP_CHAIN_ID\n token: 'USDC'\n token_contract: typeof USDC_ADDRESS\n}\n\nexport interface TopupToolResult {\n [key: string]: unknown\n content: Array<{ type: 'text'; text: string }>\n structuredContent: {\n schema: 'chain-insights.result.v1'\n tool: typeof TOPUP_TOOL_NAME\n hint: string | null\n facts: { wallet_configured: boolean; topup: TopupViewFacts | null }\n }\n isError: false\n}\n\n/**\n * The wallet_topup answer. The text is complete on its own, for hosts that\n * draw no view. `walletAddress` null means no local wallet exists.\n */\nexport function topupToolResult(walletAddress: string | null): TopupToolResult {\n if (walletAddress === null) {\n return {\n content: [{ type: 'text', text: NO_WALLET_TEXT }],\n structuredContent: {\n schema: 'chain-insights.result.v1',\n tool: TOPUP_TOOL_NAME,\n hint: 'Run `cia wallet create`.',\n facts: { wallet_configured: false, topup: null },\n },\n isError: false,\n }\n }\n const address = assertWalletAddress(walletAddress)\n const topup: TopupViewFacts = {\n wallet_address: address,\n network: 'Base',\n network_display: TOPUP_NETWORK_DISPLAY,\n chain_id: TOPUP_CHAIN_ID,\n token: 'USDC',\n token_contract: USDC_ADDRESS,\n }\n return {\n content: [\n {\n type: 'text',\n text: [\n `Payment wallet: ${address}`,\n `Network: ${TOPUP_NETWORK_DISPLAY} (chain ID ${TOPUP_CHAIN_ID})`,\n `Token: USDC (contract ${USDC_ADDRESS})`,\n `To top up, send USDC on ${TOPUP_NETWORK_DISPLAY} to the wallet address. Chain Insights makes no payment from here.`,\n ].join('\\n'),\n },\n ],\n structuredContent: {\n schema: 'chain-insights.result.v1',\n tool: TOPUP_TOOL_NAME,\n hint: null,\n facts: { wallet_configured: true, topup },\n },\n isError: false,\n }\n}\n\nexport interface SubscriptionTopupViewFacts {\n wallet_address: string\n network: typeof ROBINHOOD_NETWORK_DISPLAY\n network_display: typeof ROBINHOOD_NETWORK_DISPLAY\n chain_id: typeof ROBINHOOD_CHAIN_ID\n token: typeof CIA_SYMBOL\n token_contract: typeof CIA_TOKEN_ADDRESS\n gas_token: 'ETH'\n cia_balance: string\n eth_balance: string\n}\n\nexport interface SubscriptionTopupToolResult {\n [key: string]: unknown\n content: Array<{ type: 'text'; text: string }>\n structuredContent: {\n schema: 'chain-insights.result.v1'\n tool: typeof SUBSCRIPTION_TOPUP_TOOL\n hint: string | null\n facts: { wallet_configured: boolean; topup: SubscriptionTopupViewFacts | null }\n }\n isError: false\n}\n\n/** The rule the subscription_topup answer ends with. */\nexport const SUBSCRIPTION_TOPUP_RULE =\n 'Fund this wallet. The user buys days in a terminal with `cia subscription buy --days N`. An agent cannot buy. Never send $CIA by hand to any other address: it is not credited.'\n\n/**\n * The subscription_topup answer: the local wallet on Robinhood Chain, $CIA,\n * the balances ('unknown' when no RPC answers). It names the user's own\n * wallet only. `walletAddress` null means no local wallet exists.\n */\nexport function subscriptionTopupToolResult(\n walletAddress: string | null,\n balances: { cia: string; eth: string } = { cia: 'unknown', eth: 'unknown' }\n): SubscriptionTopupToolResult {\n if (walletAddress === null) {\n return {\n content: [{ type: 'text', text: 'No local wallet exists yet. Run `cia wallet create` to make one, then ask to top up again.' }],\n structuredContent: {\n schema: 'chain-insights.result.v1',\n tool: SUBSCRIPTION_TOPUP_TOOL,\n hint: 'Run `cia wallet create`.',\n facts: { wallet_configured: false, topup: null },\n },\n isError: false,\n }\n }\n const address = assertWalletAddress(walletAddress)\n const topup: SubscriptionTopupViewFacts = {\n wallet_address: address,\n network: ROBINHOOD_NETWORK_DISPLAY,\n network_display: ROBINHOOD_NETWORK_DISPLAY,\n chain_id: ROBINHOOD_CHAIN_ID,\n token: CIA_SYMBOL,\n token_contract: CIA_TOKEN_ADDRESS,\n gas_token: 'ETH',\n cia_balance: balances.cia,\n eth_balance: balances.eth,\n }\n return {\n content: [\n {\n type: 'text',\n text: [\n `Subscription wallet: ${address}`,\n `Network: ${ROBINHOOD_NETWORK_DISPLAY} (chain ID ${ROBINHOOD_CHAIN_ID})`,\n `Token: ${CIA_SYMBOL} (contract ${CIA_TOKEN_ADDRESS}), with ETH for gas`,\n `Balance: ${balances.cia} ${CIA_SYMBOL}, ${balances.eth} ETH`,\n SUBSCRIPTION_TOPUP_RULE,\n ].join('\\n'),\n },\n ],\n structuredContent: {\n schema: 'chain-insights.result.v1',\n tool: SUBSCRIPTION_TOPUP_TOOL,\n hint: null,\n facts: { wallet_configured: true, topup },\n },\n isError: false,\n }\n}\n\nlet extAppsScript: string | null = null\n\n/**\n * The browser build of the ext-apps App client (the same 1.x pin as\n * the Chain Insights UI package), turned from an ES module into a script that publishes\n * its exports on globalThis, so the view stays one self-contained file.\n */\nexport function extAppsBrowserScript(): string {\n if (extAppsScript !== null) return extAppsScript\n const require = createRequire(import.meta.url)\n const source = readFileSync(\n require.resolve('@modelcontextprotocol/ext-apps/app-with-deps'),\n 'utf8'\n )\n const match = /export\\s*\\{([^}]*)\\}\\s*;?\\s*(?:\\/\\/# sourceMappingURL=\\S*\\s*)?$/.exec(source)\n if (!match) throw new Error('The ext-apps app bundle does not end with one export list')\n const fields = match[1]\n .split(',')\n .map((part) => part.trim())\n .filter(Boolean)\n .map((part) => {\n const [local, exported] = part.split(/\\s+as\\s+/)\n return `${JSON.stringify(exported ?? local)}:${local}`\n })\n extAppsScript = `${source.slice(0, match.index)}\\nglobalThis.${EXT_APPS_GLOBAL}={${fields.join(',')}};\\n`\n extAppsScript = extAppsScript.replaceAll('</script', '<\\\\/script')\n return extAppsScript\n}\n\nconst TOPUP_VIEW_STYLE = `\n:root {\n color-scheme: light;\n --ci-text: var(--color-text-primary, #1d1b16);\n --ci-muted: var(--color-text-secondary, #5c5647);\n --ci-surface: var(--color-background-secondary, #f7f4ec);\n --ci-border: var(--color-border-primary, #d9d2c0);\n --ci-accent: #8a6a12;\n --ci-on-accent: #ffffff;\n --ci-qr-dark: #000000;\n --ci-qr-light: #ffffff;\n --ci-font: var(--font-sans, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif);\n --ci-mono: var(--font-mono, ui-monospace, 'SF Mono', Menlo, Consolas, monospace);\n}\nhtml[data-theme='dark'] {\n color-scheme: dark;\n --ci-text: var(--color-text-primary, #f2eee4);\n --ci-muted: var(--color-text-secondary, #b8b09c);\n --ci-surface: var(--color-background-secondary, #1f1d19);\n --ci-border: var(--color-border-primary, #3d392f);\n --ci-accent: #e3c46a;\n --ci-on-accent: #1d1b16;\n}\n* { box-sizing: border-box; margin: 0; padding: 0; }\nhtml, body { background: transparent; }\nbody { font-family: var(--ci-font); color: var(--ci-text); font-size: 14px; line-height: 1.45; padding: 4px; }\n.card { max-width: 420px; margin: 0 auto; padding: 20px; border: 1px solid var(--ci-border); border-radius: 12px; background: var(--ci-surface); }\nh1 { font-size: 16px; font-weight: 600; margin-bottom: 4px; }\n.lead, .note, .muted { color: var(--ci-muted); }\n.lead { margin-bottom: 16px; }\n.qr-tile { display: flex; justify-content: center; margin-bottom: 16px; }\n.qr { width: 200px; height: 200px; border-radius: 8px; }\n.qr-dark { fill: var(--ci-qr-dark); }\n.qr-light { fill: var(--ci-qr-light); }\ndl { display: grid; grid-template-columns: max-content 1fr; gap: 6px 12px; margin-bottom: 16px; }\ndt { color: var(--ci-muted); }\ndd { overflow-wrap: anywhere; }\ncode { font-family: var(--ci-mono); font-size: 12px; }\n.address { display: block; margin-bottom: 6px; }\nbutton { font: inherit; font-size: 13px; padding: 4px 12px; border-radius: 6px; border: 0; background: var(--ci-accent); color: var(--ci-on-accent); cursor: pointer; }\nbutton:focus-visible { outline: 2px solid var(--ci-accent); outline-offset: 2px; }\n.copy-state { margin-left: 8px; font-size: 12px; color: var(--ci-muted); }\n.skeleton { height: 14px; border-radius: 4px; background: var(--ci-border); margin-bottom: 10px; }\n.skeleton.short { width: 60%; }\n`\n\nconst TOPUP_VIEW_SCRIPT = `\n(function () {\n var extApps = globalThis.${EXT_APPS_GLOBAL};\n var qrModules = __QR_MODULES__;\n var root = document.getElementById('topup');\n var ADDRESS = /^0x[0-9a-fA-F]{40}$/;\n\n function el(tag, attrs, children) {\n var node = document.createElement(tag);\n Object.keys(attrs || {}).forEach(function (key) {\n if (key === 'text') node.textContent = attrs[key];\n else node.setAttribute(key, attrs[key]);\n });\n (children || []).forEach(function (child) {\n node.append(typeof child === 'string' ? document.createTextNode(child) : child);\n });\n return node;\n }\n\n function qrSvg(address) {\n var modules = qrModules(address);\n var quiet = 4;\n var total = modules.length + quiet * 2;\n var ns = 'http://www.w3.org/2000/svg';\n var svg = document.createElementNS(ns, 'svg');\n svg.setAttribute('class', 'qr');\n svg.setAttribute('viewBox', '0 0 ' + total + ' ' + total);\n svg.setAttribute('shape-rendering', 'crispEdges');\n svg.setAttribute('role', 'img');\n svg.setAttribute('aria-label', 'QR code of the wallet address ' + address);\n var light = document.createElementNS(ns, 'rect');\n light.setAttribute('class', 'qr-light');\n light.setAttribute('width', String(total));\n light.setAttribute('height', String(total));\n svg.appendChild(light);\n var d = '';\n for (var row = 0; row < modules.length; row++) {\n for (var col = 0; col < modules.length; col++) {\n if (modules[row][col] === 1) d += 'M' + (col + quiet) + ' ' + (row + quiet) + 'h1v1h-1z';\n }\n }\n var dark = document.createElementNS(ns, 'path');\n dark.setAttribute('class', 'qr-dark');\n dark.setAttribute('d', d);\n svg.appendChild(dark);\n return svg;\n }\n\n function show(children) {\n root.replaceChildren(el('div', { class: 'card' }, children));\n }\n\n function resultText(result) {\n return ((result && result.content) || [])\n .filter(function (item) { return item && item.type === 'text' && typeof item.text === 'string'; })\n .map(function (item) { return item.text; })\n .join('\\\\n');\n }\n\n function copyButton(address, target) {\n var state = el('span', { class: 'copy-state', 'aria-live': 'polite' });\n var button = el('button', { type: 'button', text: 'Copy address' });\n button.addEventListener('click', function () {\n function selectIt(message) {\n var range = document.createRange();\n range.selectNodeContents(target);\n var selection = window.getSelection();\n selection.removeAllRanges();\n selection.addRange(range);\n state.textContent = message;\n }\n if (navigator.clipboard && navigator.clipboard.writeText) {\n navigator.clipboard.writeText(address).then(\n function () { state.textContent = 'Copied'; },\n function () { selectIt('Selected. Press Ctrl+C to copy.'); }\n );\n } else {\n selectIt('Selected. Press Ctrl+C to copy.');\n }\n });\n return el('div', {}, [button, state]);\n }\n\n function render(result) {\n var structured = result && result.structuredContent;\n var facts = structured && (structured.tool === '${TOPUP_TOOL_NAME}' || structured.tool === '${SUBSCRIPTION_TOPUP_TOOL}') ? structured.facts : null;\n if (!result || result.isError || !facts) {\n show([el('h1', { text: 'Wallet top-up' }), el('p', { class: 'muted', text: resultText(result) || 'The wallet details did not arrive.' })]);\n return;\n }\n if (!facts.topup) {\n show([\n el('h1', { text: 'No payment wallet yet' }),\n el('p', { class: 'lead' }, ['Create one with ', el('code', { text: 'cia wallet create' }), ', then ask to top up again.'])\n ]);\n return;\n }\n var topup = facts.topup;\n if (!ADDRESS.test(topup.wallet_address)) {\n show([el('h1', { text: 'Wallet top-up' }), el('p', { class: 'muted', text: 'The wallet address is not a valid 0x address.' })]);\n return;\n }\n var address = el('code', { class: 'address', text: topup.wallet_address });\n show([\n el('h1', { text: 'Top up your Chain Insights wallet' }),\n el('p', { class: 'lead', text: 'Send ' + topup.token + ' on ' + topup.network_display + ' to this address.' }),\n el('div', { class: 'qr-tile' }, [qrSvg(topup.wallet_address)]),\n el('dl', {}, [\n el('dt', { text: 'Network' }), el('dd', { text: topup.network_display + ' (chain ID ' + topup.chain_id + ')' }),\n el('dt', { text: 'Token' }), el('dd', { text: topup.token }),\n el('dt', { text: 'Token contract' }), el('dd', {}, [el('code', { text: topup.token_contract })]),\n el('dt', { text: 'Wallet' }), el('dd', {}, [address, copyButton(topup.wallet_address, address)])\n ]),\n el('p', { class: 'note', text: 'This view shows an address only. It signs nothing and sends nothing.' })\n ]);\n }\n\n function applyContext(context) {\n if (!context) return;\n if (context.theme === 'light' || context.theme === 'dark') extApps.applyDocumentTheme(context.theme);\n var styles = context.styles;\n if (styles && styles.variables) extApps.applyHostStyleVariables(styles.variables);\n if (styles && styles.css && styles.css.fonts) extApps.applyHostFonts(styles.css.fonts);\n }\n\n // Until the host names its theme, follow the system; the host's theme wins.\n if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {\n document.documentElement.setAttribute('data-theme', 'dark');\n }\n\n var app = new extApps.App({ name: 'chain-insights-topup', version: '1.0.0' }, {});\n app.ontoolresult = function (result) { render(result); };\n app.onhostcontextchanged = function (change) { applyContext(change); };\n app.onteardown = function () { return {}; };\n app.connect(new extApps.PostMessageTransport(window.parent, window.parent)).then(\n function () { applyContext(app.getHostContext()); },\n function () {\n show([el('h1', { text: 'Wallet top-up' }), el('p', { class: 'muted', text: 'This view opens inside Claude, next to the ${TOPUP_TOOL_NAME} answer.' })]);\n }\n );\n})();\n`\n\n/**\n * The QR encoder as browser source: an expression that evaluates to\n * `qrModules`. A transform that keeps function names (esbuild keepNames, as\n * tsx runs it) wraps inner functions in `__name(...)`; the wrapper gives that\n * helper a do-nothing body so the source runs anywhere.\n */\nexport function qrModulesSource(): string {\n return `(function () { var __name = function (target) { return target; }; return (${qrModules.toString()}); })()`\n}\n\n/** The one self-contained HTML file of ui://chain-insights/topup. */\nexport function topupViewHtml(): string {\n const script = TOPUP_VIEW_SCRIPT.replace('__QR_MODULES__', () => qrModulesSource())\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Wallet top-up</title>\n<style>${TOPUP_VIEW_STYLE}</style>\n</head>\n<body>\n<main id=\"topup\" aria-live=\"polite\"><div class=\"card\"><div class=\"skeleton\"></div><div class=\"skeleton short\"></div><p class=\"muted\">Waiting for the wallet details.</p></div></main>\n<script type=\"module\">${extAppsBrowserScript()}</script>\n<script type=\"module\">${script.replaceAll('</script', '<\\\\/script')}</script>\n</body>\n</html>`\n}\n\n/** The parts of McpServer the top-up view registers on. */\nexport type TopupViewServer = Pick<\n import('@modelcontextprotocol/sdk/server/mcp.js').McpServer,\n 'registerTool' | 'registerResource'\n>\n\nexport interface TopupViewDependencies {\n /** The local wallet address, or null when no wallet exists. Throws when it cannot be read. */\n walletAddress: () => Promise<string | null>\n /** The $CIA and ETH balances on Robinhood Chain, 'unknown' when no RPC answers. */\n robinhoodBalances?: (address: string) => Promise<{ cia: string; eth: string }>\n}\n\nasync function readRobinhoodBalances(address: string): Promise<{ cia: string; eth: string }> {\n const [cia, eth] = await Promise.all([getCiaBalance(address), getRobinhoodEthBalance(address)])\n return { cia, eth }\n}\n\n/**\n * Register the wallet_topup tool and its view on the local proxy. The tool\n * reads the local wallet address and nothing else.\n */\nexport async function registerTopupView(\n server: TopupViewServer,\n deps: TopupViewDependencies\n): Promise<void> {\n const { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } =\n await import('@modelcontextprotocol/ext-apps/server')\n const uiMeta = { prefersBorder: false, csp: { resourceDomains: ['https://assets.claude.ai'] } }\n\n registerAppTool(\n server,\n TOPUP_TOOL_NAME,\n {\n title: 'Top Up Wallet',\n description: WALLET_TOPUP_DESCRIPTION,\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n },\n _meta: { ui: { resourceUri: TOPUP_VIEW_URI } },\n },\n async () => {\n try {\n return topupToolResult(await deps.walletAddress())\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: `Top-up failed: ${(err as Error).message}` }],\n isError: true,\n }\n }\n }\n )\n\n registerAppTool(\n server,\n SUBSCRIPTION_TOPUP_TOOL,\n {\n title: 'Top Up Subscription Wallet',\n description: SUBSCRIPTION_TOPUP_DESCRIPTION,\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n // It reads the balances on Robinhood Chain.\n openWorldHint: true,\n },\n _meta: { ui: { resourceUri: TOPUP_VIEW_URI } },\n },\n async () => {\n try {\n const address = await deps.walletAddress()\n if (address === null) return subscriptionTopupToolResult(null)\n const balances = await (deps.robinhoodBalances ?? readRobinhoodBalances)(address)\n return subscriptionTopupToolResult(address, balances)\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: `Top-up failed: ${(err as Error).message}` }],\n isError: true,\n }\n }\n }\n )\n\n registerAppResource(\n server,\n 'Chain Insights wallet top-up',\n TOPUP_VIEW_URI,\n {\n description:\n 'The local wallet address and its QR code: on Base Mainnet with USDC, or on Robinhood Chain with $CIA.',\n _meta: { ui: uiMeta },\n },\n async () => ({\n contents: [\n {\n uri: TOPUP_VIEW_URI,\n mimeType: RESOURCE_MIME_TYPE,\n text: topupViewHtml(),\n _meta: { ui: uiMeta },\n },\n ],\n })\n )\n}\n"],"mappings":"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"}
|