@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 +434 -0
- package/dist/index.cjs +204 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +203 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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,
|