@lumiapassport/ui-kit 1.1.0 → 1.2.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 ADDED
@@ -0,0 +1,434 @@
1
+ # @lumiapassport/ui-kit
2
+
3
+ React UI components and hooks for Lumia Passport - a secure, user-friendly authentication and Account Abstraction wallet solution with MPC (Multi-Party Computation) key management.
4
+
5
+ ## Features
6
+
7
+ - 🔐 **Secure Authentication** - Multiple auth methods: Email, Passkey, Telegram, Wallet connect
8
+ - 🔑 **MPC Key Management** - Distributed key generation with iframe isolation
9
+ - 💼 **Account Abstraction** - ERC-4337 compliant smart contract wallets
10
+ - 🎨 **Pre-built UI Components** - Ready-to-use React components with customizable themes
11
+ - ⚡ **Easy Integration** - Just wrap your app with `LumiaPassportProvider`
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @lumiapassport/ui-kit @lumiapassport/core
17
+ # or
18
+ pnpm add @lumiapassport/ui-kit @lumiapassport/core
19
+ # or
20
+ yarn add @lumiapassport/ui-kit @lumiapassport/core
21
+ ```
22
+
23
+ ### Peer Dependencies
24
+
25
+ The following packages are required as peer dependencies:
26
+
27
+ ```bash
28
+ npm install react react-dom viem wagmi @tanstack/react-query
29
+ ```
30
+
31
+ ## Quick Start
32
+
33
+ ### 1. Wrap your app with `LumiaPassportProvider`
34
+
35
+ ```tsx
36
+ import { LumiaPassportProvider } from '@lumiapassport/ui-kit';
37
+ import '@lumiapassport/ui-kit/dist/styles.css';
38
+
39
+ function App() {
40
+ return (
41
+ <LumiaPassportProvider
42
+ config={{
43
+ projectId: 'your-project-id', // Get from Lumia Passport Dashboard
44
+ services: {
45
+ tssUrl: 'https://api.lumiapassport.com/tss',
46
+ bundlerUrl: 'https://api.lumiapassport.com/rundler',
47
+ shareVaultUrl: 'https://api.lumiapassport.com/vault',
48
+ }
49
+ }}
50
+ >
51
+ <YourApp />
52
+ </LumiaPassportProvider>
53
+ );
54
+ }
55
+ ```
56
+
57
+ ### 2. Add the Connect Button
58
+
59
+ ```tsx
60
+ import { ConnectWalletButton } from '@lumiapassport/ui-kit';
61
+
62
+ function YourApp() {
63
+ return (
64
+ <div>
65
+ <h1>My App</h1>
66
+ <ConnectWalletButton />
67
+ </div>
68
+ );
69
+ }
70
+ ```
71
+
72
+ That's it! The `ConnectWalletButton` provides a complete authentication UI with wallet management.
73
+
74
+ ## Configuration Options
75
+
76
+ ### Basic Configuration
77
+
78
+ ```tsx
79
+ <LumiaPassportProvider
80
+ config={{
81
+ projectId: 'your-project-id', // Required
82
+
83
+ // Optional: Service URLs (defaults to production)
84
+ services: {
85
+ tssUrl: 'https://api.lumiapassport.com/tss',
86
+ bundlerUrl: 'https://api.lumiapassport.com/rundler',
87
+ shareVaultUrl: 'https://api.lumiapassport.com/vault',
88
+ iframeUrl: 'https://auth.lumiapassport.com',
89
+ },
90
+
91
+ // Optional: Custom RPC and network
92
+ rpcUrl: 'https://beam-rpc.lumia.org',
93
+ explorerUrl: 'https://beam-explorer.lumia.org',
94
+
95
+ // Optional: Custom smart contract addresses
96
+ aaFactoryAddress: '0x...',
97
+ paymasterAddress: '0x...',
98
+ }}
99
+ >
100
+ ```
101
+
102
+ ### Advanced Configuration
103
+
104
+ ```tsx
105
+ <LumiaPassportProvider
106
+ config={{
107
+ projectId: 'your-project-id',
108
+
109
+ // Authentication providers
110
+ authProviders: {
111
+ email: true, // Email OTP
112
+ passkey: true, // Passkey/WebAuthn
113
+ telegram: true, // Telegram Mini App
114
+ wallet: true, // External wallet connection
115
+ },
116
+
117
+ // UI customization
118
+ theme: {
119
+ mode: 'dark', // 'light' | 'dark' | 'auto'
120
+ primaryColor: '#6366f1', // Custom brand color
121
+ },
122
+
123
+ // Features
124
+ features: {
125
+ backup: true, // Enable key backup/recovery
126
+ mpcSecurity: true, // Enable MPC iframe isolation
127
+ },
128
+ }}
129
+ >
130
+ ```
131
+
132
+ ## Using Hooks
133
+
134
+ ### useAuth - Authentication State
135
+
136
+ ```tsx
137
+ import { useAuth } from '@lumiapassport/ui-kit';
138
+
139
+ function MyComponent() {
140
+ const {
141
+ user, // Current user info
142
+ isAuthenticated, // Auth status
143
+ login, // Login function
144
+ logout, // Logout function
145
+ loading, // Loading state
146
+ } = useAuth();
147
+
148
+ if (loading) return <div>Loading...</div>;
149
+
150
+ if (!isAuthenticated) {
151
+ return <button onClick={() => login()}>Sign In</button>;
152
+ }
153
+
154
+ return (
155
+ <div>
156
+ <p>Welcome, {user.userId}</p>
157
+ <button onClick={() => logout()}>Sign Out</button>
158
+ </div>
159
+ );
160
+ }
161
+ ```
162
+
163
+ ### useWallet - Wallet Operations
164
+
165
+ ```tsx
166
+ import { useWallet } from '@lumiapassport/ui-kit';
167
+
168
+ function SendTransaction() {
169
+ const { address, sendTransaction, balance } = useWallet();
170
+
171
+ const handleSend = async () => {
172
+ const hash = await sendTransaction({
173
+ to: '0x...',
174
+ value: '0.1', // ETH amount
175
+ });
176
+ console.log('Transaction hash:', hash);
177
+ };
178
+
179
+ return (
180
+ <div>
181
+ <p>Address: {address}</p>
182
+ <p>Balance: {balance} ETH</p>
183
+ <button onClick={handleSend}>Send Transaction</button>
184
+ </div>
185
+ );
186
+ }
187
+ ```
188
+
189
+ ### useSendTransaction - Send Transactions
190
+
191
+ ```tsx
192
+ import { useSendTransaction } from '@lumiapassport/ui-kit';
193
+
194
+ function TransactionExample() {
195
+ const { sendTransaction, isPending } = useSendTransaction();
196
+
197
+ const handleSend = async () => {
198
+ try {
199
+ const userOpHash = await sendTransaction({
200
+ to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
201
+ value: '1000000000000000000', // 1 ETH in wei
202
+ data: '0x', // Optional contract call data
203
+ });
204
+ console.log('UserOp hash:', userOpHash);
205
+ } catch (error) {
206
+ console.error('Transaction failed:', error);
207
+ }
208
+ };
209
+
210
+ return (
211
+ <div>
212
+ <button onClick={handleSend} disabled={isPending}>
213
+ {isPending ? 'Sending...' : 'Send Transaction'}
214
+ </button>
215
+ </div>
216
+ );
217
+ }
218
+ ```
219
+
220
+ ### prepareUserOperation - Prepare for Backend Submission
221
+
222
+ ```tsx
223
+ import { prepareUserOperation, useLumiaSession } from '@lumiapassport/ui-kit';
224
+
225
+ function BackendSubmissionExample() {
226
+ const { session } = useLumiaSession();
227
+
228
+ const handlePrepare = async () => {
229
+ if (!session) return;
230
+
231
+ // Prepare and sign UserOp without sending to bundler
232
+ const { userOp, userOpHash } = await prepareUserOperation(
233
+ session,
234
+ '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', // to
235
+ '1000000000000000000', // 1 ETH in wei
236
+ '0x', // data
237
+ 'standard', // fee type: 'economy' | 'standard' | 'fast'
238
+ 'v0.7' // EntryPoint version
239
+ );
240
+
241
+ // Send to backend for validation and submission
242
+ await fetch('/api/submit-transaction', {
243
+ method: 'POST',
244
+ headers: { 'Content-Type': 'application/json' },
245
+ body: JSON.stringify({
246
+ userOp,
247
+ userOpHash,
248
+ ownerAddress: session.ownerAddress // for signature verification
249
+ }),
250
+ });
251
+ };
252
+
253
+ return (
254
+ <button onClick={handlePrepare}>
255
+ Prepare & Send to Backend
256
+ </button>
257
+ );
258
+ }
259
+ ```
260
+
261
+ **Backend example (using @lumiapassport/core):**
262
+
263
+ ```typescript
264
+ import { sendUserOperationRaw } from '@lumiapassport/core';
265
+ import { recoverAddress } from 'viem';
266
+
267
+ // Receive from frontend
268
+ const { userOp, userOpHash, ownerAddress } = await request.json();
269
+
270
+ // Verify signature
271
+ const recoveredAddress = await recoverAddress({
272
+ hash: userOpHash,
273
+ signature: userOp.signature,
274
+ });
275
+
276
+ if (recoveredAddress.toLowerCase() !== ownerAddress.toLowerCase()) {
277
+ throw new Error('Invalid signature');
278
+ }
279
+
280
+ // Submit to bundler
281
+ const txHash = await sendUserOperationRaw(userOp);
282
+ return { success: true, txHash };
283
+ ```
284
+
285
+ ## Components
286
+
287
+ ### ConnectWalletButton
288
+
289
+ Pre-built button with authentication modal.
290
+
291
+ ```tsx
292
+ <ConnectWalletButton
293
+ className="custom-class"
294
+ onConnect={(user) => console.log('Connected:', user)}
295
+ onDisconnect={() => console.log('Disconnected')}
296
+ />
297
+ ```
298
+
299
+ ### Custom Authentication Flow
300
+
301
+ ```tsx
302
+ import { AuthModal, useAuthModal } from '@lumiapassport/ui-kit';
303
+
304
+ function CustomAuth() {
305
+ const { openAuth } = useAuthModal();
306
+
307
+ return (
308
+ <div>
309
+ <button onClick={() => openAuth()}>
310
+ Custom Sign In
311
+ </button>
312
+ <AuthModal />
313
+ </div>
314
+ );
315
+ }
316
+ ```
317
+
318
+ ## Authentication Methods
319
+
320
+ ### Email OTP
321
+
322
+ Users receive a one-time code via email.
323
+
324
+ ```tsx
325
+ // Configured by default, no additional setup needed
326
+ ```
327
+
328
+ ### Passkey (WebAuthn)
329
+
330
+ Secure biometric authentication with device passkeys.
331
+
332
+ ```tsx
333
+ // Configured by default
334
+ // Users can register passkey after initial login
335
+ ```
336
+
337
+ ### Telegram Mini App
338
+
339
+ Authentication via Telegram for mini apps.
340
+
341
+ ```tsx
342
+ // Requires Telegram bot configuration
343
+ // Set in config:
344
+ config={{
345
+ telegram: {
346
+ botName: 'your_bot_name',
347
+ }
348
+ }}
349
+ ```
350
+
351
+ ### External Wallet
352
+
353
+ Connect existing wallets (MetaMask, WalletConnect, etc.).
354
+
355
+ ```tsx
356
+ // Configured by default
357
+ // Uses RainbowKit for wallet connections
358
+ ```
359
+
360
+ ## Styling
361
+
362
+ ### Using Built-in Themes
363
+
364
+ ```tsx
365
+ import '@lumiapassport/ui-kit/dist/styles.css';
366
+
367
+ <LumiaPassportProvider
368
+ config={{
369
+ projectId: 'your-project-id',
370
+ theme: {
371
+ mode: 'dark', // or 'light', 'auto'
372
+ }
373
+ }}
374
+ >
375
+ ```
376
+
377
+ ### Custom Styling
378
+
379
+ Override CSS variables for custom branding:
380
+
381
+ ```css
382
+ .lumia-scope {
383
+ --lumia-primary: #6366f1;
384
+ --lumia-background: #0f172a;
385
+ --lumia-surface: #1e293b;
386
+ --lumia-text-primary: #f8fafc;
387
+ --lumia-text-secondary: #cbd5e1;
388
+ }
389
+ ```
390
+
391
+ ## TypeScript Support
392
+
393
+ Full TypeScript support with exported types:
394
+
395
+ ```tsx
396
+ import type {
397
+ LumiaPassportConfig,
398
+ User,
399
+ AuthProvider,
400
+ WalletInfo,
401
+ } from '@lumiapassport/ui-kit';
402
+ ```
403
+
404
+ ## Examples
405
+
406
+ Check out the `/examples` directory for complete working examples:
407
+
408
+ - **React + Vite** - Modern React setup with Vite
409
+ - **Next.js** - Next.js App Router integration
410
+ - **React + TypeScript** - Full TypeScript example
411
+
412
+ ## Security
413
+
414
+ - 🔒 **MPC Key Management** - Keys are split between client and server using threshold cryptography
415
+ - 🏝️ **Iframe Isolation** - Sensitive operations run in isolated iframe context
416
+ - 🔐 **No Private Key Exposure** - Private keys never exist in complete form on client
417
+ - ✅ **Non-custodial** - Users maintain full control of their accounts
418
+
419
+ ## Need Help?
420
+
421
+ - 📖 [Documentation](https://docs.lumiapassport.com)
422
+ - 💬 [Discord Community](https://discord.gg/lumia)
423
+ - 🐛 [Report Issues](https://github.com/lumiachain/lumia-passport-sdk/issues)
424
+ - 📧 [Email Support](mailto:support@lumia.org)
425
+
426
+ ## License
427
+
428
+ MIT License - see LICENSE file for details.
429
+
430
+ ## Links
431
+
432
+ - [Website](https://lumiapassport.com)
433
+ - [GitHub](https://github.com/lumiachain/lumia-passport-sdk)
434
+ - [NPM Package](https://www.npmjs.com/package/@lumiapassport/ui-kit)
package/dist/index.cjs CHANGED
@@ -2807,6 +2807,7 @@ __export(index_exports, {
2807
2807
  UserOpStatus: () => UserOpStatus,
2808
2808
  getUserProfile: () => getUserProfile,
2809
2809
  lumiaBeam: () => lumiaBeam,
2810
+ prepareUserOperation: () => prepareUserOperation,
2810
2811
  queryClient: () => queryClient,
2811
2812
  sendUserOperation: () => sendUserOperation,
2812
2813
  updateUserProfile: () => updateUserProfile,
@@ -5294,6 +5295,208 @@ async function sendUserOperation(session, callTarget, amountWei, innerData = "0x
5294
5295
  }
5295
5296
  return hash;
5296
5297
  }
5298
+ async function prepareUserOperation(session, callTarget, amountWei, innerData = "0x", feeType = "standard", entryPointVersion = "v0.7") {
5299
+ const entryPointAddress = entryPointVersion === "v0.6" ? ENTRYPOINT_V06 : ENTRYPOINT_V07;
5300
+ const amountWeiBigInt = BigInt(amountWei);
5301
+ const isMinimalTest = callTarget === "0x0000000000000000000000000000000000000000" && amountWei === "0" && innerData === "0x";
5302
+ let callData;
5303
+ if (isMinimalTest) {
5304
+ callData = "0x";
5305
+ } else {
5306
+ callData = (0, import_viem3.encodeFunctionData)({ abi: executeAbi, functionName: "execute", args: [callTarget, amountWeiBigInt, innerData] });
5307
+ }
5308
+ let isDeployed = false;
5309
+ let deploymentMethod = "unknown";
5310
+ try {
5311
+ const code = await publicClient.getCode({ address: session.smartAccountAddress });
5312
+ if (code && code !== "0x" && code.length > 2) {
5313
+ isDeployed = true;
5314
+ deploymentMethod = "getCode";
5315
+ }
5316
+ } catch {
5317
+ }
5318
+ const nonce = await fetchEntryPointNonce(session.smartAccountAddress, entryPointAddress);
5319
+ const nonceValue = BigInt(nonce);
5320
+ if (!isDeployed && nonceValue !== 0n) throw new Error(`Undeployed account has non-zero nonce: ${nonce}. This will cause CodeHashChanged error.`);
5321
+ const shouldIncludeFactory = !isDeployed;
5322
+ let userOp;
5323
+ if (shouldIncludeFactory) {
5324
+ const compatCreateAbi = [{ type: "function", name: "createAccount", stateMutability: "payable", inputs: [{ name: "owner", type: "address" }, { name: "salt", type: "bytes32" }], outputs: [{ name: "", type: "address" }] }];
5325
+ const saltZero = "0x0000000000000000000000000000000000000000000000000000000000000000";
5326
+ const factoryData = (0, import_viem3.encodeFunctionData)({ abi: compatCreateAbi, functionName: "createAccount", args: [session.ownerAddress, saltZero] });
5327
+ userOp = await (0, import_bundler.createUserOperationWithDynamicFees)(
5328
+ session.smartAccountAddress,
5329
+ nonce,
5330
+ callData,
5331
+ true,
5332
+ session.factoryAddress,
5333
+ factoryData,
5334
+ feeType
5335
+ );
5336
+ } else {
5337
+ userOp = await (0, import_bundler.createUserOperationWithDynamicFees)(
5338
+ session.smartAccountAddress,
5339
+ nonce,
5340
+ callData,
5341
+ false,
5342
+ void 0,
5343
+ void 0,
5344
+ feeType
5345
+ );
5346
+ }
5347
+ const ensureGenerousDefaults = () => {
5348
+ const toHex2 = (v) => `0x${v.toString(16)}`;
5349
+ const minCallGas = 0x493e0n;
5350
+ const minVerificationGas = 0x989680n;
5351
+ const minPreVerificationGas = 0x30d40n;
5352
+ if (BigInt(userOp.callGasLimit || "0x0") < minCallGas) userOp.callGasLimit = toHex2(minCallGas);
5353
+ if (BigInt(userOp.verificationGasLimit || "0x0") < minVerificationGas) userOp.verificationGasLimit = toHex2(minVerificationGas);
5354
+ if (BigInt(userOp.preVerificationGas || "0x0") < minPreVerificationGas) userOp.preVerificationGas = toHex2(minPreVerificationGas);
5355
+ };
5356
+ const enforceCaps = (usePaymaster) => {
5357
+ try {
5358
+ const envCaps = typeof import_meta !== "undefined" && import_meta.env || {};
5359
+ const maxBundlerVerifGas = envCaps.VITE_MAX_VERIFICATION_GAS ? BigInt(envCaps.VITE_MAX_VERIFICATION_GAS) : MAX_BUNDLER_VERIFICATION_GAS;
5360
+ const maxCallGas = envCaps.VITE_MAX_CALL_GAS_LIMIT ? BigInt(envCaps.VITE_MAX_CALL_GAS_LIMIT) : 0x7a120n;
5361
+ const toHex2 = (v) => `0x${v.toString(16)}`;
5362
+ const maxAccountVerifGas = usePaymaster ? maxBundlerVerifGas - PAYMASTER_VERIFICATION_GAS : maxBundlerVerifGas;
5363
+ const verGas = BigInt(userOp.verificationGasLimit || "0x0");
5364
+ if (verGas > maxAccountVerifGas) userOp.verificationGasLimit = toHex2(maxAccountVerifGas);
5365
+ const callGas = BigInt(userOp.callGasLimit || "0x0");
5366
+ if (callGas > maxCallGas) userOp.callGasLimit = toHex2(maxCallGas);
5367
+ } catch {
5368
+ }
5369
+ };
5370
+ let estimated = false;
5371
+ try {
5372
+ const gasEst = await (0, import_bundler.estimateUserOperationGas)({ ...userOp, signature: `0x${"00".repeat(65)}` });
5373
+ userOp.callGasLimit = gasEst.callGasLimit;
5374
+ userOp.verificationGasLimit = gasEst.verificationGasLimit;
5375
+ userOp.preVerificationGas = gasEst.preVerificationGas;
5376
+ ensureGenerousDefaults();
5377
+ enforceCaps(session.usePaymaster);
5378
+ estimated = true;
5379
+ } catch {
5380
+ ensureGenerousDefaults();
5381
+ enforceCaps(session.usePaymaster);
5382
+ }
5383
+ try {
5384
+ const toHex2 = (v) => `0x${v.toString(16)}`;
5385
+ const isContractCall = !!userOp.callData && userOp.callData !== "0x";
5386
+ if (isContractCall) {
5387
+ const currentVer = BigInt(userOp.verificationGasLimit || "0x0");
5388
+ const call = BigInt(userOp.callGasLimit || "0x0");
5389
+ const postOp = 150000n;
5390
+ const safety10k = 10000n;
5391
+ let buffer = call + postOp + safety10k;
5392
+ buffer += buffer / 63n;
5393
+ const newVer = currentVer + buffer;
5394
+ userOp.verificationGasLimit = toHex2(newVer);
5395
+ enforceCaps(session.usePaymaster);
5396
+ }
5397
+ } catch {
5398
+ }
5399
+ if (session.usePaymaster && LUMIA_PAYMASTER_ADDRESS) {
5400
+ userOp.paymaster = LUMIA_PAYMASTER_ADDRESS;
5401
+ userOp.paymasterData = "0x";
5402
+ userOp.paymasterVerificationGasLimit = PAYMASTER_VERIFICATION_GAS_LIMIT;
5403
+ userOp.paymasterPostOpGasLimit = PAYMASTER_POSTOP_GAS_LIMIT;
5404
+ }
5405
+ userOp.nonce = nonce;
5406
+ let opHash;
5407
+ if (entryPointVersion === "v0.6") {
5408
+ const userOpV06 = convertUserOpV07ToV06(userOp);
5409
+ opHash = await publicClient.readContract({
5410
+ address: entryPointAddress,
5411
+ abi: [{ type: "function", name: "getUserOpHash", inputs: [{ name: "userOp", type: "tuple", components: [{ name: "sender", type: "address" }, { name: "nonce", type: "uint256" }, { name: "initCode", type: "bytes" }, { name: "callData", type: "bytes" }, { name: "callGasLimit", type: "uint256" }, { name: "verificationGasLimit", type: "uint256" }, { name: "preVerificationGas", type: "uint256" }, { name: "maxFeePerGas", type: "uint256" }, { name: "maxPriorityFeePerGas", type: "uint256" }, { name: "paymasterAndData", type: "bytes" }, { name: "signature", type: "bytes" }] }], outputs: [{ name: "", type: "bytes32" }] }],
5412
+ functionName: "getUserOpHash",
5413
+ args: [{
5414
+ sender: userOpV06.sender,
5415
+ nonce: BigInt(userOpV06.nonce),
5416
+ initCode: userOpV06.initCode,
5417
+ callData: userOpV06.callData,
5418
+ callGasLimit: BigInt(userOpV06.callGasLimit),
5419
+ verificationGasLimit: BigInt(userOpV06.verificationGasLimit),
5420
+ preVerificationGas: BigInt(userOpV06.preVerificationGas),
5421
+ maxFeePerGas: BigInt(userOpV06.maxFeePerGas),
5422
+ maxPriorityFeePerGas: BigInt(userOpV06.maxPriorityFeePerGas),
5423
+ paymasterAndData: userOpV06.paymasterAndData,
5424
+ signature: "0x"
5425
+ }]
5426
+ });
5427
+ } else {
5428
+ const hasFactoryData = !!(userOp.factory && userOp.factoryData);
5429
+ const initCode = hasFactoryData ? (() => {
5430
+ const factoryAddr = userOp.factory.startsWith("0x") ? userOp.factory.slice(2) : userOp.factory;
5431
+ const factoryDataClean = userOp.factoryData.startsWith("0x") ? userOp.factoryData.slice(2) : userOp.factoryData;
5432
+ return `0x${factoryAddr}${factoryDataClean}`;
5433
+ })() : "0x";
5434
+ const accountGasLimits = pack2x128(BigInt(userOp.verificationGasLimit), BigInt(userOp.callGasLimit));
5435
+ const gasFees = pack2x128(BigInt(userOp.maxPriorityFeePerGas), BigInt(userOp.maxFeePerGas));
5436
+ let paymasterAndData = "0x";
5437
+ if (userOp.paymaster && userOp.paymaster !== "0x0000000000000000000000000000000000000000") {
5438
+ const verificationGasLimit = userOp.paymasterVerificationGasLimit || "0x186a0";
5439
+ const postOpGasLimit = userOp.paymasterPostOpGasLimit || "0x186a0";
5440
+ const paymasterDataField = userOp.paymasterData || "0x";
5441
+ const packedPaymasterGasLimits = pack2x128(BigInt(verificationGasLimit), BigInt(postOpGasLimit));
5442
+ const paymasterAddr = userOp.paymaster.startsWith("0x") ? userOp.paymaster.slice(2) : userOp.paymaster;
5443
+ const paymasterDataClean = paymasterDataField === "0x" ? "" : paymasterDataField.startsWith("0x") ? paymasterDataField.slice(2) : paymasterDataField;
5444
+ paymasterAndData = `0x${paymasterAddr}${packedPaymasterGasLimits.slice(2)}${paymasterDataClean}`;
5445
+ }
5446
+ const packedForHash = {
5447
+ sender: session.smartAccountAddress,
5448
+ nonce: BigInt(nonce),
5449
+ initCode,
5450
+ callData,
5451
+ accountGasLimits,
5452
+ preVerificationGas: BigInt(userOp.preVerificationGas),
5453
+ gasFees,
5454
+ paymasterAndData,
5455
+ signature: "0x"
5456
+ };
5457
+ opHash = await publicClient.readContract({
5458
+ address: entryPointAddress,
5459
+ abi: import_account_abstraction2.entryPoint07Abi,
5460
+ functionName: "getUserOpHash",
5461
+ args: [packedForHash]
5462
+ });
5463
+ }
5464
+ let signature;
5465
+ if (session.mpcUserId) {
5466
+ const mpcSig = await signDigestWithMpc(session.mpcUserId, opHash, {
5467
+ sender: userOp.sender,
5468
+ nonce: userOp.nonce,
5469
+ callData: userOp.callData,
5470
+ callGasLimit: userOp.callGasLimit,
5471
+ verificationGasLimit: userOp.verificationGasLimit,
5472
+ preVerificationGas: userOp.preVerificationGas,
5473
+ maxFeePerGas: userOp.maxFeePerGas,
5474
+ maxPriorityFeePerGas: userOp.maxPriorityFeePerGas,
5475
+ paymaster: userOp.paymaster,
5476
+ factory: userOp.factory,
5477
+ factoryData: userOp.factoryData
5478
+ });
5479
+ if (!mpcSig) throw new Error("MPC signing failed");
5480
+ signature = mpcSig;
5481
+ } else if (session.ownerPrivateKey) {
5482
+ const account = (0, import_accounts.privateKeyToAccount)(session.ownerPrivateKey);
5483
+ const rawSig = await account.sign({ hash: opHash });
5484
+ signature = normalizeSignature(rawSig);
5485
+ } else {
5486
+ throw new Error("No signing method available");
5487
+ }
5488
+ userOp.signature = signature;
5489
+ if (typeof userOp.sender !== "string") {
5490
+ userOp.sender = session.smartAccountAddress;
5491
+ }
5492
+ console.log("[Account] \u2705 Prepared signed UserOp (not sent):", JSON.stringify(userOp, (key, value) => typeof value === "bigint" ? `0x${value.toString(16)}` : value, 2));
5493
+ console.log("[Account] \u{1F511} UserOp Hash:", opHash);
5494
+ if (entryPointVersion === "v0.6") {
5495
+ const userOpV06 = convertUserOpV07ToV06(userOp);
5496
+ return { userOp: userOpV06, userOpHash: opHash };
5497
+ }
5498
+ return { userOp, userOpHash: opHash };
5499
+ }
5297
5500
  async function getEntryPointDeposit(address, entryPointVersion = "v0.7") {
5298
5501
  const entryPointAddress = entryPointVersion === "v0.6" ? ENTRYPOINT_V06 : ENTRYPOINT_V07;
5299
5502
  const depositAbi = [{ type: "function", name: "balanceOf", stateMutability: "view", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }] }];
@@ -9524,6 +9727,7 @@ function useSmartAccountTransactions() {
9524
9727
  UserOpStatus,
9525
9728
  getUserProfile,
9526
9729
  lumiaBeam,
9730
+ prepareUserOperation,
9527
9731
  queryClient,
9528
9732
  sendUserOperation,
9529
9733
  updateUserProfile,