@vaultkeepr/smart-account 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.d.mts +159 -0
- package/dist/index.d.ts +159 -0
- package/dist/index.js +684 -0
- package/dist/index.mjs +635 -0
- package/package.json +41 -0
- package/src/index.ts +73 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
// src/kernel.ts
|
|
2
|
+
import {
|
|
3
|
+
createPublicClient,
|
|
4
|
+
http
|
|
5
|
+
} from "viem";
|
|
6
|
+
import { toKernelSmartAccount } from "permissionless/accounts";
|
|
7
|
+
import { createPimlicoClient } from "permissionless/clients/pimlico";
|
|
8
|
+
import {
|
|
9
|
+
createBundlerClient,
|
|
10
|
+
entryPoint07Address
|
|
11
|
+
} from "viem/account-abstraction";
|
|
12
|
+
|
|
13
|
+
// src/config.ts
|
|
14
|
+
import { base } from "viem/chains";
|
|
15
|
+
var TARGET_CHAIN = base;
|
|
16
|
+
function getPimlicoApiKey() {
|
|
17
|
+
let key = "";
|
|
18
|
+
try {
|
|
19
|
+
key = process.env.PIMLICO_API_KEY || "";
|
|
20
|
+
} catch {
|
|
21
|
+
}
|
|
22
|
+
if (key) return key;
|
|
23
|
+
try {
|
|
24
|
+
key = process.env.NEXT_PUBLIC_PIMLICO_API_KEY || "";
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
return key;
|
|
28
|
+
}
|
|
29
|
+
function getPimlicoUrl() {
|
|
30
|
+
let proxyUrl = "";
|
|
31
|
+
try {
|
|
32
|
+
proxyUrl = process.env.PIMLICO_PROXY_URL || "";
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
if (!proxyUrl) {
|
|
36
|
+
try {
|
|
37
|
+
proxyUrl = process.env.NEXT_PUBLIC_PIMLICO_PROXY_URL || "";
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (!proxyUrl) {
|
|
42
|
+
try {
|
|
43
|
+
proxyUrl = process.env.EXPO_PUBLIC_PIMLICO_PROXY_URL || "";
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (proxyUrl) {
|
|
48
|
+
return proxyUrl;
|
|
49
|
+
}
|
|
50
|
+
throw new Error(
|
|
51
|
+
"[SmartAccount] Aucun proxy bundler configure. Configurez PIMLICO_PROXY_URL (le fallback direct api.pimlico.io a ete supprime pour ne pas exposer la cle API dans le bundle client)."
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
function isSmartAccountConfigured() {
|
|
55
|
+
let proxyUrl = "";
|
|
56
|
+
try {
|
|
57
|
+
proxyUrl = process.env.PIMLICO_PROXY_URL || "";
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
60
|
+
if (!proxyUrl) {
|
|
61
|
+
try {
|
|
62
|
+
proxyUrl = process.env.NEXT_PUBLIC_PIMLICO_PROXY_URL || "";
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!proxyUrl) {
|
|
67
|
+
try {
|
|
68
|
+
proxyUrl = process.env.EXPO_PUBLIC_PIMLICO_PROXY_URL || "";
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return !!proxyUrl;
|
|
73
|
+
}
|
|
74
|
+
function getTargetChain() {
|
|
75
|
+
return TARGET_CHAIN;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/kernel.ts
|
|
79
|
+
import { logger, redactAddress } from "@vaultkeepr/logger";
|
|
80
|
+
async function computeSmartAccountAddress(ownerAccount) {
|
|
81
|
+
const publicClient = createPublicClient({
|
|
82
|
+
chain: TARGET_CHAIN,
|
|
83
|
+
transport: http()
|
|
84
|
+
});
|
|
85
|
+
const kernelAccount = await toKernelSmartAccount({
|
|
86
|
+
client: publicClient,
|
|
87
|
+
owners: [ownerAccount],
|
|
88
|
+
entryPoint: {
|
|
89
|
+
address: entryPoint07Address,
|
|
90
|
+
version: "0.7"
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
return kernelAccount.address;
|
|
94
|
+
}
|
|
95
|
+
var _cached = null;
|
|
96
|
+
var _cachedKey = null;
|
|
97
|
+
async function createVaultSmartAccount(params) {
|
|
98
|
+
const { ownerAccount, mode } = params;
|
|
99
|
+
const cacheKey = `${mode}:${ownerAccount.address.toLowerCase()}`;
|
|
100
|
+
if (_cached && _cachedKey === cacheKey) return _cached;
|
|
101
|
+
const publicClient = createPublicClient({
|
|
102
|
+
chain: TARGET_CHAIN,
|
|
103
|
+
transport: http()
|
|
104
|
+
});
|
|
105
|
+
const kernelAccount = await toKernelSmartAccount({
|
|
106
|
+
client: publicClient,
|
|
107
|
+
owners: [ownerAccount],
|
|
108
|
+
entryPoint: {
|
|
109
|
+
address: entryPoint07Address,
|
|
110
|
+
version: "0.7"
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
const pimlicoUrl = getPimlicoUrl();
|
|
114
|
+
const pimlicoClient = createPimlicoClient({
|
|
115
|
+
transport: http(pimlicoUrl),
|
|
116
|
+
entryPoint: {
|
|
117
|
+
address: entryPoint07Address,
|
|
118
|
+
version: "0.7"
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
const bundlerClient = createBundlerClient({
|
|
122
|
+
account: kernelAccount,
|
|
123
|
+
chain: TARGET_CHAIN,
|
|
124
|
+
transport: http(pimlicoUrl),
|
|
125
|
+
paymaster: {
|
|
126
|
+
async getPaymasterData(parameters) {
|
|
127
|
+
return pimlicoClient.getPaymasterData(parameters);
|
|
128
|
+
},
|
|
129
|
+
async getPaymasterStubData(parameters) {
|
|
130
|
+
return pimlicoClient.getPaymasterStubData(parameters);
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
userOperation: {
|
|
134
|
+
async estimateFeesPerGas() {
|
|
135
|
+
return (await pimlicoClient.getUserOperationGasPrice()).fast;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
const result = {
|
|
140
|
+
address: kernelAccount.address,
|
|
141
|
+
ownerAddress: ownerAccount.address,
|
|
142
|
+
ownerAccount,
|
|
143
|
+
client: bundlerClient,
|
|
144
|
+
chain: TARGET_CHAIN,
|
|
145
|
+
mode
|
|
146
|
+
};
|
|
147
|
+
_cached = result;
|
|
148
|
+
_cachedKey = cacheKey;
|
|
149
|
+
logger.debug(
|
|
150
|
+
`[SmartAccount] Kernel cr\xE9\xE9 (${mode}) \u2014 SA: ${redactAddress(kernelAccount.address)} | Owner: ${redactAddress(ownerAccount.address)}`
|
|
151
|
+
);
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
function getCachedSmartAccountAddress() {
|
|
155
|
+
return _cached?.address ?? null;
|
|
156
|
+
}
|
|
157
|
+
function getCachedSmartAccountMode() {
|
|
158
|
+
return _cached?.mode ?? null;
|
|
159
|
+
}
|
|
160
|
+
function clearSmartAccountCache() {
|
|
161
|
+
_cached = null;
|
|
162
|
+
_cachedKey = null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/owner.ts
|
|
166
|
+
import { getHiddenWalletFromPassword, getHiddenWalletLegacy } from "@vaultkeepr/core";
|
|
167
|
+
async function getOwnerFromPassword(password, secretKey) {
|
|
168
|
+
return getHiddenWalletFromPassword(password, secretKey);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/cidRegistry.ts
|
|
172
|
+
import {
|
|
173
|
+
createPublicClient as createPublicClient2,
|
|
174
|
+
encodeFunctionData,
|
|
175
|
+
http as http2
|
|
176
|
+
} from "viem";
|
|
177
|
+
import { logger as logger2 } from "@vaultkeepr/logger";
|
|
178
|
+
var CID_REGISTRY_ABI = [
|
|
179
|
+
{
|
|
180
|
+
name: "setCid",
|
|
181
|
+
type: "function",
|
|
182
|
+
stateMutability: "nonpayable",
|
|
183
|
+
inputs: [{ name: "cid", type: "string" }],
|
|
184
|
+
outputs: []
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: "deleteCid",
|
|
188
|
+
type: "function",
|
|
189
|
+
stateMutability: "nonpayable",
|
|
190
|
+
inputs: [],
|
|
191
|
+
outputs: []
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: "getCid",
|
|
195
|
+
type: "function",
|
|
196
|
+
stateMutability: "view",
|
|
197
|
+
inputs: [{ name: "account", type: "address" }],
|
|
198
|
+
outputs: [{ name: "", type: "string" }]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: "hasCid",
|
|
202
|
+
type: "function",
|
|
203
|
+
stateMutability: "view",
|
|
204
|
+
inputs: [{ name: "account", type: "address" }],
|
|
205
|
+
outputs: [{ name: "", type: "bool" }]
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "getUpdatedAt",
|
|
209
|
+
type: "function",
|
|
210
|
+
stateMutability: "view",
|
|
211
|
+
inputs: [{ name: "account", type: "address" }],
|
|
212
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: "nextAllowedUpdate",
|
|
216
|
+
type: "function",
|
|
217
|
+
stateMutability: "view",
|
|
218
|
+
inputs: [{ name: "account", type: "address" }],
|
|
219
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: "cooldownSeconds",
|
|
223
|
+
type: "function",
|
|
224
|
+
stateMutability: "view",
|
|
225
|
+
inputs: [],
|
|
226
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
227
|
+
}
|
|
228
|
+
];
|
|
229
|
+
function getCidRegistryAddress() {
|
|
230
|
+
let addr = "";
|
|
231
|
+
try {
|
|
232
|
+
addr = process.env.CID_REGISTRY_ADDRESS || "";
|
|
233
|
+
} catch {
|
|
234
|
+
}
|
|
235
|
+
if (addr) return addr;
|
|
236
|
+
try {
|
|
237
|
+
addr = process.env.NEXT_PUBLIC_CID_REGISTRY_ADDRESS || "";
|
|
238
|
+
} catch {
|
|
239
|
+
}
|
|
240
|
+
if (addr) return addr;
|
|
241
|
+
try {
|
|
242
|
+
addr = process.env.EXPO_PUBLIC_CID_REGISTRY_ADDRESS || "";
|
|
243
|
+
} catch {
|
|
244
|
+
}
|
|
245
|
+
if (addr) return addr;
|
|
246
|
+
return "0x0000000000000000000000000000000000000000";
|
|
247
|
+
}
|
|
248
|
+
function isCidRegistryConfigured() {
|
|
249
|
+
return getCidRegistryAddress() !== "0x0000000000000000000000000000000000000000" && isSmartAccountConfigured();
|
|
250
|
+
}
|
|
251
|
+
async function publishCidOnChain(smartAccount, cid) {
|
|
252
|
+
if (!isCidRegistryConfigured()) {
|
|
253
|
+
logger2.warn(
|
|
254
|
+
"[CidRegistry] Non configur\xE9, skip on-chain publish"
|
|
255
|
+
);
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
const calldata = encodeFunctionData({
|
|
260
|
+
abi: CID_REGISTRY_ABI,
|
|
261
|
+
functionName: "setCid",
|
|
262
|
+
args: [cid]
|
|
263
|
+
});
|
|
264
|
+
const userOpHash = await smartAccount.client.sendUserOperation({
|
|
265
|
+
calls: [
|
|
266
|
+
{
|
|
267
|
+
to: getCidRegistryAddress(),
|
|
268
|
+
data: calldata,
|
|
269
|
+
value: BigInt(0)
|
|
270
|
+
}
|
|
271
|
+
]
|
|
272
|
+
});
|
|
273
|
+
logger2.debug(
|
|
274
|
+
`[CidRegistry] UserOp soumise: ${userOpHash}`
|
|
275
|
+
);
|
|
276
|
+
const receipt = await smartAccount.client.waitForUserOperationReceipt({
|
|
277
|
+
hash: userOpHash
|
|
278
|
+
});
|
|
279
|
+
logger2.debug(
|
|
280
|
+
`[CidRegistry] CID publi\xE9 on-chain \u2014 tx: ${receipt.receipt.transactionHash} | CID: ${cid.slice(0, 20)}...`
|
|
281
|
+
);
|
|
282
|
+
return receipt.receipt.transactionHash;
|
|
283
|
+
} catch (error) {
|
|
284
|
+
logger2.error("[CidRegistry] Erreur:", error instanceof Error ? error.message : error);
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
async function readCidOnChain(smartAccountAddress) {
|
|
289
|
+
if (!isCidRegistryConfigured()) return null;
|
|
290
|
+
try {
|
|
291
|
+
const publicClient = createPublicClient2({
|
|
292
|
+
chain: TARGET_CHAIN,
|
|
293
|
+
transport: http2()
|
|
294
|
+
});
|
|
295
|
+
const cid = await publicClient.readContract({
|
|
296
|
+
address: getCidRegistryAddress(),
|
|
297
|
+
abi: CID_REGISTRY_ABI,
|
|
298
|
+
functionName: "getCid",
|
|
299
|
+
args: [smartAccountAddress]
|
|
300
|
+
});
|
|
301
|
+
return cid && cid.length > 0 ? cid : null;
|
|
302
|
+
} catch (error) {
|
|
303
|
+
logger2.error("[CidRegistry] Erreur lecture:", error instanceof Error ? error.message : error);
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// src/onChainSync.ts
|
|
309
|
+
import { logger as logger3 } from "@vaultkeepr/logger";
|
|
310
|
+
var DEFAULT_DEBOUNCE_MS = 5 * 60 * 1e3;
|
|
311
|
+
var _pendingCid = null;
|
|
312
|
+
var _pendingSmartAccount = null;
|
|
313
|
+
var _debounceTimer = null;
|
|
314
|
+
var _lastPublishedCid = null;
|
|
315
|
+
var _lastPublishTime = 0;
|
|
316
|
+
var _isPublishing = false;
|
|
317
|
+
function scheduleCidOnChainSync(smartAccount, cid, debounceMs = DEFAULT_DEBOUNCE_MS) {
|
|
318
|
+
if (!isCidRegistryConfigured()) return;
|
|
319
|
+
if (cid === _lastPublishedCid) {
|
|
320
|
+
logger3.debug("[OnChainSync] CID identique, skip");
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const sa = smartAccount ?? _pendingSmartAccount;
|
|
324
|
+
if (!sa) {
|
|
325
|
+
logger3.warn("[OnChainSync] Pas de Smart Account disponible, skip");
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
_pendingCid = cid;
|
|
329
|
+
_pendingSmartAccount = sa;
|
|
330
|
+
if (_debounceTimer) {
|
|
331
|
+
clearTimeout(_debounceTimer);
|
|
332
|
+
}
|
|
333
|
+
_debounceTimer = setTimeout(() => {
|
|
334
|
+
_executeSync();
|
|
335
|
+
}, debounceMs);
|
|
336
|
+
logger3.debug(
|
|
337
|
+
`[OnChainSync] CID planifi\xE9 dans ${(debounceMs / 1e3).toFixed(0)}s : ${cid.slice(0, 20)}...`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
async function flushCidOnChainSync() {
|
|
341
|
+
logger3.debug(
|
|
342
|
+
"[OnChainSync] flush demand\xE9 \u2014 pending:",
|
|
343
|
+
_pendingCid?.slice(0, 20),
|
|
344
|
+
"SA:",
|
|
345
|
+
!!_pendingSmartAccount
|
|
346
|
+
);
|
|
347
|
+
if (!_pendingCid || !_pendingSmartAccount) {
|
|
348
|
+
logger3.debug("[OnChainSync] Rien \xE0 flush");
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
if (_debounceTimer) {
|
|
352
|
+
clearTimeout(_debounceTimer);
|
|
353
|
+
_debounceTimer = null;
|
|
354
|
+
}
|
|
355
|
+
logger3.debug("[OnChainSync] Flush imm\xE9diat...");
|
|
356
|
+
return _executeSync();
|
|
357
|
+
}
|
|
358
|
+
function getOnChainSyncStatus() {
|
|
359
|
+
return {
|
|
360
|
+
hasPending: _pendingCid !== null && _pendingCid !== _lastPublishedCid,
|
|
361
|
+
pendingCid: _pendingCid,
|
|
362
|
+
lastPublishedCid: _lastPublishedCid,
|
|
363
|
+
lastPublishTime: _lastPublishTime,
|
|
364
|
+
isPublishing: _isPublishing
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
function resetOnChainSync() {
|
|
368
|
+
if (_debounceTimer) {
|
|
369
|
+
clearTimeout(_debounceTimer);
|
|
370
|
+
_debounceTimer = null;
|
|
371
|
+
}
|
|
372
|
+
_pendingCid = null;
|
|
373
|
+
_pendingSmartAccount = null;
|
|
374
|
+
_lastPublishedCid = null;
|
|
375
|
+
_lastPublishTime = 0;
|
|
376
|
+
_isPublishing = false;
|
|
377
|
+
}
|
|
378
|
+
async function _executeSync() {
|
|
379
|
+
if (!_pendingCid || !_pendingSmartAccount || _isPublishing) return false;
|
|
380
|
+
if (_pendingCid === _lastPublishedCid) {
|
|
381
|
+
_pendingCid = null;
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
_isPublishing = true;
|
|
385
|
+
const cid = _pendingCid;
|
|
386
|
+
const sa = _pendingSmartAccount;
|
|
387
|
+
try {
|
|
388
|
+
logger3.debug(`[OnChainSync] Publication on-chain: ${cid.slice(0, 20)}...`);
|
|
389
|
+
const txHash = await publishCidOnChain(sa, cid);
|
|
390
|
+
if (txHash) {
|
|
391
|
+
_lastPublishedCid = cid;
|
|
392
|
+
_lastPublishTime = Date.now();
|
|
393
|
+
_pendingCid = null;
|
|
394
|
+
logger3.debug(
|
|
395
|
+
`[OnChainSync] \u2705 CID publi\xE9 on-chain : ${cid.slice(0, 20)}...`
|
|
396
|
+
);
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
logger3.warn("[OnChainSync] Publication \xE9chou\xE9e (null txHash)");
|
|
400
|
+
return false;
|
|
401
|
+
} catch (error) {
|
|
402
|
+
logger3.error("[OnChainSync] Erreur publication:", error instanceof Error ? error.message : error);
|
|
403
|
+
return false;
|
|
404
|
+
} finally {
|
|
405
|
+
_isPublishing = false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// src/identity.ts
|
|
410
|
+
import { logger as logger4, redactAddress as redactAddress2 } from "@vaultkeepr/logger";
|
|
411
|
+
var _state = {
|
|
412
|
+
owner: null,
|
|
413
|
+
smartAccount: null,
|
|
414
|
+
mode: null,
|
|
415
|
+
initialized: false
|
|
416
|
+
};
|
|
417
|
+
var _listeners = /* @__PURE__ */ new Set();
|
|
418
|
+
function _notify() {
|
|
419
|
+
for (const fn of _listeners) {
|
|
420
|
+
try {
|
|
421
|
+
fn(_state);
|
|
422
|
+
} catch {
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
async function initIdentityFromPassword(password, secretKey) {
|
|
427
|
+
const owner = await getOwnerFromPassword(password, secretKey);
|
|
428
|
+
let sa = null;
|
|
429
|
+
if (isSmartAccountConfigured()) {
|
|
430
|
+
try {
|
|
431
|
+
sa = await createVaultSmartAccount({
|
|
432
|
+
ownerAccount: owner,
|
|
433
|
+
mode: "password"
|
|
434
|
+
});
|
|
435
|
+
} catch (e) {
|
|
436
|
+
logger4.warn("[Identity] SA init failed (non-blocking):", e.message);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
_state = {
|
|
440
|
+
owner,
|
|
441
|
+
smartAccount: sa,
|
|
442
|
+
mode: "password",
|
|
443
|
+
initialized: true
|
|
444
|
+
};
|
|
445
|
+
_notify();
|
|
446
|
+
logger4.debug(
|
|
447
|
+
`[Identity] Connect\xE9 (password) \u2014 adresse: ${redactAddress2(owner.address)}` + (sa ? ` | SA: ${redactAddress2(sa.address)}` : "") + (secretKey ? ` | SK: \u2713` : " | no secretKey")
|
|
448
|
+
);
|
|
449
|
+
return {
|
|
450
|
+
address: owner.address,
|
|
451
|
+
smartAccountAddress: sa?.address ?? null
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
async function initIdentityFromSigner(signer, mode = "passkey") {
|
|
455
|
+
let sa = null;
|
|
456
|
+
if (isSmartAccountConfigured()) {
|
|
457
|
+
try {
|
|
458
|
+
sa = await createVaultSmartAccount({ ownerAccount: signer, mode });
|
|
459
|
+
} catch (e) {
|
|
460
|
+
logger4.warn("[Identity] SA init failed (non-blocking):", e.message);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
_state = {
|
|
464
|
+
owner: signer,
|
|
465
|
+
smartAccount: sa,
|
|
466
|
+
mode,
|
|
467
|
+
initialized: true
|
|
468
|
+
};
|
|
469
|
+
_notify();
|
|
470
|
+
logger4.info(
|
|
471
|
+
`[Identity] Connected (${mode}) \u2014 address: ${signer.address}` + (sa ? ` | SA: ${sa.address}` : "")
|
|
472
|
+
);
|
|
473
|
+
return {
|
|
474
|
+
address: signer.address,
|
|
475
|
+
smartAccountAddress: sa?.address ?? null
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
function clearIdentity() {
|
|
479
|
+
_state = {
|
|
480
|
+
owner: null,
|
|
481
|
+
smartAccount: null,
|
|
482
|
+
mode: null,
|
|
483
|
+
initialized: false
|
|
484
|
+
};
|
|
485
|
+
clearSmartAccountCache();
|
|
486
|
+
_notify();
|
|
487
|
+
logger4.info("[Identity] Disconnected");
|
|
488
|
+
}
|
|
489
|
+
function getIdentityAddress() {
|
|
490
|
+
return _state.owner?.address ?? null;
|
|
491
|
+
}
|
|
492
|
+
function isIdentityConnected() {
|
|
493
|
+
return _state.initialized && _state.owner !== null;
|
|
494
|
+
}
|
|
495
|
+
function getIdentityOwner() {
|
|
496
|
+
return _state.owner;
|
|
497
|
+
}
|
|
498
|
+
function getIdentitySmartAccount() {
|
|
499
|
+
return _state.smartAccount;
|
|
500
|
+
}
|
|
501
|
+
function getIdentityMode() {
|
|
502
|
+
return _state.mode;
|
|
503
|
+
}
|
|
504
|
+
async function signMessageWithIdentity(message) {
|
|
505
|
+
if (!_state.owner) {
|
|
506
|
+
throw new Error("[Identity] Pas connect\xE9 \u2014 impossible de signer");
|
|
507
|
+
}
|
|
508
|
+
return _state.owner.signMessage({ message });
|
|
509
|
+
}
|
|
510
|
+
function subscribeIdentity(listener) {
|
|
511
|
+
_listeners.add(listener);
|
|
512
|
+
return () => {
|
|
513
|
+
_listeners.delete(listener);
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
function getIdentitySnapshot() {
|
|
517
|
+
return _state;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// src/useIdentity.ts
|
|
521
|
+
import { useSyncExternalStore, useCallback } from "react";
|
|
522
|
+
var _emptyState = {
|
|
523
|
+
owner: null,
|
|
524
|
+
smartAccount: null,
|
|
525
|
+
mode: null,
|
|
526
|
+
initialized: false
|
|
527
|
+
};
|
|
528
|
+
function getServerSnapshot() {
|
|
529
|
+
return _emptyState;
|
|
530
|
+
}
|
|
531
|
+
function useIdentity() {
|
|
532
|
+
const state = useSyncExternalStore(
|
|
533
|
+
subscribeIdentity,
|
|
534
|
+
getIdentitySnapshot,
|
|
535
|
+
getServerSnapshot
|
|
536
|
+
);
|
|
537
|
+
const signMessage = useCallback(
|
|
538
|
+
async (message) => {
|
|
539
|
+
return signMessageWithIdentity(message);
|
|
540
|
+
},
|
|
541
|
+
[]
|
|
542
|
+
);
|
|
543
|
+
const disconnect = useCallback(() => {
|
|
544
|
+
clearIdentity();
|
|
545
|
+
}, []);
|
|
546
|
+
const notifyOnChainSync = useCallback(
|
|
547
|
+
(cid) => {
|
|
548
|
+
const sa = getIdentitySmartAccount();
|
|
549
|
+
if (!sa) return;
|
|
550
|
+
scheduleCidOnChainSync(sa, cid);
|
|
551
|
+
},
|
|
552
|
+
[]
|
|
553
|
+
);
|
|
554
|
+
const cleanupOnLock = useCallback(async () => {
|
|
555
|
+
await flushCidOnChainSync().catch(() => {
|
|
556
|
+
});
|
|
557
|
+
clearSmartAccountCache();
|
|
558
|
+
resetOnChainSync();
|
|
559
|
+
clearIdentity();
|
|
560
|
+
}, []);
|
|
561
|
+
const flushOnChainSync = useCallback(async () => {
|
|
562
|
+
await flushCidOnChainSync().catch(() => {
|
|
563
|
+
});
|
|
564
|
+
}, []);
|
|
565
|
+
return {
|
|
566
|
+
address: state.owner?.address ?? null,
|
|
567
|
+
isConnected: state.initialized && state.owner !== null,
|
|
568
|
+
mode: state.mode,
|
|
569
|
+
smartAccountAddress: state.smartAccount?.address ?? null,
|
|
570
|
+
signMessage,
|
|
571
|
+
disconnect,
|
|
572
|
+
notifyOnChainSync,
|
|
573
|
+
cleanupOnLock,
|
|
574
|
+
flushOnChainSync
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// src/secretKey.ts
|
|
579
|
+
import { bytesToHex } from "viem";
|
|
580
|
+
function generateSecretKey() {
|
|
581
|
+
const bytes = new Uint8Array(32);
|
|
582
|
+
if (typeof globalThis.crypto?.getRandomValues === "function") {
|
|
583
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
584
|
+
} else {
|
|
585
|
+
for (let i = 0; i < 32; i++) {
|
|
586
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return bytesToHex(bytes).slice(2);
|
|
590
|
+
}
|
|
591
|
+
function isValidSecretKey(key) {
|
|
592
|
+
return typeof key === "string" && /^[0-9a-f]{64}$/i.test(key);
|
|
593
|
+
}
|
|
594
|
+
function formatSecretKeyForDisplay(key) {
|
|
595
|
+
const clean = key.replace(/[^0-9a-f]/gi, "").toLowerCase();
|
|
596
|
+
return clean.match(/.{1,8}/g)?.join("-") ?? clean;
|
|
597
|
+
}
|
|
598
|
+
function parseSecretKeyInput(input) {
|
|
599
|
+
return input.replace(/[-\s]/g, "").toLowerCase();
|
|
600
|
+
}
|
|
601
|
+
export {
|
|
602
|
+
CID_REGISTRY_ABI,
|
|
603
|
+
clearIdentity,
|
|
604
|
+
clearSmartAccountCache,
|
|
605
|
+
computeSmartAccountAddress,
|
|
606
|
+
createVaultSmartAccount,
|
|
607
|
+
flushCidOnChainSync,
|
|
608
|
+
formatSecretKeyForDisplay,
|
|
609
|
+
generateSecretKey,
|
|
610
|
+
getCachedSmartAccountAddress,
|
|
611
|
+
getCachedSmartAccountMode,
|
|
612
|
+
getIdentityAddress,
|
|
613
|
+
getIdentityMode,
|
|
614
|
+
getIdentityOwner,
|
|
615
|
+
getIdentitySmartAccount,
|
|
616
|
+
getIdentitySnapshot,
|
|
617
|
+
getOnChainSyncStatus,
|
|
618
|
+
getOwnerFromPassword,
|
|
619
|
+
getPimlicoApiKey,
|
|
620
|
+
getTargetChain,
|
|
621
|
+
initIdentityFromPassword,
|
|
622
|
+
initIdentityFromSigner,
|
|
623
|
+
isCidRegistryConfigured,
|
|
624
|
+
isIdentityConnected,
|
|
625
|
+
isSmartAccountConfigured,
|
|
626
|
+
isValidSecretKey,
|
|
627
|
+
parseSecretKeyInput,
|
|
628
|
+
publishCidOnChain,
|
|
629
|
+
readCidOnChain,
|
|
630
|
+
resetOnChainSync,
|
|
631
|
+
scheduleCidOnChainSync,
|
|
632
|
+
signMessageWithIdentity,
|
|
633
|
+
subscribeIdentity,
|
|
634
|
+
useIdentity
|
|
635
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vaultkeepr/smart-account",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "ERC-4337 Smart Account (Kernel) with Pimlico Paymaster — universal module for Web, Extension, iOS, and Android",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "./src/index.ts",
|
|
7
|
+
"module": "./src/index.ts",
|
|
8
|
+
"types": "./src/index.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./src/index.ts",
|
|
12
|
+
"import": "./src/index.ts",
|
|
13
|
+
"require": "./src/index.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@vaultkeepr/logger": "0.1.1",
|
|
18
|
+
"@vaultkeepr/core": "0.1.1"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"permissionless": ">=0.3.0",
|
|
22
|
+
"viem": ">=2.0.0",
|
|
23
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"tsup": "^8.3.5",
|
|
27
|
+
"typescript": "^5.6.3",
|
|
28
|
+
"vitest": "^4.1.11",
|
|
29
|
+
"react": "^19.2.0",
|
|
30
|
+
"@types/react": "^19.0.0"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
37
|
+
"clean": "rm -rf dist",
|
|
38
|
+
"test": "vitest run --passWithNoTests",
|
|
39
|
+
"lint": "eslint src/"
|
|
40
|
+
}
|
|
41
|
+
}
|