cilantro-react 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/README.md +647 -0
- package/dist/index.d.mts +616 -0
- package/dist/index.d.ts +616 -0
- package/dist/index.js +2867 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2837 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +51 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2837 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
// src/providers/CilantroProvider.tsx
|
|
4
|
+
import { useEffect as useEffect3 } from "react";
|
|
5
|
+
|
|
6
|
+
// src/providers/CilantroAuthProvider.tsx
|
|
7
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
8
|
+
import { clearAuth } from "cilantro-sdk";
|
|
9
|
+
import { login as cilantroLogin } from "cilantro-sdk/auth";
|
|
10
|
+
import { create as createUser } from "cilantro-sdk/user";
|
|
11
|
+
|
|
12
|
+
// src/core/auth.ts
|
|
13
|
+
import { setAuth } from "cilantro-sdk";
|
|
14
|
+
var platformApiKey = "";
|
|
15
|
+
var apiUrl;
|
|
16
|
+
function setAuthConfig(config) {
|
|
17
|
+
platformApiKey = config.platformApiKey;
|
|
18
|
+
apiUrl = config.apiUrl;
|
|
19
|
+
const authPayload = {};
|
|
20
|
+
if (config.jwt) {
|
|
21
|
+
authPayload.jwt = config.jwt;
|
|
22
|
+
}
|
|
23
|
+
if (platformApiKey) {
|
|
24
|
+
authPayload.platformApiKey = platformApiKey;
|
|
25
|
+
}
|
|
26
|
+
if (apiUrl) {
|
|
27
|
+
authPayload.apiUrl = apiUrl;
|
|
28
|
+
}
|
|
29
|
+
setAuth(authPayload);
|
|
30
|
+
}
|
|
31
|
+
function setSdkAuth(jwt) {
|
|
32
|
+
const authConfig = {};
|
|
33
|
+
if (jwt) {
|
|
34
|
+
authConfig.jwt = jwt;
|
|
35
|
+
}
|
|
36
|
+
if (platformApiKey) {
|
|
37
|
+
authConfig.platformApiKey = platformApiKey;
|
|
38
|
+
}
|
|
39
|
+
if (apiUrl) {
|
|
40
|
+
authConfig.apiUrl = apiUrl;
|
|
41
|
+
}
|
|
42
|
+
setAuth(authConfig);
|
|
43
|
+
}
|
|
44
|
+
function getPlatformApiKey() {
|
|
45
|
+
return platformApiKey;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/core/types.ts
|
|
49
|
+
function extractResponseData(response) {
|
|
50
|
+
if (!response) return null;
|
|
51
|
+
if (typeof response !== "object" || response === null) {
|
|
52
|
+
return response;
|
|
53
|
+
}
|
|
54
|
+
if ("data" in response) {
|
|
55
|
+
const data = response.data;
|
|
56
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
57
|
+
const dataObj = data;
|
|
58
|
+
if ("authenticationSigners" in dataObj || "onChainSigners" in dataObj) {
|
|
59
|
+
const authSigners = Array.isArray(dataObj.authenticationSigners) ? dataObj.authenticationSigners : [];
|
|
60
|
+
const onChainSigners = Array.isArray(dataObj.onChainSigners) ? dataObj.onChainSigners : [];
|
|
61
|
+
return [...authSigners, ...onChainSigners];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return data;
|
|
65
|
+
}
|
|
66
|
+
if ("success" in response && "data" in response) {
|
|
67
|
+
const data = response.data;
|
|
68
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
69
|
+
const dataObj = data;
|
|
70
|
+
if ("authenticationSigners" in dataObj || "onChainSigners" in dataObj) {
|
|
71
|
+
const authSigners = Array.isArray(dataObj.authenticationSigners) ? dataObj.authenticationSigners : [];
|
|
72
|
+
const onChainSigners = Array.isArray(dataObj.onChainSigners) ? dataObj.onChainSigners : [];
|
|
73
|
+
return [...authSigners, ...onChainSigners];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return data;
|
|
77
|
+
}
|
|
78
|
+
if ("authenticationSigners" in response || "onChainSigners" in response) {
|
|
79
|
+
const responseObj = response;
|
|
80
|
+
const authSigners = Array.isArray(responseObj.authenticationSigners) ? responseObj.authenticationSigners : [];
|
|
81
|
+
const onChainSigners = Array.isArray(responseObj.onChainSigners) ? responseObj.onChainSigners : [];
|
|
82
|
+
return [...authSigners, ...onChainSigners];
|
|
83
|
+
}
|
|
84
|
+
return response;
|
|
85
|
+
}
|
|
86
|
+
function extractOnChainSigners(response) {
|
|
87
|
+
if (!response || typeof response !== "object" || response === null) return [];
|
|
88
|
+
let signersObj = null;
|
|
89
|
+
if ("data" in response) {
|
|
90
|
+
const data = response.data;
|
|
91
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
92
|
+
const dataObj = data;
|
|
93
|
+
if ("authenticationSigners" in dataObj || "onChainSigners" in dataObj) signersObj = dataObj;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (!signersObj && "success" in response && "data" in response) {
|
|
97
|
+
const data = response.data;
|
|
98
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
99
|
+
const dataObj = data;
|
|
100
|
+
if ("authenticationSigners" in dataObj || "onChainSigners" in dataObj) signersObj = dataObj;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (!signersObj && ("authenticationSigners" in response || "onChainSigners" in response)) {
|
|
104
|
+
signersObj = response;
|
|
105
|
+
}
|
|
106
|
+
if (signersObj) {
|
|
107
|
+
return Array.isArray(signersObj.onChainSigners) ? signersObj.onChainSigners : [];
|
|
108
|
+
}
|
|
109
|
+
if (Array.isArray(response)) return response;
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
function extractAuthenticationSigners(response) {
|
|
113
|
+
if (!response || typeof response !== "object" || response === null) return [];
|
|
114
|
+
let signersObj = null;
|
|
115
|
+
if ("data" in response) {
|
|
116
|
+
const data = response.data;
|
|
117
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
118
|
+
const dataObj = data;
|
|
119
|
+
if ("authenticationSigners" in dataObj || "onChainSigners" in dataObj) signersObj = dataObj;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!signersObj && "success" in response && "data" in response) {
|
|
123
|
+
const data = response.data;
|
|
124
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
125
|
+
const dataObj = data;
|
|
126
|
+
if ("authenticationSigners" in dataObj || "onChainSigners" in dataObj) signersObj = dataObj;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (!signersObj && ("authenticationSigners" in response || "onChainSigners" in response)) {
|
|
130
|
+
signersObj = response;
|
|
131
|
+
}
|
|
132
|
+
if (signersObj) {
|
|
133
|
+
return Array.isArray(signersObj.authenticationSigners) ? signersObj.authenticationSigners : [];
|
|
134
|
+
}
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
function isErrorWithResponse(error) {
|
|
138
|
+
return typeof error === "object" && error !== null && "response" in error;
|
|
139
|
+
}
|
|
140
|
+
function isErrorWithMessage(error) {
|
|
141
|
+
return typeof error === "object" && error !== null && "message" in error;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/core/error-utils.ts
|
|
145
|
+
function extractErrorMessage(error) {
|
|
146
|
+
if (!error) return "An unknown error occurred";
|
|
147
|
+
if (error instanceof Error) return error.message;
|
|
148
|
+
if (isErrorWithResponse(error)) {
|
|
149
|
+
if (error.response?.data?.message) return String(error.response.data.message);
|
|
150
|
+
if (error.response?.data?.error) return String(error.response.data.error);
|
|
151
|
+
}
|
|
152
|
+
if (isErrorWithMessage(error)) return String(error.message);
|
|
153
|
+
if (typeof error === "object" && error !== null && "error" in error) {
|
|
154
|
+
return String(error.error);
|
|
155
|
+
}
|
|
156
|
+
return String(error);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/providers/CilantroAuthProvider.tsx
|
|
160
|
+
import { jsx } from "react/jsx-runtime";
|
|
161
|
+
var AuthContext = createContext(void 0);
|
|
162
|
+
var DEFAULT_JWT_STORAGE_KEY = "cilantro_jwt";
|
|
163
|
+
function CilantroAuthProvider({
|
|
164
|
+
children,
|
|
165
|
+
platformApiKey: platformApiKey2,
|
|
166
|
+
apiUrl: apiUrl2,
|
|
167
|
+
jwtStorageKey = DEFAULT_JWT_STORAGE_KEY,
|
|
168
|
+
onLoginSuccess,
|
|
169
|
+
onLogout,
|
|
170
|
+
onRegisterSuccess
|
|
171
|
+
}) {
|
|
172
|
+
const [user, setUser] = useState(null);
|
|
173
|
+
const [token, setToken] = useState(null);
|
|
174
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
setAuthConfig({ platformApiKey: platformApiKey2, apiUrl: apiUrl2 });
|
|
177
|
+
setSdkAuth(null);
|
|
178
|
+
const storedToken = typeof window !== "undefined" ? localStorage.getItem(jwtStorageKey) : null;
|
|
179
|
+
if (storedToken) {
|
|
180
|
+
setToken(storedToken);
|
|
181
|
+
setSdkAuth(storedToken);
|
|
182
|
+
try {
|
|
183
|
+
const payload = JSON.parse(atob(storedToken.split(".")[1]));
|
|
184
|
+
setUser({
|
|
185
|
+
username: payload.username,
|
|
186
|
+
email: payload.email,
|
|
187
|
+
userType: payload.userType
|
|
188
|
+
});
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
setIsLoading(false);
|
|
193
|
+
}, [platformApiKey2, apiUrl2, jwtStorageKey]);
|
|
194
|
+
const login = async (usernameOrEmail, password) => {
|
|
195
|
+
try {
|
|
196
|
+
const result = await cilantroLogin({ usernameOrEmail, password });
|
|
197
|
+
const responseData = extractResponseData(result);
|
|
198
|
+
if (!responseData?.jwt) throw new Error("No JWT token received from server");
|
|
199
|
+
const jwt = responseData.jwt;
|
|
200
|
+
const userType = responseData.userType;
|
|
201
|
+
setToken(jwt);
|
|
202
|
+
setSdkAuth(jwt);
|
|
203
|
+
if (typeof window !== "undefined") {
|
|
204
|
+
localStorage.setItem(jwtStorageKey, jwt);
|
|
205
|
+
document.cookie = `cilantro_jwt=${jwt}; path=/; max-age=${60 * 60 * 24 * 7}`;
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
const payload = JSON.parse(atob(jwt.split(".")[1]));
|
|
209
|
+
setUser({
|
|
210
|
+
username: payload.username,
|
|
211
|
+
email: payload.email,
|
|
212
|
+
userType: userType ?? payload.userType
|
|
213
|
+
});
|
|
214
|
+
} catch {
|
|
215
|
+
setUser({ userType });
|
|
216
|
+
}
|
|
217
|
+
onLoginSuccess?.();
|
|
218
|
+
} catch (error) {
|
|
219
|
+
throw new Error(extractErrorMessage(error));
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
const register = async (username, email, password, isActive = true) => {
|
|
223
|
+
try {
|
|
224
|
+
setSdkAuth(null);
|
|
225
|
+
const platformKey = getPlatformApiKey();
|
|
226
|
+
const userData = { username, email, password, isActive };
|
|
227
|
+
if (platformKey) userData.platformApiKey = platformKey;
|
|
228
|
+
await createUser(userData);
|
|
229
|
+
await login(username, password);
|
|
230
|
+
onRegisterSuccess?.();
|
|
231
|
+
} catch (error) {
|
|
232
|
+
throw new Error(extractErrorMessage(error));
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
const logout = () => {
|
|
236
|
+
setUser(null);
|
|
237
|
+
setToken(null);
|
|
238
|
+
clearAuth();
|
|
239
|
+
if (typeof window !== "undefined") {
|
|
240
|
+
localStorage.removeItem(jwtStorageKey);
|
|
241
|
+
const keys = Object.keys(localStorage);
|
|
242
|
+
keys.forEach((key) => {
|
|
243
|
+
if (key.startsWith("delegated-key-")) localStorage.removeItem(key);
|
|
244
|
+
});
|
|
245
|
+
document.cookie = "cilantro_jwt=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT";
|
|
246
|
+
}
|
|
247
|
+
setSdkAuth(null);
|
|
248
|
+
onLogout?.();
|
|
249
|
+
};
|
|
250
|
+
const value = {
|
|
251
|
+
user,
|
|
252
|
+
token,
|
|
253
|
+
isAuthenticated: !!token,
|
|
254
|
+
login,
|
|
255
|
+
register,
|
|
256
|
+
logout,
|
|
257
|
+
isLoading
|
|
258
|
+
};
|
|
259
|
+
return /* @__PURE__ */ jsx(AuthContext.Provider, { value, children });
|
|
260
|
+
}
|
|
261
|
+
function useCilantroAuth() {
|
|
262
|
+
const context = useContext(AuthContext);
|
|
263
|
+
if (context === void 0) {
|
|
264
|
+
throw new Error("useCilantroAuth must be used within a CilantroAuthProvider");
|
|
265
|
+
}
|
|
266
|
+
return context;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/providers/WalletProvider.tsx
|
|
270
|
+
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect2, useState as useState2 } from "react";
|
|
271
|
+
import { findAll as findAllWallets } from "cilantro-sdk/wallet";
|
|
272
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
273
|
+
var WalletContext = createContext2(void 0);
|
|
274
|
+
var DEFAULT_STORAGE_KEY = "cilantro_selected_wallet_id";
|
|
275
|
+
function WalletProvider({ children, storageKey = DEFAULT_STORAGE_KEY }) {
|
|
276
|
+
const { token } = useCilantroAuth();
|
|
277
|
+
const [wallets, setWallets] = useState2([]);
|
|
278
|
+
const [selectedWallet, setSelectedWallet] = useState2(null);
|
|
279
|
+
const [isLoading, setIsLoading] = useState2(true);
|
|
280
|
+
useEffect2(() => {
|
|
281
|
+
if (token) {
|
|
282
|
+
loadWallets();
|
|
283
|
+
} else {
|
|
284
|
+
setWallets([]);
|
|
285
|
+
setSelectedWallet(null);
|
|
286
|
+
setIsLoading(false);
|
|
287
|
+
}
|
|
288
|
+
}, [token]);
|
|
289
|
+
useEffect2(() => {
|
|
290
|
+
if (wallets.length > 0 && !selectedWallet) {
|
|
291
|
+
const storedWalletId = typeof window !== "undefined" ? localStorage.getItem(storageKey) : null;
|
|
292
|
+
if (storedWalletId) {
|
|
293
|
+
const wallet = wallets.find((w) => w.id === storedWalletId || w.walletId === storedWalletId);
|
|
294
|
+
if (wallet) {
|
|
295
|
+
setSelectedWallet(wallet);
|
|
296
|
+
} else {
|
|
297
|
+
setSelectedWallet(wallets[0]);
|
|
298
|
+
if (typeof window !== "undefined") localStorage.setItem(storageKey, wallets[0].id);
|
|
299
|
+
}
|
|
300
|
+
} else {
|
|
301
|
+
if (wallets.length > 0) {
|
|
302
|
+
setSelectedWallet(wallets[0]);
|
|
303
|
+
if (typeof window !== "undefined") localStorage.setItem(storageKey, wallets[0].id);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
} else if (wallets.length === 0) {
|
|
307
|
+
setSelectedWallet(null);
|
|
308
|
+
if (typeof window !== "undefined") localStorage.removeItem(storageKey);
|
|
309
|
+
}
|
|
310
|
+
}, [wallets, storageKey]);
|
|
311
|
+
const loadWallets = async () => {
|
|
312
|
+
try {
|
|
313
|
+
setIsLoading(true);
|
|
314
|
+
if (token) setSdkAuth(token);
|
|
315
|
+
const result = await findAllWallets();
|
|
316
|
+
const walletsList = extractResponseData(result) ?? [];
|
|
317
|
+
const formattedWallets = Array.isArray(walletsList) ? walletsList.map((wallet) => {
|
|
318
|
+
const w = wallet;
|
|
319
|
+
return {
|
|
320
|
+
id: String(w.walletId ?? w.id ?? ""),
|
|
321
|
+
walletId: String(w.walletId ?? w.id ?? ""),
|
|
322
|
+
walletName: w.walletName ?? "",
|
|
323
|
+
address: w.walletAddress ?? w.address ?? "",
|
|
324
|
+
walletAddress: w.walletAddress ?? w.address ?? "",
|
|
325
|
+
chain: w.chain ?? "solana",
|
|
326
|
+
active: w.active !== false,
|
|
327
|
+
...w
|
|
328
|
+
};
|
|
329
|
+
}) : [];
|
|
330
|
+
setWallets(formattedWallets);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.error("Failed to load wallets:", error);
|
|
333
|
+
setWallets([]);
|
|
334
|
+
} finally {
|
|
335
|
+
setIsLoading(false);
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
const selectWallet = (walletId) => {
|
|
339
|
+
const wallet = wallets.find((w) => w.id === walletId || w.walletId === walletId);
|
|
340
|
+
if (wallet) {
|
|
341
|
+
setSelectedWallet(wallet);
|
|
342
|
+
if (typeof window !== "undefined") localStorage.setItem(storageKey, wallet.id);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
const refreshWallets = async () => {
|
|
346
|
+
await loadWallets();
|
|
347
|
+
};
|
|
348
|
+
const value = {
|
|
349
|
+
selectedWallet,
|
|
350
|
+
wallets,
|
|
351
|
+
selectWallet,
|
|
352
|
+
refreshWallets,
|
|
353
|
+
isLoading
|
|
354
|
+
};
|
|
355
|
+
return /* @__PURE__ */ jsx2(WalletContext.Provider, { value, children });
|
|
356
|
+
}
|
|
357
|
+
function useWallets() {
|
|
358
|
+
const context = useContext2(WalletContext);
|
|
359
|
+
if (context === void 0) {
|
|
360
|
+
throw new Error("useWallets must be used within a WalletProvider");
|
|
361
|
+
}
|
|
362
|
+
return context;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// src/core/storage-adapter.ts
|
|
366
|
+
import { createIndexedDBAdapter } from "cilantro-sdk/helpers";
|
|
367
|
+
var storageAdapterInstance = null;
|
|
368
|
+
var initializationPromise = null;
|
|
369
|
+
async function initializeStorageAdapter() {
|
|
370
|
+
if (storageAdapterInstance) return storageAdapterInstance;
|
|
371
|
+
if (initializationPromise) return initializationPromise;
|
|
372
|
+
if (typeof window === "undefined") {
|
|
373
|
+
throw new Error("Storage adapter can only be initialized in browser environment.");
|
|
374
|
+
}
|
|
375
|
+
initializationPromise = (async () => {
|
|
376
|
+
try {
|
|
377
|
+
storageAdapterInstance = createIndexedDBAdapter();
|
|
378
|
+
return storageAdapterInstance;
|
|
379
|
+
} catch (error) {
|
|
380
|
+
initializationPromise = null;
|
|
381
|
+
throw error;
|
|
382
|
+
}
|
|
383
|
+
})();
|
|
384
|
+
return initializationPromise;
|
|
385
|
+
}
|
|
386
|
+
function getStorageAdapter() {
|
|
387
|
+
if (storageAdapterInstance) return storageAdapterInstance;
|
|
388
|
+
if (typeof window === "undefined") {
|
|
389
|
+
throw new Error("Storage adapter can only be initialized in browser environment.");
|
|
390
|
+
}
|
|
391
|
+
storageAdapterInstance = createIndexedDBAdapter();
|
|
392
|
+
return storageAdapterInstance;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/providers/CilantroProvider.tsx
|
|
396
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
397
|
+
function CilantroProvider({
|
|
398
|
+
children,
|
|
399
|
+
platformApiKey: platformApiKey2,
|
|
400
|
+
apiUrl: apiUrl2,
|
|
401
|
+
jwtStorageKey,
|
|
402
|
+
walletStorageKey,
|
|
403
|
+
onLoginSuccess,
|
|
404
|
+
onLogout,
|
|
405
|
+
onRegisterSuccess
|
|
406
|
+
}) {
|
|
407
|
+
useEffect3(() => {
|
|
408
|
+
initializeStorageAdapter().catch((err) => {
|
|
409
|
+
console.error("[CilantroProvider] Failed to initialize storage adapter:", err);
|
|
410
|
+
});
|
|
411
|
+
}, []);
|
|
412
|
+
return /* @__PURE__ */ jsx3(
|
|
413
|
+
CilantroAuthProvider,
|
|
414
|
+
{
|
|
415
|
+
platformApiKey: platformApiKey2,
|
|
416
|
+
apiUrl: apiUrl2,
|
|
417
|
+
jwtStorageKey,
|
|
418
|
+
onLoginSuccess,
|
|
419
|
+
onLogout,
|
|
420
|
+
onRegisterSuccess,
|
|
421
|
+
children: /* @__PURE__ */ jsx3(WalletProvider, { storageKey: walletStorageKey, children })
|
|
422
|
+
}
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// src/hooks/useSigners.ts
|
|
427
|
+
import { useState as useState3, useEffect as useEffect4, useCallback } from "react";
|
|
428
|
+
|
|
429
|
+
// src/core/signer-helpers.ts
|
|
430
|
+
import { getSigners, parseSignerResponse } from "cilantro-sdk/helpers";
|
|
431
|
+
import { listSigners } from "cilantro-sdk/wallet";
|
|
432
|
+
async function loadSigners(walletId) {
|
|
433
|
+
if (!walletId) return [];
|
|
434
|
+
try {
|
|
435
|
+
let signersResult = await getSigners(walletId);
|
|
436
|
+
let onChainSigners = extractOnChainSigners(signersResult);
|
|
437
|
+
let authSigners = extractAuthenticationSigners(signersResult);
|
|
438
|
+
if ((!onChainSigners || !Array.isArray(onChainSigners) || onChainSigners.length === 0) && (!authSigners || !Array.isArray(authSigners) || authSigners.length === 0)) {
|
|
439
|
+
const response = await listSigners(walletId);
|
|
440
|
+
onChainSigners = extractOnChainSigners(response);
|
|
441
|
+
authSigners = extractAuthenticationSigners(response);
|
|
442
|
+
}
|
|
443
|
+
const processedOnChain = onChainSigners.filter((s) => s && typeof s === "object").map((signer) => processSigner(signer, "onchain"));
|
|
444
|
+
const processedAuth = authSigners.filter((s) => s && typeof s === "object").map((signer) => processSigner(signer, "authenticationsigner"));
|
|
445
|
+
return [...processedOnChain, ...processedAuth].filter((s) => s.id && (s.type || s.signerType));
|
|
446
|
+
} catch (error) {
|
|
447
|
+
console.error("Failed to load signers:", error);
|
|
448
|
+
return [];
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function processSigner(signer, category) {
|
|
452
|
+
const parsed = parseSignerResponse(signer);
|
|
453
|
+
const signerId = signer.id ?? signer.signerId ?? parsed.signerId;
|
|
454
|
+
const signerType = signer.signerType ?? signer.type ?? parsed.type;
|
|
455
|
+
const signerPubkey = signer.signerPubkey ?? signer.publicKey ?? parsed.publicKey ?? "";
|
|
456
|
+
const authId = signer.authId ?? parsed.email ?? parsed.phone;
|
|
457
|
+
const walletId = signer.walletId ?? parsed.walletId;
|
|
458
|
+
let email = signer.email ?? parsed.email;
|
|
459
|
+
let phone = signer.phone ?? parsed.phone;
|
|
460
|
+
if (!email && !phone && authId) {
|
|
461
|
+
if (signerType === "email") email = authId;
|
|
462
|
+
else if (signerType === "phone") phone = authId;
|
|
463
|
+
}
|
|
464
|
+
const signerConfig = signer.signerConfig;
|
|
465
|
+
if (signerConfig) {
|
|
466
|
+
if (!email && signerConfig.email) email = signerConfig.email;
|
|
467
|
+
if (!phone && signerConfig.phone) phone = signerConfig.phone;
|
|
468
|
+
}
|
|
469
|
+
const isActive = signer.isActive !== void 0 ? signer.isActive : parsed.isActive !== void 0 ? parsed.isActive : true;
|
|
470
|
+
return {
|
|
471
|
+
...signer,
|
|
472
|
+
id: signerId,
|
|
473
|
+
signerId,
|
|
474
|
+
walletId: walletId ?? "",
|
|
475
|
+
type: signerType,
|
|
476
|
+
signerType,
|
|
477
|
+
publicKey: signerPubkey,
|
|
478
|
+
signerPubkey,
|
|
479
|
+
email,
|
|
480
|
+
phone,
|
|
481
|
+
isActive,
|
|
482
|
+
credentialId: signer.credentialId ?? (signerType === "passkey" ? authId : void 0),
|
|
483
|
+
authId,
|
|
484
|
+
deviceIdentities: signer.deviceIdentities,
|
|
485
|
+
signerConfig: signer.signerConfig,
|
|
486
|
+
permissions: signer.permissions,
|
|
487
|
+
signerCategory: category
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function getSignerType(signer) {
|
|
491
|
+
return signer.type || signer.signerType || "";
|
|
492
|
+
}
|
|
493
|
+
function getSignerTypeLabel(type) {
|
|
494
|
+
if (!type) return "Unknown";
|
|
495
|
+
const t = type.toLowerCase();
|
|
496
|
+
if (t === "email") return "Email";
|
|
497
|
+
if (t === "phone") return "Phone";
|
|
498
|
+
if (t === "external" || t === "external-wallet") return "External Wallet";
|
|
499
|
+
if (t === "passkey") return "Passkey";
|
|
500
|
+
if (t === "api-key" || t === "apikey") return "API Key";
|
|
501
|
+
return type;
|
|
502
|
+
}
|
|
503
|
+
function getSignerDisplayName(signer) {
|
|
504
|
+
const signerType = getSignerType(signer);
|
|
505
|
+
const typeLabel = getSignerTypeLabel(signerType);
|
|
506
|
+
const shortId = signer.id.length > 8 ? `${signer.id.slice(0, 4)}...${signer.id.slice(-4)}` : signer.id;
|
|
507
|
+
if (signer.email) return `${typeLabel}: ${signer.email}`;
|
|
508
|
+
if (signer.phone) return `${typeLabel}: ${signer.phone}`;
|
|
509
|
+
if (signerType === "passkey") {
|
|
510
|
+
const credId = signer.credentialId ?? signer.authId ?? signer.signerConfig?.credentialId;
|
|
511
|
+
if (credId) {
|
|
512
|
+
const shortCredId = credId.length > 12 ? `${credId.slice(0, 6)}...${credId.slice(-6)}` : credId;
|
|
513
|
+
return `${typeLabel} (${shortCredId})`;
|
|
514
|
+
}
|
|
515
|
+
return `${typeLabel} (${shortId})`;
|
|
516
|
+
}
|
|
517
|
+
return `${typeLabel} (${shortId})`;
|
|
518
|
+
}
|
|
519
|
+
function getSignerUniqueId(signer) {
|
|
520
|
+
if (signer.id) return signer.id.length > 12 ? `${signer.id.slice(0, 6)}...${signer.id.slice(-6)}` : signer.id;
|
|
521
|
+
if (signer.publicKey) return `${signer.publicKey.slice(0, 6)}...${signer.publicKey.slice(-6)}`;
|
|
522
|
+
return "Unknown";
|
|
523
|
+
}
|
|
524
|
+
function extractSignerInfo(signer) {
|
|
525
|
+
const signerType = signer.type || signer.signerType;
|
|
526
|
+
const signerId = signer.id || signer.signerId;
|
|
527
|
+
if (!signerType) throw new Error(`Signer type is missing. Signer data: ${JSON.stringify(signer)}`);
|
|
528
|
+
if (!signerId) throw new Error(`Signer ID is missing. Signer data: ${JSON.stringify(signer)}`);
|
|
529
|
+
return { signerId, signerType };
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// src/hooks/useSigners.ts
|
|
533
|
+
function useSigners(options = {}) {
|
|
534
|
+
const { walletId: walletIdOption } = options;
|
|
535
|
+
const { token } = useCilantroAuth();
|
|
536
|
+
const [signers, setSigners] = useState3([]);
|
|
537
|
+
const [isLoading, setIsLoading] = useState3(false);
|
|
538
|
+
const [error, setError] = useState3(null);
|
|
539
|
+
const load = useCallback(async (walletId) => {
|
|
540
|
+
if (!walletId) {
|
|
541
|
+
setSigners([]);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
setIsLoading(true);
|
|
545
|
+
setError(null);
|
|
546
|
+
try {
|
|
547
|
+
if (token) setSdkAuth(token);
|
|
548
|
+
const list = await loadSigners(walletId);
|
|
549
|
+
setSigners(list);
|
|
550
|
+
} catch (err) {
|
|
551
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
552
|
+
setError(message);
|
|
553
|
+
setSigners([]);
|
|
554
|
+
} finally {
|
|
555
|
+
setIsLoading(false);
|
|
556
|
+
}
|
|
557
|
+
}, [token]);
|
|
558
|
+
useEffect4(() => {
|
|
559
|
+
const id = walletIdOption ?? void 0;
|
|
560
|
+
if (id) load(id);
|
|
561
|
+
else {
|
|
562
|
+
setSigners([]);
|
|
563
|
+
setError(null);
|
|
564
|
+
}
|
|
565
|
+
}, [walletIdOption, load]);
|
|
566
|
+
const refresh = useCallback(async () => {
|
|
567
|
+
const id = walletIdOption ?? void 0;
|
|
568
|
+
if (id) await load(id);
|
|
569
|
+
}, [walletIdOption, load]);
|
|
570
|
+
return { signers, isLoading, error, refresh };
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// src/hooks/useSignerSelection.ts
|
|
574
|
+
import { useState as useState4, useEffect as useEffect5, useCallback as useCallback2 } from "react";
|
|
575
|
+
function useSignerSelection(options = {}) {
|
|
576
|
+
const { walletId: walletIdOverride, signingMethod = "sdk-signer" } = options;
|
|
577
|
+
const { selectedWallet } = useWallets();
|
|
578
|
+
const effectiveWalletId = walletIdOverride ?? selectedWallet?.id ?? selectedWallet?.walletId ?? "";
|
|
579
|
+
const { signers: availableSigners, isLoading: isLoadingSigners } = useSigners({
|
|
580
|
+
walletId: effectiveWalletId || null
|
|
581
|
+
});
|
|
582
|
+
const [selectedWalletId, setSelectedWalletId] = useState4(effectiveWalletId);
|
|
583
|
+
const [selectedSigner, setSelectedSigner] = useState4(null);
|
|
584
|
+
useEffect5(() => {
|
|
585
|
+
setSelectedWalletId(effectiveWalletId);
|
|
586
|
+
}, [effectiveWalletId]);
|
|
587
|
+
useEffect5(() => {
|
|
588
|
+
if (signingMethod !== "sdk-signer") {
|
|
589
|
+
setSelectedSigner(null);
|
|
590
|
+
}
|
|
591
|
+
}, [signingMethod]);
|
|
592
|
+
const reset = useCallback2(() => {
|
|
593
|
+
setSelectedWalletId("");
|
|
594
|
+
setSelectedSigner(null);
|
|
595
|
+
}, []);
|
|
596
|
+
return {
|
|
597
|
+
selectedWalletId: selectedWalletId || effectiveWalletId,
|
|
598
|
+
setSelectedWalletId,
|
|
599
|
+
availableSigners,
|
|
600
|
+
selectedSigner,
|
|
601
|
+
setSelectedSigner,
|
|
602
|
+
isLoadingSigners,
|
|
603
|
+
reset
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// src/hooks/useMessageSigning.ts
|
|
608
|
+
import { useState as useState5 } from "react";
|
|
609
|
+
|
|
610
|
+
// src/core/signer-signing/core.ts
|
|
611
|
+
import { PublicKey } from "@solana/web3.js";
|
|
612
|
+
import {
|
|
613
|
+
deriveSignerKeypair,
|
|
614
|
+
getEmailSignerKeypair,
|
|
615
|
+
getPhoneSignerKeypair
|
|
616
|
+
} from "cilantro-sdk/helpers";
|
|
617
|
+
|
|
618
|
+
// src/core/signer-signing/constants.ts
|
|
619
|
+
var CACHE_CONFIG = { enabled: true, ttl: 3e5 };
|
|
620
|
+
var SIGNER_TYPES = {
|
|
621
|
+
EMAIL: "email",
|
|
622
|
+
PHONE: "phone",
|
|
623
|
+
PASSKEY: "passkey",
|
|
624
|
+
EXTERNAL: "external",
|
|
625
|
+
API_KEY: "apiKey"
|
|
626
|
+
};
|
|
627
|
+
function isValidSignerType(type) {
|
|
628
|
+
return Object.values(SIGNER_TYPES).includes(type);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// src/core/signer-signing/storage.ts
|
|
632
|
+
function getSignerStorage() {
|
|
633
|
+
return getStorageAdapter();
|
|
634
|
+
}
|
|
635
|
+
function getStorageOptions() {
|
|
636
|
+
return {
|
|
637
|
+
deviceKeyManager: getSignerStorage(),
|
|
638
|
+
cache: CACHE_CONFIG
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// src/core/signer-signing/core.ts
|
|
643
|
+
async function getSignerPublicKey(walletId, signer, signerType, signerId) {
|
|
644
|
+
const existingPubkey = signer.signerPubkey || signer.publicKey;
|
|
645
|
+
if (existingPubkey && typeof existingPubkey === "string" && existingPubkey.trim()) {
|
|
646
|
+
return new PublicKey(existingPubkey.trim());
|
|
647
|
+
}
|
|
648
|
+
const storageOptions = getStorageOptions();
|
|
649
|
+
if (signerType === SIGNER_TYPES.EMAIL) {
|
|
650
|
+
const keypair = await getEmailSignerKeypair(walletId, signerId, storageOptions);
|
|
651
|
+
return new PublicKey(keypair.publicKey);
|
|
652
|
+
}
|
|
653
|
+
if (signerType === SIGNER_TYPES.PHONE) {
|
|
654
|
+
const keypair = await getPhoneSignerKeypair(walletId, signerId, storageOptions);
|
|
655
|
+
return new PublicKey(keypair.publicKey);
|
|
656
|
+
}
|
|
657
|
+
if (signerType === SIGNER_TYPES.EXTERNAL || signerType === SIGNER_TYPES.API_KEY) {
|
|
658
|
+
const keypair = await deriveSignerKeypair(walletId, signerId, signerType, storageOptions);
|
|
659
|
+
return new PublicKey(keypair.publicKey);
|
|
660
|
+
}
|
|
661
|
+
throw new Error(`Unsupported signer type: ${signerType}`);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// src/core/signer-signing/message-signing.ts
|
|
665
|
+
import {
|
|
666
|
+
deriveSignerKeypair as deriveSignerKeypair2,
|
|
667
|
+
signWithSigner,
|
|
668
|
+
signWithEmailSigner,
|
|
669
|
+
signWithPhoneSigner,
|
|
670
|
+
getEmailSignerKeypair as getEmailSignerKeypair2,
|
|
671
|
+
getPhoneSignerKeypair as getPhoneSignerKeypair2
|
|
672
|
+
} from "cilantro-sdk/helpers";
|
|
673
|
+
|
|
674
|
+
// src/core/signer-creation.ts
|
|
675
|
+
import { PublicKey as PublicKey2 } from "@solana/web3.js";
|
|
676
|
+
import {
|
|
677
|
+
createEmailSignerHelper as createEmailSignerSDK,
|
|
678
|
+
createPhoneSignerHelper as createPhoneSignerSDK,
|
|
679
|
+
isValidEmail,
|
|
680
|
+
isValidPhone,
|
|
681
|
+
authenticateWithPasskey,
|
|
682
|
+
findExistingPasskeySigner,
|
|
683
|
+
formatAuthenticationResponse,
|
|
684
|
+
isWebAuthnSupported,
|
|
685
|
+
registerPasskeyComplete,
|
|
686
|
+
signWithPasskeySigner
|
|
687
|
+
} from "cilantro-sdk/helpers";
|
|
688
|
+
import { sendRawPasskeyTransaction } from "cilantro-sdk/transactions";
|
|
689
|
+
import { createExternalWalletSigner, startPasskeyAuthentication } from "cilantro-sdk/wallet";
|
|
690
|
+
async function createEmailSignerHelper(walletId, email) {
|
|
691
|
+
const trimmedEmail = email.trim();
|
|
692
|
+
if (!trimmedEmail) throw new Error("Email address is required");
|
|
693
|
+
if (!isValidEmail(trimmedEmail)) throw new Error("Invalid email address format");
|
|
694
|
+
const deviceKeyManager = getStorageAdapter();
|
|
695
|
+
return await createEmailSignerSDK(walletId, { email: trimmedEmail, deviceKeyManager });
|
|
696
|
+
}
|
|
697
|
+
async function createPhoneSignerHelper(walletId, phone) {
|
|
698
|
+
const trimmedPhone = phone.trim();
|
|
699
|
+
if (!trimmedPhone) throw new Error("Phone number is required");
|
|
700
|
+
if (!isValidPhone(trimmedPhone)) throw new Error("Invalid phone number format");
|
|
701
|
+
const deviceKeyManager = getStorageAdapter();
|
|
702
|
+
return await createPhoneSignerSDK(walletId, { phone: trimmedPhone, deviceKeyManager });
|
|
703
|
+
}
|
|
704
|
+
async function createExternalSignerHelper(walletId, address) {
|
|
705
|
+
if (!address.trim()) throw new Error("Wallet address is required");
|
|
706
|
+
return await createExternalWalletSigner(walletId, { address: address.trim(), chain: "solana" });
|
|
707
|
+
}
|
|
708
|
+
async function registerPasskeySigner(walletId) {
|
|
709
|
+
if (!isWebAuthnSupported()) throw new Error("WebAuthn is not supported in this browser");
|
|
710
|
+
return await registerPasskeyComplete(walletId);
|
|
711
|
+
}
|
|
712
|
+
async function signWithPasskey(walletId, signerId, message, options) {
|
|
713
|
+
if (!isWebAuthnSupported()) throw new Error("WebAuthn is not supported in this browser");
|
|
714
|
+
const messageBytes = new TextEncoder().encode(message);
|
|
715
|
+
const signature = await signWithPasskeySigner(walletId, signerId, messageBytes, options);
|
|
716
|
+
return {
|
|
717
|
+
signature: Buffer.from(signature).toString("hex"),
|
|
718
|
+
signer: signerId
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
async function signAndSendPasskeyTransaction(walletId, signerId, transaction, options) {
|
|
722
|
+
if (!isWebAuthnSupported()) throw new Error("WebAuthn is not supported in this browser");
|
|
723
|
+
let credentialId = options?.credentialId;
|
|
724
|
+
if (!credentialId) {
|
|
725
|
+
const signers = await loadSigners(walletId);
|
|
726
|
+
const passkeySigner = signers.find((s) => s.id === signerId || s.signerId === signerId);
|
|
727
|
+
if (!passkeySigner) {
|
|
728
|
+
throw new Error(
|
|
729
|
+
`Passkey signer ${signerId} not found in wallet. Available signers: ${signers.map((s) => `${s.id} (${s.type})`).join(", ")}`
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
credentialId = passkeySigner.credentialId ?? passkeySigner.signerConfig?.credentialId ?? passkeySigner.authId;
|
|
733
|
+
if (!credentialId) {
|
|
734
|
+
throw new Error(`Credential ID not found for passkey signer ${signerId}.`);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
let mintAddress = options?.mintAddress;
|
|
738
|
+
if (!mintAddress && transaction.instructions.length > 0) {
|
|
739
|
+
const systemProgramId = new PublicKey2("11111111111111111111111111111111");
|
|
740
|
+
const feePayer = transaction.feePayer || systemProgramId;
|
|
741
|
+
transaction.instructions.forEach((ix) => {
|
|
742
|
+
ix.keys.forEach((key) => {
|
|
743
|
+
if (key.isWritable && key.isSigner && !key.pubkey.equals(feePayer) && !mintAddress) {
|
|
744
|
+
mintAddress = key.pubkey.toString();
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
const unsignedTransaction = transaction.serialize({ verifySignatures: false }).toString("base64");
|
|
750
|
+
const authOptions = await startPasskeyAuthentication(walletId, {
|
|
751
|
+
credentialId: options?.credentialId
|
|
752
|
+
});
|
|
753
|
+
const authData = extractResponseData(authOptions);
|
|
754
|
+
if (!authData) throw new Error("Failed to get authentication options");
|
|
755
|
+
const authDataValue = authData && typeof authData === "object" && "data" in authData ? authData.data : authData;
|
|
756
|
+
const credential = await authenticateWithPasskey(
|
|
757
|
+
authDataValue,
|
|
758
|
+
{ useBrowserAutofill: options?.useBrowserAutofill ?? false }
|
|
759
|
+
);
|
|
760
|
+
const formattedCredential = formatAuthenticationResponse(credential);
|
|
761
|
+
const dto = {
|
|
762
|
+
walletId,
|
|
763
|
+
credentialId,
|
|
764
|
+
unsignedTransaction,
|
|
765
|
+
signature: formattedCredential.response.signature,
|
|
766
|
+
authenticatorData: formattedCredential.response.authenticatorData,
|
|
767
|
+
clientDataJSON: formattedCredential.response.clientDataJSON
|
|
768
|
+
};
|
|
769
|
+
const result = await sendRawPasskeyTransaction(dto);
|
|
770
|
+
const resultData = extractResponseData(result);
|
|
771
|
+
return { signature: resultData?.signature ?? "", status: resultData?.status };
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// src/core/signer-signing/errors.ts
|
|
775
|
+
import {
|
|
776
|
+
DeviceKeyNotFoundError,
|
|
777
|
+
DeviceKeyMismatchError,
|
|
778
|
+
SignerInactiveError,
|
|
779
|
+
SignerNotFoundError
|
|
780
|
+
} from "cilantro-sdk/helpers";
|
|
781
|
+
function handleDeviceKeyError(error, signerType) {
|
|
782
|
+
if (error instanceof DeviceKeyNotFoundError) {
|
|
783
|
+
throw new Error(
|
|
784
|
+
`Device key not found. Please create the ${signerType} signer first using the Signers page. The device key is generated when you create the signer.`
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
if (error instanceof DeviceKeyMismatchError) {
|
|
788
|
+
throw new Error(
|
|
789
|
+
"Device key mismatch. The stored device key doesn't match the server. You may need to create a new signer or update the device identity."
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
if (error instanceof SignerNotFoundError) {
|
|
793
|
+
throw new Error(`Signer not found. Please ensure the ${signerType} signer exists.`);
|
|
794
|
+
}
|
|
795
|
+
if (error instanceof SignerInactiveError) {
|
|
796
|
+
throw new Error(`${signerType} signer is not active. Please activate the signer first.`);
|
|
797
|
+
}
|
|
798
|
+
if (error instanceof Error) {
|
|
799
|
+
if (error.message.includes("Device key not found") || error.name === "DeviceKeyNotFoundError") {
|
|
800
|
+
throw new Error(
|
|
801
|
+
`Device key not found. Please create the ${signerType} signer first using the Signers page. The device key is generated when you create the signer.`
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
if (error.message.includes("Device key mismatch") || error.name === "DeviceKeyMismatchError") {
|
|
805
|
+
throw new Error(
|
|
806
|
+
"Device key mismatch. The stored device key doesn't match the server. You may need to create a new signer or update the device identity."
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
throw error;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// src/core/signer-signing/signer-types.ts
|
|
814
|
+
function extractSignerInfo2(signer) {
|
|
815
|
+
const signerType = signer.type || signer.signerType;
|
|
816
|
+
const signerId = signer.id || signer.signerId;
|
|
817
|
+
if (!signerType || !signerId) {
|
|
818
|
+
throw new Error(`Signer type or ID is missing. Signer data: ${JSON.stringify(signer)}`);
|
|
819
|
+
}
|
|
820
|
+
if (!isValidSignerType(signerType)) {
|
|
821
|
+
throw new Error(`Unsupported signer type: ${signerType}`);
|
|
822
|
+
}
|
|
823
|
+
return { signerType, signerId };
|
|
824
|
+
}
|
|
825
|
+
function requiresActiveStatus(signerType) {
|
|
826
|
+
return signerType === SIGNER_TYPES.EMAIL || signerType === SIGNER_TYPES.PHONE || signerType === SIGNER_TYPES.EXTERNAL || signerType === SIGNER_TYPES.API_KEY;
|
|
827
|
+
}
|
|
828
|
+
function validateSignerActive(signer, signerType) {
|
|
829
|
+
if (requiresActiveStatus(signerType) && !signer.isActive) {
|
|
830
|
+
throw new Error(`${signerType} signer is not active. Please activate the signer first.`);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// src/core/signer-signing/message-signing.ts
|
|
835
|
+
async function signMessageWithSigner(walletId, signer, messageText) {
|
|
836
|
+
const { signerType, signerId } = extractSignerInfo2(signer);
|
|
837
|
+
const message = new TextEncoder().encode(messageText);
|
|
838
|
+
if (signerType === SIGNER_TYPES.EMAIL) {
|
|
839
|
+
validateSignerActive(signer, signerType);
|
|
840
|
+
const storageOptions = getStorageOptions();
|
|
841
|
+
try {
|
|
842
|
+
const signature = await signWithEmailSigner(walletId, signerId, message, storageOptions);
|
|
843
|
+
const keypair = await getEmailSignerKeypair2(walletId, signerId, storageOptions);
|
|
844
|
+
return {
|
|
845
|
+
signature: Buffer.from(signature).toString("hex"),
|
|
846
|
+
publicKey: Buffer.from(keypair.publicKey).toString("hex"),
|
|
847
|
+
signerType: SIGNER_TYPES.EMAIL
|
|
848
|
+
};
|
|
849
|
+
} catch (error) {
|
|
850
|
+
throw handleDeviceKeyError(error, SIGNER_TYPES.EMAIL);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
if (signerType === SIGNER_TYPES.PHONE) {
|
|
854
|
+
validateSignerActive(signer, signerType);
|
|
855
|
+
const storageOptions = getStorageOptions();
|
|
856
|
+
try {
|
|
857
|
+
const signature = await signWithPhoneSigner(walletId, signerId, message, storageOptions);
|
|
858
|
+
const keypair = await getPhoneSignerKeypair2(walletId, signerId, storageOptions);
|
|
859
|
+
return {
|
|
860
|
+
signature: Buffer.from(signature).toString("hex"),
|
|
861
|
+
publicKey: Buffer.from(keypair.publicKey).toString("hex"),
|
|
862
|
+
signerType: SIGNER_TYPES.PHONE
|
|
863
|
+
};
|
|
864
|
+
} catch (error) {
|
|
865
|
+
throw handleDeviceKeyError(error, SIGNER_TYPES.PHONE);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
if (signerType === SIGNER_TYPES.PASSKEY) {
|
|
869
|
+
const signResult = await signWithPasskey(walletId, signerId, messageText, { useBrowserAutofill: false });
|
|
870
|
+
return {
|
|
871
|
+
signature: signResult.signature,
|
|
872
|
+
signerType: SIGNER_TYPES.PASSKEY,
|
|
873
|
+
signer: signResult.signer
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
if (signerType === SIGNER_TYPES.EXTERNAL || signerType === SIGNER_TYPES.API_KEY) {
|
|
877
|
+
validateSignerActive(signer, signerType);
|
|
878
|
+
const storageOptions = getStorageOptions();
|
|
879
|
+
const signature = await signWithSigner(walletId, signerId, signerType, message, storageOptions);
|
|
880
|
+
const keypair = await deriveSignerKeypair2(walletId, signerId, signerType, storageOptions);
|
|
881
|
+
return {
|
|
882
|
+
signature: Buffer.from(signature).toString("hex"),
|
|
883
|
+
publicKey: Buffer.from(keypair.publicKey).toString("base64"),
|
|
884
|
+
signerType
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
throw new Error(`Unsupported signer type: ${signerType}`);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// src/core/signer-signing/transaction-signing.ts
|
|
891
|
+
import {
|
|
892
|
+
signWithSigner as signWithSigner2,
|
|
893
|
+
signWithEmailSigner as signWithEmailSigner2,
|
|
894
|
+
signWithPhoneSigner as signWithPhoneSigner2,
|
|
895
|
+
signTransactionWithEmailSigner,
|
|
896
|
+
signTransactionWithPhoneSigner
|
|
897
|
+
} from "cilantro-sdk/helpers";
|
|
898
|
+
import { submitTransaction } from "cilantro-sdk/wallet";
|
|
899
|
+
|
|
900
|
+
// src/core/signer-signing/validation.ts
|
|
901
|
+
import { PublicKey as PublicKey3, Transaction } from "@solana/web3.js";
|
|
902
|
+
async function validateSignerInTransaction(signedTransactionBase64, expectedSignerPublicKey) {
|
|
903
|
+
const errors = [];
|
|
904
|
+
const warnings = [];
|
|
905
|
+
try {
|
|
906
|
+
const transactionBuffer = Buffer.from(signedTransactionBase64, "base64");
|
|
907
|
+
const transaction = Transaction.from(transactionBuffer);
|
|
908
|
+
let allSignaturesValid = true;
|
|
909
|
+
try {
|
|
910
|
+
transaction.verifySignatures();
|
|
911
|
+
} catch (error) {
|
|
912
|
+
allSignaturesValid = false;
|
|
913
|
+
errors.push(`Signature verification failed: ${extractErrorMessage(error)}`);
|
|
914
|
+
}
|
|
915
|
+
const signers = transaction.signatures.map((sig) => {
|
|
916
|
+
const publicKeyStr = sig.publicKey.toString();
|
|
917
|
+
const hasSignature = sig.signature !== null && sig.signature.length > 0;
|
|
918
|
+
const signatureLength = sig.signature !== null ? sig.signature.length : 0;
|
|
919
|
+
let matchesExpected = false;
|
|
920
|
+
if (expectedSignerPublicKey) {
|
|
921
|
+
const expectedPubkey = typeof expectedSignerPublicKey === "string" ? new PublicKey3(expectedSignerPublicKey) : expectedSignerPublicKey;
|
|
922
|
+
matchesExpected = sig.publicKey.equals(expectedPubkey);
|
|
923
|
+
}
|
|
924
|
+
if (!hasSignature) warnings.push(`Signer ${publicKeyStr} has no signature (unsigned)`);
|
|
925
|
+
return { publicKey: publicKeyStr, hasSignature, signatureLength, matchesExpected };
|
|
926
|
+
});
|
|
927
|
+
let expectedSignerFound = false;
|
|
928
|
+
if (expectedSignerPublicKey) {
|
|
929
|
+
const expectedPubkey = typeof expectedSignerPublicKey === "string" ? new PublicKey3(expectedSignerPublicKey) : expectedSignerPublicKey;
|
|
930
|
+
expectedSignerFound = signers.some((s) => s.matchesExpected);
|
|
931
|
+
if (!expectedSignerFound) {
|
|
932
|
+
errors.push(`Expected signer ${expectedPubkey.toString()} not found in transaction signers`);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (expectedSignerFound && expectedSignerPublicKey) {
|
|
936
|
+
const expectedSigner = signers.find((s) => s.matchesExpected);
|
|
937
|
+
if (expectedSigner && !expectedSigner.hasSignature) {
|
|
938
|
+
errors.push(
|
|
939
|
+
`Expected signer ${typeof expectedSignerPublicKey === "string" ? expectedSignerPublicKey : expectedSignerPublicKey.toString()} is present but has no signature`
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
const isValid = allSignaturesValid && (expectedSignerPublicKey ? expectedSignerFound : true) && errors.length === 0;
|
|
944
|
+
return {
|
|
945
|
+
isValid,
|
|
946
|
+
transaction,
|
|
947
|
+
signers,
|
|
948
|
+
expectedSignerFound,
|
|
949
|
+
allSignaturesValid,
|
|
950
|
+
feePayer: transaction.feePayer?.toString(),
|
|
951
|
+
recentBlockhash: transaction.recentBlockhash ?? void 0,
|
|
952
|
+
errors,
|
|
953
|
+
warnings
|
|
954
|
+
};
|
|
955
|
+
} catch (error) {
|
|
956
|
+
errors.push(`Failed to deserialize transaction: ${extractErrorMessage(error)}`);
|
|
957
|
+
throw new Error(
|
|
958
|
+
`Transaction validation failed: ${errors.join("; ")}
|
|
959
|
+
Original error: ${extractErrorMessage(error)}`
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// src/core/signer-signing/transaction-signing.ts
|
|
965
|
+
function prepareTransactionForSigning(transaction, signerPublicKey) {
|
|
966
|
+
const signerPubkeyStr = signerPublicKey.toString();
|
|
967
|
+
const hasSignerInSignatures = transaction.signatures.some(
|
|
968
|
+
(sig) => sig.publicKey.toString() === signerPubkeyStr
|
|
969
|
+
);
|
|
970
|
+
if (!hasSignerInSignatures) {
|
|
971
|
+
transaction.signatures.push({
|
|
972
|
+
publicKey: signerPublicKey,
|
|
973
|
+
signature: null
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
if (transaction.feePayer) {
|
|
977
|
+
const feePayerStr = transaction.feePayer.toString();
|
|
978
|
+
const hasFeePayerInSignatures = transaction.signatures.some(
|
|
979
|
+
(sig) => sig.publicKey.toString() === feePayerStr
|
|
980
|
+
);
|
|
981
|
+
if (!hasFeePayerInSignatures) {
|
|
982
|
+
transaction.signatures.push({ publicKey: transaction.feePayer, signature: null });
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
async function signTransactionWithEmailOrPhone(walletId, signerId, signerType, unsignedTransactionBase64) {
|
|
987
|
+
const storageOptions = getStorageOptions();
|
|
988
|
+
if (signerType === SIGNER_TYPES.EMAIL) {
|
|
989
|
+
return await signTransactionWithEmailSigner(
|
|
990
|
+
walletId,
|
|
991
|
+
signerId,
|
|
992
|
+
unsignedTransactionBase64,
|
|
993
|
+
storageOptions
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
return await signTransactionWithPhoneSigner(
|
|
997
|
+
walletId,
|
|
998
|
+
signerId,
|
|
999
|
+
unsignedTransactionBase64,
|
|
1000
|
+
storageOptions
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
async function submitSignedTransaction(walletId, signedTransactionBase64) {
|
|
1004
|
+
const submitResult = await submitTransaction(walletId, {
|
|
1005
|
+
signedTransaction: signedTransactionBase64
|
|
1006
|
+
});
|
|
1007
|
+
const resultData = extractResponseData(submitResult);
|
|
1008
|
+
if (!resultData?.signature) throw new Error("Server did not return a transaction signature");
|
|
1009
|
+
return {
|
|
1010
|
+
signature: resultData.signature,
|
|
1011
|
+
confirmationStatus: resultData.status === "confirmed" ? "confirmed" : "pending"
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
async function handlePasskeyTransaction(walletId, signerId, transaction, connection) {
|
|
1015
|
+
if (!connection) {
|
|
1016
|
+
throw new Error(
|
|
1017
|
+
"Connection is required for passkey sign-and-send. Pass the connection from your Solana config to useTransactionSigning / TransactionSigningForm."
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
|
|
1021
|
+
const result = await signAndSendPasskeyTransaction(walletId, signerId, transaction, {
|
|
1022
|
+
useBrowserAutofill: false
|
|
1023
|
+
});
|
|
1024
|
+
let confirmationStatus = "confirmed";
|
|
1025
|
+
try {
|
|
1026
|
+
await connection.confirmTransaction({
|
|
1027
|
+
signature: result.signature,
|
|
1028
|
+
blockhash,
|
|
1029
|
+
lastValidBlockHeight
|
|
1030
|
+
});
|
|
1031
|
+
} catch (error) {
|
|
1032
|
+
const errorMsg = extractErrorMessage(error);
|
|
1033
|
+
if (errorMsg.includes("block height exceeded") || errorMsg.includes("expired") || errorMsg.includes("timeout")) {
|
|
1034
|
+
confirmationStatus = "pending";
|
|
1035
|
+
} else {
|
|
1036
|
+
throw new Error(`Transaction confirmation failed: ${errorMsg}. Signature: ${result.signature}`);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
return { signature: result.signature, confirmationStatus };
|
|
1040
|
+
}
|
|
1041
|
+
async function handleEmailOrPhoneTransaction(walletId, signer, signerType, signerId, transaction) {
|
|
1042
|
+
validateSignerActive(signer, signerType);
|
|
1043
|
+
const signerPublicKey = await getSignerPublicKey(walletId, signer, signerType, signerId);
|
|
1044
|
+
prepareTransactionForSigning(transaction, signerPublicKey);
|
|
1045
|
+
const unsignedTransactionBase64 = Buffer.from(
|
|
1046
|
+
transaction.serialize({ verifySignatures: false })
|
|
1047
|
+
).toString("base64");
|
|
1048
|
+
let signedTransactionBase64;
|
|
1049
|
+
try {
|
|
1050
|
+
signedTransactionBase64 = await signTransactionWithEmailOrPhone(
|
|
1051
|
+
walletId,
|
|
1052
|
+
signerId,
|
|
1053
|
+
signerType,
|
|
1054
|
+
unsignedTransactionBase64
|
|
1055
|
+
);
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
handleDeviceKeyError(error, signerType);
|
|
1058
|
+
throw error;
|
|
1059
|
+
}
|
|
1060
|
+
try {
|
|
1061
|
+
const validationResult = await validateSignerInTransaction(
|
|
1062
|
+
signedTransactionBase64,
|
|
1063
|
+
signerPublicKey
|
|
1064
|
+
);
|
|
1065
|
+
if (!validationResult.isValid) {
|
|
1066
|
+
console.error("Transaction validation failed:", {
|
|
1067
|
+
errors: validationResult.errors,
|
|
1068
|
+
warnings: validationResult.warnings
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
} catch (validationError) {
|
|
1072
|
+
console.error("Validation error (non-fatal):", extractErrorMessage(validationError));
|
|
1073
|
+
}
|
|
1074
|
+
return await submitSignedTransaction(walletId, signedTransactionBase64);
|
|
1075
|
+
}
|
|
1076
|
+
async function handleExternalOrApiKeyTransaction(walletId, signer, signerType, signerId, transaction) {
|
|
1077
|
+
validateSignerActive(signer, signerType);
|
|
1078
|
+
const messageBytes = transaction.serializeMessage();
|
|
1079
|
+
const storageOptions = getStorageOptions();
|
|
1080
|
+
const signatureBytes = await signWithSigner2(
|
|
1081
|
+
walletId,
|
|
1082
|
+
signerId,
|
|
1083
|
+
signerType,
|
|
1084
|
+
messageBytes,
|
|
1085
|
+
storageOptions
|
|
1086
|
+
);
|
|
1087
|
+
const signerPublicKey = await getSignerPublicKey(walletId, signer, signerType, signerId);
|
|
1088
|
+
const signatureBuffer = Buffer.from(signatureBytes);
|
|
1089
|
+
transaction.addSignature(signerPublicKey, signatureBuffer);
|
|
1090
|
+
const signedTransactionBytes = transaction.serialize();
|
|
1091
|
+
const signedTransactionBase64 = Buffer.from(signedTransactionBytes).toString("base64");
|
|
1092
|
+
return await submitSignedTransaction(walletId, signedTransactionBase64);
|
|
1093
|
+
}
|
|
1094
|
+
async function signTransactionWithSigner(walletId, signer, transactionBuffer) {
|
|
1095
|
+
const { signerType, signerId } = extractSignerInfo2(signer);
|
|
1096
|
+
const storageOptions = getStorageOptions();
|
|
1097
|
+
if (signerType === SIGNER_TYPES.EMAIL) {
|
|
1098
|
+
validateSignerActive(signer, signerType);
|
|
1099
|
+
try {
|
|
1100
|
+
const signature = await signWithEmailSigner2(walletId, signerId, transactionBuffer, storageOptions);
|
|
1101
|
+
return { signature: Buffer.from(signature).toString("hex"), signerType: SIGNER_TYPES.EMAIL };
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
throw handleDeviceKeyError(error, SIGNER_TYPES.EMAIL);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
if (signerType === SIGNER_TYPES.PHONE) {
|
|
1107
|
+
validateSignerActive(signer, signerType);
|
|
1108
|
+
try {
|
|
1109
|
+
const signature = await signWithPhoneSigner2(walletId, signerId, transactionBuffer, storageOptions);
|
|
1110
|
+
return { signature: Buffer.from(signature).toString("hex"), signerType: SIGNER_TYPES.PHONE };
|
|
1111
|
+
} catch (error) {
|
|
1112
|
+
throw handleDeviceKeyError(error, SIGNER_TYPES.PHONE);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
if (signerType === SIGNER_TYPES.PASSKEY) {
|
|
1116
|
+
const { signWithPasskeySigner: signWithPasskeySigner2 } = await import("cilantro-sdk/helpers");
|
|
1117
|
+
const signature = await signWithPasskeySigner2(walletId, signerId, transactionBuffer, {
|
|
1118
|
+
useBrowserAutofill: false
|
|
1119
|
+
});
|
|
1120
|
+
return { signature: Buffer.from(signature).toString("hex"), signerType: SIGNER_TYPES.PASSKEY };
|
|
1121
|
+
}
|
|
1122
|
+
if (signerType === SIGNER_TYPES.EXTERNAL || signerType === SIGNER_TYPES.API_KEY) {
|
|
1123
|
+
validateSignerActive(signer, signerType);
|
|
1124
|
+
const signature = await signWithSigner2(
|
|
1125
|
+
walletId,
|
|
1126
|
+
signerId,
|
|
1127
|
+
signerType,
|
|
1128
|
+
transactionBuffer,
|
|
1129
|
+
storageOptions
|
|
1130
|
+
);
|
|
1131
|
+
return { signature: Buffer.from(signature).toString("hex"), signerType };
|
|
1132
|
+
}
|
|
1133
|
+
throw new Error(`Unsupported signer type: ${signerType}`);
|
|
1134
|
+
}
|
|
1135
|
+
async function signAndSendTransactionWithSigner(walletId, signer, transaction, connection) {
|
|
1136
|
+
const { signerType, signerId } = extractSignerInfo2(signer);
|
|
1137
|
+
if (signerType === SIGNER_TYPES.PASSKEY) {
|
|
1138
|
+
return await handlePasskeyTransaction(walletId, signerId, transaction, connection);
|
|
1139
|
+
}
|
|
1140
|
+
if (signerType === SIGNER_TYPES.EMAIL || signerType === SIGNER_TYPES.PHONE) {
|
|
1141
|
+
return await handleEmailOrPhoneTransaction(walletId, signer, signerType, signerId, transaction);
|
|
1142
|
+
}
|
|
1143
|
+
if (signerType === SIGNER_TYPES.EXTERNAL || signerType === SIGNER_TYPES.API_KEY) {
|
|
1144
|
+
return await handleExternalOrApiKeyTransaction(walletId, signer, signerType, signerId, transaction);
|
|
1145
|
+
}
|
|
1146
|
+
throw new Error(`Unsupported signer type: ${signerType}`);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// src/core/signer-signing/wallet-data.ts
|
|
1150
|
+
import { PublicKey as PublicKey4 } from "@solana/web3.js";
|
|
1151
|
+
import { findOne as findOneWallet } from "cilantro-sdk/wallet";
|
|
1152
|
+
async function getWalletData(walletId) {
|
|
1153
|
+
const walletDataResponse = await findOneWallet(walletId);
|
|
1154
|
+
const walletData = extractResponseData(walletDataResponse);
|
|
1155
|
+
if (!walletData) throw new Error("Wallet data is empty");
|
|
1156
|
+
const address = String(
|
|
1157
|
+
walletData.address ?? walletData.walletAddress ?? walletData.solanaAddress ?? walletData.publicKey ?? walletData.pubkey ?? ""
|
|
1158
|
+
).trim();
|
|
1159
|
+
if (!address) throw new Error(`No wallet address found. Available fields: ${Object.keys(walletData).join(", ")}`);
|
|
1160
|
+
const walletPublicKey = new PublicKey4(address);
|
|
1161
|
+
const adminData = walletData.admin;
|
|
1162
|
+
const adminPubkey = String(
|
|
1163
|
+
walletData.adminSignerPubkey ?? walletData.adminSigner ?? adminData?.publicKey ?? ""
|
|
1164
|
+
).trim();
|
|
1165
|
+
if (!adminPubkey) {
|
|
1166
|
+
throw new Error(
|
|
1167
|
+
`adminSignerPubkey not found in wallet data. Available fields: ${Object.keys(walletData).join(", ")}`
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
const adminSignerPubkey = new PublicKey4(adminPubkey);
|
|
1171
|
+
return { walletPublicKey, adminSignerPubkey };
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
// src/hooks/useMessageSigning.ts
|
|
1175
|
+
function useMessageSigning(options) {
|
|
1176
|
+
const {
|
|
1177
|
+
token,
|
|
1178
|
+
signingMethod,
|
|
1179
|
+
selectedSigner,
|
|
1180
|
+
selectedWalletId,
|
|
1181
|
+
walletAdapterSignMessage,
|
|
1182
|
+
walletAdapterPublicKey
|
|
1183
|
+
} = options;
|
|
1184
|
+
const [messageText, setMessageText] = useState5("Hello, Solana!");
|
|
1185
|
+
const [signResultState, setSignResultState] = useState5({ status: "idle" });
|
|
1186
|
+
const [isSigning, setIsSigning] = useState5(false);
|
|
1187
|
+
const handleSign = async () => {
|
|
1188
|
+
setIsSigning(true);
|
|
1189
|
+
setSignResultState({ status: "loading" });
|
|
1190
|
+
try {
|
|
1191
|
+
if (token) setSdkAuth(token);
|
|
1192
|
+
if (!messageText.trim()) throw new Error("Please enter a message to sign.");
|
|
1193
|
+
if (signingMethod === "wallet-adapter") {
|
|
1194
|
+
if (!walletAdapterSignMessage) {
|
|
1195
|
+
throw new Error("Wallet adapter signMessage not provided.");
|
|
1196
|
+
}
|
|
1197
|
+
const message = new TextEncoder().encode(messageText);
|
|
1198
|
+
const signature = await walletAdapterSignMessage(message);
|
|
1199
|
+
setSignResultState({
|
|
1200
|
+
status: "success",
|
|
1201
|
+
message: "Message signed successfully!",
|
|
1202
|
+
detail: {
|
|
1203
|
+
message: messageText,
|
|
1204
|
+
signature: Array.from(signature).map((b) => b.toString(16).padStart(2, "0")).join(""),
|
|
1205
|
+
publicKey: walletAdapterPublicKey ?? void 0
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
if (!selectedSigner || !selectedWalletId) throw new Error("Please select a signer.");
|
|
1211
|
+
const result = await signMessageWithSigner(selectedWalletId, selectedSigner, messageText);
|
|
1212
|
+
const { signerType } = extractSignerInfo(selectedSigner);
|
|
1213
|
+
setSignResultState({
|
|
1214
|
+
status: "success",
|
|
1215
|
+
message: `Message signed successfully with ${signerType} signer!`,
|
|
1216
|
+
detail: { message: messageText, ...result }
|
|
1217
|
+
});
|
|
1218
|
+
} catch (error) {
|
|
1219
|
+
const errorMsg = extractErrorMessage(error);
|
|
1220
|
+
const isCancelled = errorMsg.toLowerCase().includes("cancel") || errorMsg.toLowerCase().includes("notallowederror");
|
|
1221
|
+
setSignResultState({
|
|
1222
|
+
status: "error",
|
|
1223
|
+
message: isCancelled ? "User cancelled authentication" : errorMsg
|
|
1224
|
+
});
|
|
1225
|
+
} finally {
|
|
1226
|
+
setIsSigning(false);
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
const reset = () => {
|
|
1230
|
+
setMessageText("Hello, Solana!");
|
|
1231
|
+
setSignResultState({ status: "idle" });
|
|
1232
|
+
};
|
|
1233
|
+
return {
|
|
1234
|
+
messageText,
|
|
1235
|
+
setMessageText,
|
|
1236
|
+
signResultState,
|
|
1237
|
+
isSigning,
|
|
1238
|
+
handleSign,
|
|
1239
|
+
reset
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// src/hooks/useTransactionSigning.ts
|
|
1244
|
+
import { useState as useState6 } from "react";
|
|
1245
|
+
function useTransactionSigning(options) {
|
|
1246
|
+
const {
|
|
1247
|
+
token,
|
|
1248
|
+
signingMethod,
|
|
1249
|
+
selectedSigner,
|
|
1250
|
+
selectedWalletId,
|
|
1251
|
+
walletAdapterSignTransaction,
|
|
1252
|
+
walletAdapterPublicKey,
|
|
1253
|
+
connection
|
|
1254
|
+
} = options;
|
|
1255
|
+
const [transactionResultState, setTransactionResultState] = useState6({ status: "idle" });
|
|
1256
|
+
const [isSigningTransaction, setIsSigningTransaction] = useState6(false);
|
|
1257
|
+
const [isSendingTransaction, setIsSendingTransaction] = useState6(false);
|
|
1258
|
+
const signTransaction = async (transaction) => {
|
|
1259
|
+
setIsSigningTransaction(true);
|
|
1260
|
+
setTransactionResultState({ status: "loading" });
|
|
1261
|
+
try {
|
|
1262
|
+
if (token) setSdkAuth(token);
|
|
1263
|
+
if (signingMethod === "wallet-adapter") {
|
|
1264
|
+
if (!walletAdapterSignTransaction || !walletAdapterPublicKey) {
|
|
1265
|
+
throw new Error("Wallet adapter signTransaction or publicKey not provided.");
|
|
1266
|
+
}
|
|
1267
|
+
const signed = await walletAdapterSignTransaction(transaction);
|
|
1268
|
+
setTransactionResultState({
|
|
1269
|
+
status: "success",
|
|
1270
|
+
message: "Transaction signed successfully!",
|
|
1271
|
+
detail: { transaction: signed.serialize().toString("base64") }
|
|
1272
|
+
});
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
if (!selectedSigner || !selectedWalletId) throw new Error("Please select a signer.");
|
|
1276
|
+
const { signerId, signerType } = extractSignerInfo(selectedSigner);
|
|
1277
|
+
if (signerType === "passkey") {
|
|
1278
|
+
throw new Error("Passkey signers cannot be used for sign-only. Use sign-and-send instead.");
|
|
1279
|
+
}
|
|
1280
|
+
const transactionBuffer = transaction.serializeMessage();
|
|
1281
|
+
const result = await signTransactionWithSigner(
|
|
1282
|
+
selectedWalletId,
|
|
1283
|
+
selectedSigner,
|
|
1284
|
+
transactionBuffer
|
|
1285
|
+
);
|
|
1286
|
+
setTransactionResultState({
|
|
1287
|
+
status: "success",
|
|
1288
|
+
message: `Transaction signed with ${signerType} signer!`,
|
|
1289
|
+
detail: { ...result }
|
|
1290
|
+
});
|
|
1291
|
+
} catch (error) {
|
|
1292
|
+
const errorMsg = extractErrorMessage(error);
|
|
1293
|
+
const isCancelled = errorMsg.toLowerCase().includes("cancel") || errorMsg.toLowerCase().includes("notallowederror");
|
|
1294
|
+
setTransactionResultState({
|
|
1295
|
+
status: "error",
|
|
1296
|
+
message: isCancelled ? "User cancelled authentication" : errorMsg
|
|
1297
|
+
});
|
|
1298
|
+
} finally {
|
|
1299
|
+
setIsSigningTransaction(false);
|
|
1300
|
+
}
|
|
1301
|
+
};
|
|
1302
|
+
const signAndSendTransaction = async (transaction) => {
|
|
1303
|
+
setIsSendingTransaction(true);
|
|
1304
|
+
setTransactionResultState({ status: "loading" });
|
|
1305
|
+
try {
|
|
1306
|
+
if (token) setSdkAuth(token);
|
|
1307
|
+
if (signingMethod === "wallet-adapter") {
|
|
1308
|
+
if (!walletAdapterSignTransaction || !walletAdapterPublicKey || !connection) {
|
|
1309
|
+
throw new Error("Wallet adapter signTransaction, publicKey, or connection not provided.");
|
|
1310
|
+
}
|
|
1311
|
+
const signed = await walletAdapterSignTransaction(transaction);
|
|
1312
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
|
|
1313
|
+
const signature = await connection.sendRawTransaction(Buffer.from(signed.serialize()));
|
|
1314
|
+
await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight });
|
|
1315
|
+
setTransactionResultState({
|
|
1316
|
+
status: "success",
|
|
1317
|
+
message: "Transaction sent successfully!",
|
|
1318
|
+
detail: { signature, explorerUrl: `https://solscan.io/tx/${signature}?cluster=devnet` }
|
|
1319
|
+
});
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
if (!selectedSigner || !selectedWalletId) throw new Error("Please select a signer.");
|
|
1323
|
+
const { signerType } = extractSignerInfo(selectedSigner);
|
|
1324
|
+
const result = await signAndSendTransactionWithSigner(
|
|
1325
|
+
selectedWalletId,
|
|
1326
|
+
selectedSigner,
|
|
1327
|
+
transaction,
|
|
1328
|
+
connection ?? void 0
|
|
1329
|
+
);
|
|
1330
|
+
setTransactionResultState({
|
|
1331
|
+
status: "success",
|
|
1332
|
+
message: `Transaction sent with ${signerType} signer!`,
|
|
1333
|
+
detail: {
|
|
1334
|
+
signature: result.signature,
|
|
1335
|
+
confirmationStatus: result.confirmationStatus,
|
|
1336
|
+
explorerUrl: `https://solscan.io/tx/${result.signature}?cluster=devnet`
|
|
1337
|
+
}
|
|
1338
|
+
});
|
|
1339
|
+
} catch (error) {
|
|
1340
|
+
const errorMsg = extractErrorMessage(error);
|
|
1341
|
+
const isCancelled = errorMsg.toLowerCase().includes("cancel") || errorMsg.toLowerCase().includes("notallowederror");
|
|
1342
|
+
setTransactionResultState({
|
|
1343
|
+
status: "error",
|
|
1344
|
+
message: isCancelled ? "User cancelled transaction" : errorMsg,
|
|
1345
|
+
detail: { error: errorMsg, walletId: selectedWalletId }
|
|
1346
|
+
});
|
|
1347
|
+
} finally {
|
|
1348
|
+
setIsSendingTransaction(false);
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
const reset = () => {
|
|
1352
|
+
setTransactionResultState({ status: "idle" });
|
|
1353
|
+
};
|
|
1354
|
+
return {
|
|
1355
|
+
transactionResultState,
|
|
1356
|
+
isSigningTransaction,
|
|
1357
|
+
isSendingTransaction,
|
|
1358
|
+
signTransaction,
|
|
1359
|
+
signAndSendTransaction,
|
|
1360
|
+
reset
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
// src/ui/select.tsx
|
|
1365
|
+
import * as React from "react";
|
|
1366
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
1367
|
+
|
|
1368
|
+
// src/ui/cn.ts
|
|
1369
|
+
import { clsx } from "clsx";
|
|
1370
|
+
import { twMerge } from "tailwind-merge";
|
|
1371
|
+
function cn(...inputs) {
|
|
1372
|
+
return twMerge(clsx(inputs));
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
// src/ui/select.tsx
|
|
1376
|
+
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
1377
|
+
var Select = SelectPrimitive.Root;
|
|
1378
|
+
var SelectValue = SelectPrimitive.Value;
|
|
1379
|
+
var SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
1380
|
+
SelectPrimitive.Trigger,
|
|
1381
|
+
{
|
|
1382
|
+
ref,
|
|
1383
|
+
className: cn(
|
|
1384
|
+
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
1385
|
+
className
|
|
1386
|
+
),
|
|
1387
|
+
...props,
|
|
1388
|
+
children: [
|
|
1389
|
+
children,
|
|
1390
|
+
/* @__PURE__ */ jsx4(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx4("span", { className: "ml-2 h-4 w-4 shrink-0 opacity-50", children: "\u25BC" }) })
|
|
1391
|
+
]
|
|
1392
|
+
}
|
|
1393
|
+
));
|
|
1394
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
1395
|
+
var SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx4(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsx4(
|
|
1396
|
+
SelectPrimitive.Content,
|
|
1397
|
+
{
|
|
1398
|
+
ref,
|
|
1399
|
+
className: cn(
|
|
1400
|
+
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
1401
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
1402
|
+
className
|
|
1403
|
+
),
|
|
1404
|
+
position,
|
|
1405
|
+
...props,
|
|
1406
|
+
children: /* @__PURE__ */ jsx4(
|
|
1407
|
+
SelectPrimitive.Viewport,
|
|
1408
|
+
{
|
|
1409
|
+
className: cn(
|
|
1410
|
+
"p-1",
|
|
1411
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
1412
|
+
),
|
|
1413
|
+
children
|
|
1414
|
+
}
|
|
1415
|
+
)
|
|
1416
|
+
}
|
|
1417
|
+
) }));
|
|
1418
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
1419
|
+
var SelectItem = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
1420
|
+
SelectPrimitive.Item,
|
|
1421
|
+
{
|
|
1422
|
+
ref,
|
|
1423
|
+
className: cn(
|
|
1424
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
1425
|
+
className
|
|
1426
|
+
),
|
|
1427
|
+
...props,
|
|
1428
|
+
children: [
|
|
1429
|
+
/* @__PURE__ */ jsx4("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx4(SelectPrimitive.ItemIndicator, { children: "\u2713" }) }),
|
|
1430
|
+
/* @__PURE__ */ jsx4(SelectPrimitive.ItemText, { children })
|
|
1431
|
+
]
|
|
1432
|
+
}
|
|
1433
|
+
));
|
|
1434
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
1435
|
+
|
|
1436
|
+
// src/components/WalletSelector.tsx
|
|
1437
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1438
|
+
function WalletSelector(props) {
|
|
1439
|
+
const {
|
|
1440
|
+
value,
|
|
1441
|
+
onWalletChange,
|
|
1442
|
+
className,
|
|
1443
|
+
classNames,
|
|
1444
|
+
placeholder = "Select a wallet",
|
|
1445
|
+
renderTrigger,
|
|
1446
|
+
renderList,
|
|
1447
|
+
children
|
|
1448
|
+
} = props;
|
|
1449
|
+
const { wallets, selectedWallet, selectWallet, isLoading, refreshWallets } = useWallets();
|
|
1450
|
+
const effectiveValue = value ?? selectedWallet?.id ?? selectedWallet?.walletId ?? "";
|
|
1451
|
+
const selected = wallets.find((w) => w.id === effectiveValue || w.walletId === effectiveValue) ?? selectedWallet;
|
|
1452
|
+
const handleSelect = (wallet) => {
|
|
1453
|
+
selectWallet(wallet.id);
|
|
1454
|
+
onWalletChange?.(wallet.id, wallet);
|
|
1455
|
+
};
|
|
1456
|
+
const handleValueChange = (id) => {
|
|
1457
|
+
selectWallet(id);
|
|
1458
|
+
onWalletChange?.(id, wallets.find((w) => w.id === id || w.walletId === id) ?? null);
|
|
1459
|
+
};
|
|
1460
|
+
if (children) {
|
|
1461
|
+
return /* @__PURE__ */ jsx5(Fragment, { children: children({
|
|
1462
|
+
wallets,
|
|
1463
|
+
selectedWallet: selected,
|
|
1464
|
+
selectWallet: (id) => {
|
|
1465
|
+
selectWallet(id);
|
|
1466
|
+
onWalletChange?.(id, wallets.find((w) => w.id === id || w.walletId === id) ?? null);
|
|
1467
|
+
},
|
|
1468
|
+
isLoading,
|
|
1469
|
+
refreshWallets
|
|
1470
|
+
}) });
|
|
1471
|
+
}
|
|
1472
|
+
if (renderTrigger || renderList) {
|
|
1473
|
+
return /* @__PURE__ */ jsxs2("div", { className: cn(className, classNames?.root), "data-cilantro-wallet-selector": true, children: [
|
|
1474
|
+
renderTrigger?.({ selectedWallet: selected, wallets, isLoading, open: false, setOpen: () => {
|
|
1475
|
+
} }),
|
|
1476
|
+
renderList?.({ wallets, selectedWallet: selected, onSelect: handleSelect, isLoading })
|
|
1477
|
+
] });
|
|
1478
|
+
}
|
|
1479
|
+
return /* @__PURE__ */ jsx5("div", { className: cn(className, classNames?.root), "data-cilantro-wallet-selector": true, children: /* @__PURE__ */ jsxs2(Select, { value: effectiveValue || void 0, onValueChange: handleValueChange, disabled: isLoading, children: [
|
|
1480
|
+
/* @__PURE__ */ jsx5(SelectTrigger, { className: classNames?.trigger, "aria-label": "Select wallet", children: /* @__PURE__ */ jsx5(SelectValue, { placeholder: isLoading ? "Loading..." : placeholder }) }),
|
|
1481
|
+
/* @__PURE__ */ jsx5(SelectContent, { className: classNames?.content, children: wallets.map((w) => /* @__PURE__ */ jsx5(SelectItem, { value: w.id, className: classNames?.item, children: w.walletName || w.id }, w.id)) })
|
|
1482
|
+
] }) });
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
// src/ui/button.tsx
|
|
1486
|
+
import * as React2 from "react";
|
|
1487
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
1488
|
+
import { cva } from "class-variance-authority";
|
|
1489
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1490
|
+
var buttonVariants = cva(
|
|
1491
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
1492
|
+
{
|
|
1493
|
+
variants: {
|
|
1494
|
+
variant: {
|
|
1495
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
1496
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
1497
|
+
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
1498
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
1499
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
1500
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
1501
|
+
},
|
|
1502
|
+
size: {
|
|
1503
|
+
default: "h-10 px-4 py-2",
|
|
1504
|
+
sm: "h-9 rounded-md px-3",
|
|
1505
|
+
lg: "h-11 rounded-md px-8",
|
|
1506
|
+
icon: "h-10 w-10"
|
|
1507
|
+
}
|
|
1508
|
+
},
|
|
1509
|
+
defaultVariants: {
|
|
1510
|
+
variant: "default",
|
|
1511
|
+
size: "default"
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
);
|
|
1515
|
+
var Button = React2.forwardRef(
|
|
1516
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
1517
|
+
const Comp = asChild ? Slot : "button";
|
|
1518
|
+
return /* @__PURE__ */ jsx6(Comp, { className: cn(buttonVariants({ variant, size, className })), ref, ...props });
|
|
1519
|
+
}
|
|
1520
|
+
);
|
|
1521
|
+
Button.displayName = "Button";
|
|
1522
|
+
|
|
1523
|
+
// src/components/SignerSelector.tsx
|
|
1524
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1525
|
+
function SignerSelector({
|
|
1526
|
+
selectedWalletId,
|
|
1527
|
+
availableSigners,
|
|
1528
|
+
selectedSigner,
|
|
1529
|
+
isLoadingSigners = false,
|
|
1530
|
+
onSignerSelect,
|
|
1531
|
+
className,
|
|
1532
|
+
classNames,
|
|
1533
|
+
renderList,
|
|
1534
|
+
children
|
|
1535
|
+
}) {
|
|
1536
|
+
if (!selectedWalletId) return null;
|
|
1537
|
+
if (children) {
|
|
1538
|
+
return /* @__PURE__ */ jsx7(Fragment2, { children: children({ signers: availableSigners, selectedSigner, onSignerSelect, isLoading: isLoadingSigners }) });
|
|
1539
|
+
}
|
|
1540
|
+
if (renderList) {
|
|
1541
|
+
return /* @__PURE__ */ jsx7("div", { className: cn(className, classNames?.root), "data-cilantro-signer-selector": true, children: renderList({
|
|
1542
|
+
signers: availableSigners,
|
|
1543
|
+
selectedSigner,
|
|
1544
|
+
onSelect: onSignerSelect,
|
|
1545
|
+
isLoading: isLoadingSigners,
|
|
1546
|
+
getSignerDisplayName,
|
|
1547
|
+
getSignerTypeLabel,
|
|
1548
|
+
getSignerUniqueId
|
|
1549
|
+
}) });
|
|
1550
|
+
}
|
|
1551
|
+
return /* @__PURE__ */ jsx7("div", { className: cn(className, classNames?.root), "data-cilantro-signer-selector": true, role: "listbox", "aria-label": "Select signer", children: isLoadingSigners ? /* @__PURE__ */ jsx7("p", { className: cn("text-sm text-muted-foreground", classNames?.message), children: "Loading signers..." }) : availableSigners.length === 0 ? /* @__PURE__ */ jsx7("p", { className: cn("text-sm text-muted-foreground", classNames?.message), children: "No signers for this wallet." }) : /* @__PURE__ */ jsx7("ul", { className: cn("space-y-1", classNames?.list), children: availableSigners.map((signer) => /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsxs3(
|
|
1552
|
+
Button,
|
|
1553
|
+
{
|
|
1554
|
+
type: "button",
|
|
1555
|
+
variant: selectedSigner?.id === signer.id ? "secondary" : "ghost",
|
|
1556
|
+
size: "sm",
|
|
1557
|
+
className: cn("w-full justify-start", classNames?.item),
|
|
1558
|
+
onClick: () => onSignerSelect(signer),
|
|
1559
|
+
"aria-pressed": selectedSigner?.id === signer.id,
|
|
1560
|
+
children: [
|
|
1561
|
+
getSignerDisplayName(signer),
|
|
1562
|
+
" (",
|
|
1563
|
+
getSignerTypeLabel(signer.type || signer.signerType || ""),
|
|
1564
|
+
")"
|
|
1565
|
+
]
|
|
1566
|
+
}
|
|
1567
|
+
) }, signer.id)) }) });
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// src/components/DelegatedKeySelector.tsx
|
|
1571
|
+
import { useState as useState7, useEffect as useEffect6, useCallback as useCallback3 } from "react";
|
|
1572
|
+
import { findAll } from "cilantro-sdk/delegated-keys";
|
|
1573
|
+
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1574
|
+
function DelegatedKeySelector(props) {
|
|
1575
|
+
const {
|
|
1576
|
+
walletId,
|
|
1577
|
+
value,
|
|
1578
|
+
onChange,
|
|
1579
|
+
filterActive = true,
|
|
1580
|
+
className,
|
|
1581
|
+
classNames,
|
|
1582
|
+
placeholder = "Select a delegated key",
|
|
1583
|
+
children
|
|
1584
|
+
} = props;
|
|
1585
|
+
const { token } = useCilantroAuth();
|
|
1586
|
+
const [keys, setKeys] = useState7([]);
|
|
1587
|
+
const [isLoading, setIsLoading] = useState7(false);
|
|
1588
|
+
const [error, setError] = useState7(null);
|
|
1589
|
+
const loadKeys = useCallback3(async () => {
|
|
1590
|
+
if (!walletId) {
|
|
1591
|
+
setKeys([]);
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
setIsLoading(true);
|
|
1595
|
+
setError(null);
|
|
1596
|
+
try {
|
|
1597
|
+
if (token) setSdkAuth(token);
|
|
1598
|
+
const result = await findAll(walletId);
|
|
1599
|
+
const keysData = extractResponseData(result) ?? [];
|
|
1600
|
+
const list = Array.isArray(keysData) ? keysData : [];
|
|
1601
|
+
let loaded = list.filter((k) => k != null && typeof k === "object" && "id" in k).map((k) => ({
|
|
1602
|
+
id: String(k.id ?? ""),
|
|
1603
|
+
walletId: String(k.walletId ?? ""),
|
|
1604
|
+
name: k.name,
|
|
1605
|
+
publicKey: String(k.publicKey ?? ""),
|
|
1606
|
+
permissions: k.permissions ?? {},
|
|
1607
|
+
isActive: k.isActive !== false,
|
|
1608
|
+
createdAt: k.createdAt,
|
|
1609
|
+
expiresAt: k.expiresAt,
|
|
1610
|
+
...k
|
|
1611
|
+
}));
|
|
1612
|
+
if (filterActive) {
|
|
1613
|
+
const now = Date.now();
|
|
1614
|
+
loaded = loaded.filter((key) => {
|
|
1615
|
+
if (!key.isActive) return false;
|
|
1616
|
+
const exp = key.expiresAt ? new Date(key.expiresAt).getTime() : null;
|
|
1617
|
+
return exp === null || exp > now;
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
setKeys(loaded);
|
|
1621
|
+
} catch (err) {
|
|
1622
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
1623
|
+
setKeys([]);
|
|
1624
|
+
} finally {
|
|
1625
|
+
setIsLoading(false);
|
|
1626
|
+
}
|
|
1627
|
+
}, [walletId, token, filterActive]);
|
|
1628
|
+
useEffect6(() => {
|
|
1629
|
+
loadKeys();
|
|
1630
|
+
}, [loadKeys]);
|
|
1631
|
+
const onSelect = (key) => {
|
|
1632
|
+
onChange?.(key.id, key);
|
|
1633
|
+
};
|
|
1634
|
+
const handleValueChange = (id) => {
|
|
1635
|
+
const key = keys.find((k) => k.id === id) ?? null;
|
|
1636
|
+
onChange?.(id, key);
|
|
1637
|
+
};
|
|
1638
|
+
if (children) {
|
|
1639
|
+
return /* @__PURE__ */ jsx8(Fragment3, { children: children({ keys, selectedKeyId: value, onSelect, isLoading, error, refresh: loadKeys }) });
|
|
1640
|
+
}
|
|
1641
|
+
if (!walletId) {
|
|
1642
|
+
return /* @__PURE__ */ jsx8("div", { className: cn(className, classNames?.root, "text-sm text-muted-foreground"), children: "Select a wallet first" });
|
|
1643
|
+
}
|
|
1644
|
+
return /* @__PURE__ */ jsx8("div", { className: cn(className, classNames?.root), "data-cilantro-delegated-key-selector": true, children: isLoading ? /* @__PURE__ */ jsx8("p", { className: cn("text-sm text-muted-foreground", classNames?.message), children: "Loading delegated keys..." }) : error ? /* @__PURE__ */ jsx8("p", { className: cn("text-sm text-destructive", classNames?.message), role: "alert", children: error }) : keys.length === 0 ? /* @__PURE__ */ jsx8("p", { className: cn("text-sm text-muted-foreground", classNames?.message), children: "No delegated keys found." }) : /* @__PURE__ */ jsxs4(Select, { value: (value ?? "") || void 0, onValueChange: handleValueChange, children: [
|
|
1645
|
+
/* @__PURE__ */ jsx8(SelectTrigger, { className: classNames?.trigger, "aria-label": "Select delegated key", children: /* @__PURE__ */ jsx8(SelectValue, { placeholder }) }),
|
|
1646
|
+
/* @__PURE__ */ jsx8(SelectContent, { className: classNames?.content, children: keys.map((k) => /* @__PURE__ */ jsx8(SelectItem, { value: k.id, className: classNames?.item, children: k.name || k.publicKey.slice(0, 8) + "..." }, k.id)) })
|
|
1647
|
+
] }) });
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
// src/ui/textarea.tsx
|
|
1651
|
+
import * as React3 from "react";
|
|
1652
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1653
|
+
var Textarea = React3.forwardRef(
|
|
1654
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
|
|
1655
|
+
"textarea",
|
|
1656
|
+
{
|
|
1657
|
+
className: cn(
|
|
1658
|
+
"flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
1659
|
+
className
|
|
1660
|
+
),
|
|
1661
|
+
ref,
|
|
1662
|
+
...props
|
|
1663
|
+
}
|
|
1664
|
+
)
|
|
1665
|
+
);
|
|
1666
|
+
Textarea.displayName = "Textarea";
|
|
1667
|
+
|
|
1668
|
+
// src/ui/label.tsx
|
|
1669
|
+
import * as React4 from "react";
|
|
1670
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
1671
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
1672
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1673
|
+
var labelVariants = cva2(
|
|
1674
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
1675
|
+
);
|
|
1676
|
+
var Label = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx10(LabelPrimitive.Root, { ref, className: cn(labelVariants(), className), ...props }));
|
|
1677
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
1678
|
+
|
|
1679
|
+
// src/ui/card.tsx
|
|
1680
|
+
import * as React5 from "react";
|
|
1681
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
1682
|
+
var Card = React5.forwardRef(
|
|
1683
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
|
|
1684
|
+
"div",
|
|
1685
|
+
{
|
|
1686
|
+
ref,
|
|
1687
|
+
className: cn("rounded-xl border bg-card text-card-foreground shadow", className),
|
|
1688
|
+
...props
|
|
1689
|
+
}
|
|
1690
|
+
)
|
|
1691
|
+
);
|
|
1692
|
+
Card.displayName = "Card";
|
|
1693
|
+
var CardHeader = React5.forwardRef(
|
|
1694
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx11("div", { ref, className: cn("flex flex-col space-y-1.5 p-6", className), ...props })
|
|
1695
|
+
);
|
|
1696
|
+
CardHeader.displayName = "CardHeader";
|
|
1697
|
+
var CardTitle = React5.forwardRef(
|
|
1698
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
|
|
1699
|
+
"h3",
|
|
1700
|
+
{
|
|
1701
|
+
ref,
|
|
1702
|
+
className: cn("font-semibold leading-none tracking-tight", className),
|
|
1703
|
+
...props
|
|
1704
|
+
}
|
|
1705
|
+
)
|
|
1706
|
+
);
|
|
1707
|
+
CardTitle.displayName = "CardTitle";
|
|
1708
|
+
var CardDescription = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11("p", { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
|
|
1709
|
+
CardDescription.displayName = "CardDescription";
|
|
1710
|
+
var CardContent = React5.forwardRef(
|
|
1711
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx11("div", { ref, className: cn("p-6 pt-0", className), ...props })
|
|
1712
|
+
);
|
|
1713
|
+
CardContent.displayName = "CardContent";
|
|
1714
|
+
var CardFooter = React5.forwardRef(
|
|
1715
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx11("div", { ref, className: cn("flex items-center p-6 pt-0", className), ...props })
|
|
1716
|
+
);
|
|
1717
|
+
CardFooter.displayName = "CardFooter";
|
|
1718
|
+
|
|
1719
|
+
// src/components/MessageSigningForm.tsx
|
|
1720
|
+
import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1721
|
+
function MessageSigningForm({
|
|
1722
|
+
token: tokenOverride,
|
|
1723
|
+
selectedWalletId: walletIdOverride,
|
|
1724
|
+
selectedSigner: signerOverride,
|
|
1725
|
+
signingMethod: methodOverride,
|
|
1726
|
+
walletAdapterSignMessage,
|
|
1727
|
+
walletAdapterPublicKey,
|
|
1728
|
+
className,
|
|
1729
|
+
classNames,
|
|
1730
|
+
showContext = true,
|
|
1731
|
+
showCharCount = false,
|
|
1732
|
+
children
|
|
1733
|
+
}) {
|
|
1734
|
+
const { token: contextToken } = useCilantroAuth();
|
|
1735
|
+
const token = tokenOverride ?? contextToken;
|
|
1736
|
+
const selection = useSignerSelection({
|
|
1737
|
+
signingMethod: methodOverride ?? "sdk-signer",
|
|
1738
|
+
walletId: walletIdOverride
|
|
1739
|
+
});
|
|
1740
|
+
const selectedWalletId = walletIdOverride ?? selection.selectedWalletId;
|
|
1741
|
+
const selectedSigner = signerOverride ?? selection.selectedSigner;
|
|
1742
|
+
const signingMethod = methodOverride ?? "sdk-signer";
|
|
1743
|
+
const signing = useMessageSigning({
|
|
1744
|
+
token,
|
|
1745
|
+
signingMethod,
|
|
1746
|
+
selectedSigner,
|
|
1747
|
+
selectedWalletId,
|
|
1748
|
+
walletAdapterSignMessage,
|
|
1749
|
+
walletAdapterPublicKey
|
|
1750
|
+
});
|
|
1751
|
+
if (children) {
|
|
1752
|
+
return /* @__PURE__ */ jsx12("div", { className: cn(className, classNames?.root), "data-cilantro-message-signing-form": true, children: children({
|
|
1753
|
+
messageText: signing.messageText,
|
|
1754
|
+
setMessageText: signing.setMessageText,
|
|
1755
|
+
signResultState: signing.signResultState,
|
|
1756
|
+
isSigning: signing.isSigning,
|
|
1757
|
+
handleSign: signing.handleSign,
|
|
1758
|
+
reset: signing.reset
|
|
1759
|
+
}) });
|
|
1760
|
+
}
|
|
1761
|
+
const resultStatus = signing.signResultState.status;
|
|
1762
|
+
const isSuccess = resultStatus === "success";
|
|
1763
|
+
const isError = resultStatus === "error";
|
|
1764
|
+
return /* @__PURE__ */ jsxs5(Card, { className: cn(className, classNames?.root), "data-cilantro-message-signing-form": true, children: [
|
|
1765
|
+
/* @__PURE__ */ jsxs5(CardHeader, { className: classNames?.header, children: [
|
|
1766
|
+
/* @__PURE__ */ jsx12(CardTitle, { className: cn("text-lg", classNames?.title), children: "Sign message" }),
|
|
1767
|
+
/* @__PURE__ */ jsx12(CardDescription, { className: classNames?.description, children: "Sign a message with your selected wallet or signer. The signature proves you control the key." }),
|
|
1768
|
+
showContext && (selectedWalletId || selectedSigner) && /* @__PURE__ */ jsxs5("p", { className: cn("mt-1 text-xs text-muted-foreground", classNames?.context), children: [
|
|
1769
|
+
selectedWalletId && /* @__PURE__ */ jsxs5("span", { children: [
|
|
1770
|
+
"Wallet: ",
|
|
1771
|
+
selectedWalletId.slice(0, 8),
|
|
1772
|
+
"\u2026"
|
|
1773
|
+
] }),
|
|
1774
|
+
selectedSigner && /* @__PURE__ */ jsxs5("span", { className: selectedWalletId ? " ml-2" : "", children: [
|
|
1775
|
+
"Signer: ",
|
|
1776
|
+
getSignerDisplayName(selectedSigner),
|
|
1777
|
+
" (",
|
|
1778
|
+
getSignerTypeLabel(selectedSigner.type || selectedSigner.signerType || ""),
|
|
1779
|
+
")"
|
|
1780
|
+
] })
|
|
1781
|
+
] })
|
|
1782
|
+
] }),
|
|
1783
|
+
/* @__PURE__ */ jsxs5(CardContent, { className: "space-y-4", children: [
|
|
1784
|
+
/* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
|
|
1785
|
+
/* @__PURE__ */ jsx12(Label, { htmlFor: "cilantro-message-text", className: classNames?.label, children: "Message" }),
|
|
1786
|
+
/* @__PURE__ */ jsx12(
|
|
1787
|
+
Textarea,
|
|
1788
|
+
{
|
|
1789
|
+
id: "cilantro-message-text",
|
|
1790
|
+
className: classNames?.textarea,
|
|
1791
|
+
value: signing.messageText,
|
|
1792
|
+
onChange: (e) => signing.setMessageText(e.target.value),
|
|
1793
|
+
placeholder: "Enter the message you want to sign...",
|
|
1794
|
+
rows: 4
|
|
1795
|
+
}
|
|
1796
|
+
),
|
|
1797
|
+
showCharCount && /* @__PURE__ */ jsxs5("p", { className: cn("text-right text-xs text-muted-foreground", classNames?.charCount), children: [
|
|
1798
|
+
signing.messageText.length,
|
|
1799
|
+
" character",
|
|
1800
|
+
signing.messageText.length !== 1 ? "s" : ""
|
|
1801
|
+
] })
|
|
1802
|
+
] }),
|
|
1803
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-center", children: [
|
|
1804
|
+
/* @__PURE__ */ jsx12(
|
|
1805
|
+
Button,
|
|
1806
|
+
{
|
|
1807
|
+
type: "button",
|
|
1808
|
+
className: cn("w-full sm:w-auto", classNames?.button),
|
|
1809
|
+
onClick: signing.handleSign,
|
|
1810
|
+
disabled: signing.isSigning || !signing.messageText.trim(),
|
|
1811
|
+
children: signing.isSigning ? "Signing..." : "Sign message"
|
|
1812
|
+
}
|
|
1813
|
+
),
|
|
1814
|
+
/* @__PURE__ */ jsx12(
|
|
1815
|
+
Button,
|
|
1816
|
+
{
|
|
1817
|
+
type: "button",
|
|
1818
|
+
variant: "outline",
|
|
1819
|
+
className: cn("w-full sm:w-auto", classNames?.resetButton),
|
|
1820
|
+
onClick: signing.reset,
|
|
1821
|
+
disabled: signing.isSigning,
|
|
1822
|
+
children: "Clear"
|
|
1823
|
+
}
|
|
1824
|
+
)
|
|
1825
|
+
] }),
|
|
1826
|
+
resultStatus !== "idle" && /* @__PURE__ */ jsxs5(
|
|
1827
|
+
"div",
|
|
1828
|
+
{
|
|
1829
|
+
className: cn(
|
|
1830
|
+
"rounded-lg border px-3 py-2 text-sm",
|
|
1831
|
+
isSuccess && cn("border-green-500/50 bg-green-500/10 text-green-700 dark:text-green-400", classNames?.resultSuccess),
|
|
1832
|
+
isError && cn("border-destructive/50 bg-destructive/10 text-destructive", classNames?.resultError),
|
|
1833
|
+
resultStatus === "loading" && "border-muted bg-muted/50 text-muted-foreground",
|
|
1834
|
+
classNames?.result
|
|
1835
|
+
),
|
|
1836
|
+
"data-status": resultStatus,
|
|
1837
|
+
children: [
|
|
1838
|
+
signing.signResultState.message,
|
|
1839
|
+
signing.signResultState.detail != null && /* @__PURE__ */ jsx12("pre", { className: cn("mt-2 overflow-auto rounded-md bg-muted/80 p-2 text-xs", classNames?.resultPre), children: JSON.stringify(signing.signResultState.detail, null, 2) })
|
|
1840
|
+
]
|
|
1841
|
+
}
|
|
1842
|
+
)
|
|
1843
|
+
] })
|
|
1844
|
+
] });
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
// src/components/TransactionSigningForm.tsx
|
|
1848
|
+
import { jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1849
|
+
function TransactionSigningForm({
|
|
1850
|
+
token: tokenOverride,
|
|
1851
|
+
selectedWalletId: walletIdOverride,
|
|
1852
|
+
selectedSigner: signerOverride,
|
|
1853
|
+
signingMethod: methodOverride,
|
|
1854
|
+
walletAdapterSignTransaction,
|
|
1855
|
+
walletAdapterPublicKey,
|
|
1856
|
+
connection,
|
|
1857
|
+
className,
|
|
1858
|
+
classNames,
|
|
1859
|
+
showContext = true,
|
|
1860
|
+
children
|
|
1861
|
+
}) {
|
|
1862
|
+
const { token: contextToken } = useCilantroAuth();
|
|
1863
|
+
const token = tokenOverride ?? contextToken;
|
|
1864
|
+
const selection = useSignerSelection({
|
|
1865
|
+
signingMethod: methodOverride ?? "sdk-signer",
|
|
1866
|
+
walletId: walletIdOverride
|
|
1867
|
+
});
|
|
1868
|
+
const selectedWalletId = walletIdOverride ?? selection.selectedWalletId;
|
|
1869
|
+
const selectedSigner = signerOverride ?? selection.selectedSigner;
|
|
1870
|
+
const signingMethod = methodOverride ?? "sdk-signer";
|
|
1871
|
+
const signing = useTransactionSigning({
|
|
1872
|
+
token,
|
|
1873
|
+
signingMethod,
|
|
1874
|
+
selectedSigner,
|
|
1875
|
+
selectedWalletId,
|
|
1876
|
+
walletAdapterSignTransaction,
|
|
1877
|
+
walletAdapterPublicKey,
|
|
1878
|
+
connection
|
|
1879
|
+
});
|
|
1880
|
+
if (children) {
|
|
1881
|
+
return /* @__PURE__ */ jsx13("div", { className: cn(className, classNames?.root), "data-cilantro-transaction-signing-form": true, children: children({
|
|
1882
|
+
transactionResultState: signing.transactionResultState,
|
|
1883
|
+
isSigningTransaction: signing.isSigningTransaction,
|
|
1884
|
+
isSendingTransaction: signing.isSendingTransaction,
|
|
1885
|
+
signTransaction: signing.signTransaction,
|
|
1886
|
+
signAndSendTransaction: signing.signAndSendTransaction,
|
|
1887
|
+
reset: signing.reset
|
|
1888
|
+
}) });
|
|
1889
|
+
}
|
|
1890
|
+
const resultStatus = signing.transactionResultState.status;
|
|
1891
|
+
const isSuccess = resultStatus === "success";
|
|
1892
|
+
const isError = resultStatus === "error";
|
|
1893
|
+
return /* @__PURE__ */ jsxs6(Card, { className: cn(className, classNames?.root), "data-cilantro-transaction-signing-form": true, children: [
|
|
1894
|
+
/* @__PURE__ */ jsxs6(CardHeader, { className: classNames?.header, children: [
|
|
1895
|
+
/* @__PURE__ */ jsx13(CardTitle, { className: cn("text-lg", classNames?.title), children: "Sign transaction" }),
|
|
1896
|
+
/* @__PURE__ */ jsxs6(CardDescription, { className: classNames?.description, children: [
|
|
1897
|
+
"Build a transaction in your app, then pass it to ",
|
|
1898
|
+
/* @__PURE__ */ jsx13("code", { className: "text-xs", children: "signTransaction(tx)" }),
|
|
1899
|
+
" to sign only, or ",
|
|
1900
|
+
/* @__PURE__ */ jsx13("code", { className: "text-xs", children: "signAndSendTransaction(tx)" }),
|
|
1901
|
+
" to sign and send. Use the render props (children) to wire your own UI."
|
|
1902
|
+
] }),
|
|
1903
|
+
showContext && (selectedWalletId || selectedSigner) && /* @__PURE__ */ jsxs6("p", { className: cn("mt-1 text-xs text-muted-foreground", classNames?.context), children: [
|
|
1904
|
+
selectedWalletId && /* @__PURE__ */ jsxs6("span", { children: [
|
|
1905
|
+
"Wallet: ",
|
|
1906
|
+
selectedWalletId.slice(0, 8),
|
|
1907
|
+
"\u2026"
|
|
1908
|
+
] }),
|
|
1909
|
+
selectedSigner && /* @__PURE__ */ jsxs6("span", { className: selectedWalletId ? " ml-2" : "", children: [
|
|
1910
|
+
"Signer: ",
|
|
1911
|
+
getSignerDisplayName(selectedSigner),
|
|
1912
|
+
" (",
|
|
1913
|
+
getSignerTypeLabel(selectedSigner.type || selectedSigner.signerType || ""),
|
|
1914
|
+
")"
|
|
1915
|
+
] })
|
|
1916
|
+
] })
|
|
1917
|
+
] }),
|
|
1918
|
+
/* @__PURE__ */ jsxs6(CardContent, { className: "space-y-4", children: [
|
|
1919
|
+
/* @__PURE__ */ jsx13(
|
|
1920
|
+
Button,
|
|
1921
|
+
{
|
|
1922
|
+
type: "button",
|
|
1923
|
+
variant: "outline",
|
|
1924
|
+
className: classNames?.button,
|
|
1925
|
+
onClick: signing.reset,
|
|
1926
|
+
disabled: signing.isSigningTransaction || signing.isSendingTransaction,
|
|
1927
|
+
children: "Reset status"
|
|
1928
|
+
}
|
|
1929
|
+
),
|
|
1930
|
+
resultStatus !== "idle" && /* @__PURE__ */ jsxs6(
|
|
1931
|
+
"div",
|
|
1932
|
+
{
|
|
1933
|
+
className: cn(
|
|
1934
|
+
"rounded-lg border px-3 py-2 text-sm",
|
|
1935
|
+
isSuccess && cn("border-green-500/50 bg-green-500/10 text-green-700 dark:text-green-400", classNames?.resultSuccess),
|
|
1936
|
+
isError && cn("border-destructive/50 bg-destructive/10 text-destructive", classNames?.resultError),
|
|
1937
|
+
resultStatus === "loading" && "border-muted bg-muted/50 text-muted-foreground",
|
|
1938
|
+
classNames?.result
|
|
1939
|
+
),
|
|
1940
|
+
"data-status": resultStatus,
|
|
1941
|
+
children: [
|
|
1942
|
+
signing.transactionResultState.message,
|
|
1943
|
+
signing.transactionResultState.detail != null && /* @__PURE__ */ jsx13("pre", { className: cn("mt-2 overflow-auto rounded-md bg-muted/80 p-2 text-xs", classNames?.resultPre), children: JSON.stringify(signing.transactionResultState.detail, null, 2) })
|
|
1944
|
+
]
|
|
1945
|
+
}
|
|
1946
|
+
)
|
|
1947
|
+
] })
|
|
1948
|
+
] });
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
// src/components/LoginForm.tsx
|
|
1952
|
+
import { useState as useState8 } from "react";
|
|
1953
|
+
|
|
1954
|
+
// src/ui/input.tsx
|
|
1955
|
+
import * as React6 from "react";
|
|
1956
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1957
|
+
var Input = React6.forwardRef(
|
|
1958
|
+
({ className, type, ...props }, ref) => {
|
|
1959
|
+
return /* @__PURE__ */ jsx14(
|
|
1960
|
+
"input",
|
|
1961
|
+
{
|
|
1962
|
+
type,
|
|
1963
|
+
className: cn(
|
|
1964
|
+
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
1965
|
+
className
|
|
1966
|
+
),
|
|
1967
|
+
ref,
|
|
1968
|
+
...props
|
|
1969
|
+
}
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
);
|
|
1973
|
+
Input.displayName = "Input";
|
|
1974
|
+
|
|
1975
|
+
// src/components/LoginForm.tsx
|
|
1976
|
+
import { jsx as jsx15, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1977
|
+
function LoginForm({
|
|
1978
|
+
className,
|
|
1979
|
+
classNames,
|
|
1980
|
+
onSuccess,
|
|
1981
|
+
onError,
|
|
1982
|
+
submitLabel = "Sign in",
|
|
1983
|
+
title = "Sign in",
|
|
1984
|
+
description,
|
|
1985
|
+
renderSwitchToRegister
|
|
1986
|
+
}) {
|
|
1987
|
+
const { login, isLoading } = useCilantroAuth();
|
|
1988
|
+
const [usernameOrEmail, setUsernameOrEmail] = useState8("");
|
|
1989
|
+
const [password, setPassword] = useState8("");
|
|
1990
|
+
const [error, setError] = useState8(null);
|
|
1991
|
+
const handleSubmit = async (e) => {
|
|
1992
|
+
e.preventDefault();
|
|
1993
|
+
setError(null);
|
|
1994
|
+
try {
|
|
1995
|
+
await login(usernameOrEmail.trim(), password);
|
|
1996
|
+
onSuccess?.();
|
|
1997
|
+
} catch (err) {
|
|
1998
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1999
|
+
setError(message);
|
|
2000
|
+
onError?.(message);
|
|
2001
|
+
}
|
|
2002
|
+
};
|
|
2003
|
+
return /* @__PURE__ */ jsxs7(Card, { className: cn(className, classNames?.root), "data-cilantro-login-form": true, children: [
|
|
2004
|
+
/* @__PURE__ */ jsxs7(CardHeader, { className: classNames?.header, children: [
|
|
2005
|
+
/* @__PURE__ */ jsx15(CardTitle, { className: classNames?.title, children: title }),
|
|
2006
|
+
description != null && /* @__PURE__ */ jsx15(CardDescription, { className: classNames?.description, children: description })
|
|
2007
|
+
] }),
|
|
2008
|
+
/* @__PURE__ */ jsx15(CardContent, { children: /* @__PURE__ */ jsxs7("form", { onSubmit: handleSubmit, className: cn("space-y-4", classNames?.form), children: [
|
|
2009
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-2", children: [
|
|
2010
|
+
/* @__PURE__ */ jsx15(Label, { htmlFor: "cilantro-login-username", className: classNames?.label, children: "Username or email" }),
|
|
2011
|
+
/* @__PURE__ */ jsx15(
|
|
2012
|
+
Input,
|
|
2013
|
+
{
|
|
2014
|
+
id: "cilantro-login-username",
|
|
2015
|
+
type: "text",
|
|
2016
|
+
autoComplete: "username",
|
|
2017
|
+
className: classNames?.input,
|
|
2018
|
+
value: usernameOrEmail,
|
|
2019
|
+
onChange: (e) => setUsernameOrEmail(e.target.value),
|
|
2020
|
+
placeholder: "Username or email",
|
|
2021
|
+
required: true,
|
|
2022
|
+
disabled: isLoading
|
|
2023
|
+
}
|
|
2024
|
+
)
|
|
2025
|
+
] }),
|
|
2026
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-2", children: [
|
|
2027
|
+
/* @__PURE__ */ jsx15(Label, { htmlFor: "cilantro-login-password", className: classNames?.label, children: "Password" }),
|
|
2028
|
+
/* @__PURE__ */ jsx15(
|
|
2029
|
+
Input,
|
|
2030
|
+
{
|
|
2031
|
+
id: "cilantro-login-password",
|
|
2032
|
+
type: "password",
|
|
2033
|
+
autoComplete: "current-password",
|
|
2034
|
+
className: classNames?.input,
|
|
2035
|
+
value: password,
|
|
2036
|
+
onChange: (e) => setPassword(e.target.value),
|
|
2037
|
+
placeholder: "Password",
|
|
2038
|
+
required: true,
|
|
2039
|
+
disabled: isLoading
|
|
2040
|
+
}
|
|
2041
|
+
)
|
|
2042
|
+
] }),
|
|
2043
|
+
error && /* @__PURE__ */ jsx15(
|
|
2044
|
+
"div",
|
|
2045
|
+
{
|
|
2046
|
+
className: cn(
|
|
2047
|
+
"rounded-md border border-destructive/50 bg-destructive/10 px-3 py-2 text-sm text-destructive",
|
|
2048
|
+
classNames?.error
|
|
2049
|
+
),
|
|
2050
|
+
role: "alert",
|
|
2051
|
+
children: error
|
|
2052
|
+
}
|
|
2053
|
+
),
|
|
2054
|
+
/* @__PURE__ */ jsx15(
|
|
2055
|
+
Button,
|
|
2056
|
+
{
|
|
2057
|
+
type: "submit",
|
|
2058
|
+
className: cn("w-full", classNames?.submitButton),
|
|
2059
|
+
disabled: isLoading || !usernameOrEmail.trim() || !password,
|
|
2060
|
+
children: isLoading ? "Signing in..." : submitLabel
|
|
2061
|
+
}
|
|
2062
|
+
),
|
|
2063
|
+
renderSwitchToRegister && /* @__PURE__ */ jsx15("div", { className: "text-center text-sm text-muted-foreground", children: renderSwitchToRegister() })
|
|
2064
|
+
] }) })
|
|
2065
|
+
] });
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
// src/components/RegisterForm.tsx
|
|
2069
|
+
import { useState as useState9 } from "react";
|
|
2070
|
+
import { jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2071
|
+
function RegisterForm({
|
|
2072
|
+
className,
|
|
2073
|
+
classNames,
|
|
2074
|
+
onSuccess,
|
|
2075
|
+
onError,
|
|
2076
|
+
submitLabel = "Create account",
|
|
2077
|
+
title = "Create an account",
|
|
2078
|
+
description,
|
|
2079
|
+
isActive = true,
|
|
2080
|
+
renderSwitchToLogin
|
|
2081
|
+
}) {
|
|
2082
|
+
const { register, isLoading } = useCilantroAuth();
|
|
2083
|
+
const [username, setUsername] = useState9("");
|
|
2084
|
+
const [email, setEmail] = useState9("");
|
|
2085
|
+
const [password, setPassword] = useState9("");
|
|
2086
|
+
const [error, setError] = useState9(null);
|
|
2087
|
+
const handleSubmit = async (e) => {
|
|
2088
|
+
e.preventDefault();
|
|
2089
|
+
setError(null);
|
|
2090
|
+
try {
|
|
2091
|
+
await register(username.trim(), email.trim(), password, isActive);
|
|
2092
|
+
onSuccess?.();
|
|
2093
|
+
} catch (err) {
|
|
2094
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2095
|
+
setError(message);
|
|
2096
|
+
onError?.(message);
|
|
2097
|
+
}
|
|
2098
|
+
};
|
|
2099
|
+
return /* @__PURE__ */ jsxs8(Card, { className: cn(className, classNames?.root), "data-cilantro-register-form": true, children: [
|
|
2100
|
+
/* @__PURE__ */ jsxs8(CardHeader, { className: classNames?.header, children: [
|
|
2101
|
+
/* @__PURE__ */ jsx16(CardTitle, { className: classNames?.title, children: title }),
|
|
2102
|
+
description != null && /* @__PURE__ */ jsx16(CardDescription, { className: classNames?.description, children: description })
|
|
2103
|
+
] }),
|
|
2104
|
+
/* @__PURE__ */ jsx16(CardContent, { children: /* @__PURE__ */ jsxs8("form", { onSubmit: handleSubmit, className: cn("space-y-4", classNames?.form), children: [
|
|
2105
|
+
/* @__PURE__ */ jsxs8("div", { className: "space-y-2", children: [
|
|
2106
|
+
/* @__PURE__ */ jsx16(Label, { htmlFor: "cilantro-register-username", className: classNames?.label, children: "Username" }),
|
|
2107
|
+
/* @__PURE__ */ jsx16(
|
|
2108
|
+
Input,
|
|
2109
|
+
{
|
|
2110
|
+
id: "cilantro-register-username",
|
|
2111
|
+
type: "text",
|
|
2112
|
+
autoComplete: "username",
|
|
2113
|
+
className: classNames?.input,
|
|
2114
|
+
value: username,
|
|
2115
|
+
onChange: (e) => setUsername(e.target.value),
|
|
2116
|
+
placeholder: "Username",
|
|
2117
|
+
required: true,
|
|
2118
|
+
disabled: isLoading
|
|
2119
|
+
}
|
|
2120
|
+
)
|
|
2121
|
+
] }),
|
|
2122
|
+
/* @__PURE__ */ jsxs8("div", { className: "space-y-2", children: [
|
|
2123
|
+
/* @__PURE__ */ jsx16(Label, { htmlFor: "cilantro-register-email", className: classNames?.label, children: "Email" }),
|
|
2124
|
+
/* @__PURE__ */ jsx16(
|
|
2125
|
+
Input,
|
|
2126
|
+
{
|
|
2127
|
+
id: "cilantro-register-email",
|
|
2128
|
+
type: "email",
|
|
2129
|
+
autoComplete: "email",
|
|
2130
|
+
className: classNames?.input,
|
|
2131
|
+
value: email,
|
|
2132
|
+
onChange: (e) => setEmail(e.target.value),
|
|
2133
|
+
placeholder: "Email",
|
|
2134
|
+
required: true,
|
|
2135
|
+
disabled: isLoading
|
|
2136
|
+
}
|
|
2137
|
+
)
|
|
2138
|
+
] }),
|
|
2139
|
+
/* @__PURE__ */ jsxs8("div", { className: "space-y-2", children: [
|
|
2140
|
+
/* @__PURE__ */ jsx16(Label, { htmlFor: "cilantro-register-password", className: classNames?.label, children: "Password" }),
|
|
2141
|
+
/* @__PURE__ */ jsx16(
|
|
2142
|
+
Input,
|
|
2143
|
+
{
|
|
2144
|
+
id: "cilantro-register-password",
|
|
2145
|
+
type: "password",
|
|
2146
|
+
autoComplete: "new-password",
|
|
2147
|
+
className: classNames?.input,
|
|
2148
|
+
value: password,
|
|
2149
|
+
onChange: (e) => setPassword(e.target.value),
|
|
2150
|
+
placeholder: "Password",
|
|
2151
|
+
required: true,
|
|
2152
|
+
disabled: isLoading
|
|
2153
|
+
}
|
|
2154
|
+
)
|
|
2155
|
+
] }),
|
|
2156
|
+
error && /* @__PURE__ */ jsx16(
|
|
2157
|
+
"div",
|
|
2158
|
+
{
|
|
2159
|
+
className: cn(
|
|
2160
|
+
"rounded-md border border-destructive/50 bg-destructive/10 px-3 py-2 text-sm text-destructive",
|
|
2161
|
+
classNames?.error
|
|
2162
|
+
),
|
|
2163
|
+
role: "alert",
|
|
2164
|
+
children: error
|
|
2165
|
+
}
|
|
2166
|
+
),
|
|
2167
|
+
/* @__PURE__ */ jsx16(
|
|
2168
|
+
Button,
|
|
2169
|
+
{
|
|
2170
|
+
type: "submit",
|
|
2171
|
+
className: cn("w-full", classNames?.submitButton),
|
|
2172
|
+
disabled: isLoading || !username.trim() || !email.trim() || !password,
|
|
2173
|
+
children: isLoading ? "Creating account..." : submitLabel
|
|
2174
|
+
}
|
|
2175
|
+
),
|
|
2176
|
+
renderSwitchToLogin && /* @__PURE__ */ jsx16("div", { className: "text-center text-sm text-muted-foreground", children: renderSwitchToLogin() })
|
|
2177
|
+
] }) })
|
|
2178
|
+
] });
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
// src/components/AuthForm.tsx
|
|
2182
|
+
import { useState as useState10 } from "react";
|
|
2183
|
+
import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2184
|
+
function AuthForm({
|
|
2185
|
+
defaultMode = "login",
|
|
2186
|
+
className,
|
|
2187
|
+
classNames,
|
|
2188
|
+
onSuccess,
|
|
2189
|
+
onError,
|
|
2190
|
+
loginSubmitLabel = "Sign in",
|
|
2191
|
+
registerSubmitLabel = "Create account",
|
|
2192
|
+
loginTitle = "Welcome back",
|
|
2193
|
+
registerTitle = "Create an account",
|
|
2194
|
+
loginDescription = "Sign in with your username or email.",
|
|
2195
|
+
registerDescription = "Enter your details to get started.",
|
|
2196
|
+
switchToRegisterText = "Don't have an account? Create one",
|
|
2197
|
+
switchToLoginText = "Already have an account? Sign in",
|
|
2198
|
+
isActive = true
|
|
2199
|
+
}) {
|
|
2200
|
+
const { login, register, isLoading } = useCilantroAuth();
|
|
2201
|
+
const [mode, setMode] = useState10(defaultMode);
|
|
2202
|
+
const [usernameOrEmail, setUsernameOrEmail] = useState10("");
|
|
2203
|
+
const [username, setUsername] = useState10("");
|
|
2204
|
+
const [email, setEmail] = useState10("");
|
|
2205
|
+
const [password, setPassword] = useState10("");
|
|
2206
|
+
const [error, setError] = useState10(null);
|
|
2207
|
+
const isLogin = mode === "login";
|
|
2208
|
+
const handleSubmit = async (e) => {
|
|
2209
|
+
e.preventDefault();
|
|
2210
|
+
setError(null);
|
|
2211
|
+
try {
|
|
2212
|
+
if (isLogin) {
|
|
2213
|
+
await login(usernameOrEmail.trim(), password);
|
|
2214
|
+
} else {
|
|
2215
|
+
await register(username.trim(), email.trim(), password, isActive);
|
|
2216
|
+
}
|
|
2217
|
+
onSuccess?.();
|
|
2218
|
+
} catch (err) {
|
|
2219
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2220
|
+
setError(message);
|
|
2221
|
+
onError?.(message);
|
|
2222
|
+
}
|
|
2223
|
+
};
|
|
2224
|
+
const switchMode = () => {
|
|
2225
|
+
setMode(isLogin ? "register" : "login");
|
|
2226
|
+
setError(null);
|
|
2227
|
+
};
|
|
2228
|
+
const canSubmit = isLogin ? usernameOrEmail.trim().length > 0 && password.length > 0 : username.trim().length > 0 && email.trim().length > 0 && password.length > 0;
|
|
2229
|
+
return /* @__PURE__ */ jsxs9(Card, { className: cn("w-full max-w-sm", className, classNames?.root), "data-cilantro-auth-form": true, children: [
|
|
2230
|
+
/* @__PURE__ */ jsxs9(CardHeader, { className: cn("space-y-1 text-center sm:text-left", classNames?.header), children: [
|
|
2231
|
+
/* @__PURE__ */ jsx17(CardTitle, { className: cn("text-xl", classNames?.title), children: isLogin ? loginTitle : registerTitle }),
|
|
2232
|
+
/* @__PURE__ */ jsx17(CardDescription, { className: classNames?.description, children: isLogin ? loginDescription : registerDescription })
|
|
2233
|
+
] }),
|
|
2234
|
+
/* @__PURE__ */ jsx17(CardContent, { children: /* @__PURE__ */ jsxs9("form", { onSubmit: handleSubmit, className: cn("space-y-4", classNames?.form), children: [
|
|
2235
|
+
isLogin ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
2236
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
|
|
2237
|
+
/* @__PURE__ */ jsx17(Label, { htmlFor: "cilantro-auth-username", className: classNames?.label, children: "Username or email" }),
|
|
2238
|
+
/* @__PURE__ */ jsx17(
|
|
2239
|
+
Input,
|
|
2240
|
+
{
|
|
2241
|
+
id: "cilantro-auth-username",
|
|
2242
|
+
type: "text",
|
|
2243
|
+
autoComplete: "username",
|
|
2244
|
+
className: classNames?.input,
|
|
2245
|
+
value: usernameOrEmail,
|
|
2246
|
+
onChange: (e) => setUsernameOrEmail(e.target.value),
|
|
2247
|
+
placeholder: "you@example.com",
|
|
2248
|
+
required: true,
|
|
2249
|
+
disabled: isLoading
|
|
2250
|
+
}
|
|
2251
|
+
)
|
|
2252
|
+
] }),
|
|
2253
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
|
|
2254
|
+
/* @__PURE__ */ jsx17(Label, { htmlFor: "cilantro-auth-password", className: classNames?.label, children: "Password" }),
|
|
2255
|
+
/* @__PURE__ */ jsx17(
|
|
2256
|
+
Input,
|
|
2257
|
+
{
|
|
2258
|
+
id: "cilantro-auth-password",
|
|
2259
|
+
type: "password",
|
|
2260
|
+
autoComplete: "current-password",
|
|
2261
|
+
className: classNames?.input,
|
|
2262
|
+
value: password,
|
|
2263
|
+
onChange: (e) => setPassword(e.target.value),
|
|
2264
|
+
placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
|
|
2265
|
+
required: true,
|
|
2266
|
+
disabled: isLoading
|
|
2267
|
+
}
|
|
2268
|
+
)
|
|
2269
|
+
] })
|
|
2270
|
+
] }) : /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
2271
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
|
|
2272
|
+
/* @__PURE__ */ jsx17(Label, { htmlFor: "cilantro-auth-reg-username", className: classNames?.label, children: "Username" }),
|
|
2273
|
+
/* @__PURE__ */ jsx17(
|
|
2274
|
+
Input,
|
|
2275
|
+
{
|
|
2276
|
+
id: "cilantro-auth-reg-username",
|
|
2277
|
+
type: "text",
|
|
2278
|
+
autoComplete: "username",
|
|
2279
|
+
className: classNames?.input,
|
|
2280
|
+
value: username,
|
|
2281
|
+
onChange: (e) => setUsername(e.target.value),
|
|
2282
|
+
placeholder: "johndoe",
|
|
2283
|
+
required: true,
|
|
2284
|
+
disabled: isLoading
|
|
2285
|
+
}
|
|
2286
|
+
)
|
|
2287
|
+
] }),
|
|
2288
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
|
|
2289
|
+
/* @__PURE__ */ jsx17(Label, { htmlFor: "cilantro-auth-reg-email", className: classNames?.label, children: "Email" }),
|
|
2290
|
+
/* @__PURE__ */ jsx17(
|
|
2291
|
+
Input,
|
|
2292
|
+
{
|
|
2293
|
+
id: "cilantro-auth-reg-email",
|
|
2294
|
+
type: "email",
|
|
2295
|
+
autoComplete: "email",
|
|
2296
|
+
className: classNames?.input,
|
|
2297
|
+
value: email,
|
|
2298
|
+
onChange: (e) => setEmail(e.target.value),
|
|
2299
|
+
placeholder: "you@example.com",
|
|
2300
|
+
required: true,
|
|
2301
|
+
disabled: isLoading
|
|
2302
|
+
}
|
|
2303
|
+
)
|
|
2304
|
+
] }),
|
|
2305
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
|
|
2306
|
+
/* @__PURE__ */ jsx17(Label, { htmlFor: "cilantro-auth-reg-password", className: classNames?.label, children: "Password" }),
|
|
2307
|
+
/* @__PURE__ */ jsx17(
|
|
2308
|
+
Input,
|
|
2309
|
+
{
|
|
2310
|
+
id: "cilantro-auth-reg-password",
|
|
2311
|
+
type: "password",
|
|
2312
|
+
autoComplete: "new-password",
|
|
2313
|
+
className: classNames?.input,
|
|
2314
|
+
value: password,
|
|
2315
|
+
onChange: (e) => setPassword(e.target.value),
|
|
2316
|
+
placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
|
|
2317
|
+
required: true,
|
|
2318
|
+
disabled: isLoading
|
|
2319
|
+
}
|
|
2320
|
+
)
|
|
2321
|
+
] })
|
|
2322
|
+
] }),
|
|
2323
|
+
error && /* @__PURE__ */ jsx17(
|
|
2324
|
+
"div",
|
|
2325
|
+
{
|
|
2326
|
+
className: cn(
|
|
2327
|
+
"rounded-md border border-destructive/50 bg-destructive/10 px-3 py-2 text-sm text-destructive",
|
|
2328
|
+
classNames?.error
|
|
2329
|
+
),
|
|
2330
|
+
role: "alert",
|
|
2331
|
+
children: error
|
|
2332
|
+
}
|
|
2333
|
+
),
|
|
2334
|
+
/* @__PURE__ */ jsx17(
|
|
2335
|
+
Button,
|
|
2336
|
+
{
|
|
2337
|
+
type: "submit",
|
|
2338
|
+
className: cn("w-full", classNames?.submitButton),
|
|
2339
|
+
disabled: isLoading || !canSubmit,
|
|
2340
|
+
children: isLoading ? isLogin ? "Signing in..." : "Creating account..." : isLogin ? loginSubmitLabel : registerSubmitLabel
|
|
2341
|
+
}
|
|
2342
|
+
),
|
|
2343
|
+
/* @__PURE__ */ jsx17("p", { className: cn("text-center text-sm text-muted-foreground", classNames?.toggle), children: /* @__PURE__ */ jsx17(
|
|
2344
|
+
"button",
|
|
2345
|
+
{
|
|
2346
|
+
type: "button",
|
|
2347
|
+
className: cn("underline underline-offset-2 hover:text-foreground", classNames?.toggleLink),
|
|
2348
|
+
onClick: switchMode,
|
|
2349
|
+
disabled: isLoading,
|
|
2350
|
+
children: isLogin ? switchToRegisterText : switchToLoginText
|
|
2351
|
+
}
|
|
2352
|
+
) })
|
|
2353
|
+
] }) })
|
|
2354
|
+
] });
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
// src/components/AuthGuard.tsx
|
|
2358
|
+
import { Fragment as Fragment5, jsx as jsx18 } from "react/jsx-runtime";
|
|
2359
|
+
function AuthGuard({
|
|
2360
|
+
children,
|
|
2361
|
+
fallback,
|
|
2362
|
+
className,
|
|
2363
|
+
classNames,
|
|
2364
|
+
showFallback = true
|
|
2365
|
+
}) {
|
|
2366
|
+
const { isAuthenticated, isLoading } = useCilantroAuth();
|
|
2367
|
+
if (isLoading) {
|
|
2368
|
+
return /* @__PURE__ */ jsx18("div", { className: cn(className, classNames?.root), "data-cilantro-auth-guard": true, children: /* @__PURE__ */ jsx18("div", { className: cn("text-sm text-muted-foreground", classNames?.fallback), children: "Loading..." }) });
|
|
2369
|
+
}
|
|
2370
|
+
if (!isAuthenticated) {
|
|
2371
|
+
if (!showFallback) return null;
|
|
2372
|
+
return /* @__PURE__ */ jsx18("div", { className: cn(className, classNames?.root), "data-cilantro-auth-guard": true, children: fallback ?? /* @__PURE__ */ jsx18(LoginForm, { className: classNames?.fallback }) });
|
|
2373
|
+
}
|
|
2374
|
+
return /* @__PURE__ */ jsx18(Fragment5, { children });
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
// src/components/AddSignerForm.tsx
|
|
2378
|
+
import { useState as useState11 } from "react";
|
|
2379
|
+
|
|
2380
|
+
// src/ui/dialog.tsx
|
|
2381
|
+
import * as React7 from "react";
|
|
2382
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
2383
|
+
import { jsx as jsx19, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2384
|
+
var Dialog = DialogPrimitive.Root;
|
|
2385
|
+
var DialogPortal = DialogPrimitive.Portal;
|
|
2386
|
+
var DialogOverlay = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx19(
|
|
2387
|
+
DialogPrimitive.Overlay,
|
|
2388
|
+
{
|
|
2389
|
+
ref,
|
|
2390
|
+
className: cn(
|
|
2391
|
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
2392
|
+
className
|
|
2393
|
+
),
|
|
2394
|
+
...props
|
|
2395
|
+
}
|
|
2396
|
+
));
|
|
2397
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
2398
|
+
var DialogContent = React7.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs10(DialogPortal, { children: [
|
|
2399
|
+
/* @__PURE__ */ jsx19(DialogOverlay, {}),
|
|
2400
|
+
/* @__PURE__ */ jsx19(
|
|
2401
|
+
DialogPrimitive.Content,
|
|
2402
|
+
{
|
|
2403
|
+
ref,
|
|
2404
|
+
className: cn(
|
|
2405
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
2406
|
+
className
|
|
2407
|
+
),
|
|
2408
|
+
...props,
|
|
2409
|
+
children
|
|
2410
|
+
}
|
|
2411
|
+
)
|
|
2412
|
+
] }));
|
|
2413
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
2414
|
+
var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx19("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
|
|
2415
|
+
DialogHeader.displayName = "DialogHeader";
|
|
2416
|
+
var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx19(
|
|
2417
|
+
"div",
|
|
2418
|
+
{
|
|
2419
|
+
className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className),
|
|
2420
|
+
...props
|
|
2421
|
+
}
|
|
2422
|
+
);
|
|
2423
|
+
DialogFooter.displayName = "DialogFooter";
|
|
2424
|
+
var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx19(
|
|
2425
|
+
DialogPrimitive.Title,
|
|
2426
|
+
{
|
|
2427
|
+
ref,
|
|
2428
|
+
className: cn("text-lg font-semibold leading-none tracking-tight", className),
|
|
2429
|
+
...props
|
|
2430
|
+
}
|
|
2431
|
+
));
|
|
2432
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
2433
|
+
var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx19(
|
|
2434
|
+
DialogPrimitive.Description,
|
|
2435
|
+
{
|
|
2436
|
+
ref,
|
|
2437
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
2438
|
+
...props
|
|
2439
|
+
}
|
|
2440
|
+
));
|
|
2441
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
2442
|
+
|
|
2443
|
+
// src/components/AddSignerForm.tsx
|
|
2444
|
+
import { Fragment as Fragment6, jsx as jsx20, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2445
|
+
function AddSignerForm({
|
|
2446
|
+
walletId,
|
|
2447
|
+
open = true,
|
|
2448
|
+
onOpenChange,
|
|
2449
|
+
onSuccess,
|
|
2450
|
+
onCancel,
|
|
2451
|
+
onError,
|
|
2452
|
+
className,
|
|
2453
|
+
classNames,
|
|
2454
|
+
asDialog = true
|
|
2455
|
+
}) {
|
|
2456
|
+
const [signerType, setSignerType] = useState11(null);
|
|
2457
|
+
const [email, setEmail] = useState11("");
|
|
2458
|
+
const [phone, setPhone] = useState11("");
|
|
2459
|
+
const [address, setAddress] = useState11("");
|
|
2460
|
+
const [isSubmitting, setIsSubmitting] = useState11(false);
|
|
2461
|
+
const [error, setError] = useState11(null);
|
|
2462
|
+
const resetForm = () => {
|
|
2463
|
+
setSignerType(null);
|
|
2464
|
+
setEmail("");
|
|
2465
|
+
setPhone("");
|
|
2466
|
+
setAddress("");
|
|
2467
|
+
setError(null);
|
|
2468
|
+
};
|
|
2469
|
+
const handleClose = (open2) => {
|
|
2470
|
+
if (!open2) {
|
|
2471
|
+
resetForm();
|
|
2472
|
+
onCancel?.();
|
|
2473
|
+
}
|
|
2474
|
+
onOpenChange?.(open2);
|
|
2475
|
+
};
|
|
2476
|
+
const handleSubmit = async (e) => {
|
|
2477
|
+
e.preventDefault();
|
|
2478
|
+
if (!signerType) return;
|
|
2479
|
+
setError(null);
|
|
2480
|
+
setIsSubmitting(true);
|
|
2481
|
+
try {
|
|
2482
|
+
if (signerType === "email") {
|
|
2483
|
+
await createEmailSignerHelper(walletId, email);
|
|
2484
|
+
} else if (signerType === "phone") {
|
|
2485
|
+
await createPhoneSignerHelper(walletId, phone);
|
|
2486
|
+
} else if (signerType === "passkey") {
|
|
2487
|
+
await registerPasskeySigner(walletId);
|
|
2488
|
+
} else if (signerType === "external") {
|
|
2489
|
+
await createExternalSignerHelper(walletId, address);
|
|
2490
|
+
}
|
|
2491
|
+
resetForm();
|
|
2492
|
+
onSuccess?.();
|
|
2493
|
+
handleClose(false);
|
|
2494
|
+
} catch (err) {
|
|
2495
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2496
|
+
setError(message);
|
|
2497
|
+
onError?.(message);
|
|
2498
|
+
} finally {
|
|
2499
|
+
setIsSubmitting(false);
|
|
2500
|
+
}
|
|
2501
|
+
};
|
|
2502
|
+
const handlePasskeyClick = () => {
|
|
2503
|
+
setSignerType("passkey");
|
|
2504
|
+
setError(null);
|
|
2505
|
+
setIsSubmitting(true);
|
|
2506
|
+
registerPasskeySigner(walletId).then(() => {
|
|
2507
|
+
resetForm();
|
|
2508
|
+
onSuccess?.();
|
|
2509
|
+
handleClose(false);
|
|
2510
|
+
}).catch((err) => {
|
|
2511
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2512
|
+
setError(message);
|
|
2513
|
+
onError?.(message);
|
|
2514
|
+
}).finally(() => setIsSubmitting(false));
|
|
2515
|
+
};
|
|
2516
|
+
const formContent = /* @__PURE__ */ jsxs11("form", { onSubmit: handleSubmit, className: cn("space-y-4", classNames?.form), children: [
|
|
2517
|
+
!signerType ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
2518
|
+
/* @__PURE__ */ jsxs11("div", { className: "space-y-2", children: [
|
|
2519
|
+
/* @__PURE__ */ jsx20(Label, { className: classNames?.label, children: "Signer type" }),
|
|
2520
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap gap-2", children: [
|
|
2521
|
+
/* @__PURE__ */ jsx20(
|
|
2522
|
+
Button,
|
|
2523
|
+
{
|
|
2524
|
+
type: "button",
|
|
2525
|
+
variant: "outline",
|
|
2526
|
+
size: "sm",
|
|
2527
|
+
onClick: () => setSignerType("email"),
|
|
2528
|
+
disabled: isSubmitting,
|
|
2529
|
+
children: "Email"
|
|
2530
|
+
}
|
|
2531
|
+
),
|
|
2532
|
+
/* @__PURE__ */ jsx20(
|
|
2533
|
+
Button,
|
|
2534
|
+
{
|
|
2535
|
+
type: "button",
|
|
2536
|
+
variant: "outline",
|
|
2537
|
+
size: "sm",
|
|
2538
|
+
onClick: () => setSignerType("phone"),
|
|
2539
|
+
disabled: isSubmitting,
|
|
2540
|
+
children: "Phone"
|
|
2541
|
+
}
|
|
2542
|
+
),
|
|
2543
|
+
/* @__PURE__ */ jsx20(
|
|
2544
|
+
Button,
|
|
2545
|
+
{
|
|
2546
|
+
type: "button",
|
|
2547
|
+
variant: "outline",
|
|
2548
|
+
size: "sm",
|
|
2549
|
+
onClick: handlePasskeyClick,
|
|
2550
|
+
disabled: isSubmitting,
|
|
2551
|
+
children: "Passkey"
|
|
2552
|
+
}
|
|
2553
|
+
),
|
|
2554
|
+
/* @__PURE__ */ jsx20(
|
|
2555
|
+
Button,
|
|
2556
|
+
{
|
|
2557
|
+
type: "button",
|
|
2558
|
+
variant: "outline",
|
|
2559
|
+
size: "sm",
|
|
2560
|
+
onClick: () => setSignerType("external"),
|
|
2561
|
+
disabled: isSubmitting,
|
|
2562
|
+
children: "External wallet"
|
|
2563
|
+
}
|
|
2564
|
+
)
|
|
2565
|
+
] })
|
|
2566
|
+
] }),
|
|
2567
|
+
asDialog && /* @__PURE__ */ jsx20(DialogFooter, { className: "gap-2 sm:gap-0", children: /* @__PURE__ */ jsx20(
|
|
2568
|
+
Button,
|
|
2569
|
+
{
|
|
2570
|
+
type: "button",
|
|
2571
|
+
variant: "outline",
|
|
2572
|
+
className: classNames?.cancelButton,
|
|
2573
|
+
onClick: () => handleClose(false),
|
|
2574
|
+
disabled: isSubmitting,
|
|
2575
|
+
children: "Cancel"
|
|
2576
|
+
}
|
|
2577
|
+
) })
|
|
2578
|
+
] }) : signerType === "email" ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
2579
|
+
/* @__PURE__ */ jsxs11("div", { className: "space-y-2", children: [
|
|
2580
|
+
/* @__PURE__ */ jsx20(Label, { htmlFor: "add-signer-email", className: classNames?.label, children: "Email" }),
|
|
2581
|
+
/* @__PURE__ */ jsx20(
|
|
2582
|
+
Input,
|
|
2583
|
+
{
|
|
2584
|
+
id: "add-signer-email",
|
|
2585
|
+
type: "email",
|
|
2586
|
+
autoComplete: "email",
|
|
2587
|
+
className: classNames?.input,
|
|
2588
|
+
value: email,
|
|
2589
|
+
onChange: (e) => setEmail(e.target.value),
|
|
2590
|
+
placeholder: "email@example.com",
|
|
2591
|
+
required: true,
|
|
2592
|
+
disabled: isSubmitting
|
|
2593
|
+
}
|
|
2594
|
+
)
|
|
2595
|
+
] }),
|
|
2596
|
+
asDialog && /* @__PURE__ */ jsxs11(DialogFooter, { className: "gap-2 sm:gap-0", children: [
|
|
2597
|
+
/* @__PURE__ */ jsx20(
|
|
2598
|
+
Button,
|
|
2599
|
+
{
|
|
2600
|
+
type: "button",
|
|
2601
|
+
variant: "outline",
|
|
2602
|
+
className: classNames?.cancelButton,
|
|
2603
|
+
onClick: () => setSignerType(null),
|
|
2604
|
+
disabled: isSubmitting,
|
|
2605
|
+
children: "Back"
|
|
2606
|
+
}
|
|
2607
|
+
),
|
|
2608
|
+
/* @__PURE__ */ jsx20(Button, { type: "submit", className: classNames?.submitButton, disabled: isSubmitting, children: isSubmitting ? "Adding..." : "Add signer" })
|
|
2609
|
+
] }),
|
|
2610
|
+
!asDialog && /* @__PURE__ */ jsxs11("div", { className: "flex gap-2", children: [
|
|
2611
|
+
/* @__PURE__ */ jsx20(
|
|
2612
|
+
Button,
|
|
2613
|
+
{
|
|
2614
|
+
type: "button",
|
|
2615
|
+
variant: "outline",
|
|
2616
|
+
className: classNames?.cancelButton,
|
|
2617
|
+
onClick: () => setSignerType(null),
|
|
2618
|
+
disabled: isSubmitting,
|
|
2619
|
+
children: "Back"
|
|
2620
|
+
}
|
|
2621
|
+
),
|
|
2622
|
+
/* @__PURE__ */ jsx20(Button, { type: "submit", className: classNames?.submitButton, disabled: isSubmitting, children: isSubmitting ? "Adding..." : "Add signer" })
|
|
2623
|
+
] })
|
|
2624
|
+
] }) : signerType === "phone" ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
2625
|
+
/* @__PURE__ */ jsxs11("div", { className: "space-y-2", children: [
|
|
2626
|
+
/* @__PURE__ */ jsx20(Label, { htmlFor: "add-signer-phone", className: classNames?.label, children: "Phone number" }),
|
|
2627
|
+
/* @__PURE__ */ jsx20(
|
|
2628
|
+
Input,
|
|
2629
|
+
{
|
|
2630
|
+
id: "add-signer-phone",
|
|
2631
|
+
type: "tel",
|
|
2632
|
+
autoComplete: "tel",
|
|
2633
|
+
className: classNames?.input,
|
|
2634
|
+
value: phone,
|
|
2635
|
+
onChange: (e) => setPhone(e.target.value),
|
|
2636
|
+
placeholder: "+1234567890",
|
|
2637
|
+
required: true,
|
|
2638
|
+
disabled: isSubmitting
|
|
2639
|
+
}
|
|
2640
|
+
)
|
|
2641
|
+
] }),
|
|
2642
|
+
asDialog && /* @__PURE__ */ jsxs11(DialogFooter, { className: "gap-2 sm:gap-0", children: [
|
|
2643
|
+
/* @__PURE__ */ jsx20(
|
|
2644
|
+
Button,
|
|
2645
|
+
{
|
|
2646
|
+
type: "button",
|
|
2647
|
+
variant: "outline",
|
|
2648
|
+
className: classNames?.cancelButton,
|
|
2649
|
+
onClick: () => setSignerType(null),
|
|
2650
|
+
disabled: isSubmitting,
|
|
2651
|
+
children: "Back"
|
|
2652
|
+
}
|
|
2653
|
+
),
|
|
2654
|
+
/* @__PURE__ */ jsx20(Button, { type: "submit", className: classNames?.submitButton, disabled: isSubmitting, children: isSubmitting ? "Adding..." : "Add signer" })
|
|
2655
|
+
] }),
|
|
2656
|
+
!asDialog && /* @__PURE__ */ jsxs11("div", { className: "flex gap-2", children: [
|
|
2657
|
+
/* @__PURE__ */ jsx20(
|
|
2658
|
+
Button,
|
|
2659
|
+
{
|
|
2660
|
+
type: "button",
|
|
2661
|
+
variant: "outline",
|
|
2662
|
+
className: classNames?.cancelButton,
|
|
2663
|
+
onClick: () => setSignerType(null),
|
|
2664
|
+
disabled: isSubmitting,
|
|
2665
|
+
children: "Back"
|
|
2666
|
+
}
|
|
2667
|
+
),
|
|
2668
|
+
/* @__PURE__ */ jsx20(Button, { type: "submit", className: classNames?.submitButton, disabled: isSubmitting, children: isSubmitting ? "Adding..." : "Add signer" })
|
|
2669
|
+
] })
|
|
2670
|
+
] }) : signerType === "external" ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
2671
|
+
/* @__PURE__ */ jsxs11("div", { className: "space-y-2", children: [
|
|
2672
|
+
/* @__PURE__ */ jsx20(Label, { htmlFor: "add-signer-address", className: classNames?.label, children: "Wallet address" }),
|
|
2673
|
+
/* @__PURE__ */ jsx20(
|
|
2674
|
+
Input,
|
|
2675
|
+
{
|
|
2676
|
+
id: "add-signer-address",
|
|
2677
|
+
type: "text",
|
|
2678
|
+
className: classNames?.input,
|
|
2679
|
+
value: address,
|
|
2680
|
+
onChange: (e) => setAddress(e.target.value),
|
|
2681
|
+
placeholder: "Solana address",
|
|
2682
|
+
required: true,
|
|
2683
|
+
disabled: isSubmitting
|
|
2684
|
+
}
|
|
2685
|
+
)
|
|
2686
|
+
] }),
|
|
2687
|
+
asDialog && /* @__PURE__ */ jsxs11(DialogFooter, { className: "gap-2 sm:gap-0", children: [
|
|
2688
|
+
/* @__PURE__ */ jsx20(
|
|
2689
|
+
Button,
|
|
2690
|
+
{
|
|
2691
|
+
type: "button",
|
|
2692
|
+
variant: "outline",
|
|
2693
|
+
className: classNames?.cancelButton,
|
|
2694
|
+
onClick: () => setSignerType(null),
|
|
2695
|
+
disabled: isSubmitting,
|
|
2696
|
+
children: "Back"
|
|
2697
|
+
}
|
|
2698
|
+
),
|
|
2699
|
+
/* @__PURE__ */ jsx20(Button, { type: "submit", className: classNames?.submitButton, disabled: isSubmitting, children: isSubmitting ? "Adding..." : "Add signer" })
|
|
2700
|
+
] }),
|
|
2701
|
+
!asDialog && /* @__PURE__ */ jsxs11("div", { className: "flex gap-2", children: [
|
|
2702
|
+
/* @__PURE__ */ jsx20(
|
|
2703
|
+
Button,
|
|
2704
|
+
{
|
|
2705
|
+
type: "button",
|
|
2706
|
+
variant: "outline",
|
|
2707
|
+
className: classNames?.cancelButton,
|
|
2708
|
+
onClick: () => setSignerType(null),
|
|
2709
|
+
disabled: isSubmitting,
|
|
2710
|
+
children: "Back"
|
|
2711
|
+
}
|
|
2712
|
+
),
|
|
2713
|
+
/* @__PURE__ */ jsx20(Button, { type: "submit", className: classNames?.submitButton, disabled: isSubmitting, children: isSubmitting ? "Adding..." : "Add signer" })
|
|
2714
|
+
] })
|
|
2715
|
+
] }) : null,
|
|
2716
|
+
error && /* @__PURE__ */ jsx20("p", { className: cn("text-sm text-destructive", classNames?.error), role: "alert", children: error })
|
|
2717
|
+
] });
|
|
2718
|
+
if (asDialog) {
|
|
2719
|
+
return /* @__PURE__ */ jsx20(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs11(DialogContent, { className: cn(classNames?.dialog), children: [
|
|
2720
|
+
/* @__PURE__ */ jsxs11(DialogHeader, { children: [
|
|
2721
|
+
/* @__PURE__ */ jsx20(DialogTitle, { children: "Add signer" }),
|
|
2722
|
+
/* @__PURE__ */ jsx20(DialogDescription, { children: "Add a new signer to this wallet." })
|
|
2723
|
+
] }),
|
|
2724
|
+
formContent
|
|
2725
|
+
] }) });
|
|
2726
|
+
}
|
|
2727
|
+
return /* @__PURE__ */ jsxs11("div", { className: cn(className, classNames?.root), "data-cilantro-add-signer-form": true, children: [
|
|
2728
|
+
/* @__PURE__ */ jsx20("h3", { className: "text-sm font-medium mb-2", children: "Add signer" }),
|
|
2729
|
+
formContent
|
|
2730
|
+
] });
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
// src/components/SignerList.tsx
|
|
2734
|
+
import { useState as useState12 } from "react";
|
|
2735
|
+
import { Fragment as Fragment7, jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2736
|
+
function SignerList({
|
|
2737
|
+
walletId,
|
|
2738
|
+
className,
|
|
2739
|
+
classNames,
|
|
2740
|
+
onSignerAdded,
|
|
2741
|
+
children
|
|
2742
|
+
}) {
|
|
2743
|
+
const { signers, isLoading, error, refresh } = useSigners({ walletId });
|
|
2744
|
+
const [addSignerOpen, setAddSignerOpen] = useState12(false);
|
|
2745
|
+
const handleAddSuccess = () => {
|
|
2746
|
+
refresh();
|
|
2747
|
+
onSignerAdded?.();
|
|
2748
|
+
};
|
|
2749
|
+
if (children) {
|
|
2750
|
+
return /* @__PURE__ */ jsxs12(Fragment7, { children: [
|
|
2751
|
+
children({
|
|
2752
|
+
signers,
|
|
2753
|
+
isLoading,
|
|
2754
|
+
error,
|
|
2755
|
+
refresh,
|
|
2756
|
+
openAddSigner: () => setAddSignerOpen(true)
|
|
2757
|
+
}),
|
|
2758
|
+
/* @__PURE__ */ jsx21(
|
|
2759
|
+
AddSignerForm,
|
|
2760
|
+
{
|
|
2761
|
+
walletId,
|
|
2762
|
+
open: addSignerOpen,
|
|
2763
|
+
onOpenChange: setAddSignerOpen,
|
|
2764
|
+
onSuccess: handleAddSuccess,
|
|
2765
|
+
asDialog: true
|
|
2766
|
+
}
|
|
2767
|
+
)
|
|
2768
|
+
] });
|
|
2769
|
+
}
|
|
2770
|
+
if (!walletId) {
|
|
2771
|
+
return /* @__PURE__ */ jsx21("div", { className: cn(className, classNames?.root), "data-cilantro-signer-list": true, children: /* @__PURE__ */ jsx21("p", { className: cn("text-sm text-muted-foreground", classNames?.message), children: "Select a wallet first." }) });
|
|
2772
|
+
}
|
|
2773
|
+
return /* @__PURE__ */ jsxs12("div", { className: cn(className, classNames?.root), "data-cilantro-signer-list": true, children: [
|
|
2774
|
+
/* @__PURE__ */ jsxs12("div", { className: cn("flex items-center justify-between gap-2 mb-2", classNames?.header), children: [
|
|
2775
|
+
/* @__PURE__ */ jsx21("span", { className: "text-sm font-medium", children: "Signers" }),
|
|
2776
|
+
/* @__PURE__ */ jsx21(
|
|
2777
|
+
Button,
|
|
2778
|
+
{
|
|
2779
|
+
type: "button",
|
|
2780
|
+
size: "sm",
|
|
2781
|
+
variant: "outline",
|
|
2782
|
+
className: classNames?.addButton,
|
|
2783
|
+
onClick: () => setAddSignerOpen(true),
|
|
2784
|
+
disabled: isLoading,
|
|
2785
|
+
children: "Add signer"
|
|
2786
|
+
}
|
|
2787
|
+
)
|
|
2788
|
+
] }),
|
|
2789
|
+
isLoading ? /* @__PURE__ */ jsx21("p", { className: cn("text-sm text-muted-foreground", classNames?.message), children: "Loading signers..." }) : error ? /* @__PURE__ */ jsx21("p", { className: cn("text-sm text-destructive", classNames?.message), role: "alert", children: error }) : signers.length === 0 ? /* @__PURE__ */ jsx21("p", { className: cn("text-sm text-muted-foreground", classNames?.message), children: "No signers. Add one to get started." }) : /* @__PURE__ */ jsx21("ul", { className: cn("space-y-1", classNames?.list), role: "list", children: signers.map((signer) => /* @__PURE__ */ jsxs12("li", { className: cn("text-sm", classNames?.item), children: [
|
|
2790
|
+
getSignerDisplayName(signer),
|
|
2791
|
+
" (",
|
|
2792
|
+
getSignerTypeLabel(signer.type || signer.signerType || ""),
|
|
2793
|
+
")"
|
|
2794
|
+
] }, signer.id)) }),
|
|
2795
|
+
/* @__PURE__ */ jsx21(
|
|
2796
|
+
AddSignerForm,
|
|
2797
|
+
{
|
|
2798
|
+
walletId,
|
|
2799
|
+
open: addSignerOpen,
|
|
2800
|
+
onOpenChange: setAddSignerOpen,
|
|
2801
|
+
onSuccess: handleAddSuccess,
|
|
2802
|
+
asDialog: true
|
|
2803
|
+
}
|
|
2804
|
+
)
|
|
2805
|
+
] });
|
|
2806
|
+
}
|
|
2807
|
+
export {
|
|
2808
|
+
AddSignerForm,
|
|
2809
|
+
AuthForm,
|
|
2810
|
+
AuthGuard,
|
|
2811
|
+
CilantroAuthProvider,
|
|
2812
|
+
CilantroProvider,
|
|
2813
|
+
DelegatedKeySelector,
|
|
2814
|
+
LoginForm,
|
|
2815
|
+
MessageSigningForm,
|
|
2816
|
+
RegisterForm,
|
|
2817
|
+
SIGNER_TYPES,
|
|
2818
|
+
SignerList,
|
|
2819
|
+
SignerSelector,
|
|
2820
|
+
TransactionSigningForm,
|
|
2821
|
+
WalletProvider,
|
|
2822
|
+
WalletSelector,
|
|
2823
|
+
extractErrorMessage,
|
|
2824
|
+
extractResponseData,
|
|
2825
|
+
getSignerPublicKey,
|
|
2826
|
+
getWalletData,
|
|
2827
|
+
signAndSendTransactionWithSigner,
|
|
2828
|
+
signMessageWithSigner,
|
|
2829
|
+
signTransactionWithSigner,
|
|
2830
|
+
useCilantroAuth,
|
|
2831
|
+
useMessageSigning,
|
|
2832
|
+
useSignerSelection,
|
|
2833
|
+
useSigners,
|
|
2834
|
+
useTransactionSigning,
|
|
2835
|
+
useWallets
|
|
2836
|
+
};
|
|
2837
|
+
//# sourceMappingURL=index.mjs.map
|