@solana/connector 0.2.0 → 0.2.2
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/README.md +11 -11
- package/dist/{chunk-HPQ5T32K.mjs → chunk-3623Z2QL.mjs} +4 -4
- package/dist/{chunk-HPQ5T32K.mjs.map → chunk-3623Z2QL.mjs.map} +1 -1
- package/dist/{chunk-UCISIAOG.mjs → chunk-A7WQ3K75.mjs} +3 -3
- package/dist/{chunk-UCISIAOG.mjs.map → chunk-A7WQ3K75.mjs.map} +1 -1
- package/dist/{chunk-SITQ4JWM.js → chunk-BF67LVVM.js} +17 -21
- package/dist/chunk-BF67LVVM.js.map +1 -0
- package/dist/{chunk-RTXUS5KG.mjs → chunk-DFHJYZKZ.mjs} +84 -71
- package/dist/chunk-DFHJYZKZ.mjs.map +1 -0
- package/dist/chunk-E3DAIOYS.mjs +22 -0
- package/dist/chunk-E3DAIOYS.mjs.map +1 -0
- package/dist/chunk-FG7HTQTV.mjs +7 -0
- package/dist/chunk-FG7HTQTV.mjs.map +1 -0
- package/dist/{chunk-BJAVJQLK.js → chunk-I2XX5FUG.js} +6 -6
- package/dist/{chunk-BJAVJQLK.js.map → chunk-I2XX5FUG.js.map} +1 -1
- package/dist/chunk-L5FWMNWO.js +9 -0
- package/dist/chunk-L5FWMNWO.js.map +1 -0
- package/dist/{chunk-HN5AJF7F.js → chunk-NLPEO5GT.js} +5 -5
- package/dist/{chunk-HN5AJF7F.js.map → chunk-NLPEO5GT.js.map} +1 -1
- package/dist/chunk-QST7XLMB.js +26 -0
- package/dist/chunk-QST7XLMB.js.map +1 -0
- package/dist/{chunk-ZZTY3O4N.mjs → chunk-SJCQ3KZE.mjs} +4 -7
- package/dist/chunk-SJCQ3KZE.mjs.map +1 -0
- package/dist/{chunk-IDTUFDNB.mjs → chunk-SQ2JEA2M.mjs} +58 -11
- package/dist/chunk-SQ2JEA2M.mjs.map +1 -0
- package/dist/{chunk-BZ2VBJCZ.js → chunk-TRSJSU33.js} +141 -94
- package/dist/chunk-TRSJSU33.js.map +1 -0
- package/dist/{chunk-EM4KNOKG.js → chunk-VVLY6QPI.js} +202 -189
- package/dist/chunk-VVLY6QPI.js.map +1 -0
- package/dist/compat.js +2 -2
- package/dist/compat.mjs +1 -1
- package/dist/fireblocks-FLKRTJU3.js +67 -0
- package/dist/fireblocks-FLKRTJU3.js.map +1 -0
- package/dist/fireblocks-KCJV3GEK.mjs +65 -0
- package/dist/fireblocks-KCJV3GEK.mjs.map +1 -0
- package/dist/headless.d.mts +6 -11
- package/dist/headless.d.ts +6 -11
- package/dist/headless.js +147 -147
- package/dist/headless.mjs +4 -4
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +188 -188
- package/dist/index.mjs +5 -5
- package/dist/privy-6LYDE24Z.mjs +64 -0
- package/dist/privy-6LYDE24Z.mjs.map +1 -0
- package/dist/privy-ID4YFTKR.js +66 -0
- package/dist/privy-ID4YFTKR.js.map +1 -0
- package/dist/protocol-G-z1lRqo.d.mts +166 -0
- package/dist/protocol-G-z1lRqo.d.ts +166 -0
- package/dist/react.d.mts +18 -6
- package/dist/react.d.ts +18 -6
- package/dist/react.js +49 -49
- package/dist/react.mjs +2 -2
- package/dist/remote.d.mts +46 -0
- package/dist/remote.d.ts +46 -0
- package/dist/remote.js +212 -0
- package/dist/remote.js.map +1 -0
- package/dist/remote.mjs +198 -0
- package/dist/remote.mjs.map +1 -0
- package/dist/server.d.mts +141 -0
- package/dist/server.d.ts +141 -0
- package/dist/server.js +189 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +187 -0
- package/dist/server.mjs.map +1 -0
- package/dist/{standard-shim-tmnQelaJ.d.ts → standard-shim-Cg6fmjK_.d.ts} +10 -0
- package/dist/{standard-shim-CGB88PPO.d.mts → standard-shim-Cz4UNS7t.d.mts} +10 -0
- package/dist/{walletconnect-447EY3OJ.js → walletconnect-F2M3PAAN.js} +8 -8
- package/dist/{walletconnect-447EY3OJ.js.map → walletconnect-F2M3PAAN.js.map} +1 -1
- package/dist/walletconnect-Z6LPGALR.mjs +3 -0
- package/dist/{walletconnect-U455PO4I.mjs.map → walletconnect-Z6LPGALR.mjs.map} +1 -1
- package/package.json +27 -2
- package/dist/chunk-BZ2VBJCZ.js.map +0 -1
- package/dist/chunk-EM4KNOKG.js.map +0 -1
- package/dist/chunk-IDTUFDNB.mjs.map +0 -1
- package/dist/chunk-RTXUS5KG.mjs.map +0 -1
- package/dist/chunk-SITQ4JWM.js.map +0 -1
- package/dist/chunk-ZZTY3O4N.mjs.map +0 -1
- package/dist/walletconnect-U455PO4I.mjs +0 -3
package/dist/server.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkQST7XLMB_js = require('./chunk-QST7XLMB.js');
|
|
4
|
+
|
|
5
|
+
// src/server/route-handlers.ts
|
|
6
|
+
function jsonResponse(data, status = 200) {
|
|
7
|
+
return new Response(JSON.stringify(data), {
|
|
8
|
+
status,
|
|
9
|
+
headers: { "Content-Type": "application/json" }
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function errorResponse(code, message, status = 400, details) {
|
|
13
|
+
return jsonResponse({
|
|
14
|
+
error: { code, message, details }
|
|
15
|
+
}, status);
|
|
16
|
+
}
|
|
17
|
+
function defaultAuthorize(request) {
|
|
18
|
+
let token = process.env.CONNECTOR_SIGNER_TOKEN;
|
|
19
|
+
if (!token)
|
|
20
|
+
return console.warn("[connector/server] CONNECTOR_SIGNER_TOKEN not set - rejecting all requests"), false;
|
|
21
|
+
let authHeader = request.headers.get("Authorization");
|
|
22
|
+
if (!authHeader)
|
|
23
|
+
return false;
|
|
24
|
+
let parts = authHeader.split(" ");
|
|
25
|
+
return (parts.length === 2 && parts[0].toLowerCase() === "bearer" ? parts[1] : authHeader) === token;
|
|
26
|
+
}
|
|
27
|
+
async function sendTransaction(rpc, signedTxBytes) {
|
|
28
|
+
let base64Tx = chunkQST7XLMB_js.encodeBase64(signedTxBytes), result = await (await fetch(rpc.url, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: { "Content-Type": "application/json" },
|
|
31
|
+
body: JSON.stringify({
|
|
32
|
+
jsonrpc: "2.0",
|
|
33
|
+
id: 1,
|
|
34
|
+
method: "sendTransaction",
|
|
35
|
+
params: [
|
|
36
|
+
base64Tx,
|
|
37
|
+
{
|
|
38
|
+
encoding: "base64",
|
|
39
|
+
skipPreflight: false,
|
|
40
|
+
preflightCommitment: rpc.commitment || "confirmed"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
})
|
|
44
|
+
})).json();
|
|
45
|
+
if (result.error)
|
|
46
|
+
throw new Error(result.error.message || "RPC error");
|
|
47
|
+
return result.result;
|
|
48
|
+
}
|
|
49
|
+
var cachedSigner = null;
|
|
50
|
+
async function loadSigner(config) {
|
|
51
|
+
if (cachedSigner)
|
|
52
|
+
return cachedSigner;
|
|
53
|
+
if (config.type === "custom")
|
|
54
|
+
return cachedSigner = config.signer, cachedSigner;
|
|
55
|
+
if (config.type === "fireblocks") {
|
|
56
|
+
let { loadFireblocksSigner } = await import('./fireblocks-FLKRTJU3.js');
|
|
57
|
+
return cachedSigner = await loadFireblocksSigner(config), cachedSigner;
|
|
58
|
+
}
|
|
59
|
+
if (config.type === "privy") {
|
|
60
|
+
let { loadPrivySigner } = await import('./privy-ID4YFTKR.js');
|
|
61
|
+
return cachedSigner = await loadPrivySigner(config), cachedSigner;
|
|
62
|
+
}
|
|
63
|
+
throw new Error(`Unknown provider type: ${config.type}`);
|
|
64
|
+
}
|
|
65
|
+
function createRemoteSignerRouteHandlers(config) {
|
|
66
|
+
let {
|
|
67
|
+
provider,
|
|
68
|
+
authorize = defaultAuthorize,
|
|
69
|
+
policy,
|
|
70
|
+
rpc,
|
|
71
|
+
chains = ["solana:mainnet", "solana:devnet"],
|
|
72
|
+
name = "Remote Signer",
|
|
73
|
+
icon
|
|
74
|
+
} = config;
|
|
75
|
+
async function GET(request) {
|
|
76
|
+
try {
|
|
77
|
+
if (!await authorize(request))
|
|
78
|
+
return errorResponse("UNAUTHORIZED", "Unauthorized", 401);
|
|
79
|
+
let signer = await loadSigner(provider);
|
|
80
|
+
if (!await signer.isAvailable())
|
|
81
|
+
return errorResponse("PROVIDER_ERROR", "Signer not available", 503);
|
|
82
|
+
let capabilities = {
|
|
83
|
+
signTransaction: true,
|
|
84
|
+
signAllTransactions: true,
|
|
85
|
+
signMessage: true,
|
|
86
|
+
signAndSendTransaction: !!rpc
|
|
87
|
+
}, metadata = {
|
|
88
|
+
address: signer.address,
|
|
89
|
+
chains,
|
|
90
|
+
capabilities,
|
|
91
|
+
name,
|
|
92
|
+
icon
|
|
93
|
+
};
|
|
94
|
+
return jsonResponse(metadata);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
return console.error("[connector/server] GET error:", error), errorResponse(
|
|
97
|
+
"INTERNAL_ERROR",
|
|
98
|
+
error instanceof Error ? error.message : "Internal server error",
|
|
99
|
+
500
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function POST(request) {
|
|
104
|
+
try {
|
|
105
|
+
if (!await authorize(request))
|
|
106
|
+
return errorResponse("UNAUTHORIZED", "Unauthorized", 401);
|
|
107
|
+
let body;
|
|
108
|
+
try {
|
|
109
|
+
body = await request.json();
|
|
110
|
+
} catch {
|
|
111
|
+
return errorResponse("INVALID_REQUEST", "Invalid JSON body", 400);
|
|
112
|
+
}
|
|
113
|
+
if (!body.operation)
|
|
114
|
+
return errorResponse("INVALID_REQUEST", "Missing operation field", 400);
|
|
115
|
+
let signer = await loadSigner(provider);
|
|
116
|
+
switch (body.operation) {
|
|
117
|
+
case "signTransaction": {
|
|
118
|
+
if (!body.transaction)
|
|
119
|
+
return errorResponse("INVALID_REQUEST", "Missing transaction field", 400);
|
|
120
|
+
let txBytes = chunkQST7XLMB_js.decodeBase64(body.transaction);
|
|
121
|
+
if (policy?.validateTransaction && !await policy.validateTransaction(txBytes, request))
|
|
122
|
+
return errorResponse("POLICY_VIOLATION", "Transaction rejected by policy", 403);
|
|
123
|
+
let signedTx = await signer.signTransaction(txBytes), response = {
|
|
124
|
+
signedTransaction: chunkQST7XLMB_js.encodeBase64(signedTx)
|
|
125
|
+
};
|
|
126
|
+
return jsonResponse(response);
|
|
127
|
+
}
|
|
128
|
+
case "signAllTransactions": {
|
|
129
|
+
if (!body.transactions || !Array.isArray(body.transactions))
|
|
130
|
+
return errorResponse("INVALID_REQUEST", "Missing transactions array", 400);
|
|
131
|
+
let txBytesArray = body.transactions.map((tx) => chunkQST7XLMB_js.decodeBase64(tx));
|
|
132
|
+
if (policy?.validateTransaction) {
|
|
133
|
+
for (let txBytes of txBytesArray)
|
|
134
|
+
if (!await policy.validateTransaction(txBytes, request))
|
|
135
|
+
return errorResponse("POLICY_VIOLATION", "Transaction rejected by policy", 403);
|
|
136
|
+
}
|
|
137
|
+
let response = {
|
|
138
|
+
signedTransactions: (await signer.signAllTransactions(txBytesArray)).map((tx) => chunkQST7XLMB_js.encodeBase64(tx))
|
|
139
|
+
};
|
|
140
|
+
return jsonResponse(response);
|
|
141
|
+
}
|
|
142
|
+
case "signMessage": {
|
|
143
|
+
if (!body.message)
|
|
144
|
+
return errorResponse("INVALID_REQUEST", "Missing message field", 400);
|
|
145
|
+
let messageBytes = chunkQST7XLMB_js.decodeBase64(body.message);
|
|
146
|
+
if (policy?.validateMessage && !await policy.validateMessage(messageBytes, request))
|
|
147
|
+
return errorResponse("POLICY_VIOLATION", "Message rejected by policy", 403);
|
|
148
|
+
let signature = await signer.signMessage(messageBytes), response = {
|
|
149
|
+
signature: chunkQST7XLMB_js.encodeBase64(signature)
|
|
150
|
+
};
|
|
151
|
+
return jsonResponse(response);
|
|
152
|
+
}
|
|
153
|
+
case "signAndSendTransaction": {
|
|
154
|
+
if (!rpc)
|
|
155
|
+
return errorResponse("INVALID_OPERATION", "signAndSendTransaction not enabled", 400);
|
|
156
|
+
if (!body.transaction)
|
|
157
|
+
return errorResponse("INVALID_REQUEST", "Missing transaction field", 400);
|
|
158
|
+
let txBytes = chunkQST7XLMB_js.decodeBase64(body.transaction);
|
|
159
|
+
if (policy?.validateTransaction && !await policy.validateTransaction(txBytes, request))
|
|
160
|
+
return errorResponse("POLICY_VIOLATION", "Transaction rejected by policy", 403);
|
|
161
|
+
let signedTx = await signer.signTransaction(txBytes);
|
|
162
|
+
try {
|
|
163
|
+
let response = { signature: await sendTransaction(rpc, signedTx) };
|
|
164
|
+
return jsonResponse(response);
|
|
165
|
+
} catch (error) {
|
|
166
|
+
return errorResponse(
|
|
167
|
+
"SEND_FAILED",
|
|
168
|
+
error instanceof Error ? error.message : "Failed to send transaction",
|
|
169
|
+
500
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
default:
|
|
174
|
+
return errorResponse(
|
|
175
|
+
"INVALID_OPERATION",
|
|
176
|
+
`Unknown operation: ${body.operation}`,
|
|
177
|
+
400
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
} catch (error) {
|
|
181
|
+
return console.error("[connector/server] POST error:", error), error instanceof Error && (error.message.includes("rate limit") || error.message.includes("429")) ? errorResponse("PROVIDER_ERROR", "Rate limited, please retry", 429) : errorResponse("SIGNING_FAILED", error instanceof Error ? error.message : "Signing failed", 500);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return { GET, POST };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
exports.createRemoteSignerRouteHandlers = createRemoteSignerRouteHandlers;
|
|
188
|
+
//# sourceMappingURL=server.js.map
|
|
189
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/route-handlers.ts"],"names":["encodeBase64","decodeBase64"],"mappings":"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","file":"server.js","sourcesContent":["/**\n * @solana/connector/server - Route Handlers\n *\n * Creates Next.js Route Handler compatible GET/POST handlers for remote signing.\n * Designed for Node.js runtime (not Edge) to support crypto operations.\n */\n\nimport type {\n RemoteSignerMetadata,\n RemoteSignerCapabilities,\n RemoteSignerRequest,\n SignTransactionResponse,\n SignAllTransactionsResponse,\n SignMessageResponse,\n SignAndSendTransactionResponse,\n RemoteSignerErrorCode,\n RemoteSignerErrorResponse,\n} from '../remote/protocol';\nimport { decodeBase64, encodeBase64 } from '../remote/protocol';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Signer interface that providers must implement\n */\nexport interface RemoteSigner {\n /** Signer's Solana address (base58) */\n readonly address: string;\n\n /** Sign transaction bytes, return signed transaction bytes */\n signTransaction(transactionBytes: Uint8Array): Promise<Uint8Array>;\n\n /** Sign multiple transactions */\n signAllTransactions(transactions: Uint8Array[]): Promise<Uint8Array[]>;\n\n /** Sign a message, return 64-byte signature */\n signMessage(message: Uint8Array): Promise<Uint8Array>;\n\n /** Check if signer is available/healthy */\n isAvailable(): Promise<boolean>;\n}\n\n/**\n * Provider configuration\n */\nexport type ProviderType = 'fireblocks' | 'privy' | 'custom';\n\nexport interface FireblocksProviderConfig {\n type: 'fireblocks';\n /** Fireblocks API key */\n apiKey: string;\n /** RSA private key PEM for JWT signing */\n privateKeyPem: string;\n /** Vault account ID */\n vaultAccountId: string;\n /** Asset ID (default: 'SOL') */\n assetId?: string;\n /** API base URL (default: https://api.fireblocks.io) */\n apiBaseUrl?: string;\n}\n\nexport interface PrivyProviderConfig {\n type: 'privy';\n /** Privy app ID */\n appId: string;\n /** Privy app secret */\n appSecret: string;\n /** Privy wallet ID */\n walletId: string;\n /** API base URL (default: https://api.privy.io/v1) */\n apiBaseUrl?: string;\n}\n\nexport interface CustomProviderConfig {\n type: 'custom';\n /** Custom signer implementation */\n signer: RemoteSigner;\n}\n\nexport type ProviderConfig = FireblocksProviderConfig | PrivyProviderConfig | CustomProviderConfig;\n\n/**\n * Authorization callback\n */\nexport type AuthorizeCallback = (request: Request) => Promise<boolean> | boolean;\n\n/**\n * Policy validation callback\n */\nexport interface PolicyHooks {\n /** Validate a transaction before signing (return false to reject) */\n validateTransaction?: (transactionBytes: Uint8Array, request: Request) => Promise<boolean> | boolean;\n /** Validate a message before signing (return false to reject) */\n validateMessage?: (messageBytes: Uint8Array, request: Request) => Promise<boolean> | boolean;\n}\n\n/**\n * RPC configuration for sign+send operations\n */\nexport interface RpcConfig {\n /** Solana RPC endpoint URL */\n url: string;\n /** Commitment level for confirmations */\n commitment?: 'processed' | 'confirmed' | 'finalized';\n}\n\n/**\n * Route handler configuration\n */\nexport interface RemoteSignerRouteHandlersConfig {\n /** Provider configuration */\n provider: ProviderConfig;\n /**\n * Authorization callback. Default: checks for Authorization header matching CONNECTOR_SIGNER_TOKEN env var.\n * Return true to allow, false to reject.\n */\n authorize?: AuthorizeCallback;\n /** Policy hooks for transaction/message validation */\n policy?: PolicyHooks;\n /** RPC configuration (required for signAndSendTransaction) */\n rpc?: RpcConfig;\n /** Supported chains (default: ['solana:mainnet', 'solana:devnet']) */\n chains?: string[];\n /** Wallet name shown in metadata */\n name?: string;\n /** Wallet icon URL */\n icon?: string;\n}\n\n/**\n * Next.js compatible request/response types\n */\nexport interface NextRequest extends Request {\n // Next.js adds extra properties, but we only need standard Request\n}\n\nexport type NextResponse = Response;\n\n/**\n * Route handlers object returned by the factory\n */\nexport interface RouteHandlers {\n GET: (request: NextRequest) => Promise<NextResponse>;\n POST: (request: NextRequest) => Promise<NextResponse>;\n}\n\n// ============================================================================\n// Helper functions\n// ============================================================================\n\nfunction jsonResponse(data: unknown, status = 200): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\nfunction errorResponse(code: RemoteSignerErrorCode, message: string, status = 400, details?: unknown): Response {\n const body: RemoteSignerErrorResponse = {\n error: { code, message, details },\n };\n return jsonResponse(body, status);\n}\n\n/**\n * Default authorization: check Authorization header against CONNECTOR_SIGNER_TOKEN env var\n */\nfunction defaultAuthorize(request: Request): boolean {\n const token = process.env.CONNECTOR_SIGNER_TOKEN;\n if (!token) {\n // No token configured = reject all (safe default)\n console.warn('[connector/server] CONNECTOR_SIGNER_TOKEN not set - rejecting all requests');\n return false;\n }\n\n const authHeader = request.headers.get('Authorization');\n if (!authHeader) {\n return false;\n }\n\n // Support \"Bearer <token>\" format\n const parts = authHeader.split(' ');\n const providedToken = parts.length === 2 && parts[0].toLowerCase() === 'bearer' ? parts[1] : authHeader;\n\n return providedToken === token;\n}\n\n/**\n * Send a signed transaction to Solana RPC\n */\nasync function sendTransaction(rpc: RpcConfig, signedTxBytes: Uint8Array): Promise<string> {\n const base64Tx = encodeBase64(signedTxBytes);\n\n const response = await fetch(rpc.url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n jsonrpc: '2.0',\n id: 1,\n method: 'sendTransaction',\n params: [\n base64Tx,\n {\n encoding: 'base64',\n skipPreflight: false,\n preflightCommitment: rpc.commitment || 'confirmed',\n },\n ],\n }),\n });\n\n const result = await response.json();\n\n if (result.error) {\n throw new Error(result.error.message || 'RPC error');\n }\n\n return result.result as string;\n}\n\n// ============================================================================\n// Provider loader\n// ============================================================================\n\nlet cachedSigner: RemoteSigner | null = null;\n\nasync function loadSigner(config: ProviderConfig): Promise<RemoteSigner> {\n if (cachedSigner) {\n return cachedSigner;\n }\n\n if (config.type === 'custom') {\n cachedSigner = config.signer;\n return cachedSigner;\n }\n\n if (config.type === 'fireblocks') {\n const { loadFireblocksSigner } = await import('./providers/fireblocks');\n cachedSigner = await loadFireblocksSigner(config);\n return cachedSigner;\n }\n\n if (config.type === 'privy') {\n const { loadPrivySigner } = await import('./providers/privy');\n cachedSigner = await loadPrivySigner(config);\n return cachedSigner;\n }\n\n throw new Error(`Unknown provider type: ${(config as ProviderConfig).type}`);\n}\n\n// ============================================================================\n// Route Handler Factory\n// ============================================================================\n\n/**\n * Create Next.js Route Handlers for remote signing\n *\n * @example\n * ```typescript\n * // app/api/connector-signer/route.ts\n * import { createRemoteSignerRouteHandlers } from '@solana/connector/server';\n *\n * const { GET, POST } = createRemoteSignerRouteHandlers({\n * provider: {\n * type: 'fireblocks',\n * apiKey: process.env.FIREBLOCKS_API_KEY!,\n * privateKeyPem: process.env.FIREBLOCKS_PRIVATE_KEY!,\n * vaultAccountId: process.env.FIREBLOCKS_VAULT_ID!,\n * },\n * rpc: {\n * url: process.env.SOLANA_RPC_URL!,\n * },\n * });\n *\n * export { GET, POST };\n * export const runtime = 'nodejs';\n * ```\n */\nexport function createRemoteSignerRouteHandlers(config: RemoteSignerRouteHandlersConfig): RouteHandlers {\n const {\n provider,\n authorize = defaultAuthorize,\n policy,\n rpc,\n chains = ['solana:mainnet', 'solana:devnet'],\n name = 'Remote Signer',\n icon,\n } = config;\n\n // GET handler: return signer metadata\n async function GET(request: NextRequest): Promise<NextResponse> {\n try {\n // Check authorization\n const authorized = await authorize(request);\n if (!authorized) {\n return errorResponse('UNAUTHORIZED', 'Unauthorized', 401);\n }\n\n // Load signer\n const signer = await loadSigner(provider);\n\n // Check availability\n const available = await signer.isAvailable();\n if (!available) {\n return errorResponse('PROVIDER_ERROR', 'Signer not available', 503);\n }\n\n // Build capabilities\n const capabilities: RemoteSignerCapabilities = {\n signTransaction: true,\n signAllTransactions: true,\n signMessage: true,\n signAndSendTransaction: Boolean(rpc),\n };\n\n // Build metadata response\n const metadata: RemoteSignerMetadata = {\n address: signer.address,\n chains,\n capabilities,\n name,\n icon,\n };\n\n return jsonResponse(metadata);\n } catch (error) {\n console.error('[connector/server] GET error:', error);\n return errorResponse(\n 'INTERNAL_ERROR',\n error instanceof Error ? error.message : 'Internal server error',\n 500,\n );\n }\n }\n\n // POST handler: handle signing operations\n async function POST(request: NextRequest): Promise<NextResponse> {\n try {\n // Check authorization\n const authorized = await authorize(request);\n if (!authorized) {\n return errorResponse('UNAUTHORIZED', 'Unauthorized', 401);\n }\n\n // Parse request body\n let body: RemoteSignerRequest;\n try {\n body = await request.json();\n } catch {\n return errorResponse('INVALID_REQUEST', 'Invalid JSON body', 400);\n }\n\n if (!body.operation) {\n return errorResponse('INVALID_REQUEST', 'Missing operation field', 400);\n }\n\n // Load signer\n const signer = await loadSigner(provider);\n\n // Handle operations\n switch (body.operation) {\n case 'signTransaction': {\n if (!body.transaction) {\n return errorResponse('INVALID_REQUEST', 'Missing transaction field', 400);\n }\n\n const txBytes = decodeBase64(body.transaction);\n\n // Policy check\n if (policy?.validateTransaction) {\n const allowed = await policy.validateTransaction(txBytes, request);\n if (!allowed) {\n return errorResponse('POLICY_VIOLATION', 'Transaction rejected by policy', 403);\n }\n }\n\n const signedTx = await signer.signTransaction(txBytes);\n const response: SignTransactionResponse = {\n signedTransaction: encodeBase64(signedTx),\n };\n return jsonResponse(response);\n }\n\n case 'signAllTransactions': {\n if (!body.transactions || !Array.isArray(body.transactions)) {\n return errorResponse('INVALID_REQUEST', 'Missing transactions array', 400);\n }\n\n const txBytesArray = body.transactions.map(tx => decodeBase64(tx));\n\n // Policy check for each transaction\n if (policy?.validateTransaction) {\n for (const txBytes of txBytesArray) {\n const allowed = await policy.validateTransaction(txBytes, request);\n if (!allowed) {\n return errorResponse('POLICY_VIOLATION', 'Transaction rejected by policy', 403);\n }\n }\n }\n\n const signedTxs = await signer.signAllTransactions(txBytesArray);\n const response: SignAllTransactionsResponse = {\n signedTransactions: signedTxs.map(tx => encodeBase64(tx)),\n };\n return jsonResponse(response);\n }\n\n case 'signMessage': {\n if (!body.message) {\n return errorResponse('INVALID_REQUEST', 'Missing message field', 400);\n }\n\n const messageBytes = decodeBase64(body.message);\n\n // Policy check\n if (policy?.validateMessage) {\n const allowed = await policy.validateMessage(messageBytes, request);\n if (!allowed) {\n return errorResponse('POLICY_VIOLATION', 'Message rejected by policy', 403);\n }\n }\n\n const signature = await signer.signMessage(messageBytes);\n const response: SignMessageResponse = {\n signature: encodeBase64(signature),\n };\n return jsonResponse(response);\n }\n\n case 'signAndSendTransaction': {\n if (!rpc) {\n return errorResponse('INVALID_OPERATION', 'signAndSendTransaction not enabled', 400);\n }\n\n if (!body.transaction) {\n return errorResponse('INVALID_REQUEST', 'Missing transaction field', 400);\n }\n\n const txBytes = decodeBase64(body.transaction);\n\n // Policy check\n if (policy?.validateTransaction) {\n const allowed = await policy.validateTransaction(txBytes, request);\n if (!allowed) {\n return errorResponse('POLICY_VIOLATION', 'Transaction rejected by policy', 403);\n }\n }\n\n // Sign\n const signedTx = await signer.signTransaction(txBytes);\n\n // Send\n try {\n const signature = await sendTransaction(rpc, signedTx);\n const response: SignAndSendTransactionResponse = { signature };\n return jsonResponse(response);\n } catch (error) {\n return errorResponse(\n 'SEND_FAILED',\n error instanceof Error ? error.message : 'Failed to send transaction',\n 500,\n );\n }\n }\n\n default:\n return errorResponse(\n 'INVALID_OPERATION',\n `Unknown operation: ${(body as { operation: string }).operation}`,\n 400,\n );\n }\n } catch (error) {\n console.error('[connector/server] POST error:', error);\n\n // Check for known signer errors\n if (error instanceof Error) {\n if (error.message.includes('rate limit') || error.message.includes('429')) {\n return errorResponse('PROVIDER_ERROR', 'Rate limited, please retry', 429);\n }\n }\n\n return errorResponse('SIGNING_FAILED', error instanceof Error ? error.message : 'Signing failed', 500);\n }\n }\n\n return { GET, POST };\n}\n"]}
|
package/dist/server.mjs
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { decodeBase64, encodeBase64 } from './chunk-E3DAIOYS.mjs';
|
|
2
|
+
|
|
3
|
+
// src/server/route-handlers.ts
|
|
4
|
+
function jsonResponse(data, status = 200) {
|
|
5
|
+
return new Response(JSON.stringify(data), {
|
|
6
|
+
status,
|
|
7
|
+
headers: { "Content-Type": "application/json" }
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
function errorResponse(code, message, status = 400, details) {
|
|
11
|
+
return jsonResponse({
|
|
12
|
+
error: { code, message, details }
|
|
13
|
+
}, status);
|
|
14
|
+
}
|
|
15
|
+
function defaultAuthorize(request) {
|
|
16
|
+
let token = process.env.CONNECTOR_SIGNER_TOKEN;
|
|
17
|
+
if (!token)
|
|
18
|
+
return console.warn("[connector/server] CONNECTOR_SIGNER_TOKEN not set - rejecting all requests"), false;
|
|
19
|
+
let authHeader = request.headers.get("Authorization");
|
|
20
|
+
if (!authHeader)
|
|
21
|
+
return false;
|
|
22
|
+
let parts = authHeader.split(" ");
|
|
23
|
+
return (parts.length === 2 && parts[0].toLowerCase() === "bearer" ? parts[1] : authHeader) === token;
|
|
24
|
+
}
|
|
25
|
+
async function sendTransaction(rpc, signedTxBytes) {
|
|
26
|
+
let base64Tx = encodeBase64(signedTxBytes), result = await (await fetch(rpc.url, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: { "Content-Type": "application/json" },
|
|
29
|
+
body: JSON.stringify({
|
|
30
|
+
jsonrpc: "2.0",
|
|
31
|
+
id: 1,
|
|
32
|
+
method: "sendTransaction",
|
|
33
|
+
params: [
|
|
34
|
+
base64Tx,
|
|
35
|
+
{
|
|
36
|
+
encoding: "base64",
|
|
37
|
+
skipPreflight: false,
|
|
38
|
+
preflightCommitment: rpc.commitment || "confirmed"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
})
|
|
42
|
+
})).json();
|
|
43
|
+
if (result.error)
|
|
44
|
+
throw new Error(result.error.message || "RPC error");
|
|
45
|
+
return result.result;
|
|
46
|
+
}
|
|
47
|
+
var cachedSigner = null;
|
|
48
|
+
async function loadSigner(config) {
|
|
49
|
+
if (cachedSigner)
|
|
50
|
+
return cachedSigner;
|
|
51
|
+
if (config.type === "custom")
|
|
52
|
+
return cachedSigner = config.signer, cachedSigner;
|
|
53
|
+
if (config.type === "fireblocks") {
|
|
54
|
+
let { loadFireblocksSigner } = await import('./fireblocks-KCJV3GEK.mjs');
|
|
55
|
+
return cachedSigner = await loadFireblocksSigner(config), cachedSigner;
|
|
56
|
+
}
|
|
57
|
+
if (config.type === "privy") {
|
|
58
|
+
let { loadPrivySigner } = await import('./privy-6LYDE24Z.mjs');
|
|
59
|
+
return cachedSigner = await loadPrivySigner(config), cachedSigner;
|
|
60
|
+
}
|
|
61
|
+
throw new Error(`Unknown provider type: ${config.type}`);
|
|
62
|
+
}
|
|
63
|
+
function createRemoteSignerRouteHandlers(config) {
|
|
64
|
+
let {
|
|
65
|
+
provider,
|
|
66
|
+
authorize = defaultAuthorize,
|
|
67
|
+
policy,
|
|
68
|
+
rpc,
|
|
69
|
+
chains = ["solana:mainnet", "solana:devnet"],
|
|
70
|
+
name = "Remote Signer",
|
|
71
|
+
icon
|
|
72
|
+
} = config;
|
|
73
|
+
async function GET(request) {
|
|
74
|
+
try {
|
|
75
|
+
if (!await authorize(request))
|
|
76
|
+
return errorResponse("UNAUTHORIZED", "Unauthorized", 401);
|
|
77
|
+
let signer = await loadSigner(provider);
|
|
78
|
+
if (!await signer.isAvailable())
|
|
79
|
+
return errorResponse("PROVIDER_ERROR", "Signer not available", 503);
|
|
80
|
+
let capabilities = {
|
|
81
|
+
signTransaction: true,
|
|
82
|
+
signAllTransactions: true,
|
|
83
|
+
signMessage: true,
|
|
84
|
+
signAndSendTransaction: !!rpc
|
|
85
|
+
}, metadata = {
|
|
86
|
+
address: signer.address,
|
|
87
|
+
chains,
|
|
88
|
+
capabilities,
|
|
89
|
+
name,
|
|
90
|
+
icon
|
|
91
|
+
};
|
|
92
|
+
return jsonResponse(metadata);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
return console.error("[connector/server] GET error:", error), errorResponse(
|
|
95
|
+
"INTERNAL_ERROR",
|
|
96
|
+
error instanceof Error ? error.message : "Internal server error",
|
|
97
|
+
500
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function POST(request) {
|
|
102
|
+
try {
|
|
103
|
+
if (!await authorize(request))
|
|
104
|
+
return errorResponse("UNAUTHORIZED", "Unauthorized", 401);
|
|
105
|
+
let body;
|
|
106
|
+
try {
|
|
107
|
+
body = await request.json();
|
|
108
|
+
} catch {
|
|
109
|
+
return errorResponse("INVALID_REQUEST", "Invalid JSON body", 400);
|
|
110
|
+
}
|
|
111
|
+
if (!body.operation)
|
|
112
|
+
return errorResponse("INVALID_REQUEST", "Missing operation field", 400);
|
|
113
|
+
let signer = await loadSigner(provider);
|
|
114
|
+
switch (body.operation) {
|
|
115
|
+
case "signTransaction": {
|
|
116
|
+
if (!body.transaction)
|
|
117
|
+
return errorResponse("INVALID_REQUEST", "Missing transaction field", 400);
|
|
118
|
+
let txBytes = decodeBase64(body.transaction);
|
|
119
|
+
if (policy?.validateTransaction && !await policy.validateTransaction(txBytes, request))
|
|
120
|
+
return errorResponse("POLICY_VIOLATION", "Transaction rejected by policy", 403);
|
|
121
|
+
let signedTx = await signer.signTransaction(txBytes), response = {
|
|
122
|
+
signedTransaction: encodeBase64(signedTx)
|
|
123
|
+
};
|
|
124
|
+
return jsonResponse(response);
|
|
125
|
+
}
|
|
126
|
+
case "signAllTransactions": {
|
|
127
|
+
if (!body.transactions || !Array.isArray(body.transactions))
|
|
128
|
+
return errorResponse("INVALID_REQUEST", "Missing transactions array", 400);
|
|
129
|
+
let txBytesArray = body.transactions.map((tx) => decodeBase64(tx));
|
|
130
|
+
if (policy?.validateTransaction) {
|
|
131
|
+
for (let txBytes of txBytesArray)
|
|
132
|
+
if (!await policy.validateTransaction(txBytes, request))
|
|
133
|
+
return errorResponse("POLICY_VIOLATION", "Transaction rejected by policy", 403);
|
|
134
|
+
}
|
|
135
|
+
let response = {
|
|
136
|
+
signedTransactions: (await signer.signAllTransactions(txBytesArray)).map((tx) => encodeBase64(tx))
|
|
137
|
+
};
|
|
138
|
+
return jsonResponse(response);
|
|
139
|
+
}
|
|
140
|
+
case "signMessage": {
|
|
141
|
+
if (!body.message)
|
|
142
|
+
return errorResponse("INVALID_REQUEST", "Missing message field", 400);
|
|
143
|
+
let messageBytes = decodeBase64(body.message);
|
|
144
|
+
if (policy?.validateMessage && !await policy.validateMessage(messageBytes, request))
|
|
145
|
+
return errorResponse("POLICY_VIOLATION", "Message rejected by policy", 403);
|
|
146
|
+
let signature = await signer.signMessage(messageBytes), response = {
|
|
147
|
+
signature: encodeBase64(signature)
|
|
148
|
+
};
|
|
149
|
+
return jsonResponse(response);
|
|
150
|
+
}
|
|
151
|
+
case "signAndSendTransaction": {
|
|
152
|
+
if (!rpc)
|
|
153
|
+
return errorResponse("INVALID_OPERATION", "signAndSendTransaction not enabled", 400);
|
|
154
|
+
if (!body.transaction)
|
|
155
|
+
return errorResponse("INVALID_REQUEST", "Missing transaction field", 400);
|
|
156
|
+
let txBytes = decodeBase64(body.transaction);
|
|
157
|
+
if (policy?.validateTransaction && !await policy.validateTransaction(txBytes, request))
|
|
158
|
+
return errorResponse("POLICY_VIOLATION", "Transaction rejected by policy", 403);
|
|
159
|
+
let signedTx = await signer.signTransaction(txBytes);
|
|
160
|
+
try {
|
|
161
|
+
let response = { signature: await sendTransaction(rpc, signedTx) };
|
|
162
|
+
return jsonResponse(response);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
return errorResponse(
|
|
165
|
+
"SEND_FAILED",
|
|
166
|
+
error instanceof Error ? error.message : "Failed to send transaction",
|
|
167
|
+
500
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
default:
|
|
172
|
+
return errorResponse(
|
|
173
|
+
"INVALID_OPERATION",
|
|
174
|
+
`Unknown operation: ${body.operation}`,
|
|
175
|
+
400
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
return console.error("[connector/server] POST error:", error), error instanceof Error && (error.message.includes("rate limit") || error.message.includes("429")) ? errorResponse("PROVIDER_ERROR", "Rate limited, please retry", 429) : errorResponse("SIGNING_FAILED", error instanceof Error ? error.message : "Signing failed", 500);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return { GET, POST };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export { createRemoteSignerRouteHandlers };
|
|
186
|
+
//# sourceMappingURL=server.mjs.map
|
|
187
|
+
//# sourceMappingURL=server.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/route-handlers.ts"],"names":[],"mappings":"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","file":"server.mjs","sourcesContent":["/**\n * @solana/connector/server - Route Handlers\n *\n * Creates Next.js Route Handler compatible GET/POST handlers for remote signing.\n * Designed for Node.js runtime (not Edge) to support crypto operations.\n */\n\nimport type {\n RemoteSignerMetadata,\n RemoteSignerCapabilities,\n RemoteSignerRequest,\n SignTransactionResponse,\n SignAllTransactionsResponse,\n SignMessageResponse,\n SignAndSendTransactionResponse,\n RemoteSignerErrorCode,\n RemoteSignerErrorResponse,\n} from '../remote/protocol';\nimport { decodeBase64, encodeBase64 } from '../remote/protocol';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Signer interface that providers must implement\n */\nexport interface RemoteSigner {\n /** Signer's Solana address (base58) */\n readonly address: string;\n\n /** Sign transaction bytes, return signed transaction bytes */\n signTransaction(transactionBytes: Uint8Array): Promise<Uint8Array>;\n\n /** Sign multiple transactions */\n signAllTransactions(transactions: Uint8Array[]): Promise<Uint8Array[]>;\n\n /** Sign a message, return 64-byte signature */\n signMessage(message: Uint8Array): Promise<Uint8Array>;\n\n /** Check if signer is available/healthy */\n isAvailable(): Promise<boolean>;\n}\n\n/**\n * Provider configuration\n */\nexport type ProviderType = 'fireblocks' | 'privy' | 'custom';\n\nexport interface FireblocksProviderConfig {\n type: 'fireblocks';\n /** Fireblocks API key */\n apiKey: string;\n /** RSA private key PEM for JWT signing */\n privateKeyPem: string;\n /** Vault account ID */\n vaultAccountId: string;\n /** Asset ID (default: 'SOL') */\n assetId?: string;\n /** API base URL (default: https://api.fireblocks.io) */\n apiBaseUrl?: string;\n}\n\nexport interface PrivyProviderConfig {\n type: 'privy';\n /** Privy app ID */\n appId: string;\n /** Privy app secret */\n appSecret: string;\n /** Privy wallet ID */\n walletId: string;\n /** API base URL (default: https://api.privy.io/v1) */\n apiBaseUrl?: string;\n}\n\nexport interface CustomProviderConfig {\n type: 'custom';\n /** Custom signer implementation */\n signer: RemoteSigner;\n}\n\nexport type ProviderConfig = FireblocksProviderConfig | PrivyProviderConfig | CustomProviderConfig;\n\n/**\n * Authorization callback\n */\nexport type AuthorizeCallback = (request: Request) => Promise<boolean> | boolean;\n\n/**\n * Policy validation callback\n */\nexport interface PolicyHooks {\n /** Validate a transaction before signing (return false to reject) */\n validateTransaction?: (transactionBytes: Uint8Array, request: Request) => Promise<boolean> | boolean;\n /** Validate a message before signing (return false to reject) */\n validateMessage?: (messageBytes: Uint8Array, request: Request) => Promise<boolean> | boolean;\n}\n\n/**\n * RPC configuration for sign+send operations\n */\nexport interface RpcConfig {\n /** Solana RPC endpoint URL */\n url: string;\n /** Commitment level for confirmations */\n commitment?: 'processed' | 'confirmed' | 'finalized';\n}\n\n/**\n * Route handler configuration\n */\nexport interface RemoteSignerRouteHandlersConfig {\n /** Provider configuration */\n provider: ProviderConfig;\n /**\n * Authorization callback. Default: checks for Authorization header matching CONNECTOR_SIGNER_TOKEN env var.\n * Return true to allow, false to reject.\n */\n authorize?: AuthorizeCallback;\n /** Policy hooks for transaction/message validation */\n policy?: PolicyHooks;\n /** RPC configuration (required for signAndSendTransaction) */\n rpc?: RpcConfig;\n /** Supported chains (default: ['solana:mainnet', 'solana:devnet']) */\n chains?: string[];\n /** Wallet name shown in metadata */\n name?: string;\n /** Wallet icon URL */\n icon?: string;\n}\n\n/**\n * Next.js compatible request/response types\n */\nexport interface NextRequest extends Request {\n // Next.js adds extra properties, but we only need standard Request\n}\n\nexport type NextResponse = Response;\n\n/**\n * Route handlers object returned by the factory\n */\nexport interface RouteHandlers {\n GET: (request: NextRequest) => Promise<NextResponse>;\n POST: (request: NextRequest) => Promise<NextResponse>;\n}\n\n// ============================================================================\n// Helper functions\n// ============================================================================\n\nfunction jsonResponse(data: unknown, status = 200): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\nfunction errorResponse(code: RemoteSignerErrorCode, message: string, status = 400, details?: unknown): Response {\n const body: RemoteSignerErrorResponse = {\n error: { code, message, details },\n };\n return jsonResponse(body, status);\n}\n\n/**\n * Default authorization: check Authorization header against CONNECTOR_SIGNER_TOKEN env var\n */\nfunction defaultAuthorize(request: Request): boolean {\n const token = process.env.CONNECTOR_SIGNER_TOKEN;\n if (!token) {\n // No token configured = reject all (safe default)\n console.warn('[connector/server] CONNECTOR_SIGNER_TOKEN not set - rejecting all requests');\n return false;\n }\n\n const authHeader = request.headers.get('Authorization');\n if (!authHeader) {\n return false;\n }\n\n // Support \"Bearer <token>\" format\n const parts = authHeader.split(' ');\n const providedToken = parts.length === 2 && parts[0].toLowerCase() === 'bearer' ? parts[1] : authHeader;\n\n return providedToken === token;\n}\n\n/**\n * Send a signed transaction to Solana RPC\n */\nasync function sendTransaction(rpc: RpcConfig, signedTxBytes: Uint8Array): Promise<string> {\n const base64Tx = encodeBase64(signedTxBytes);\n\n const response = await fetch(rpc.url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n jsonrpc: '2.0',\n id: 1,\n method: 'sendTransaction',\n params: [\n base64Tx,\n {\n encoding: 'base64',\n skipPreflight: false,\n preflightCommitment: rpc.commitment || 'confirmed',\n },\n ],\n }),\n });\n\n const result = await response.json();\n\n if (result.error) {\n throw new Error(result.error.message || 'RPC error');\n }\n\n return result.result as string;\n}\n\n// ============================================================================\n// Provider loader\n// ============================================================================\n\nlet cachedSigner: RemoteSigner | null = null;\n\nasync function loadSigner(config: ProviderConfig): Promise<RemoteSigner> {\n if (cachedSigner) {\n return cachedSigner;\n }\n\n if (config.type === 'custom') {\n cachedSigner = config.signer;\n return cachedSigner;\n }\n\n if (config.type === 'fireblocks') {\n const { loadFireblocksSigner } = await import('./providers/fireblocks');\n cachedSigner = await loadFireblocksSigner(config);\n return cachedSigner;\n }\n\n if (config.type === 'privy') {\n const { loadPrivySigner } = await import('./providers/privy');\n cachedSigner = await loadPrivySigner(config);\n return cachedSigner;\n }\n\n throw new Error(`Unknown provider type: ${(config as ProviderConfig).type}`);\n}\n\n// ============================================================================\n// Route Handler Factory\n// ============================================================================\n\n/**\n * Create Next.js Route Handlers for remote signing\n *\n * @example\n * ```typescript\n * // app/api/connector-signer/route.ts\n * import { createRemoteSignerRouteHandlers } from '@solana/connector/server';\n *\n * const { GET, POST } = createRemoteSignerRouteHandlers({\n * provider: {\n * type: 'fireblocks',\n * apiKey: process.env.FIREBLOCKS_API_KEY!,\n * privateKeyPem: process.env.FIREBLOCKS_PRIVATE_KEY!,\n * vaultAccountId: process.env.FIREBLOCKS_VAULT_ID!,\n * },\n * rpc: {\n * url: process.env.SOLANA_RPC_URL!,\n * },\n * });\n *\n * export { GET, POST };\n * export const runtime = 'nodejs';\n * ```\n */\nexport function createRemoteSignerRouteHandlers(config: RemoteSignerRouteHandlersConfig): RouteHandlers {\n const {\n provider,\n authorize = defaultAuthorize,\n policy,\n rpc,\n chains = ['solana:mainnet', 'solana:devnet'],\n name = 'Remote Signer',\n icon,\n } = config;\n\n // GET handler: return signer metadata\n async function GET(request: NextRequest): Promise<NextResponse> {\n try {\n // Check authorization\n const authorized = await authorize(request);\n if (!authorized) {\n return errorResponse('UNAUTHORIZED', 'Unauthorized', 401);\n }\n\n // Load signer\n const signer = await loadSigner(provider);\n\n // Check availability\n const available = await signer.isAvailable();\n if (!available) {\n return errorResponse('PROVIDER_ERROR', 'Signer not available', 503);\n }\n\n // Build capabilities\n const capabilities: RemoteSignerCapabilities = {\n signTransaction: true,\n signAllTransactions: true,\n signMessage: true,\n signAndSendTransaction: Boolean(rpc),\n };\n\n // Build metadata response\n const metadata: RemoteSignerMetadata = {\n address: signer.address,\n chains,\n capabilities,\n name,\n icon,\n };\n\n return jsonResponse(metadata);\n } catch (error) {\n console.error('[connector/server] GET error:', error);\n return errorResponse(\n 'INTERNAL_ERROR',\n error instanceof Error ? error.message : 'Internal server error',\n 500,\n );\n }\n }\n\n // POST handler: handle signing operations\n async function POST(request: NextRequest): Promise<NextResponse> {\n try {\n // Check authorization\n const authorized = await authorize(request);\n if (!authorized) {\n return errorResponse('UNAUTHORIZED', 'Unauthorized', 401);\n }\n\n // Parse request body\n let body: RemoteSignerRequest;\n try {\n body = await request.json();\n } catch {\n return errorResponse('INVALID_REQUEST', 'Invalid JSON body', 400);\n }\n\n if (!body.operation) {\n return errorResponse('INVALID_REQUEST', 'Missing operation field', 400);\n }\n\n // Load signer\n const signer = await loadSigner(provider);\n\n // Handle operations\n switch (body.operation) {\n case 'signTransaction': {\n if (!body.transaction) {\n return errorResponse('INVALID_REQUEST', 'Missing transaction field', 400);\n }\n\n const txBytes = decodeBase64(body.transaction);\n\n // Policy check\n if (policy?.validateTransaction) {\n const allowed = await policy.validateTransaction(txBytes, request);\n if (!allowed) {\n return errorResponse('POLICY_VIOLATION', 'Transaction rejected by policy', 403);\n }\n }\n\n const signedTx = await signer.signTransaction(txBytes);\n const response: SignTransactionResponse = {\n signedTransaction: encodeBase64(signedTx),\n };\n return jsonResponse(response);\n }\n\n case 'signAllTransactions': {\n if (!body.transactions || !Array.isArray(body.transactions)) {\n return errorResponse('INVALID_REQUEST', 'Missing transactions array', 400);\n }\n\n const txBytesArray = body.transactions.map(tx => decodeBase64(tx));\n\n // Policy check for each transaction\n if (policy?.validateTransaction) {\n for (const txBytes of txBytesArray) {\n const allowed = await policy.validateTransaction(txBytes, request);\n if (!allowed) {\n return errorResponse('POLICY_VIOLATION', 'Transaction rejected by policy', 403);\n }\n }\n }\n\n const signedTxs = await signer.signAllTransactions(txBytesArray);\n const response: SignAllTransactionsResponse = {\n signedTransactions: signedTxs.map(tx => encodeBase64(tx)),\n };\n return jsonResponse(response);\n }\n\n case 'signMessage': {\n if (!body.message) {\n return errorResponse('INVALID_REQUEST', 'Missing message field', 400);\n }\n\n const messageBytes = decodeBase64(body.message);\n\n // Policy check\n if (policy?.validateMessage) {\n const allowed = await policy.validateMessage(messageBytes, request);\n if (!allowed) {\n return errorResponse('POLICY_VIOLATION', 'Message rejected by policy', 403);\n }\n }\n\n const signature = await signer.signMessage(messageBytes);\n const response: SignMessageResponse = {\n signature: encodeBase64(signature),\n };\n return jsonResponse(response);\n }\n\n case 'signAndSendTransaction': {\n if (!rpc) {\n return errorResponse('INVALID_OPERATION', 'signAndSendTransaction not enabled', 400);\n }\n\n if (!body.transaction) {\n return errorResponse('INVALID_REQUEST', 'Missing transaction field', 400);\n }\n\n const txBytes = decodeBase64(body.transaction);\n\n // Policy check\n if (policy?.validateTransaction) {\n const allowed = await policy.validateTransaction(txBytes, request);\n if (!allowed) {\n return errorResponse('POLICY_VIOLATION', 'Transaction rejected by policy', 403);\n }\n }\n\n // Sign\n const signedTx = await signer.signTransaction(txBytes);\n\n // Send\n try {\n const signature = await sendTransaction(rpc, signedTx);\n const response: SignAndSendTransactionResponse = { signature };\n return jsonResponse(response);\n } catch (error) {\n return errorResponse(\n 'SEND_FAILED',\n error instanceof Error ? error.message : 'Failed to send transaction',\n 500,\n );\n }\n }\n\n default:\n return errorResponse(\n 'INVALID_OPERATION',\n `Unknown operation: ${(body as { operation: string }).operation}`,\n 400,\n );\n }\n } catch (error) {\n console.error('[connector/server] POST error:', error);\n\n // Check for known signer errors\n if (error instanceof Error) {\n if (error.message.includes('rate limit') || error.message.includes('429')) {\n return errorResponse('PROVIDER_ERROR', 'Rate limited, please retry', 429);\n }\n }\n\n return errorResponse('SIGNING_FAILED', error instanceof Error ? error.message : 'Signing failed', 500);\n }\n }\n\n return { GET, POST };\n}\n"]}
|
|
@@ -700,6 +700,11 @@ interface ConnectorConfig {
|
|
|
700
700
|
* @see https://docs.walletconnect.network/wallet-sdk/chain-support/solana
|
|
701
701
|
*/
|
|
702
702
|
walletConnect?: WalletConnectConfig;
|
|
703
|
+
/**
|
|
704
|
+
* Additional wallets to include alongside Wallet Standard wallets.
|
|
705
|
+
* Use this to add remote/server-backed signers created via `createRemoteSignerWallet()`.
|
|
706
|
+
*/
|
|
707
|
+
additionalWallets?: Wallet[];
|
|
703
708
|
}
|
|
704
709
|
/**
|
|
705
710
|
* Health check information for connector diagnostics
|
|
@@ -991,6 +996,11 @@ interface DefaultConfigOptions {
|
|
|
991
996
|
* @see https://docs.walletconnect.network/wallet-sdk/chain-support/solana
|
|
992
997
|
*/
|
|
993
998
|
walletConnect?: boolean | SimplifiedWalletConnectConfig;
|
|
999
|
+
/**
|
|
1000
|
+
* Additional wallets to include alongside Wallet Standard wallets.
|
|
1001
|
+
* Use this to add remote/server-backed signers created via `createRemoteSignerWallet()`.
|
|
1002
|
+
*/
|
|
1003
|
+
additionalWallets?: Wallet[];
|
|
994
1004
|
}
|
|
995
1005
|
/**
|
|
996
1006
|
* Simplified WalletConnect configuration
|
|
@@ -700,6 +700,11 @@ interface ConnectorConfig {
|
|
|
700
700
|
* @see https://docs.walletconnect.network/wallet-sdk/chain-support/solana
|
|
701
701
|
*/
|
|
702
702
|
walletConnect?: WalletConnectConfig;
|
|
703
|
+
/**
|
|
704
|
+
* Additional wallets to include alongside Wallet Standard wallets.
|
|
705
|
+
* Use this to add remote/server-backed signers created via `createRemoteSignerWallet()`.
|
|
706
|
+
*/
|
|
707
|
+
additionalWallets?: Wallet[];
|
|
703
708
|
}
|
|
704
709
|
/**
|
|
705
710
|
* Health check information for connector diagnostics
|
|
@@ -991,6 +996,11 @@ interface DefaultConfigOptions {
|
|
|
991
996
|
* @see https://docs.walletconnect.network/wallet-sdk/chain-support/solana
|
|
992
997
|
*/
|
|
993
998
|
walletConnect?: boolean | SimplifiedWalletConnectConfig;
|
|
999
|
+
/**
|
|
1000
|
+
* Additional wallets to include alongside Wallet Standard wallets.
|
|
1001
|
+
* Use this to add remote/server-backed signers created via `createRemoteSignerWallet()`.
|
|
1002
|
+
*/
|
|
1003
|
+
additionalWallets?: Wallet[];
|
|
994
1004
|
}
|
|
995
1005
|
/**
|
|
996
1006
|
* Simplified WalletConnect configuration
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkNLPEO5GT_js = require('./chunk-NLPEO5GT.js');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
Object.defineProperty(exports, "createMockWalletConnectTransport", {
|
|
8
8
|
enumerable: true,
|
|
9
|
-
get: function () { return
|
|
9
|
+
get: function () { return chunkNLPEO5GT_js.createMockWalletConnectTransport; }
|
|
10
10
|
});
|
|
11
11
|
Object.defineProperty(exports, "createWalletConnectTransport", {
|
|
12
12
|
enumerable: true,
|
|
13
|
-
get: function () { return
|
|
13
|
+
get: function () { return chunkNLPEO5GT_js.createWalletConnectTransport; }
|
|
14
14
|
});
|
|
15
15
|
Object.defineProperty(exports, "createWalletConnectWallet", {
|
|
16
16
|
enumerable: true,
|
|
17
|
-
get: function () { return
|
|
17
|
+
get: function () { return chunkNLPEO5GT_js.createWalletConnectWallet; }
|
|
18
18
|
});
|
|
19
19
|
Object.defineProperty(exports, "isWalletConnectAvailable", {
|
|
20
20
|
enumerable: true,
|
|
21
|
-
get: function () { return
|
|
21
|
+
get: function () { return chunkNLPEO5GT_js.isWalletConnectAvailable; }
|
|
22
22
|
});
|
|
23
23
|
Object.defineProperty(exports, "registerWalletConnectWallet", {
|
|
24
24
|
enumerable: true,
|
|
25
|
-
get: function () { return
|
|
25
|
+
get: function () { return chunkNLPEO5GT_js.registerWalletConnectWallet; }
|
|
26
26
|
});
|
|
27
|
-
//# sourceMappingURL=walletconnect-
|
|
28
|
-
//# sourceMappingURL=walletconnect-
|
|
27
|
+
//# sourceMappingURL=walletconnect-F2M3PAAN.js.map
|
|
28
|
+
//# sourceMappingURL=walletconnect-F2M3PAAN.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"walletconnect-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"walletconnect-F2M3PAAN.js"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { createMockWalletConnectTransport, createWalletConnectTransport, createWalletConnectWallet, isWalletConnectAvailable, registerWalletConnectWallet } from './chunk-A7WQ3K75.mjs';
|
|
2
|
+
//# sourceMappingURL=walletconnect-Z6LPGALR.mjs.map
|
|
3
|
+
//# sourceMappingURL=walletconnect-Z6LPGALR.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"walletconnect-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"walletconnect-Z6LPGALR.mjs"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solana/connector",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Headless wallet connector client and React provider built on Wallet Standard",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -26,6 +26,16 @@
|
|
|
26
26
|
"types": "./dist/compat.d.ts",
|
|
27
27
|
"import": "./dist/compat.mjs",
|
|
28
28
|
"require": "./dist/compat.js"
|
|
29
|
+
},
|
|
30
|
+
"./remote": {
|
|
31
|
+
"types": "./dist/remote.d.ts",
|
|
32
|
+
"import": "./dist/remote.mjs",
|
|
33
|
+
"require": "./dist/remote.js"
|
|
34
|
+
},
|
|
35
|
+
"./server": {
|
|
36
|
+
"types": "./dist/server.d.ts",
|
|
37
|
+
"import": "./dist/server.mjs",
|
|
38
|
+
"require": "./dist/server.js"
|
|
29
39
|
}
|
|
30
40
|
},
|
|
31
41
|
"files": [
|
|
@@ -49,7 +59,13 @@
|
|
|
49
59
|
"peerDependencies": {
|
|
50
60
|
"@solana/web3.js": "^1.0.0",
|
|
51
61
|
"react": ">=18.0.0",
|
|
52
|
-
"@walletconnect/universal-provider": "^2.0.0"
|
|
62
|
+
"@walletconnect/universal-provider": "^2.0.0",
|
|
63
|
+
"@solana/keychain": "^0.2.1",
|
|
64
|
+
"@solana/keychain-fireblocks": "^0.2.1",
|
|
65
|
+
"@solana/keychain-privy": "^0.2.1",
|
|
66
|
+
"@solana/keychain-turnkey": "^0.2.1",
|
|
67
|
+
"@solana/keychain-vault": "^0.2.1",
|
|
68
|
+
"@solana/keychain-aws-kms": "^0.2.1"
|
|
53
69
|
},
|
|
54
70
|
"peerDependenciesMeta": {
|
|
55
71
|
"react": {
|
|
@@ -63,6 +79,15 @@
|
|
|
63
79
|
},
|
|
64
80
|
"@solana/web3.js": {
|
|
65
81
|
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"@solana/keychain": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"@solana/keychain-fireblocks": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"@solana/keychain-privy": {
|
|
90
|
+
"optional": true
|
|
66
91
|
}
|
|
67
92
|
},
|
|
68
93
|
"dependencies": {
|