@jerydam/lumina-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BUTTON_FIXES.md +59 -0
- package/DOCS_INDEX.md +332 -0
- package/DOCUMENTATION.md +252 -0
- package/DOCUMENTATION_BUILD_SUMMARY.md +376 -0
- package/DOCUMENTATION_COMPLETE.md +311 -0
- package/FEATURES.md +333 -0
- package/Lumina-sdk/src/components/lumina-provider.tsx +46 -0
- package/Lumina-sdk/src/components/transaction-confirm.tsx +242 -0
- package/Lumina-sdk/src/components/wallet-display.tsx +157 -0
- package/Lumina-sdk/src/components/wallet-login.tsx +163 -0
- package/Lumina-sdk/src/hooks/use-mobile.ts +19 -0
- package/Lumina-sdk/src/hooks/use-toast.ts +191 -0
- package/Lumina-sdk/src/index.ts +0 -0
- package/Lumina-sdk/src/lib/api.ts +66 -0
- package/Lumina-sdk/src/lib/utils.ts +6 -0
- package/Lumina-sdk/src/package.json +42 -0
- package/Lumina-sdk/src/tsconfig.json +19 -0
- package/NEW_FILES_MANIFEST.txt +146 -0
- package/README.md +298 -0
- package/app/dashboard/analytics/page.tsx +218 -0
- package/app/dashboard/api-keys/page.tsx +260 -0
- package/app/dashboard/billing/page.tsx +412 -0
- package/app/dashboard/integration/page.tsx +185 -0
- package/app/dashboard/layout.tsx +18 -0
- package/app/dashboard/page.tsx +244 -0
- package/app/dashboard/settings/page.tsx +285 -0
- package/app/dashboard/users/page.tsx +148 -0
- package/app/docs/api/authentication/page.tsx +246 -0
- package/app/docs/api/endpoints/page.tsx +397 -0
- package/app/docs/api/errors/page.tsx +305 -0
- package/app/docs/api/overview/page.tsx +306 -0
- package/app/docs/examples/basic-setup/page.tsx +256 -0
- package/app/docs/examples/multi-chain/page.tsx +331 -0
- package/app/docs/examples/nextjs-full-stack/page.tsx +332 -0
- package/app/docs/getting-started/environment-setup/page.tsx +243 -0
- package/app/docs/getting-started/installation/page.tsx +187 -0
- package/app/docs/getting-started/introduction/page.tsx +178 -0
- package/app/docs/getting-started/quick-start/page.tsx +199 -0
- package/app/docs/guides/nextjs/page.tsx +358 -0
- package/app/docs/guides/react/page.tsx +230 -0
- package/app/docs/guides/security/page.tsx +284 -0
- package/app/docs/layout.tsx +32 -0
- package/app/docs/page.tsx +180 -0
- package/app/docs/sdk/lumina-provider/page.tsx +186 -0
- package/app/docs/sdk/transaction-confirm/page.tsx +331 -0
- package/app/docs/sdk/wallet-display/page.tsx +224 -0
- package/app/docs/sdk/wallet-login/page.tsx +207 -0
- package/app/docs/troubleshooting/common-issues/page.tsx +301 -0
- package/app/docs/troubleshooting/faq/page.tsx +105 -0
- package/app/globals.css +125 -0
- package/app/invite/[token]/page.tsx +78 -0
- package/app/layout.tsx +36 -0
- package/app/login/page.tsx +175 -0
- package/app/page.tsx +336 -0
- package/app/sdk-demo/page.tsx +239 -0
- package/components/dashboard-sidebar.tsx +113 -0
- package/components/docs/breadcrumb.tsx +51 -0
- package/components/docs/callout.tsx +53 -0
- package/components/docs/code-block.tsx +77 -0
- package/components/docs/docs-sidebar.tsx +214 -0
- package/components/docs/table-of-contents.tsx +83 -0
- package/components/sdk/lumina-provider.tsx +46 -0
- package/components/sdk/transaction-confirm.tsx +242 -0
- package/components/sdk/wallet-display.tsx +157 -0
- package/components/sdk/wallet-login.tsx +163 -0
- package/components/theme-provider.tsx +11 -0
- package/components/ui/accordion.tsx +66 -0
- package/components/ui/alert-dialog.tsx +157 -0
- package/components/ui/alert.tsx +66 -0
- package/components/ui/aspect-ratio.tsx +11 -0
- package/components/ui/avatar.tsx +53 -0
- package/components/ui/badge.tsx +46 -0
- package/components/ui/breadcrumb.tsx +109 -0
- package/components/ui/button-group.tsx +83 -0
- package/components/ui/button.tsx +60 -0
- package/components/ui/calendar.tsx +213 -0
- package/components/ui/card.tsx +92 -0
- package/components/ui/carousel.tsx +241 -0
- package/components/ui/chart.tsx +351 -0
- package/components/ui/checkbox.tsx +32 -0
- package/components/ui/collapsible.tsx +33 -0
- package/components/ui/command.tsx +184 -0
- package/components/ui/context-menu.tsx +252 -0
- package/components/ui/dialog.tsx +143 -0
- package/components/ui/drawer.tsx +135 -0
- package/components/ui/dropdown-menu.tsx +257 -0
- package/components/ui/empty.tsx +104 -0
- package/components/ui/field.tsx +244 -0
- package/components/ui/form.tsx +167 -0
- package/components/ui/hover-card.tsx +44 -0
- package/components/ui/input-group.tsx +169 -0
- package/components/ui/input-otp.tsx +77 -0
- package/components/ui/input.tsx +21 -0
- package/components/ui/item.tsx +193 -0
- package/components/ui/kbd.tsx +28 -0
- package/components/ui/label.tsx +24 -0
- package/components/ui/menubar.tsx +276 -0
- package/components/ui/navigation-menu.tsx +166 -0
- package/components/ui/pagination.tsx +127 -0
- package/components/ui/popover.tsx +48 -0
- package/components/ui/progress.tsx +31 -0
- package/components/ui/radio-group.tsx +45 -0
- package/components/ui/resizable.tsx +56 -0
- package/components/ui/scroll-area.tsx +58 -0
- package/components/ui/select.tsx +185 -0
- package/components/ui/separator.tsx +28 -0
- package/components/ui/sheet.tsx +139 -0
- package/components/ui/sidebar.tsx +726 -0
- package/components/ui/skeleton.tsx +13 -0
- package/components/ui/slider.tsx +59 -0
- package/components/ui/sonner.tsx +25 -0
- package/components/ui/spinner.tsx +16 -0
- package/components/ui/switch.tsx +29 -0
- package/components/ui/table.tsx +116 -0
- package/components/ui/tabs.tsx +66 -0
- package/components/ui/textarea.tsx +18 -0
- package/components/ui/toast.tsx +129 -0
- package/components/ui/toaster.tsx +35 -0
- package/components/ui/toggle-group.tsx +73 -0
- package/components/ui/toggle.tsx +47 -0
- package/components/ui/tooltip.tsx +61 -0
- package/components/ui/use-mobile.tsx +19 -0
- package/components/ui/use-toast.ts +191 -0
- package/components.json +21 -0
- package/hooks/use-mobile.ts +19 -0
- package/hooks/use-toast.ts +191 -0
- package/lib/api.ts +66 -0
- package/lib/utils.ts +6 -0
- package/next-env.d.ts +6 -0
- package/next.config.mjs +11 -0
- package/package.json +73 -0
- package/pnpm-workspace.yaml +5 -0
- package/postcss.config.mjs +8 -0
- package/public/apple-icon.png +0 -0
- package/public/fav.jpeg +0 -0
- package/public/fav.png +0 -0
- package/public/icon-dark-32x32.png +0 -0
- package/public/icon-light-32x32.png +0 -0
- package/public/icon.png +0 -0
- package/public/icon.svg +26 -0
- package/public/logo.jpeg +0 -0
- package/public/logo.png +0 -0
- package/public/logo2.jpeg +0 -0
- package/public/logo2.png +0 -0
- package/public/placeholder-logo.png +0 -0
- package/public/placeholder-logo.svg +1 -0
- package/public/placeholder-user.jpg +0 -0
- package/public/placeholder.jpg +0 -0
- package/public/placeholder.svg +1 -0
- package/styles/globals.css +209 -0
- package/tailwind.config.ts +15 -0
- package/tsconfig.json +41 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Breadcrumb } from '@/components/docs/breadcrumb'
|
|
4
|
+
import { CodeBlock } from '@/components/docs/code-block'
|
|
5
|
+
import { Callout } from '@/components/docs/callout'
|
|
6
|
+
|
|
7
|
+
export default function MultiChainExamplePage() {
|
|
8
|
+
return (
|
|
9
|
+
<div className="flex gap-8 max-w-7xl mx-auto px-4 py-8">
|
|
10
|
+
<main className="flex-1 min-w-0">
|
|
11
|
+
<Breadcrumb
|
|
12
|
+
items={[
|
|
13
|
+
{ label: 'Docs', href: '/docs' },
|
|
14
|
+
{ label: 'Examples', href: '/docs/examples/basic-setup' },
|
|
15
|
+
{ label: 'Multi-Chain' },
|
|
16
|
+
]}
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
<div className="mt-8 prose prose-invert max-w-none">
|
|
20
|
+
<h1>Multi-Chain Wallet Example</h1>
|
|
21
|
+
|
|
22
|
+
<p className="text-lg text-foreground/80">
|
|
23
|
+
Build a wallet application that supports multiple blockchain networks with Lumina.
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
<h2>Supported Networks</h2>
|
|
27
|
+
<CodeBlock
|
|
28
|
+
code={`const networks = {
|
|
29
|
+
ethereum: {
|
|
30
|
+
id: 1,
|
|
31
|
+
name: 'Ethereum',
|
|
32
|
+
rpc: 'https://eth-mainnet.g.alchemy.com/v2/...',
|
|
33
|
+
explorer: 'https://etherscan.io',
|
|
34
|
+
},
|
|
35
|
+
polygon: {
|
|
36
|
+
id: 137,
|
|
37
|
+
name: 'Polygon',
|
|
38
|
+
rpc: 'https://polygon-mainnet.g.alchemy.com/v2/...',
|
|
39
|
+
explorer: 'https://polygonscan.com',
|
|
40
|
+
},
|
|
41
|
+
arbitrum: {
|
|
42
|
+
id: 42161,
|
|
43
|
+
name: 'Arbitrum',
|
|
44
|
+
rpc: 'https://arbitrum-mainnet.g.alchemy.com/v2/...',
|
|
45
|
+
explorer: 'https://arbiscan.io',
|
|
46
|
+
},
|
|
47
|
+
optimism: {
|
|
48
|
+
id: 10,
|
|
49
|
+
name: 'Optimism',
|
|
50
|
+
rpc: 'https://opt-mainnet.g.alchemy.com/v2/...',
|
|
51
|
+
explorer: 'https://optimistic.etherscan.io',
|
|
52
|
+
},
|
|
53
|
+
}`}
|
|
54
|
+
language="typescript"
|
|
55
|
+
title="Supported Networks"
|
|
56
|
+
/>
|
|
57
|
+
|
|
58
|
+
<h2>Multi-Chain Provider Setup</h2>
|
|
59
|
+
<CodeBlock
|
|
60
|
+
code={`// app/providers.tsx
|
|
61
|
+
'use client'
|
|
62
|
+
|
|
63
|
+
import { ReactNode, useState } from 'react'
|
|
64
|
+
import { LuminaProvider } from '@lumina/react'
|
|
65
|
+
|
|
66
|
+
export function Providers({ children }: { children: ReactNode }) {
|
|
67
|
+
const [network, setNetwork] = useState('ethereum')
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<LuminaProvider
|
|
71
|
+
publicKey={process.env.NEXT_PUBLIC_LUMINA_PUBLIC_KEY!}
|
|
72
|
+
network={network as any}
|
|
73
|
+
onNetworkChange={setNetwork}
|
|
74
|
+
>
|
|
75
|
+
{children}
|
|
76
|
+
</LuminaProvider>
|
|
77
|
+
)
|
|
78
|
+
}`}
|
|
79
|
+
language="typescript"
|
|
80
|
+
title="Multi-Chain Provider"
|
|
81
|
+
/>
|
|
82
|
+
|
|
83
|
+
<h2>Network Selector Component</h2>
|
|
84
|
+
<CodeBlock
|
|
85
|
+
code={`'use client'
|
|
86
|
+
|
|
87
|
+
import { useState } from 'react'
|
|
88
|
+
import { useLumina } from '@lumina/react'
|
|
89
|
+
|
|
90
|
+
const networks = [
|
|
91
|
+
{ id: 'ethereum', name: 'Ethereum', icon: '⟠' },
|
|
92
|
+
{ id: 'polygon', name: 'Polygon', icon: '🟣' },
|
|
93
|
+
{ id: 'arbitrum', name: 'Arbitrum', icon: '🟦' },
|
|
94
|
+
{ id: 'optimism', name: 'Optimism', icon: '🔴' },
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
export function NetworkSelector() {
|
|
98
|
+
const { network, setNetwork } = useLumina()
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<div className="flex gap-2">
|
|
102
|
+
{networks.map((net) => (
|
|
103
|
+
<button
|
|
104
|
+
key={net.id}
|
|
105
|
+
onClick={() => setNetwork(net.id)}
|
|
106
|
+
className={
|
|
107
|
+
network === net.id
|
|
108
|
+
? 'bg-emerald-600 text-white'
|
|
109
|
+
: 'bg-white/10 text-white hover:bg-white/20'
|
|
110
|
+
}
|
|
111
|
+
>
|
|
112
|
+
{net.icon} {net.name}
|
|
113
|
+
</button>
|
|
114
|
+
))}
|
|
115
|
+
</div>
|
|
116
|
+
)
|
|
117
|
+
}`}
|
|
118
|
+
language="typescript"
|
|
119
|
+
title="Network Selector"
|
|
120
|
+
/>
|
|
121
|
+
|
|
122
|
+
<h2>Multi-Wallet Management</h2>
|
|
123
|
+
<CodeBlock
|
|
124
|
+
code={`// app/wallets/page.tsx
|
|
125
|
+
'use client'
|
|
126
|
+
|
|
127
|
+
import { useState, useEffect } from 'react'
|
|
128
|
+
import { useLumina } from '@lumina/react'
|
|
129
|
+
|
|
130
|
+
interface Wallet {
|
|
131
|
+
id: string
|
|
132
|
+
network: string
|
|
133
|
+
address: string
|
|
134
|
+
balance: string
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export default function WalletsPage() {
|
|
138
|
+
const { network, user } = useLumina()
|
|
139
|
+
const [wallets, setWallets] = useState<Wallet[]>([])
|
|
140
|
+
const [loading, setLoading] = useState(true)
|
|
141
|
+
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
loadWallets()
|
|
144
|
+
}, [user])
|
|
145
|
+
|
|
146
|
+
async function loadWallets() {
|
|
147
|
+
try {
|
|
148
|
+
const response = await fetch(\`/api/wallets?user_id=\${user.id}\`)
|
|
149
|
+
const data = await response.json()
|
|
150
|
+
setWallets(data.data.wallets)
|
|
151
|
+
} finally {
|
|
152
|
+
setLoading(false)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function createWalletForNetwork(networkId: string) {
|
|
157
|
+
try {
|
|
158
|
+
const response = await fetch('/api/wallets', {
|
|
159
|
+
method: 'POST',
|
|
160
|
+
headers: { 'Content-Type': 'application/json' },
|
|
161
|
+
body: JSON.stringify({
|
|
162
|
+
user_id: user.id,
|
|
163
|
+
network: networkId,
|
|
164
|
+
name: \`\${networkId} Wallet\`,
|
|
165
|
+
}),
|
|
166
|
+
})
|
|
167
|
+
const wallet = await response.json()
|
|
168
|
+
setWallets([...wallets, wallet.data])
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error('Failed to create wallet:', error)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<div>
|
|
176
|
+
<h1>My Multi-Chain Wallets</h1>
|
|
177
|
+
|
|
178
|
+
{loading ? (
|
|
179
|
+
<p>Loading wallets...</p>
|
|
180
|
+
) : (
|
|
181
|
+
<div>
|
|
182
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
183
|
+
{wallets.map((wallet) => (
|
|
184
|
+
<div
|
|
185
|
+
key={wallet.id}
|
|
186
|
+
className="p-4 border border-emerald-600/20 rounded-lg"
|
|
187
|
+
>
|
|
188
|
+
<h3>{wallet.network.toUpperCase()}</h3>
|
|
189
|
+
<p className="font-mono text-sm">{wallet.address}</p>
|
|
190
|
+
<p className="text-lg">Balance: {wallet.balance} ETH</p>
|
|
191
|
+
</div>
|
|
192
|
+
))}
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<button
|
|
196
|
+
onClick={() => createWalletForNetwork('polygon')}
|
|
197
|
+
className="mt-4 px-4 py-2 bg-emerald-600 text-white rounded"
|
|
198
|
+
>
|
|
199
|
+
Add Polygon Wallet
|
|
200
|
+
</button>
|
|
201
|
+
</div>
|
|
202
|
+
)}
|
|
203
|
+
</div>
|
|
204
|
+
)
|
|
205
|
+
}`}
|
|
206
|
+
language="typescript"
|
|
207
|
+
title="Multi-Wallet Component"
|
|
208
|
+
/>
|
|
209
|
+
|
|
210
|
+
<h2>Cross-Chain Transaction</h2>
|
|
211
|
+
<CodeBlock
|
|
212
|
+
code={`// lib/multi-chain.ts
|
|
213
|
+
export async function sendCrossChainTransaction({
|
|
214
|
+
sourceNetwork,
|
|
215
|
+
destinationNetwork,
|
|
216
|
+
walletId,
|
|
217
|
+
recipient,
|
|
218
|
+
amount,
|
|
219
|
+
}: {
|
|
220
|
+
sourceNetwork: string
|
|
221
|
+
destinationNetwork: string
|
|
222
|
+
walletId: string
|
|
223
|
+
recipient: string
|
|
224
|
+
amount: string
|
|
225
|
+
}) {
|
|
226
|
+
// Create transaction on source network
|
|
227
|
+
const txResponse = await fetch('/api/transactions', {
|
|
228
|
+
method: 'POST',
|
|
229
|
+
headers: { 'Content-Type': 'application/json' },
|
|
230
|
+
body: JSON.stringify({
|
|
231
|
+
wallet_id: walletId,
|
|
232
|
+
to: recipient,
|
|
233
|
+
amount,
|
|
234
|
+
token: 'ETH',
|
|
235
|
+
chain_id: sourceNetwork,
|
|
236
|
+
type: 'cross-chain-transfer',
|
|
237
|
+
destination_chain: destinationNetwork,
|
|
238
|
+
}),
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
const transaction = await txResponse.json()
|
|
242
|
+
|
|
243
|
+
return transaction.data
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Track cross-chain status
|
|
247
|
+
export async function getCrossChainStatus(transactionId: string) {
|
|
248
|
+
const response = await fetch(\`/api/transactions/\${transactionId}/status\`)
|
|
249
|
+
const data = await response.json()
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
sourceChainStatus: data.data.source_status, // pending, confirmed
|
|
253
|
+
destinationChainStatus: data.data.dest_status, // pending, completed
|
|
254
|
+
estimatedTime: data.data.estimated_time,
|
|
255
|
+
}
|
|
256
|
+
}`}
|
|
257
|
+
language="typescript"
|
|
258
|
+
title="Cross-Chain Helper"
|
|
259
|
+
/>
|
|
260
|
+
|
|
261
|
+
<h2>Gas Price Comparison</h2>
|
|
262
|
+
<CodeBlock
|
|
263
|
+
code={`'use client'
|
|
264
|
+
|
|
265
|
+
import { useEffect, useState } from 'react'
|
|
266
|
+
|
|
267
|
+
export function GasComparison() {
|
|
268
|
+
const [gasPrices, setGasPrices] = useState({})
|
|
269
|
+
|
|
270
|
+
useEffect(() => {
|
|
271
|
+
fetchGasPrices()
|
|
272
|
+
}, [])
|
|
273
|
+
|
|
274
|
+
async function fetchGasPrices() {
|
|
275
|
+
const networks = ['ethereum', 'polygon', 'arbitrum', 'optimism']
|
|
276
|
+
const prices: any = {}
|
|
277
|
+
|
|
278
|
+
for (const network of networks) {
|
|
279
|
+
try {
|
|
280
|
+
const response = await fetch(
|
|
281
|
+
\`/api/networks/\${network}/gas-price\`
|
|
282
|
+
)
|
|
283
|
+
const data = await response.json()
|
|
284
|
+
prices[network] = data.data.gas_price
|
|
285
|
+
} catch (error) {
|
|
286
|
+
console.error(\`Failed to fetch gas price for \${network}\`, error)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
setGasPrices(prices)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return (
|
|
294
|
+
<div className="p-4 bg-white/5 rounded-lg">
|
|
295
|
+
<h3>Gas Prices Comparison</h3>
|
|
296
|
+
<div className="grid grid-cols-2 gap-2">
|
|
297
|
+
{Object.entries(gasPrices).map(([network, price]: any) => (
|
|
298
|
+
<div key={network}>
|
|
299
|
+
<p className="text-sm text-gray-400">{network}</p>
|
|
300
|
+
<p className="text-lg font-bold">{price} Gwei</p>
|
|
301
|
+
</div>
|
|
302
|
+
))}
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
)
|
|
306
|
+
}`}
|
|
307
|
+
language="typescript"
|
|
308
|
+
title="Gas Price Comparison"
|
|
309
|
+
/>
|
|
310
|
+
|
|
311
|
+
<Callout type="info">
|
|
312
|
+
Use bridging services like Across or Stargate for efficient cross-chain transfers. Lumina supports integration with major bridge protocols.
|
|
313
|
+
</Callout>
|
|
314
|
+
|
|
315
|
+
<Callout type="warning">
|
|
316
|
+
Always verify gas prices and estimated transaction times before executing cross-chain transactions. Rates vary significantly between networks.
|
|
317
|
+
</Callout>
|
|
318
|
+
</div>
|
|
319
|
+
|
|
320
|
+
<div className="flex justify-between mt-12 pt-8 border-t border-white/10">
|
|
321
|
+
<a href="/docs/examples/nextjs-full-stack" className="text-emerald-400 hover:text-emerald-300">
|
|
322
|
+
← Full Stack Example
|
|
323
|
+
</a>
|
|
324
|
+
<a href="/docs/troubleshooting/faq" className="text-emerald-400 hover:text-emerald-300">
|
|
325
|
+
FAQ →
|
|
326
|
+
</a>
|
|
327
|
+
</div>
|
|
328
|
+
</main>
|
|
329
|
+
</div>
|
|
330
|
+
)
|
|
331
|
+
}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Breadcrumb } from '@/components/docs/breadcrumb'
|
|
4
|
+
import { CodeBlock } from '@/components/docs/code-block'
|
|
5
|
+
import { Callout } from '@/components/docs/callout'
|
|
6
|
+
|
|
7
|
+
export default function FullStackExamplePage() {
|
|
8
|
+
return (
|
|
9
|
+
<div className="flex gap-8 max-w-7xl mx-auto px-4 py-8">
|
|
10
|
+
<main className="flex-1 min-w-0">
|
|
11
|
+
<Breadcrumb
|
|
12
|
+
items={[
|
|
13
|
+
{ label: 'Docs', href: '/docs' },
|
|
14
|
+
{ label: 'Examples', href: '/docs/examples/basic-setup' },
|
|
15
|
+
{ label: 'Next.js Full Stack' },
|
|
16
|
+
]}
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
<div className="mt-8 prose prose-invert max-w-none">
|
|
20
|
+
<h1>Next.js Full Stack Example</h1>
|
|
21
|
+
|
|
22
|
+
<p className="text-lg text-foreground/80">
|
|
23
|
+
Complete full-stack example of a wallet application built with Next.js, including server-side and client-side components.
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
<h2>Project Structure</h2>
|
|
27
|
+
<CodeBlock
|
|
28
|
+
code={`my-wallet-app/
|
|
29
|
+
├── app/
|
|
30
|
+
│ ├── api/
|
|
31
|
+
│ │ └── wallets/
|
|
32
|
+
│ │ ├── route.ts # Create wallet
|
|
33
|
+
│ │ └── [id]/
|
|
34
|
+
│ │ └── route.ts # Get wallet
|
|
35
|
+
│ ├── wallet/
|
|
36
|
+
│ │ └── page.tsx # Wallet page
|
|
37
|
+
│ ├── layout.tsx
|
|
38
|
+
│ └── providers.tsx
|
|
39
|
+
├── lib/
|
|
40
|
+
│ └── lumina.ts # Lumina API helpers
|
|
41
|
+
├── .env.local
|
|
42
|
+
└── package.json`}
|
|
43
|
+
language="text"
|
|
44
|
+
title="File Structure"
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
<h2>Environment Setup</h2>
|
|
48
|
+
<CodeBlock
|
|
49
|
+
code={`# .env.local
|
|
50
|
+
NEXT_PUBLIC_LUMINA_PUBLIC_KEY=pk_live_abc123def456...
|
|
51
|
+
LUMINA_SECRET_KEY=sk_live_xyz789uvw012...
|
|
52
|
+
NEXT_PUBLIC_LUMINA_API_URL=https://api.lumina.dev
|
|
53
|
+
NEXT_PUBLIC_LUMINA_NETWORK=ethereum`}
|
|
54
|
+
language="bash"
|
|
55
|
+
title=".env.local"
|
|
56
|
+
/>
|
|
57
|
+
|
|
58
|
+
<h2>API Helpers</h2>
|
|
59
|
+
<CodeBlock
|
|
60
|
+
code={`// lib/lumina.ts
|
|
61
|
+
export interface LuminaConfig {
|
|
62
|
+
baseUrl: string
|
|
63
|
+
secretKey: string
|
|
64
|
+
publicKey: string
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const config: LuminaConfig = {
|
|
68
|
+
baseUrl: process.env.NEXT_PUBLIC_LUMINA_API_URL || '',
|
|
69
|
+
secretKey: process.env.LUMINA_SECRET_KEY || '',
|
|
70
|
+
publicKey: process.env.NEXT_PUBLIC_LUMINA_PUBLIC_KEY || '',
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function createWallet(userId: string) {
|
|
74
|
+
const response = await fetch(\`\${config.baseUrl}/v1/wallets\`, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: {
|
|
77
|
+
'Authorization': \`Bearer \${config.secretKey}\`,
|
|
78
|
+
'Content-Type': 'application/json',
|
|
79
|
+
},
|
|
80
|
+
body: JSON.stringify({ user_id: userId, network: 'ethereum' }),
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
const error = await response.json()
|
|
85
|
+
throw new Error(error.error.message)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return response.json()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function getWallet(walletId: string) {
|
|
92
|
+
const response = await fetch(
|
|
93
|
+
\`\${config.baseUrl}/v1/wallets/\${walletId}\`,
|
|
94
|
+
{
|
|
95
|
+
headers: {
|
|
96
|
+
'Authorization': \`Bearer \${config.secretKey}\`,
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
throw new Error('Failed to fetch wallet')
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return response.json()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function createTransaction(
|
|
109
|
+
walletId: string,
|
|
110
|
+
to: string,
|
|
111
|
+
amount: string
|
|
112
|
+
) {
|
|
113
|
+
const response = await fetch(\`\${config.baseUrl}/v1/transactions\`, {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
headers: {
|
|
116
|
+
'Authorization': \`Bearer \${config.secretKey}\`,
|
|
117
|
+
'Content-Type': 'application/json',
|
|
118
|
+
},
|
|
119
|
+
body: JSON.stringify({
|
|
120
|
+
wallet_id: walletId,
|
|
121
|
+
to,
|
|
122
|
+
amount,
|
|
123
|
+
token: 'ETH',
|
|
124
|
+
}),
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
if (!response.ok) {
|
|
128
|
+
const error = await response.json()
|
|
129
|
+
throw new Error(error.error.message)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return response.json()
|
|
133
|
+
}`}
|
|
134
|
+
language="typescript"
|
|
135
|
+
title="Helper Functions"
|
|
136
|
+
/>
|
|
137
|
+
|
|
138
|
+
<h2>Server Components</h2>
|
|
139
|
+
|
|
140
|
+
<h3>Providers Setup</h3>
|
|
141
|
+
<CodeBlock
|
|
142
|
+
code={`// app/providers.tsx
|
|
143
|
+
'use client'
|
|
144
|
+
|
|
145
|
+
import { ReactNode } from 'react'
|
|
146
|
+
import { LuminaProvider } from '@lumina/react'
|
|
147
|
+
|
|
148
|
+
export function Providers({ children }: { children: ReactNode }) {
|
|
149
|
+
return (
|
|
150
|
+
<LuminaProvider
|
|
151
|
+
publicKey={process.env.NEXT_PUBLIC_LUMINA_PUBLIC_KEY!}
|
|
152
|
+
network={process.env.NEXT_PUBLIC_LUMINA_NETWORK as any}
|
|
153
|
+
>
|
|
154
|
+
{children}
|
|
155
|
+
</LuminaProvider>
|
|
156
|
+
)
|
|
157
|
+
}`}
|
|
158
|
+
language="typescript"
|
|
159
|
+
title="Providers"
|
|
160
|
+
/>
|
|
161
|
+
|
|
162
|
+
<h3>Root Layout</h3>
|
|
163
|
+
<CodeBlock
|
|
164
|
+
code={`// app/layout.tsx
|
|
165
|
+
import { Providers } from './providers'
|
|
166
|
+
import './globals.css'
|
|
167
|
+
|
|
168
|
+
export const metadata = {
|
|
169
|
+
title: 'My Wallet',
|
|
170
|
+
description: 'Web3 wallet application',
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export default function RootLayout({
|
|
174
|
+
children,
|
|
175
|
+
}: {
|
|
176
|
+
children: React.ReactNode
|
|
177
|
+
}) {
|
|
178
|
+
return (
|
|
179
|
+
<html lang="en">
|
|
180
|
+
<body>
|
|
181
|
+
<Providers>{children}</Providers>
|
|
182
|
+
</body>
|
|
183
|
+
</html>
|
|
184
|
+
)
|
|
185
|
+
}`}
|
|
186
|
+
language="typescript"
|
|
187
|
+
title="Layout"
|
|
188
|
+
/>
|
|
189
|
+
|
|
190
|
+
<h2>API Routes</h2>
|
|
191
|
+
|
|
192
|
+
<h3>Create Wallet Endpoint</h3>
|
|
193
|
+
<CodeBlock
|
|
194
|
+
code={`// app/api/wallets/route.ts
|
|
195
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
196
|
+
import { createWallet } from '@/lib/lumina'
|
|
197
|
+
|
|
198
|
+
export async function POST(request: NextRequest) {
|
|
199
|
+
try {
|
|
200
|
+
const { userId } = await request.json()
|
|
201
|
+
|
|
202
|
+
if (!userId) {
|
|
203
|
+
return NextResponse.json(
|
|
204
|
+
{ error: 'userId is required' },
|
|
205
|
+
{ status: 400 }
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const wallet = await createWallet(userId)
|
|
210
|
+
return NextResponse.json(wallet)
|
|
211
|
+
} catch (error: any) {
|
|
212
|
+
console.error('Create wallet error:', error)
|
|
213
|
+
return NextResponse.json(
|
|
214
|
+
{ error: error.message },
|
|
215
|
+
{ status: 500 }
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
}`}
|
|
219
|
+
language="typescript"
|
|
220
|
+
title="POST /api/wallets"
|
|
221
|
+
/>
|
|
222
|
+
|
|
223
|
+
<h3>Get Wallet Endpoint</h3>
|
|
224
|
+
<CodeBlock
|
|
225
|
+
code={`// app/api/wallets/[id]/route.ts
|
|
226
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
227
|
+
import { getWallet } from '@/lib/lumina'
|
|
228
|
+
|
|
229
|
+
export async function GET(
|
|
230
|
+
request: NextRequest,
|
|
231
|
+
{ params }: { params: { id: string } }
|
|
232
|
+
) {
|
|
233
|
+
try {
|
|
234
|
+
const wallet = await getWallet(params.id)
|
|
235
|
+
return NextResponse.json(wallet)
|
|
236
|
+
} catch (error: any) {
|
|
237
|
+
return NextResponse.json(
|
|
238
|
+
{ error: error.message },
|
|
239
|
+
{ status: 500 }
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
}`}
|
|
243
|
+
language="typescript"
|
|
244
|
+
title="GET /api/wallets/[id]"
|
|
245
|
+
/>
|
|
246
|
+
|
|
247
|
+
<h2>Client Components</h2>
|
|
248
|
+
|
|
249
|
+
<h3>Wallet Page</h3>
|
|
250
|
+
<CodeBlock
|
|
251
|
+
code={`// app/wallet/page.tsx
|
|
252
|
+
'use client'
|
|
253
|
+
|
|
254
|
+
import { useState, useEffect } from 'react'
|
|
255
|
+
import { WalletDisplay, WalletLogin, TransactionConfirm } from '@lumina/react'
|
|
256
|
+
|
|
257
|
+
export default function WalletPage() {
|
|
258
|
+
const [user, setUser] = useState(null)
|
|
259
|
+
const [wallet, setWallet] = useState(null)
|
|
260
|
+
const [loading, setLoading] = useState(false)
|
|
261
|
+
const [showTransaction, setShowTransaction] = useState(false)
|
|
262
|
+
|
|
263
|
+
const handleLogin = async (userData: any) => {
|
|
264
|
+
setUser(userData)
|
|
265
|
+
setLoading(true)
|
|
266
|
+
try {
|
|
267
|
+
// Create wallet for user
|
|
268
|
+
const response = await fetch('/api/wallets', {
|
|
269
|
+
method: 'POST',
|
|
270
|
+
headers: { 'Content-Type': 'application/json' },
|
|
271
|
+
body: JSON.stringify({ userId: userData.id }),
|
|
272
|
+
})
|
|
273
|
+
const data = await response.json()
|
|
274
|
+
setWallet(data.data)
|
|
275
|
+
} catch (error) {
|
|
276
|
+
console.error('Failed to create wallet:', error)
|
|
277
|
+
} finally {
|
|
278
|
+
setLoading(false)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return (
|
|
283
|
+
<main className="max-w-4xl mx-auto p-8">
|
|
284
|
+
{!user ? (
|
|
285
|
+
<WalletLogin onSuccess={handleLogin} />
|
|
286
|
+
) : (
|
|
287
|
+
<div>
|
|
288
|
+
<h1>Welcome, {user.name}!</h1>
|
|
289
|
+
{wallet && (
|
|
290
|
+
<>
|
|
291
|
+
<WalletDisplay
|
|
292
|
+
address={wallet.address}
|
|
293
|
+
balance={wallet.balance}
|
|
294
|
+
onSend={() => setShowTransaction(true)}
|
|
295
|
+
/>
|
|
296
|
+
{showTransaction && (
|
|
297
|
+
<TransactionConfirm
|
|
298
|
+
transactionId="txn_123"
|
|
299
|
+
amount="1.0"
|
|
300
|
+
recipient="0x..."
|
|
301
|
+
onConfirm={() => setShowTransaction(false)}
|
|
302
|
+
onCancel={() => setShowTransaction(false)}
|
|
303
|
+
/>
|
|
304
|
+
)}
|
|
305
|
+
</>
|
|
306
|
+
)}
|
|
307
|
+
</div>
|
|
308
|
+
)}
|
|
309
|
+
</main>
|
|
310
|
+
)
|
|
311
|
+
}`}
|
|
312
|
+
language="typescript"
|
|
313
|
+
title="Wallet Page"
|
|
314
|
+
/>
|
|
315
|
+
|
|
316
|
+
<Callout type="success">
|
|
317
|
+
This example provides a complete foundation for building a Web3 wallet application with Lumina. Customize it further based on your specific needs.
|
|
318
|
+
</Callout>
|
|
319
|
+
</div>
|
|
320
|
+
|
|
321
|
+
<div className="flex justify-between mt-12 pt-8 border-t border-white/10">
|
|
322
|
+
<a href="/docs/guides/nextjs" className="text-emerald-400 hover:text-emerald-300">
|
|
323
|
+
← Next.js Guide
|
|
324
|
+
</a>
|
|
325
|
+
<a href="/docs/examples/multi-chain" className="text-emerald-400 hover:text-emerald-300">
|
|
326
|
+
Multi-Chain Example →
|
|
327
|
+
</a>
|
|
328
|
+
</div>
|
|
329
|
+
</main>
|
|
330
|
+
</div>
|
|
331
|
+
)
|
|
332
|
+
}
|