@walletchan/rpc 0.1.0
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/LICENSE.md +9 -0
- package/README.md +166 -0
- package/SKILL.md +149 -0
- package/dist/chains.d.ts +20 -0
- package/dist/chains.d.ts.map +1 -0
- package/dist/chains.js +130 -0
- package/dist/chains.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +78 -0
- package/dist/cli.js.map +1 -0
- package/dist/clipboard.d.ts +7 -0
- package/dist/clipboard.d.ts.map +1 -0
- package/dist/clipboard.js +75 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +104 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +21 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +117 -0
- package/dist/logger.js.map +1 -0
- package/dist/rpcHandler.d.ts +14 -0
- package/dist/rpcHandler.d.ts.map +1 -0
- package/dist/rpcHandler.js +197 -0
- package/dist/rpcHandler.js.map +1 -0
- package/dist/rpcServer.d.ts +5 -0
- package/dist/rpcServer.d.ts.map +1 -0
- package/dist/rpcServer.js +59 -0
- package/dist/rpcServer.js.map +1 -0
- package/dist/rpcTypes.d.ts +29 -0
- package/dist/rpcTypes.d.ts.map +1 -0
- package/dist/rpcTypes.js +39 -0
- package/dist/rpcTypes.js.map +1 -0
- package/dist/skill.d.ts +4 -0
- package/dist/skill.d.ts.map +1 -0
- package/dist/skill.js +158 -0
- package/dist/skill.js.map +1 -0
- package/dist/upstream.d.ts +4 -0
- package/dist/upstream.d.ts.map +1 -0
- package/dist/upstream.js +21 -0
- package/dist/upstream.js.map +1 -0
- package/dist/walletConnect.d.ts +44 -0
- package/dist/walletConnect.d.ts.map +1 -0
- package/dist/walletConnect.js +277 -0
- package/dist/walletConnect.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { serve } from "@hono/node-server";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { formatChains } from "./chains.js";
|
|
4
|
+
import { log } from "./logger.js";
|
|
5
|
+
import { handleRpcRequest } from "./rpcHandler.js";
|
|
6
|
+
import { errorResponse } from "./rpcTypes.js";
|
|
7
|
+
import { formatRuntimeSkill } from "./skill.js";
|
|
8
|
+
export function startRpcServer(config, context) {
|
|
9
|
+
const app = new Hono();
|
|
10
|
+
async function handleRpc(c) {
|
|
11
|
+
try {
|
|
12
|
+
const body = await c.req.json();
|
|
13
|
+
if (Array.isArray(body)) {
|
|
14
|
+
if (body.length === 0) {
|
|
15
|
+
return c.json(errorResponse(null, -32600, "Invalid empty JSON-RPC batch"), 400);
|
|
16
|
+
}
|
|
17
|
+
const responses = await Promise.all(body.map((request) => handleRpcRequest(request, context)));
|
|
18
|
+
return c.json(responses.filter((response) => response !== null));
|
|
19
|
+
}
|
|
20
|
+
const response = await handleRpcRequest(body, context);
|
|
21
|
+
return response ? c.json(response) : c.body(null, 204);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return c.json(errorResponse(null, -32700, "Parse error"), 400);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
app.post("/", handleRpc);
|
|
28
|
+
app.post("/rpc", handleRpc);
|
|
29
|
+
app.get("/skill.md", (c) => skillResponse(c, config, context));
|
|
30
|
+
app.get("/SKILL.md", (c) => skillResponse(c, config, context));
|
|
31
|
+
app.get("/health", (c) => c.json({
|
|
32
|
+
ok: true,
|
|
33
|
+
connected: context.wallet.connected,
|
|
34
|
+
activeChainId: context.getActiveChain().chainId,
|
|
35
|
+
chains: context.chains.map((chain) => ({
|
|
36
|
+
name: chain.name,
|
|
37
|
+
chainId: chain.chainId,
|
|
38
|
+
})),
|
|
39
|
+
}));
|
|
40
|
+
app.get("/session", (c) => c.json({
|
|
41
|
+
connected: context.wallet.connected,
|
|
42
|
+
activeChainId: context.getActiveChain().chainId,
|
|
43
|
+
chains: formatChains(context.chains),
|
|
44
|
+
session: context.wallet.connected ? context.wallet.getSessionInfo() : null,
|
|
45
|
+
}));
|
|
46
|
+
return serve({
|
|
47
|
+
fetch: app.fetch,
|
|
48
|
+
hostname: config.host,
|
|
49
|
+
port: config.port,
|
|
50
|
+
}, () => {
|
|
51
|
+
log.dim("RPC server ready");
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function skillResponse(c, config, context) {
|
|
55
|
+
return c.body(formatRuntimeSkill(config, context), 200, {
|
|
56
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=rpcServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpcServer.js","sourceRoot":"","sources":["../src/rpcServer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type JsonRpcId = string | number | null;
|
|
2
|
+
export interface JsonRpcRequest {
|
|
3
|
+
jsonrpc?: string;
|
|
4
|
+
id?: JsonRpcId;
|
|
5
|
+
method?: unknown;
|
|
6
|
+
params?: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface JsonRpcErrorPayload {
|
|
9
|
+
code: number;
|
|
10
|
+
message: string;
|
|
11
|
+
data?: unknown;
|
|
12
|
+
}
|
|
13
|
+
export interface JsonRpcResponse {
|
|
14
|
+
jsonrpc: "2.0";
|
|
15
|
+
id: JsonRpcId;
|
|
16
|
+
result?: unknown;
|
|
17
|
+
error?: JsonRpcErrorPayload;
|
|
18
|
+
}
|
|
19
|
+
export declare class RpcError extends Error {
|
|
20
|
+
readonly code: number;
|
|
21
|
+
readonly data?: unknown | undefined;
|
|
22
|
+
constructor(code: number, message: string, data?: unknown | undefined);
|
|
23
|
+
}
|
|
24
|
+
export declare function successResponse(id: JsonRpcId, result: unknown): JsonRpcResponse;
|
|
25
|
+
export declare function errorResponse(id: JsonRpcId, code: number, message: string, data?: unknown): JsonRpcResponse;
|
|
26
|
+
export declare function getErrorCode(error: unknown, fallback?: number): number;
|
|
27
|
+
export declare function getErrorMessage(error: unknown, fallback?: string): string;
|
|
28
|
+
export declare function getParamsArray(request: JsonRpcRequest): unknown[];
|
|
29
|
+
//# sourceMappingURL=rpcTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpcTypes.d.ts","sourceRoot":"","sources":["../src/rpcTypes.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAE/C,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,SAAS,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,mBAAmB,CAAC;CAC7B;AAED,qBAAa,QAAS,SAAQ,KAAK;aAEf,IAAI,EAAE,MAAM;aAEZ,IAAI,CAAC,EAAE,OAAO;gBAFd,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,EACC,IAAI,CAAC,EAAE,OAAO,YAAA;CAIjC;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAG,eAAe,CAE/E;AAED,wBAAgB,aAAa,CAC3B,EAAE,EAAE,SAAS,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,OAAO,GACb,eAAe,CAMjB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAS,GAAG,MAAM,CAItE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAmB,GAAG,MAAM,CAInF;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,EAAE,CAIjE"}
|
package/dist/rpcTypes.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export class RpcError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
data;
|
|
4
|
+
constructor(code, message, data) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.data = data;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function successResponse(id, result) {
|
|
11
|
+
return { jsonrpc: "2.0", id, result };
|
|
12
|
+
}
|
|
13
|
+
export function errorResponse(id, code, message, data) {
|
|
14
|
+
return {
|
|
15
|
+
jsonrpc: "2.0",
|
|
16
|
+
id,
|
|
17
|
+
error: data === undefined ? { code, message } : { code, message, data },
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function getErrorCode(error, fallback = -32000) {
|
|
21
|
+
if (error instanceof RpcError)
|
|
22
|
+
return error.code;
|
|
23
|
+
const code = error?.code;
|
|
24
|
+
return typeof code === "number" && Number.isInteger(code) ? code : fallback;
|
|
25
|
+
}
|
|
26
|
+
export function getErrorMessage(error, fallback = "Request failed") {
|
|
27
|
+
if (error instanceof Error && error.message)
|
|
28
|
+
return error.message;
|
|
29
|
+
const message = error?.message;
|
|
30
|
+
return typeof message === "string" && message ? message : fallback;
|
|
31
|
+
}
|
|
32
|
+
export function getParamsArray(request) {
|
|
33
|
+
if (request.params === undefined)
|
|
34
|
+
return [];
|
|
35
|
+
if (Array.isArray(request.params))
|
|
36
|
+
return request.params;
|
|
37
|
+
throw new RpcError(-32602, "params must be an array");
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=rpcTypes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpcTypes.js","sourceRoot":"","sources":["../src/rpcTypes.ts"],"names":[],"mappings":"AAsBA,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEf;IAEA;IAHlB,YACkB,IAAY,EAC5B,OAAe,EACC,IAAc;QAE9B,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,SAAI,GAAJ,IAAI,CAAQ;QAEZ,SAAI,GAAJ,IAAI,CAAU;IAGhC,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAAC,EAAa,EAAE,MAAe;IAC5D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,EAAa,EACb,IAAY,EACZ,OAAe,EACf,IAAc;IAEd,OAAO;QACL,OAAO,EAAE,KAAK;QACd,EAAE;QACF,KAAK,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;KACxE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAc,EAAE,QAAQ,GAAG,CAAC,KAAK;IAC5D,IAAI,KAAK,YAAY,QAAQ;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC;IACjD,MAAM,IAAI,GAAI,KAA4B,EAAE,IAAI,CAAC;IACjD,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAc,EAAE,QAAQ,GAAG,gBAAgB;IACzE,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC;IAClE,MAAM,OAAO,GAAI,KAA+B,EAAE,OAAO,CAAC;IAC1D,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAuB;IACpD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IAC5C,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC;IACzD,MAAM,IAAI,QAAQ,CAAC,CAAC,KAAK,EAAE,yBAAyB,CAAC,CAAC;AACxD,CAAC"}
|
package/dist/skill.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill.d.ts","sourceRoot":"","sources":["../src/skill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAiBlD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,GAAG,MAAM,CAsIjF"}
|
package/dist/skill.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { formatChains, toHexChainId } from "./chains.js";
|
|
2
|
+
const APPROVAL_METHODS = [
|
|
3
|
+
"eth_sendTransaction",
|
|
4
|
+
"personal_sign",
|
|
5
|
+
"eth_signTypedData_v3",
|
|
6
|
+
"eth_signTypedData_v4",
|
|
7
|
+
];
|
|
8
|
+
const BATCH_METHODS = [
|
|
9
|
+
"wallet_getCapabilities",
|
|
10
|
+
"wallet_sendCalls",
|
|
11
|
+
"wallet_getCallsStatus",
|
|
12
|
+
"wallet_showCallsStatus",
|
|
13
|
+
];
|
|
14
|
+
export function formatRuntimeSkill(config, context) {
|
|
15
|
+
const rpcUrl = `http://${config.host}:${config.port}`;
|
|
16
|
+
const activeChain = context.getActiveChain();
|
|
17
|
+
const activeChainId = toHexChainId(activeChain.chainId);
|
|
18
|
+
const session = getSession(context);
|
|
19
|
+
const accounts = session?.accounts || [];
|
|
20
|
+
const preferredAccount = accounts[0] || "0xYourApprovedAccount";
|
|
21
|
+
const methods = context.includeBatching
|
|
22
|
+
? [...APPROVAL_METHODS, ...BATCH_METHODS]
|
|
23
|
+
: APPROVAL_METHODS;
|
|
24
|
+
return `---
|
|
25
|
+
name: walletchan-rpc
|
|
26
|
+
description: Use when sending Ethereum JSON-RPC, Foundry, or ERC-5792 wallet_sendCalls requests through a local WalletChan RPC server with user wallet approval.
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
# WalletChan RPC
|
|
30
|
+
|
|
31
|
+
This skill was served by a live WalletChan RPC instance. Use the runtime details below as authoritative for this session.
|
|
32
|
+
|
|
33
|
+
## Runtime
|
|
34
|
+
|
|
35
|
+
- RPC URL: \`${rpcUrl}\`
|
|
36
|
+
- Connected: \`${context.wallet.connected ? "yes" : "no"}\`
|
|
37
|
+
- Approved accounts: ${accounts.length > 0 ? accounts.map((account) => `\`${account}\``).join(", ") : "`none`"}
|
|
38
|
+
- Active chain: \`${activeChain.name}\` (${activeChain.chainId}, \`${activeChainId}\`)
|
|
39
|
+
- Configured chains: \`${formatChains(context.chains)}\`
|
|
40
|
+
- Batching: \`${context.includeBatching ? "enabled" : "disabled"}\`
|
|
41
|
+
- Wallet: \`${session?.peerName || "unknown"}\`${session?.peerUrl ? ` (${session.peerUrl})` : ""}
|
|
42
|
+
|
|
43
|
+
## How To Use
|
|
44
|
+
|
|
45
|
+
Send standard JSON-RPC over HTTP to \`${rpcUrl}\` or \`${rpcUrl}/rpc\`. Any JSON-RPC client can use this endpoint, including JavaScript code, shell scripts, viem, ethers, Foundry, or an AI agent.
|
|
46
|
+
|
|
47
|
+
Use these wallet methods:
|
|
48
|
+
|
|
49
|
+
${methods.map((method) => `- \`${method}\``).join("\n")}
|
|
50
|
+
|
|
51
|
+
Read-only JSON-RPC methods may be forwarded to the configured upstream RPC. Local account and chain methods include \`eth_accounts\`, \`eth_requestAccounts\`, \`eth_chainId\`, \`net_version\`, \`web3_clientVersion\`, and \`wallet_switchEthereumChain\`.
|
|
52
|
+
|
|
53
|
+
## Safety Rules
|
|
54
|
+
|
|
55
|
+
- Use \`eth_sendTransaction\` for sends. Never use \`eth_sendRawTransaction\`, \`eth_sign\`, or \`eth_signTransaction\`.
|
|
56
|
+
- Every send/sign/batch request opens a user approval prompt in the connected wallet.
|
|
57
|
+
- If the wallet rejects a request, report the rejection to the user. Do not retry rejected sends automatically.
|
|
58
|
+
- Use only an approved account from \`eth_accounts\` as \`from\` or Foundry \`--sender\`.
|
|
59
|
+
- If a request targets a different configured chain, include the target \`chainId\` or call \`wallet_switchEthereumChain\` first.
|
|
60
|
+
- WalletConnect sessions persist across CLI restarts. Ask the user to restart with \`--force-new-session\` only when they want a fresh pairing.
|
|
61
|
+
|
|
62
|
+
## Discovery
|
|
63
|
+
|
|
64
|
+
\`\`\`bash
|
|
65
|
+
curl -s ${rpcUrl}/health
|
|
66
|
+
curl -s ${rpcUrl}/session
|
|
67
|
+
curl -s ${rpcUrl}/SKILL.md
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
\`\`\`bash
|
|
71
|
+
curl -s ${rpcUrl} \\
|
|
72
|
+
-H 'content-type: application/json' \\
|
|
73
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"eth_accounts","params":[]}'
|
|
74
|
+
\`\`\`
|
|
75
|
+
|
|
76
|
+
## wallet_sendCalls
|
|
77
|
+
|
|
78
|
+
When batching is enabled, prefer \`wallet_sendCalls\` for related write actions that should be reviewed and submitted together. The goal is to reduce the number of wallet approval popups for one user intent while preserving a clear wallet confirmation.
|
|
79
|
+
|
|
80
|
+
Use \`wallet_sendCalls\` when:
|
|
81
|
+
|
|
82
|
+
- A later call depends on an earlier call in the same user intent.
|
|
83
|
+
- The user asks for several related sends or contract writes, such as sending tokens or ETH to multiple addresses, distributing payments, claiming several rewards, minting several NFTs from the same collection, or doing several same-protocol actions.
|
|
84
|
+
- The flow would otherwise require multiple approvals for one task, such as \`approve + swap\`, \`approve + stake\`, \`approve + deposit\`, \`approve + bridge\`, or \`claim + stake\`.
|
|
85
|
+
- The calls must target one chain and use the same approved \`from\` account.
|
|
86
|
+
|
|
87
|
+
For dependent actions such as \`approve + swap\` or \`approve + stake\`, set \`atomicRequired: true\` so the wallet must execute all calls atomically or none of the material effects should land onchain. Put calls in execution order: approval first, then the action that consumes the approval. For independent but related actions, such as sending tokens to multiple recipients, still prefer one \`wallet_sendCalls\` request so the user reviews one batch instead of several popups.
|
|
88
|
+
|
|
89
|
+
Do not use \`wallet_sendCalls\` for unrelated actions that the user should consider separately, for cross-chain flows, or as a blind retry after the user rejects a request. If the user query describes one cohesive task with multiple same-chain writes, batch it.
|
|
90
|
+
|
|
91
|
+
After \`wallet_sendCalls\`, record the returned bundle ID. WalletChan returns an object with \`id\`; some wallets may call this \`batchId\`. Poll \`wallet_getCallsStatus\` with that ID until status is terminal, or use \`wallet_showCallsStatus\` to open the bundle's explorer view.
|
|
92
|
+
|
|
93
|
+
\`\`\`json
|
|
94
|
+
{
|
|
95
|
+
"jsonrpc": "2.0",
|
|
96
|
+
"id": 1,
|
|
97
|
+
"method": "wallet_sendCalls",
|
|
98
|
+
"params": [
|
|
99
|
+
{
|
|
100
|
+
"version": "2.0.0",
|
|
101
|
+
"chainId": "${activeChainId}",
|
|
102
|
+
"from": "${preferredAccount}",
|
|
103
|
+
"atomicRequired": true,
|
|
104
|
+
"calls": [
|
|
105
|
+
{
|
|
106
|
+
"to": "0xTokenAddress",
|
|
107
|
+
"value": "0x0",
|
|
108
|
+
"data": "0xApproveCalldata"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"to": "0xSwapOrStakeContract",
|
|
112
|
+
"value": "0x0",
|
|
113
|
+
"data": "0xSwapOrStakeCalldata"
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
\`\`\`
|
|
120
|
+
|
|
121
|
+
## Optional Foundry Examples
|
|
122
|
+
|
|
123
|
+
These examples are optional. WalletChan RPC is a standard JSON-RPC endpoint and is not restricted to Foundry.
|
|
124
|
+
|
|
125
|
+
For a single call, \`cast send\` is often convenient because it avoids creating a full Foundry script. Use Foundry's unlocked-account path so Foundry sends \`eth_sendTransaction\` to WalletChan RPC:
|
|
126
|
+
|
|
127
|
+
\`\`\`bash
|
|
128
|
+
cast send 0xContractAddress \\
|
|
129
|
+
"transfer(address,uint256)" 0xRecipient 1000000000000000000 \\
|
|
130
|
+
--rpc-url ${rpcUrl} \\
|
|
131
|
+
--unlocked \\
|
|
132
|
+
--from ${preferredAccount}
|
|
133
|
+
\`\`\`
|
|
134
|
+
|
|
135
|
+
For larger flows that already live in a script, use the same unlocked-account path:
|
|
136
|
+
|
|
137
|
+
\`\`\`bash
|
|
138
|
+
forge script script/Deploy.s.sol \\
|
|
139
|
+
--rpc-url ${rpcUrl} \\
|
|
140
|
+
--broadcast \\
|
|
141
|
+
--unlocked \\
|
|
142
|
+
--sender ${preferredAccount}
|
|
143
|
+
\`\`\`
|
|
144
|
+
|
|
145
|
+
Do not pass a private key for this flow.
|
|
146
|
+
`;
|
|
147
|
+
}
|
|
148
|
+
function getSession(context) {
|
|
149
|
+
if (!context.wallet.connected)
|
|
150
|
+
return null;
|
|
151
|
+
try {
|
|
152
|
+
return context.wallet.getSessionInfo();
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=skill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill.js","sourceRoot":"","sources":["../src/skill.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIzD,MAAM,gBAAgB,GAAG;IACvB,qBAAqB;IACrB,eAAe;IACf,sBAAsB;IACtB,sBAAsB;CACvB,CAAC;AAEF,MAAM,aAAa,GAAG;IACpB,wBAAwB;IACxB,kBAAkB;IAClB,uBAAuB;IACvB,wBAAwB;CACzB,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,MAAiB,EAAE,OAAmB;IACvE,MAAM,MAAM,GAAG,UAAU,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;IACtD,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAC7C,MAAM,aAAa,GAAG,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,EAAE,CAAC;IACzC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,uBAAuB,CAAC;IAChE,MAAM,OAAO,GAAG,OAAO,CAAC,eAAe;QACrC,CAAC,CAAC,CAAC,GAAG,gBAAgB,EAAE,GAAG,aAAa,CAAC;QACzC,CAAC,CAAC,gBAAgB,CAAC;IAErB,OAAO;;;;;;;;;;;eAWM,MAAM;iBACJ,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;uBACjC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ;oBAC1F,WAAW,CAAC,IAAI,OAAO,WAAW,CAAC,OAAO,OAAO,aAAa;yBACzD,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC;gBACrC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU;cAClD,OAAO,EAAE,QAAQ,IAAI,SAAS,KAAK,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE;;;;wCAIxD,MAAM,WAAW,MAAM;;;;EAI7D,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;UAgB7C,MAAM;UACN,MAAM;UACN,MAAM;;;;UAIN,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBA8BI,aAAa;iBAChB,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4BnB,MAAM;;WAET,gBAAgB;;;;;;;cAOb,MAAM;;;aAGP,gBAAgB;;;;CAI5B,CAAC;AACF,CAAC;AAED,SAAS,UAAU,CAAC,OAAmB;IACrC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RuntimeChain } from "./chains.js";
|
|
2
|
+
import type { JsonRpcRequest, JsonRpcResponse } from "./rpcTypes.js";
|
|
3
|
+
export declare function forwardToUpstream(request: JsonRpcRequest, chain: RuntimeChain, timeoutMs: number): Promise<JsonRpcResponse>;
|
|
4
|
+
//# sourceMappingURL=upstream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upstream.d.ts","sourceRoot":"","sources":["../src/upstream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGrE,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,eAAe,CAAC,CAuB1B"}
|
package/dist/upstream.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { errorResponse } from "./rpcTypes.js";
|
|
2
|
+
export async function forwardToUpstream(request, chain, timeoutMs) {
|
|
3
|
+
try {
|
|
4
|
+
const response = await fetch(chain.rpcUrl, {
|
|
5
|
+
method: "POST",
|
|
6
|
+
headers: { "Content-Type": "application/json" },
|
|
7
|
+
body: JSON.stringify({ ...request, jsonrpc: "2.0" }),
|
|
8
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
9
|
+
});
|
|
10
|
+
const data = (await response.json());
|
|
11
|
+
if (!response.ok) {
|
|
12
|
+
return errorResponse(request.id ?? null, -32603, `Upstream RPC error on ${chain.name}: ${response.status} ${response.statusText}`);
|
|
13
|
+
}
|
|
14
|
+
return data;
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
const message = error instanceof Error ? error.message : "Failed to reach upstream RPC";
|
|
18
|
+
return errorResponse(request.id ?? null, -32603, `Upstream RPC error on ${chain.name}: ${message}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=upstream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upstream.js","sourceRoot":"","sources":["../src/upstream.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAuB,EACvB,KAAmB,EACnB,SAAiB;IAEjB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE;YACzC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;YACpD,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;SACvC,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAoB,CAAC;QACxD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,aAAa,CAClB,OAAO,CAAC,EAAE,IAAI,IAAI,EAClB,CAAC,KAAK,EACN,yBAAyB,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CACjF,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;QACxF,OAAO,aAAa,CAAC,OAAO,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;IACtG,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { RuntimeChain } from "./chains.js";
|
|
2
|
+
export interface WalletConnectBridgeConfig {
|
|
3
|
+
chains: RuntimeChain[];
|
|
4
|
+
forceNewSession: boolean;
|
|
5
|
+
host: string;
|
|
6
|
+
includeBatching: boolean;
|
|
7
|
+
port: number;
|
|
8
|
+
projectId: string;
|
|
9
|
+
requestTimeoutSeconds: number;
|
|
10
|
+
}
|
|
11
|
+
export interface SessionInfo {
|
|
12
|
+
accounts: string[];
|
|
13
|
+
peerName: string;
|
|
14
|
+
peerUrl: string;
|
|
15
|
+
topic: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class WalletConnectBridge {
|
|
18
|
+
private readonly config;
|
|
19
|
+
private client;
|
|
20
|
+
private session;
|
|
21
|
+
private readonly methods;
|
|
22
|
+
constructor(config: WalletConnectBridgeConfig);
|
|
23
|
+
get connected(): boolean;
|
|
24
|
+
get sessionTopic(): string | null;
|
|
25
|
+
init(): Promise<SessionInfo | null>;
|
|
26
|
+
createSessionProposal(): Promise<{
|
|
27
|
+
uri: string;
|
|
28
|
+
approval: () => Promise<SessionInfo>;
|
|
29
|
+
}>;
|
|
30
|
+
request(chainId: number, method: string, params: unknown[]): Promise<unknown>;
|
|
31
|
+
getAccounts(chainId?: number): string[];
|
|
32
|
+
getSessionInfo(): SessionInfo;
|
|
33
|
+
disconnectStored(message?: string): Promise<void>;
|
|
34
|
+
close(): void;
|
|
35
|
+
private attachListeners;
|
|
36
|
+
private validateSession;
|
|
37
|
+
private restoreStoredSession;
|
|
38
|
+
private disconnectStoredSessions;
|
|
39
|
+
private disconnectTopic;
|
|
40
|
+
private getClient;
|
|
41
|
+
private getSession;
|
|
42
|
+
private getChainLabel;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=walletConnect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walletConnect.d.ts","sourceRoot":"","sources":["../src/walletConnect.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgChD,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,mBAAmB;IAKlB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAW;gBAEN,MAAM,EAAE,yBAAyB;IAM9D,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,YAAY,IAAI,MAAM,GAAG,IAAI,CAEhC;IAEK,IAAI,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IA8BnC,qBAAqB,IAAI,OAAO,CAAC;QACrC,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;KACtC,CAAC;IA4BI,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBnF,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAKvC,cAAc,IAAI,WAAW;IAUvB,gBAAgB,CAAC,OAAO,SAA2B,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzE,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,eAAe;IAuBvB,OAAO,CAAC,oBAAoB;YAgBd,wBAAwB;YAkBxB,eAAe;IAY7B,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,aAAa;CAItB"}
|