create-openfort 0.1.10 → 1.0.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/index.js +24 -23
  3. package/package.json +1 -1
  4. package/template/openfort-templates/firebase/AGENTS.md +1 -1
  5. package/template/openfort-templates/firebase/package.json +3 -3
  6. package/template/openfort-templates/firebase/src/App.tsx +2 -1
  7. package/template/openfort-templates/firebase/src/integrations/openfort/providers.tsx +36 -35
  8. package/template/openfort-templates/firebase/src/lib/contracts.ts +34 -0
  9. package/template/openfort-templates/firebase/src/ui/openfort/blockchain/ActionsCard.tsx +25 -13
  10. package/template/openfort-templates/firebase/src/ui/openfort/wallets/WalletCreation.tsx +108 -63
  11. package/template/openfort-templates/firebase/src/ui/openfort/wallets/WalletListCard.tsx +211 -41
  12. package/template/openfort-templates/firebase/src/ui/openfort/wallets/WalletPasswordSheets.tsx +45 -21
  13. package/template/openfort-templates/headless/AGENTS.md +1 -1
  14. package/template/openfort-templates/headless/package.json +2 -2
  15. package/template/openfort-templates/headless/src/components/cards/actions.tsx +30 -21
  16. package/template/openfort-templates/headless/src/components/cards/profile.tsx +0 -2
  17. package/template/openfort-templates/headless/src/components/cards/wallets.tsx +230 -67
  18. package/template/openfort-templates/headless/src/components/createWallet.tsx +115 -73
  19. package/template/openfort-templates/headless/src/components/passwordRecovery.tsx +48 -23
  20. package/template/openfort-templates/headless/src/components/providers.tsx +30 -25
  21. package/template/openfort-templates/headless/src/lib/contracts.ts +43 -0
  22. package/template/openfort-templates/openfort-ui/AGENTS.md +1 -1
  23. package/template/openfort-templates/openfort-ui/package.json +3 -3
  24. package/template/openfort-templates/openfort-ui/src/components/cards/actions.tsx +30 -15
  25. package/template/openfort-templates/openfort-ui/src/components/cards/auth.tsx +1 -1
  26. package/template/openfort-templates/openfort-ui/src/components/cards/wallets.tsx +232 -67
  27. package/template/openfort-templates/openfort-ui/src/components/createWallet.tsx +115 -73
  28. package/template/openfort-templates/openfort-ui/src/components/passwordRecovery.tsx +48 -23
  29. package/template/openfort-templates/openfort-ui/src/components/providers.tsx +34 -30
  30. package/template/openfort-templates/openfort-ui/src/lib/contracts.ts +35 -0
  31. package/template/openfort-templates/solana-headless/biome.json +70 -0
  32. package/template/openfort-templates/solana-headless/index.html +16 -0
  33. package/template/openfort-templates/solana-headless/package.json +34 -0
  34. package/template/openfort-templates/solana-headless/public/githubLogo.svg +5 -0
  35. package/template/openfort-templates/solana-headless/public/openfort.svg +13 -0
  36. package/template/openfort-templates/solana-headless/public/solanaLogo.svg +11 -0
  37. package/template/openfort-templates/solana-headless/src/App.tsx +7 -0
  38. package/template/openfort-templates/solana-headless/src/components/cards/auth.tsx +167 -0
  39. package/template/openfort-templates/solana-headless/src/components/cards/head.tsx +359 -0
  40. package/template/openfort-templates/solana-headless/src/components/cards/history.tsx +134 -0
  41. package/template/openfort-templates/solana-headless/src/components/cards/main.tsx +140 -0
  42. package/template/openfort-templates/solana-headless/src/components/cards/profile.tsx +80 -0
  43. package/template/openfort-templates/solana-headless/src/components/cards/send.tsx +242 -0
  44. package/template/openfort-templates/solana-headless/src/components/cards/sign.tsx +48 -0
  45. package/template/openfort-templates/solana-headless/src/components/cards/wallets.tsx +199 -0
  46. package/template/openfort-templates/solana-headless/src/components/createWallet.tsx +117 -0
  47. package/template/openfort-templates/solana-headless/src/components/passwordRecovery.tsx +167 -0
  48. package/template/openfort-templates/solana-headless/src/components/providers.tsx +23 -0
  49. package/template/openfort-templates/solana-headless/src/components/ui/Sheet.tsx +47 -0
  50. package/template/openfort-templates/solana-headless/src/components/ui/Tabs.tsx +111 -0
  51. package/template/openfort-templates/solana-headless/src/components/ui/TruncateData.tsx +31 -0
  52. package/template/openfort-templates/solana-headless/src/hooks/useSolanaMessageSigner.ts +37 -0
  53. package/template/openfort-templates/solana-headless/src/index.css +180 -0
  54. package/template/openfort-templates/solana-headless/src/lib/errors.ts +4 -0
  55. package/template/openfort-templates/solana-headless/src/lib/solana/balance.ts +17 -0
  56. package/template/openfort-templates/solana-headless/src/lib/solana/index.ts +4 -0
  57. package/template/openfort-templates/solana-headless/src/lib/solana/kora.ts +137 -0
  58. package/template/openfort-templates/solana-headless/src/lib/solana/transaction.ts +146 -0
  59. package/template/openfort-templates/solana-headless/src/lib/solana/transactionHistory.ts +39 -0
  60. package/template/openfort-templates/solana-headless/src/main.tsx +13 -0
  61. package/template/openfort-templates/solana-headless/src/vite-env.d.ts +1 -0
  62. package/template/openfort-templates/solana-headless/tsconfig.app.json +24 -0
  63. package/template/openfort-templates/solana-headless/tsconfig.json +7 -0
  64. package/template/openfort-templates/solana-headless/tsconfig.node.json +22 -0
  65. package/template/openfort-templates/solana-headless/vite.config.ts +8 -0
@@ -28,7 +28,7 @@ export const Auth = () => {
28
28
  Click the button below to sign in with Openfort. This will open a
29
29
  modal where you can choose your authentication method.
30
30
  </p>
31
- <OpenfortButton label="Sign In" />
31
+ <OpenfortButton label="Sign In" />
32
32
  </div>
33
33
  <div className="mt-8 space-y-2">
34
34
  <h2>Code Example</h2>
@@ -1,40 +1,175 @@
1
1
  import {
2
+ CheckIcon,
3
+ ChevronDownIcon,
4
+ ChevronUpIcon,
5
+ ClipboardDocumentIcon,
2
6
  FingerPrintIcon,
3
7
  KeyIcon,
4
8
  LockClosedIcon,
5
9
  } from '@heroicons/react/24/outline'
6
10
  import {
11
+ AccountTypeEnum,
7
12
  RecoveryMethod,
8
- type UserWallet,
13
+ type ConnectedEmbeddedEthereumWallet,
9
14
  useSignOut,
10
15
  useUser,
11
- useWallets,
12
16
  } from '@openfort/react'
13
- import { useState } from 'react'
17
+ import { useEthereumEmbeddedWallet } from '@openfort/react/ethereum'
18
+ import { useMemo, useState } from 'react'
14
19
  import { useAccount } from 'wagmi'
15
20
  import { CreateWallet, CreateWalletSheet } from '../createWallet'
16
21
  import { WalletRecoverPasswordSheet } from '../passwordRecovery'
17
22
 
23
+ const ACCOUNT_TYPE_LABELS: Record<AccountTypeEnum, string> = {
24
+ [AccountTypeEnum.EOA]: 'EOA',
25
+ [AccountTypeEnum.SMART_ACCOUNT]: 'SM',
26
+ [AccountTypeEnum.DELEGATED_ACCOUNT]: 'DE',
27
+ }
28
+
29
+ const VISIBLE_WALLET_COUNT = 4
30
+
31
+ const WalletRecoveryBadge = ({ wallet }: { wallet: ConnectedEmbeddedEthereumWallet }) => {
32
+ let Icon = LockClosedIcon
33
+ let text = 'Unknown'
34
+
35
+ switch (wallet.recoveryMethod) {
36
+ case RecoveryMethod.PASSWORD:
37
+ Icon = KeyIcon
38
+ text = 'Password'
39
+ break
40
+ case RecoveryMethod.AUTOMATIC:
41
+ Icon = LockClosedIcon
42
+ text = 'Automatic'
43
+ break
44
+ case RecoveryMethod.PASSKEY:
45
+ Icon = FingerPrintIcon
46
+ text = 'Passkey'
47
+ break
48
+ }
49
+
50
+ return (
51
+ <div className="flex items-center text-xs">
52
+ <span>{text}</span>
53
+ <Icon className="h-5 w-5 ml-2" />
54
+ </div>
55
+ )
56
+ }
57
+
58
+ const WalletItem = ({
59
+ wallet,
60
+ isActive,
61
+ isConnecting,
62
+ nested,
63
+ onClick,
64
+ }: {
65
+ wallet: ConnectedEmbeddedEthereumWallet
66
+ isActive: boolean
67
+ isConnecting: boolean
68
+ nested?: boolean
69
+ onClick: () => void
70
+ }) => (
71
+ <div className={nested ? 'ml-5 flex items-center gap-1' : undefined}>
72
+ {nested && <span className="text-zinc-500 text-sm">↳</span>}
73
+ <button
74
+ key={wallet.id + wallet.address}
75
+ className="px-4 py-3 border data-[active=true]:border-zinc-300 border-zinc-700 rounded data-[active=false]:cursor-pointer data-[active=false]:hover:bg-zinc-700/20 hover:border-zinc-300 transition-colors flex-1 text-sm w-full"
76
+ onClick={onClick}
77
+ data-active={isActive}
78
+ disabled={isActive || isConnecting}
79
+ type="button"
80
+ >
81
+ {isConnecting && isActive ? (
82
+ <p>Connecting...</p>
83
+ ) : (
84
+ <div className="flex justify-between items-center">
85
+ <p className="font-medium mr-2">
86
+ {`${wallet.address.substring(0, 6)}...${wallet.address.substring(wallet.address.length - 4)}`}
87
+ </p>
88
+ <div className="flex items-center gap-2">
89
+ {wallet.accountType && (
90
+ <span className="text-[10px] font-semibold leading-none border border-zinc-600 rounded px-1 py-0.5 opacity-70">
91
+ {ACCOUNT_TYPE_LABELS[wallet.accountType]}
92
+ </span>
93
+ )}
94
+ <WalletRecoveryBadge wallet={wallet} />
95
+ </div>
96
+ </div>
97
+ )}
98
+ </button>
99
+ </div>
100
+ )
101
+
18
102
  export const Wallets = () => {
19
103
  const {
20
104
  wallets,
21
- isLoadingWallets,
22
- availableWallets,
23
- setActiveWallet,
24
- isConnecting,
25
- } = useWallets()
105
+ status,
106
+ activeWallet,
107
+ setActive,
108
+ exportPrivateKey,
109
+ } = useEthereumEmbeddedWallet()
110
+ const isLoadingWallets = status === 'fetching-wallets'
111
+ const isConnecting = status === 'connecting'
26
112
  const { user, isAuthenticated } = useUser()
27
113
  const { isConnected } = useAccount()
28
114
  const [createWalletSheetOpen, setCreateWalletSheetOpen] = useState(false)
29
- const [walletToRecover, setWalletToRecover] = useState<UserWallet | null>(
30
- null,
31
- )
115
+ const [walletToRecover, setWalletToRecover] =
116
+ useState<ConnectedEmbeddedEthereumWallet | null>(null)
117
+ const [showAllWallets, setShowAllWallets] = useState(false)
118
+ const [exportedKey, setExportedKey] = useState<string | null>(null)
119
+ const [isExporting, setIsExporting] = useState(false)
120
+ const [exportError, setExportError] = useState<string | null>(null)
121
+ const [copied, setCopied] = useState(false)
32
122
  const { signOut } = useSignOut()
33
123
 
124
+ // Group wallets: EOAs at top level, Smart/Delegated accounts nested under their owner
125
+ const { topLevel, childrenByOwner } = useMemo(() => {
126
+ const ownerAddresses = new Set(wallets.map((w) => w.address.toLowerCase()))
127
+ const childrenByOwner = new Map<string, ConnectedEmbeddedEthereumWallet[]>()
128
+ const topLevel: ConnectedEmbeddedEthereumWallet[] = []
129
+
130
+ for (const wallet of wallets) {
131
+ const owner = wallet.ownerAddress?.toLowerCase()
132
+ if (owner && ownerAddresses.has(owner) && owner !== wallet.address.toLowerCase()) {
133
+ const existing = childrenByOwner.get(owner) ?? []
134
+ existing.push(wallet)
135
+ childrenByOwner.set(owner, existing)
136
+ } else {
137
+ topLevel.push(wallet)
138
+ }
139
+ }
140
+
141
+ return { topLevel, childrenByOwner }
142
+ }, [wallets])
143
+
144
+ const handleExportKey = async () => {
145
+ setIsExporting(true)
146
+ setExportError(null)
147
+ setExportedKey(null)
148
+ try {
149
+ const key = await exportPrivateKey()
150
+ setExportedKey(key)
151
+ } catch {
152
+ setExportError('Cannot export private key for this wallet.')
153
+ } finally {
154
+ setIsExporting(false)
155
+ }
156
+ }
157
+
158
+ const handleWalletClick = (wallet: ConnectedEmbeddedEthereumWallet) => {
159
+ const isActive = activeWallet?.address.toLowerCase() === wallet.address.toLowerCase()
160
+ if (isActive || isConnecting) return
161
+ if (wallet.recoveryMethod === RecoveryMethod.PASSWORD) {
162
+ setWalletToRecover(wallet)
163
+ } else {
164
+ setActive({ address: wallet.address })
165
+ }
166
+ }
167
+
168
+ if (!activeWallet && isConnecting) return <div>recovering ...</div>
34
169
  if (isLoadingWallets || (!user && isAuthenticated)) {
35
170
  return <div>Loading wallets...</div>
36
171
  }
37
- if (availableWallets.length === 0) {
172
+ if (wallets.length === 0) {
38
173
  return (
39
174
  <div className="flex gap-2 flex-col w-full">
40
175
  <h1>Create a wallet</h1>
@@ -44,46 +179,9 @@ export const Wallets = () => {
44
179
  )
45
180
  }
46
181
 
47
- const renderWalletRecovery = (wallet: UserWallet) => {
48
- let Icon = LockClosedIcon
49
- let text = 'Unknown'
50
- const method = wallet.recoveryMethod
51
-
52
- switch (method) {
53
- case RecoveryMethod.PASSWORD:
54
- Icon = KeyIcon
55
- text = 'Password'
56
- break
57
- case RecoveryMethod.AUTOMATIC:
58
- Icon = LockClosedIcon
59
- text = 'Automatic'
60
- break
61
- case RecoveryMethod.PASSKEY:
62
- Icon = FingerPrintIcon
63
- text = 'Passkey'
64
- break
65
- }
66
-
67
- return (
68
- <div className="flex items-center text-xs">
69
- <span>{text}</span>
70
- <Icon className="h-5 w-5 ml-2" />
71
- </div>
72
- )
73
- }
74
-
75
- const handleWalletClick = (wallet: UserWallet) => {
76
- if (wallet.isActive || isConnecting) return
77
- const method = wallet.recoveryMethod
78
- if (method === RecoveryMethod.PASSWORD) {
79
- setWalletToRecover(wallet)
80
- } else {
81
- setActiveWallet({
82
- walletId: 'xyz.openfort',
83
- address: wallet.address,
84
- })
85
- }
86
- }
182
+ const visibleTopLevel = showAllWallets
183
+ ? topLevel
184
+ : topLevel.slice(0, VISIBLE_WALLET_COUNT)
87
185
 
88
186
  return (
89
187
  <div className="flex flex-col w-full">
@@ -94,34 +192,101 @@ export const Wallets = () => {
94
192
  <div className="space-y-4 pb-4">
95
193
  <h2>Your Wallets</h2>
96
194
  <div className="flex flex-col space-y-2">
97
- {wallets.map((wallet) => (
195
+ {visibleTopLevel.map((wallet) => {
196
+ const isActive =
197
+ activeWallet?.address.toLowerCase() === wallet.address.toLowerCase()
198
+ const children = childrenByOwner.get(wallet.address.toLowerCase())
199
+
200
+ return (
201
+ <div key={wallet.id} className="space-y-1">
202
+ <WalletItem
203
+ wallet={wallet}
204
+ isActive={isActive}
205
+ isConnecting={isConnecting}
206
+ onClick={() => handleWalletClick(wallet)}
207
+ />
208
+ {children?.map((child) => {
209
+ const isChildActive =
210
+ activeWallet?.address.toLowerCase() === child.address.toLowerCase()
211
+ return (
212
+ <WalletItem
213
+ key={child.id}
214
+ wallet={child}
215
+ isActive={isChildActive}
216
+ isConnecting={isConnecting}
217
+ nested
218
+ onClick={() => handleWalletClick(child)}
219
+ />
220
+ )
221
+ })}
222
+ </div>
223
+ )
224
+ })}
225
+
226
+ {topLevel.length > VISIBLE_WALLET_COUNT && (
98
227
  <button
99
- key={wallet.id + wallet.address}
100
- className="px-4 py-3 border data-[active=true]:border-zinc-300 border-zinc-700 rounded data-[active=false]:cursor-pointer data-[active=false]:hover:bg-zinc-700/20 hover:border-zinc-300 transition-colors flex-1 text-sm"
101
- onClick={() => handleWalletClick(wallet)}
102
- data-active={wallet.isActive}
103
- disabled={wallet.isActive || isConnecting}
228
+ type="button"
229
+ className="flex items-center justify-center gap-1 text-xs text-zinc-400 hover:text-zinc-200 cursor-pointer transition-colors py-1"
230
+ onClick={() => setShowAllWallets((prev) => !prev)}
104
231
  >
105
- {wallet.isConnecting ? (
106
- <p>Connecting...</p>
232
+ {showAllWallets ? (
233
+ <>
234
+ Show less <ChevronUpIcon className="h-4 w-4" />
235
+ </>
107
236
  ) : (
108
- <div className="flex justify-between items-center">
109
- <p className="font-medium mr-2">
110
- {`${wallet.address.substring(0, 6)}...${wallet.address.substring(wallet.address.length - 4)}`}
111
- </p>
112
- {renderWalletRecovery(wallet)}
113
- </div>
237
+ <>
238
+ Show {topLevel.length - VISIBLE_WALLET_COUNT} more{' '}
239
+ <ChevronDownIcon className="h-4 w-4" />
240
+ </>
114
241
  )}
115
242
  </button>
116
- ))}
243
+ )}
117
244
 
118
245
  <button
119
246
  className="p-3 border border-zinc-700 rounded cursor-pointer hover:bg-zinc-700/20 hover:border-zinc-300 transition-colors flex-1"
120
247
  onClick={() => setCreateWalletSheetOpen(true)}
248
+ type="button"
121
249
  >
122
250
  + Create Wallet
123
251
  </button>
124
252
  </div>
253
+
254
+ {activeWallet && (
255
+ <div className="mt-4 space-y-2">
256
+ <button
257
+ type="button"
258
+ className="w-full p-3 border border-zinc-700 rounded cursor-pointer hover:bg-zinc-700/20 hover:border-zinc-300 transition-colors text-sm flex items-center justify-center gap-2"
259
+ onClick={handleExportKey}
260
+ disabled={isExporting}
261
+ >
262
+ <KeyIcon className="h-4 w-4" />
263
+ {isExporting ? 'Exporting...' : 'Export Private Key'}
264
+ </button>
265
+ {exportError && (
266
+ <p className="text-red-400 text-xs">{exportError}</p>
267
+ )}
268
+ {exportedKey && (
269
+ <div className="flex items-center gap-2 p-3 border border-zinc-700 rounded bg-zinc-800 text-xs break-all font-mono">
270
+ <span className="flex-1">{exportedKey}</span>
271
+ <button
272
+ type="button"
273
+ className="shrink-0 p-1 rounded hover:bg-zinc-700 transition-colors cursor-pointer"
274
+ onClick={() => {
275
+ navigator.clipboard.writeText(exportedKey)
276
+ setCopied(true)
277
+ setTimeout(() => setCopied(false), 2000)
278
+ }}
279
+ >
280
+ {copied ? (
281
+ <CheckIcon className="h-4 w-4 text-green-400" />
282
+ ) : (
283
+ <ClipboardDocumentIcon className="h-4 w-4 text-zinc-400" />
284
+ )}
285
+ </button>
286
+ </div>
287
+ )}
288
+ </div>
289
+ )}
125
290
  </div>
126
291
  <WalletRecoverPasswordSheet
127
292
  wallet={walletToRecover}
@@ -2,119 +2,161 @@ import {
2
2
  FingerPrintIcon,
3
3
  KeyIcon,
4
4
  LockClosedIcon,
5
+ WalletIcon,
5
6
  } from '@heroicons/react/24/outline'
6
- import { RecoveryMethod, useWallets } from '@openfort/react'
7
+ import { AccountTypeEnum, RecoveryMethod } from '@openfort/react'
8
+ import { useEthereumEmbeddedWallet } from '@openfort/react/ethereum'
7
9
  import { useState } from 'react'
8
10
  import { CreateWalletPasswordSheet } from './passwordRecovery'
9
11
  import { Sheet } from './ui/Sheet'
10
12
 
11
- export const CreateWalletSheet = ({
12
- open,
13
- onClose,
14
- }: {
13
+ type CreateStep = 'choose-account-type' | 'choose-recovery-method'
14
+
15
+ type CreateWalletSheetProps = {
15
16
  open: boolean
16
17
  onClose: () => void
17
- }) => {
18
+ onWalletCreated?: () => void
19
+ }
20
+
21
+ export const CreateWalletSheet = ({ open, onClose, onWalletCreated }: CreateWalletSheetProps) => {
18
22
  return (
19
23
  <Sheet
20
24
  open={open}
21
- onClose={() => {
22
- onClose()
23
- }}
25
+ onClose={onClose}
24
26
  title="Create Wallet"
25
27
  description="Please choose a recovery method for your new wallet."
26
28
  >
27
29
  <CreateWallet
28
30
  onWalletCreated={() => {
29
31
  onClose()
32
+ onWalletCreated?.()
30
33
  }}
31
34
  />
32
35
  </Sheet>
33
36
  )
34
37
  }
35
38
 
36
- export const CreateWallet = ({
37
- onWalletCreated,
38
- }: {
39
- onWalletCreated?: () => void
40
- }) => {
39
+ export const CreateWallet = ({ onWalletCreated }: { onWalletCreated?: () => void }) => {
40
+ const { create, status } = useEthereumEmbeddedWallet()
41
+ const [error, setError] = useState<string | null>(null)
42
+ const [step, setStep] = useState<CreateStep>('choose-account-type')
43
+ const [accountType, setAccountType] = useState<AccountTypeEnum>(AccountTypeEnum.SMART_ACCOUNT)
41
44
  const [passwordSheetOpen, setPasswordSheetOpen] = useState(false)
42
45
 
43
- const { isCreating, createWallet, error } = useWallets({
44
- onSuccess: () => {
45
- onWalletCreated?.()
46
- },
47
- })
46
+ const isCreating = status === 'creating'
48
47
 
49
48
  if (isCreating) {
50
49
  return <div>Creating wallet...</div>
51
50
  }
52
51
 
52
+ const handleCreate = async (recoveryMethod: RecoveryMethod, password?: string) => {
53
+ try {
54
+ await create({ recoveryMethod, accountType, ...(password && { password }) })
55
+ onWalletCreated?.()
56
+ } catch (err) {
57
+ setError(err instanceof Error ? err.message : 'Failed to create wallet')
58
+ }
59
+ }
60
+
53
61
  return (
54
62
  <>
55
- <div className="flex flex-col gap-4 wallet-option-group mb-4">
56
- <button
57
- className="wallet-option cursor-pointer"
58
- onClick={() =>
59
- createWallet({
60
- recovery: {
61
- recoveryMethod: RecoveryMethod.PASSKEY,
62
- },
63
- })
64
- }
65
- >
66
- <FingerPrintIcon />
67
- <div className="flex flex-col text-start">
68
- <h4>Passkey</h4>
69
- <p className="text-sm hover-description">
70
- Secure your wallet with biometric authentication.
71
- </p>
72
- </div>
73
- </button>
74
- <button
75
- className="wallet-option cursor-pointer"
76
- onClick={() =>
77
- createWallet({
78
- recovery: {
79
- recoveryMethod: RecoveryMethod.AUTOMATIC,
80
- },
81
- })
82
- }
83
- >
84
- <LockClosedIcon />
85
- <div className="flex flex-col text-start">
86
- <h4>Automatic recovery</h4>
87
- <p className="text-sm hover-description">
88
- Uses encryption session to recover your wallet.
89
- </p>
90
- </div>
91
- </button>
92
- <button
93
- className="wallet-option cursor-pointer"
94
- onClick={() => setPasswordSheetOpen(true)}
95
- >
96
- <KeyIcon />
97
- <div className="flex flex-col text-start">
98
- <h4>Password</h4>
99
- <p className="text-sm hover-description">
100
- Create a strong password to secure your wallet.
101
- </p>
63
+ {step === 'choose-account-type' && (
64
+ <div className="flex flex-col gap-4 wallet-option-group mb-4">
65
+ <p className="text-sm text-zinc-400">Select account type:</p>
66
+ {Object.values(AccountTypeEnum).map((type) => (
67
+ <button
68
+ key={type}
69
+ type="button"
70
+ className="wallet-option cursor-pointer"
71
+ onClick={() => {
72
+ setAccountType(type)
73
+ setStep('choose-recovery-method')
74
+ }}
75
+ >
76
+ <WalletIcon className="h-6 w-6 shrink-0" />
77
+ <div className="flex flex-col text-start">
78
+ <h4>{type}</h4>
79
+ <p className="text-sm hover-description">
80
+ {type === AccountTypeEnum.EOA && 'A standard externally owned account.'}
81
+ {type === AccountTypeEnum.SMART_ACCOUNT &&
82
+ 'A smart contract wallet (also creates an EOA owner).'}
83
+ {type === AccountTypeEnum.DELEGATED_ACCOUNT && 'A delegated smart account.'}
84
+ </p>
85
+ </div>
86
+ </button>
87
+ ))}
88
+ </div>
89
+ )}
90
+
91
+ {step === 'choose-recovery-method' && (
92
+ <>
93
+ <button
94
+ type="button"
95
+ className="text-xs text-zinc-400 mb-4 hover:text-zinc-200 flex items-center gap-1"
96
+ onClick={() => setStep('choose-account-type')}
97
+ >
98
+ ← Back ·{' '}
99
+ <span className="text-zinc-300">{accountType}</span>
100
+ </button>
101
+ <div className="flex flex-col gap-4 wallet-option-group mb-4">
102
+ <p className="text-sm text-zinc-400">Select recovery method:</p>
103
+ <button
104
+ type="button"
105
+ className="wallet-option cursor-pointer"
106
+ onClick={() => handleCreate(RecoveryMethod.PASSKEY)}
107
+ >
108
+ <FingerPrintIcon />
109
+ <div className="flex flex-col text-start">
110
+ <h4>Passkey</h4>
111
+ <p className="text-sm hover-description">
112
+ Secure your wallet with biometric authentication.
113
+ </p>
114
+ </div>
115
+ </button>
116
+ <button
117
+ type="button"
118
+ className="wallet-option cursor-pointer"
119
+ onClick={() => handleCreate(RecoveryMethod.AUTOMATIC)}
120
+ >
121
+ <LockClosedIcon />
122
+ <div className="flex flex-col text-start">
123
+ <h4>Automatic recovery</h4>
124
+ <p className="text-sm hover-description">
125
+ Uses encryption session to recover your wallet.
126
+ </p>
127
+ </div>
128
+ </button>
129
+ <button
130
+ type="button"
131
+ className="wallet-option cursor-pointer"
132
+ onClick={() => setPasswordSheetOpen(true)}
133
+ >
134
+ <KeyIcon />
135
+ <div className="flex flex-col text-start">
136
+ <h4>Password</h4>
137
+ <p className="text-sm hover-description">
138
+ Create a strong password to secure your wallet.
139
+ </p>
140
+ </div>
141
+ </button>
102
142
  </div>
103
- </button>
104
- </div>
105
- {error && (
106
- <p className="text-red-500 text-sm mb-2">Error: {error.message}</p>
143
+ </>
107
144
  )}
145
+
146
+ {error && <p className="text-red-500 text-sm mb-2">Error: {error}</p>}
147
+
108
148
  <p className="mb-4 text-xs text-zinc-400">
109
149
  Disclaimer: This is a demo of Openfort recovery methods. In production,
110
150
  it's best to choose one method for a smoother user experience.
111
151
  </p>
152
+
112
153
  <CreateWalletPasswordSheet
113
154
  open={passwordSheetOpen}
114
155
  onClose={() => setPasswordSheetOpen(false)}
115
- onCreateWallet={() => {
116
- onWalletCreated?.()
117
- }}
156
+ create={create}
157
+ status={status}
158
+ accountType={accountType}
159
+ onCreateWallet={onWalletCreated}
118
160
  />
119
161
  </>
120
162
  )