@b3dotfun/sdk 0.0.9-alpha.1 → 0.0.9-alpha.3
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/dist/cjs/anyspend/types/api.d.ts +7 -31
- package/dist/esm/anyspend/types/api.d.ts +7 -31
- package/dist/types/anyspend/types/api.d.ts +7 -31
- package/package.json +1 -1
- package/src/anyspend/README.md +70 -556
- package/src/anyspend/docs/components.md +292 -0
- package/src/anyspend/docs/contributing.md +448 -0
- package/src/anyspend/docs/error-handling.md +735 -0
- package/src/anyspend/docs/examples.md +707 -0
- package/src/anyspend/docs/hooks.md +465 -0
- package/src/anyspend/docs/installation.md +113 -0
- package/src/anyspend/types/api.ts +7 -31
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
# Examples & Use Cases
|
|
2
|
+
|
|
3
|
+
Real-world implementation examples for common AnySpend integration patterns.
|
|
4
|
+
|
|
5
|
+
## 🔄 Cross-Chain Token Swaps
|
|
6
|
+
|
|
7
|
+
### Basic Swap Interface
|
|
8
|
+
|
|
9
|
+
Perfect for DeFi applications, portfolio managers, or any app that needs token exchange functionality.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { AnySpend } from "@b3dotfun/sdk/anyspend/react";
|
|
13
|
+
|
|
14
|
+
function TokenSwapPage() {
|
|
15
|
+
const [userAddress] = useWallet(); // Your wallet hook
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className="swap-container">
|
|
19
|
+
<h1>Swap Tokens</h1>
|
|
20
|
+
<AnySpend
|
|
21
|
+
mode="page"
|
|
22
|
+
recipientAddress={userAddress}
|
|
23
|
+
onSuccess={(txHash) => {
|
|
24
|
+
// Update user's portfolio
|
|
25
|
+
toast.success("Swap completed successfully!");
|
|
26
|
+
|
|
27
|
+
// Optional: Track analytics
|
|
28
|
+
analytics.track("swap_completed", {
|
|
29
|
+
txHash,
|
|
30
|
+
userAddress,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Refresh user balances
|
|
34
|
+
queryClient.invalidateQueries(['user-balances', userAddress]);
|
|
35
|
+
}}
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Advanced Swap with Quote Preview
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { useAnyspendQuote, AnySpend } from "@b3dotfun/sdk/anyspend/react";
|
|
46
|
+
|
|
47
|
+
function AdvancedSwapInterface() {
|
|
48
|
+
const [fromToken, setFromToken] = useState(USDC_ETHEREUM);
|
|
49
|
+
const [toToken, setToToken] = useState(ETH_B3);
|
|
50
|
+
const [amount, setAmount] = useState("100");
|
|
51
|
+
const [isSwapOpen, setIsSwapOpen] = useState(false);
|
|
52
|
+
|
|
53
|
+
const quoteRequest = useMemo(() => ({
|
|
54
|
+
srcChain: fromToken.chainId,
|
|
55
|
+
dstChain: toToken.chainId,
|
|
56
|
+
srcTokenAddress: fromToken.address,
|
|
57
|
+
dstTokenAddress: toToken.address,
|
|
58
|
+
type: "swap" as const,
|
|
59
|
+
tradeType: "EXACT_INPUT" as const,
|
|
60
|
+
amount: parseUnits(amount || "0", fromToken.decimals).toString(),
|
|
61
|
+
}), [fromToken, toToken, amount]);
|
|
62
|
+
|
|
63
|
+
const { anyspendQuote, isLoadingAnyspendQuote } = useAnyspendQuote(true, quoteRequest);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div className="advanced-swap">
|
|
67
|
+
<div className="swap-form">
|
|
68
|
+
<TokenInput
|
|
69
|
+
label="From"
|
|
70
|
+
token={fromToken}
|
|
71
|
+
amount={amount}
|
|
72
|
+
onTokenChange={setFromToken}
|
|
73
|
+
onAmountChange={setAmount}
|
|
74
|
+
/>
|
|
75
|
+
|
|
76
|
+
<SwapArrowButton onClick={() => {
|
|
77
|
+
setFromToken(toToken);
|
|
78
|
+
setToToken(fromToken);
|
|
79
|
+
}} />
|
|
80
|
+
|
|
81
|
+
<TokenInput
|
|
82
|
+
label="To"
|
|
83
|
+
token={toToken}
|
|
84
|
+
amount={anyspendQuote?.expectedOutput || "0"}
|
|
85
|
+
onTokenChange={setToToken}
|
|
86
|
+
readOnly
|
|
87
|
+
/>
|
|
88
|
+
|
|
89
|
+
{anyspendQuote && (
|
|
90
|
+
<div className="quote-details">
|
|
91
|
+
<div>Rate: 1 {fromToken.symbol} = {anyspendQuote.rate} {toToken.symbol}</div>
|
|
92
|
+
<div>Network Fee: ${anyspendQuote.networkFeeUsd}</div>
|
|
93
|
+
<div>Service Fee: ${anyspendQuote.serviceFeeUsd}</div>
|
|
94
|
+
<div>Total: ${anyspendQuote.totalUsdCost}</div>
|
|
95
|
+
</div>
|
|
96
|
+
)}
|
|
97
|
+
|
|
98
|
+
<button
|
|
99
|
+
onClick={() => setIsSwapOpen(true)}
|
|
100
|
+
disabled={isLoadingAnyspendQuote || !anyspendQuote}
|
|
101
|
+
className="swap-button"
|
|
102
|
+
>
|
|
103
|
+
{isLoadingAnyspendQuote ? "Getting Quote..." : "Swap Tokens"}
|
|
104
|
+
</button>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
{isSwapOpen && (
|
|
108
|
+
<AnySpend
|
|
109
|
+
mode="modal"
|
|
110
|
+
recipientAddress={userAddress}
|
|
111
|
+
destinationTokenAddress={toToken.address}
|
|
112
|
+
destinationTokenChainId={toToken.chainId}
|
|
113
|
+
onSuccess={() => {
|
|
114
|
+
setIsSwapOpen(false);
|
|
115
|
+
toast.success("Swap completed!");
|
|
116
|
+
}}
|
|
117
|
+
/>
|
|
118
|
+
)}
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 🖼️ NFT Marketplace Integration
|
|
125
|
+
|
|
126
|
+
### Simple NFT Purchase
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { AnySpendNFTButton } from "@b3dotfun/sdk/anyspend/react";
|
|
130
|
+
|
|
131
|
+
function NFTCard({ nft }: { nft: NFTListing }) {
|
|
132
|
+
const [userAddress] = useWallet();
|
|
133
|
+
const [isOwned, setIsOwned] = useState(false);
|
|
134
|
+
|
|
135
|
+
const nftContract = {
|
|
136
|
+
chainId: nft.chainId,
|
|
137
|
+
contractAddress: nft.contractAddress,
|
|
138
|
+
price: nft.priceWei,
|
|
139
|
+
priceFormatted: nft.priceFormatted,
|
|
140
|
+
currency: nft.currency,
|
|
141
|
+
name: nft.name,
|
|
142
|
+
description: nft.description,
|
|
143
|
+
imageUrl: nft.imageUrl,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<div className="nft-card">
|
|
148
|
+
<img src={nft.imageUrl} alt={nft.name} />
|
|
149
|
+
<div className="nft-details">
|
|
150
|
+
<h3>{nft.name}</h3>
|
|
151
|
+
<p>{nft.description}</p>
|
|
152
|
+
<div className="price">
|
|
153
|
+
{nft.priceFormatted} {nft.currency.symbol}
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
{isOwned ? (
|
|
157
|
+
<div className="owned-badge">✅ Owned</div>
|
|
158
|
+
) : (
|
|
159
|
+
<AnySpendNFTButton
|
|
160
|
+
nftContract={nftContract}
|
|
161
|
+
recipientAddress={userAddress}
|
|
162
|
+
onSuccess={(txHash) => {
|
|
163
|
+
setIsOwned(true);
|
|
164
|
+
|
|
165
|
+
// Update user's NFT collection
|
|
166
|
+
queryClient.invalidateQueries(['user-nfts', userAddress]);
|
|
167
|
+
|
|
168
|
+
// Show success message with explorer link
|
|
169
|
+
toast.success(
|
|
170
|
+
<div>
|
|
171
|
+
NFT purchased successfully!
|
|
172
|
+
<a href={`https://explorer.b3.fun/tx/${txHash}`} target="_blank">
|
|
173
|
+
View Transaction
|
|
174
|
+
</a>
|
|
175
|
+
</div>
|
|
176
|
+
);
|
|
177
|
+
}}
|
|
178
|
+
/>
|
|
179
|
+
)}
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### NFT Marketplace with Bulk Purchase
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
function NFTMarketplace() {
|
|
190
|
+
const [selectedNFTs, setSelectedNFTs] = useState<NFTListing[]>([]);
|
|
191
|
+
const [userAddress] = useWallet();
|
|
192
|
+
|
|
193
|
+
const handleBulkPurchase = () => {
|
|
194
|
+
// For bulk purchases, create multiple orders or use batch contract
|
|
195
|
+
selectedNFTs.forEach((nft, index) => {
|
|
196
|
+
setTimeout(() => {
|
|
197
|
+
// Stagger purchases to avoid rate limiting
|
|
198
|
+
createSingleNFTPurchase(nft);
|
|
199
|
+
}, index * 1000);
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<div className="marketplace">
|
|
205
|
+
<div className="nft-grid">
|
|
206
|
+
{nfts.map((nft) => (
|
|
207
|
+
<NFTCard
|
|
208
|
+
key={nft.id}
|
|
209
|
+
nft={nft}
|
|
210
|
+
onSelect={(selected) => {
|
|
211
|
+
if (selected) {
|
|
212
|
+
setSelectedNFTs([...selectedNFTs, nft]);
|
|
213
|
+
} else {
|
|
214
|
+
setSelectedNFTs(selectedNFTs.filter(n => n.id !== nft.id));
|
|
215
|
+
}
|
|
216
|
+
}}
|
|
217
|
+
/>
|
|
218
|
+
))}
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
{selectedNFTs.length > 0 && (
|
|
222
|
+
<div className="bulk-purchase">
|
|
223
|
+
<p>Selected: {selectedNFTs.length} NFTs</p>
|
|
224
|
+
<p>Total: {calculateTotal(selectedNFTs)} ETH</p>
|
|
225
|
+
<button onClick={handleBulkPurchase}>
|
|
226
|
+
Purchase Selected NFTs
|
|
227
|
+
</button>
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## 🎮 Gaming & DeFi Applications
|
|
236
|
+
|
|
237
|
+
### Staking Interface
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import { AnySpendCustom } from "@b3dotfun/sdk/anyspend/react";
|
|
241
|
+
import { encodeFunctionData } from "viem";
|
|
242
|
+
|
|
243
|
+
function StakingPool({ pool }: { pool: StakingPool }) {
|
|
244
|
+
const [stakeAmount, setStakeAmount] = useState("");
|
|
245
|
+
const [stakingDuration, setStakingDuration] = useState(30);
|
|
246
|
+
const [userAddress] = useWallet();
|
|
247
|
+
|
|
248
|
+
const stakingCalldata = useMemo(() => {
|
|
249
|
+
if (!stakeAmount) return "0x";
|
|
250
|
+
|
|
251
|
+
const amountWei = parseUnits(stakeAmount, pool.token.decimals);
|
|
252
|
+
|
|
253
|
+
return encodeFunctionData({
|
|
254
|
+
abi: stakingPoolABI,
|
|
255
|
+
functionName: "stake",
|
|
256
|
+
args: [amountWei, stakingDuration * 24 * 60 * 60], // duration in seconds
|
|
257
|
+
});
|
|
258
|
+
}, [stakeAmount, stakingDuration]);
|
|
259
|
+
|
|
260
|
+
const expectedRewards = useMemo(() => {
|
|
261
|
+
if (!stakeAmount) return "0";
|
|
262
|
+
const amount = parseFloat(stakeAmount);
|
|
263
|
+
const apy = pool.apy / 100;
|
|
264
|
+
const durationInYears = stakingDuration / 365;
|
|
265
|
+
return (amount * apy * durationInYears).toFixed(4);
|
|
266
|
+
}, [stakeAmount, stakingDuration, pool.apy]);
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<div className="staking-pool">
|
|
270
|
+
<div className="pool-info">
|
|
271
|
+
<h2>{pool.name}</h2>
|
|
272
|
+
<p>APY: {pool.apy}%</p>
|
|
273
|
+
<p>TVL: ${pool.totalValueLocked.toLocaleString()}</p>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<div className="stake-form">
|
|
277
|
+
<div className="input-group">
|
|
278
|
+
<label>Amount to stake</label>
|
|
279
|
+
<input
|
|
280
|
+
type="number"
|
|
281
|
+
value={stakeAmount}
|
|
282
|
+
onChange={(e) => setStakeAmount(e.target.value)}
|
|
283
|
+
placeholder="0.0"
|
|
284
|
+
/>
|
|
285
|
+
<span>{pool.token.symbol}</span>
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
<div className="input-group">
|
|
289
|
+
<label>Staking Duration</label>
|
|
290
|
+
<select
|
|
291
|
+
value={stakingDuration}
|
|
292
|
+
onChange={(e) => setStakingDuration(Number(e.target.value))}
|
|
293
|
+
>
|
|
294
|
+
<option value={7}>7 days (2% APY)</option>
|
|
295
|
+
<option value={30}>30 days (5% APY)</option>
|
|
296
|
+
<option value={90}>90 days (8% APY)</option>
|
|
297
|
+
<option value={365}>1 year (12% APY)</option>
|
|
298
|
+
</select>
|
|
299
|
+
</div>
|
|
300
|
+
|
|
301
|
+
<div className="rewards-preview">
|
|
302
|
+
<p>Expected rewards: {expectedRewards} {pool.token.symbol}</p>
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<AnySpendCustom
|
|
306
|
+
orderType="custom"
|
|
307
|
+
dstChainId={pool.chainId}
|
|
308
|
+
dstToken={pool.token}
|
|
309
|
+
dstAmount={parseUnits(stakeAmount || "0", pool.token.decimals).toString()}
|
|
310
|
+
contractAddress={pool.contractAddress}
|
|
311
|
+
encodedData={stakingCalldata}
|
|
312
|
+
metadata={{
|
|
313
|
+
action: "stake",
|
|
314
|
+
poolId: pool.id,
|
|
315
|
+
duration: stakingDuration,
|
|
316
|
+
expectedRewards,
|
|
317
|
+
}}
|
|
318
|
+
header={({ anyspendPrice, isLoadingAnyspendPrice }) => (
|
|
319
|
+
<div className="staking-header">
|
|
320
|
+
<h3>Stake {pool.token.symbol}</h3>
|
|
321
|
+
<div className="stake-summary">
|
|
322
|
+
<div>Amount: {stakeAmount} {pool.token.symbol}</div>
|
|
323
|
+
<div>Duration: {stakingDuration} days</div>
|
|
324
|
+
<div>Expected rewards: {expectedRewards} {pool.token.symbol}</div>
|
|
325
|
+
{anyspendPrice && (
|
|
326
|
+
<div>Total cost: ${anyspendPrice.totalUsdCost}</div>
|
|
327
|
+
)}
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
)}
|
|
331
|
+
onSuccess={(txHash) => {
|
|
332
|
+
toast.success("Staking successful!");
|
|
333
|
+
|
|
334
|
+
// Update user's staking positions
|
|
335
|
+
queryClient.invalidateQueries(['staking-positions', userAddress]);
|
|
336
|
+
|
|
337
|
+
// Reset form
|
|
338
|
+
setStakeAmount("");
|
|
339
|
+
}}
|
|
340
|
+
/>
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Gaming Spin Wheel
|
|
348
|
+
|
|
349
|
+
```tsx
|
|
350
|
+
import { AnySpendBuySpin } from "@b3dotfun/sdk/anyspend/react";
|
|
351
|
+
|
|
352
|
+
function SpinWheel({ game }: { game: GameConfig }) {
|
|
353
|
+
const [userAddress] = useWallet();
|
|
354
|
+
const [spinHistory, setSpinHistory] = useState<SpinResult[]>([]);
|
|
355
|
+
|
|
356
|
+
return (
|
|
357
|
+
<div className="spin-game">
|
|
358
|
+
<div className="wheel-container">
|
|
359
|
+
<SpinWheelVisual prizes={game.prizes} />
|
|
360
|
+
</div>
|
|
361
|
+
|
|
362
|
+
<div className="game-info">
|
|
363
|
+
<h2>{game.name}</h2>
|
|
364
|
+
<p>Cost per spin: {game.spinCost} {game.currency.symbol}</p>
|
|
365
|
+
<div className="prizes">
|
|
366
|
+
<h3>Possible Prizes:</h3>
|
|
367
|
+
{game.prizes.map((prize, index) => (
|
|
368
|
+
<div key={index} className="prize">
|
|
369
|
+
<span>{prize.name}</span>
|
|
370
|
+
<span>{prize.probability}% chance</span>
|
|
371
|
+
</div>
|
|
372
|
+
))}
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
<AnySpendBuySpin
|
|
377
|
+
gameContract={game.contractAddress}
|
|
378
|
+
spinPrice={game.spinCostWei}
|
|
379
|
+
recipientAddress={userAddress}
|
|
380
|
+
onSuccess={(txHash) => {
|
|
381
|
+
// Listen for spin result event
|
|
382
|
+
listenForSpinResult(txHash).then((result) => {
|
|
383
|
+
setSpinHistory([result, ...spinHistory]);
|
|
384
|
+
|
|
385
|
+
if (result.isWinner) {
|
|
386
|
+
toast.success(`You won ${result.prize.name}!`);
|
|
387
|
+
} else {
|
|
388
|
+
toast.info("Better luck next time!");
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
}}
|
|
392
|
+
/>
|
|
393
|
+
|
|
394
|
+
{spinHistory.length > 0 && (
|
|
395
|
+
<div className="spin-history">
|
|
396
|
+
<h3>Recent Spins</h3>
|
|
397
|
+
{spinHistory.map((spin, index) => (
|
|
398
|
+
<div key={index} className={`spin-result ${spin.isWinner ? 'winner' : 'loser'}`}>
|
|
399
|
+
<span>{spin.prize.name}</span>
|
|
400
|
+
<span>{new Date(spin.timestamp).toLocaleTimeString()}</span>
|
|
401
|
+
</div>
|
|
402
|
+
))}
|
|
403
|
+
</div>
|
|
404
|
+
)}
|
|
405
|
+
</div>
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Tournament Entry
|
|
411
|
+
|
|
412
|
+
```tsx
|
|
413
|
+
import { AnySpendTournament } from "@b3dotfun/sdk/anyspend/react";
|
|
414
|
+
|
|
415
|
+
function TournamentCard({ tournament }: { tournament: Tournament }) {
|
|
416
|
+
const [userAddress] = useWallet();
|
|
417
|
+
const [isRegistered, setIsRegistered] = useState(false);
|
|
418
|
+
|
|
419
|
+
const timeUntilStart = tournament.startTime - Date.now();
|
|
420
|
+
const isStartingSoon = timeUntilStart < 60 * 60 * 1000; // 1 hour
|
|
421
|
+
|
|
422
|
+
return (
|
|
423
|
+
<div className="tournament-card">
|
|
424
|
+
<div className="tournament-header">
|
|
425
|
+
<h3>{tournament.name}</h3>
|
|
426
|
+
<div className="tournament-status">
|
|
427
|
+
{tournament.status === "upcoming" && (
|
|
428
|
+
<span className="status upcoming">
|
|
429
|
+
Starts in {formatTimeUntil(tournament.startTime)}
|
|
430
|
+
</span>
|
|
431
|
+
)}
|
|
432
|
+
{tournament.status === "live" && (
|
|
433
|
+
<span className="status live">🔴 Live</span>
|
|
434
|
+
)}
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
|
|
438
|
+
<div className="tournament-details">
|
|
439
|
+
<div className="prize-pool">
|
|
440
|
+
<h4>Prize Pool</h4>
|
|
441
|
+
<p>{tournament.prizePool} {tournament.currency.symbol}</p>
|
|
442
|
+
</div>
|
|
443
|
+
|
|
444
|
+
<div className="participants">
|
|
445
|
+
<h4>Participants</h4>
|
|
446
|
+
<p>{tournament.currentParticipants} / {tournament.maxParticipants}</p>
|
|
447
|
+
</div>
|
|
448
|
+
|
|
449
|
+
<div className="entry-fee">
|
|
450
|
+
<h4>Entry Fee</h4>
|
|
451
|
+
<p>{tournament.entryFee} {tournament.currency.symbol}</p>
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
|
|
455
|
+
{isRegistered ? (
|
|
456
|
+
<div className="registered">
|
|
457
|
+
✅ Registered for tournament
|
|
458
|
+
</div>
|
|
459
|
+
) : tournament.status === "upcoming" && !isStartingSoon ? (
|
|
460
|
+
<AnySpendTournament
|
|
461
|
+
tournamentId={tournament.id}
|
|
462
|
+
entryFee={tournament.entryFeeWei}
|
|
463
|
+
recipientAddress={userAddress}
|
|
464
|
+
onSuccess={() => {
|
|
465
|
+
setIsRegistered(true);
|
|
466
|
+
toast.success("Successfully registered for tournament!");
|
|
467
|
+
|
|
468
|
+
// Update tournament data
|
|
469
|
+
queryClient.invalidateQueries(['tournament', tournament.id]);
|
|
470
|
+
}}
|
|
471
|
+
/>
|
|
472
|
+
) : (
|
|
473
|
+
<div className="cannot-register">
|
|
474
|
+
{isStartingSoon ? "Registration closed" : "Tournament started"}
|
|
475
|
+
</div>
|
|
476
|
+
)}
|
|
477
|
+
</div>
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
## 💰 Fiat-to-Crypto Onramp
|
|
483
|
+
|
|
484
|
+
### Simple Onboarding Flow
|
|
485
|
+
|
|
486
|
+
```tsx
|
|
487
|
+
function FiatOnramp({ targetToken }: { targetToken: Token }) {
|
|
488
|
+
const [userAddress] = useWallet();
|
|
489
|
+
|
|
490
|
+
return (
|
|
491
|
+
<div className="onramp-flow">
|
|
492
|
+
<div className="onramp-header">
|
|
493
|
+
<h2>Buy {targetToken.symbol}</h2>
|
|
494
|
+
<p>Purchase crypto with your credit card or bank account</p>
|
|
495
|
+
</div>
|
|
496
|
+
|
|
497
|
+
<AnySpend
|
|
498
|
+
defaultActiveTab="fiat"
|
|
499
|
+
destinationTokenAddress={targetToken.address}
|
|
500
|
+
destinationTokenChainId={targetToken.chainId}
|
|
501
|
+
recipientAddress={userAddress}
|
|
502
|
+
mode="page"
|
|
503
|
+
onSuccess={(txHash) => {
|
|
504
|
+
// Welcome new user
|
|
505
|
+
toast.success("Welcome to crypto! Your purchase was successful.");
|
|
506
|
+
|
|
507
|
+
// Track onboarding completion
|
|
508
|
+
analytics.track("onramp_completed", {
|
|
509
|
+
userAddress,
|
|
510
|
+
token: targetToken.symbol,
|
|
511
|
+
txHash,
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// Redirect to main app
|
|
515
|
+
router.push("/dashboard");
|
|
516
|
+
}}
|
|
517
|
+
/>
|
|
518
|
+
</div>
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Multi-Step Onboarding
|
|
524
|
+
|
|
525
|
+
```tsx
|
|
526
|
+
function OnboardingWizard() {
|
|
527
|
+
const [step, setStep] = useState(1);
|
|
528
|
+
const [userAddress, setUserAddress] = useState("");
|
|
529
|
+
const [selectedToken, setSelectedToken] = useState<Token>(USDC_BASE);
|
|
530
|
+
|
|
531
|
+
return (
|
|
532
|
+
<div className="onboarding-wizard">
|
|
533
|
+
<div className="progress-bar">
|
|
534
|
+
<div className={`step ${step >= 1 ? 'completed' : ''}`}>1. Connect Wallet</div>
|
|
535
|
+
<div className={`step ${step >= 2 ? 'completed' : ''}`}>2. Choose Token</div>
|
|
536
|
+
<div className={`step ${step >= 3 ? 'completed' : ''}`}>3. Purchase</div>
|
|
537
|
+
</div>
|
|
538
|
+
|
|
539
|
+
{step === 1 && (
|
|
540
|
+
<WalletConnectionStep
|
|
541
|
+
onConnect={(address) => {
|
|
542
|
+
setUserAddress(address);
|
|
543
|
+
setStep(2);
|
|
544
|
+
}}
|
|
545
|
+
/>
|
|
546
|
+
)}
|
|
547
|
+
|
|
548
|
+
{step === 2 && (
|
|
549
|
+
<TokenSelectionStep
|
|
550
|
+
selectedToken={selectedToken}
|
|
551
|
+
onTokenSelect={(token) => {
|
|
552
|
+
setSelectedToken(token);
|
|
553
|
+
setStep(3);
|
|
554
|
+
}}
|
|
555
|
+
/>
|
|
556
|
+
)}
|
|
557
|
+
|
|
558
|
+
{step === 3 && (
|
|
559
|
+
<div className="purchase-step">
|
|
560
|
+
<h2>Purchase {selectedToken.symbol}</h2>
|
|
561
|
+
<AnySpend
|
|
562
|
+
defaultActiveTab="fiat"
|
|
563
|
+
destinationTokenAddress={selectedToken.address}
|
|
564
|
+
destinationTokenChainId={selectedToken.chainId}
|
|
565
|
+
recipientAddress={userAddress}
|
|
566
|
+
mode="page"
|
|
567
|
+
onSuccess={() => {
|
|
568
|
+
// Complete onboarding
|
|
569
|
+
completeOnboarding(userAddress, selectedToken);
|
|
570
|
+
}}
|
|
571
|
+
/>
|
|
572
|
+
</div>
|
|
573
|
+
)}
|
|
574
|
+
</div>
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
## 🛒 E-commerce Integration
|
|
580
|
+
|
|
581
|
+
### Crypto Checkout
|
|
582
|
+
|
|
583
|
+
```tsx
|
|
584
|
+
function CryptoCheckout({ order }: { order: Order }) {
|
|
585
|
+
const [userAddress] = useWallet();
|
|
586
|
+
const [paymentMethod, setPaymentMethod] = useState<"crypto" | "fiat">("crypto");
|
|
587
|
+
|
|
588
|
+
const orderTotal = order.items.reduce((sum, item) => sum + item.price, 0);
|
|
589
|
+
|
|
590
|
+
return (
|
|
591
|
+
<div className="checkout">
|
|
592
|
+
<div className="order-summary">
|
|
593
|
+
<h2>Order Summary</h2>
|
|
594
|
+
{order.items.map((item) => (
|
|
595
|
+
<div key={item.id} className="order-item">
|
|
596
|
+
<span>{item.name}</span>
|
|
597
|
+
<span>${item.price}</span>
|
|
598
|
+
</div>
|
|
599
|
+
))}
|
|
600
|
+
<div className="total">
|
|
601
|
+
<strong>Total: ${orderTotal}</strong>
|
|
602
|
+
</div>
|
|
603
|
+
</div>
|
|
604
|
+
|
|
605
|
+
<div className="payment-section">
|
|
606
|
+
<div className="payment-method-selector">
|
|
607
|
+
<button
|
|
608
|
+
className={paymentMethod === "crypto" ? "active" : ""}
|
|
609
|
+
onClick={() => setPaymentMethod("crypto")}
|
|
610
|
+
>
|
|
611
|
+
Pay with Crypto
|
|
612
|
+
</button>
|
|
613
|
+
<button
|
|
614
|
+
className={paymentMethod === "fiat" ? "active" : ""}
|
|
615
|
+
onClick={() => setPaymentMethod("fiat")}
|
|
616
|
+
>
|
|
617
|
+
Pay with Card
|
|
618
|
+
</button>
|
|
619
|
+
</div>
|
|
620
|
+
|
|
621
|
+
<AnySpend
|
|
622
|
+
defaultActiveTab={paymentMethod}
|
|
623
|
+
destinationTokenAddress={USDC_BASE.address}
|
|
624
|
+
destinationTokenChainId={USDC_BASE.chainId}
|
|
625
|
+
recipientAddress={MERCHANT_WALLET_ADDRESS}
|
|
626
|
+
mode="page"
|
|
627
|
+
onSuccess={(txHash) => {
|
|
628
|
+
// Process order fulfillment
|
|
629
|
+
processOrder(order.id, txHash);
|
|
630
|
+
|
|
631
|
+
// Send confirmation email
|
|
632
|
+
sendOrderConfirmation(order, txHash);
|
|
633
|
+
|
|
634
|
+
// Redirect to success page
|
|
635
|
+
router.push(`/order-confirmation/${order.id}`);
|
|
636
|
+
}}
|
|
637
|
+
/>
|
|
638
|
+
</div>
|
|
639
|
+
</div>
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
## 🎯 Advanced Patterns
|
|
645
|
+
|
|
646
|
+
### Multi-Chain Portfolio Rebalancing
|
|
647
|
+
|
|
648
|
+
```tsx
|
|
649
|
+
function PortfolioRebalancer() {
|
|
650
|
+
const [userAddress] = useWallet();
|
|
651
|
+
const [targetAllocation, setTargetAllocation] = useState({
|
|
652
|
+
ETH: 50,
|
|
653
|
+
BTC: 30,
|
|
654
|
+
USDC: 20,
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
const { data: currentBalances } = useUserBalances(userAddress);
|
|
658
|
+
const rebalanceOrders = calculateRebalanceOrders(currentBalances, targetAllocation);
|
|
659
|
+
|
|
660
|
+
return (
|
|
661
|
+
<div className="portfolio-rebalancer">
|
|
662
|
+
<h2>Portfolio Rebalancing</h2>
|
|
663
|
+
|
|
664
|
+
<div className="current-allocation">
|
|
665
|
+
<h3>Current Allocation</h3>
|
|
666
|
+
<AllocationChart balances={currentBalances} />
|
|
667
|
+
</div>
|
|
668
|
+
|
|
669
|
+
<div className="target-allocation">
|
|
670
|
+
<h3>Target Allocation</h3>
|
|
671
|
+
<AllocationInputs
|
|
672
|
+
allocation={targetAllocation}
|
|
673
|
+
onChange={setTargetAllocation}
|
|
674
|
+
/>
|
|
675
|
+
</div>
|
|
676
|
+
|
|
677
|
+
<div className="rebalance-orders">
|
|
678
|
+
<h3>Required Transactions</h3>
|
|
679
|
+
{rebalanceOrders.map((order, index) => (
|
|
680
|
+
<div key={index} className="rebalance-order">
|
|
681
|
+
<p>
|
|
682
|
+
Swap {order.sellAmount} {order.sellToken.symbol} → {order.buyToken.symbol}
|
|
683
|
+
</p>
|
|
684
|
+
<AnySpendCustom
|
|
685
|
+
orderType="swap"
|
|
686
|
+
dstChainId={order.buyToken.chainId}
|
|
687
|
+
dstToken={order.buyToken}
|
|
688
|
+
dstAmount={order.buyAmountWei}
|
|
689
|
+
contractAddress="0x" // Use standard swap
|
|
690
|
+
encodedData="0x"
|
|
691
|
+
onSuccess={() => {
|
|
692
|
+
toast.success(`Rebalanced ${order.sellToken.symbol} → ${order.buyToken.symbol}`);
|
|
693
|
+
}}
|
|
694
|
+
/>
|
|
695
|
+
</div>
|
|
696
|
+
))}
|
|
697
|
+
</div>
|
|
698
|
+
</div>
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
## Next Steps
|
|
704
|
+
|
|
705
|
+
- [Error Handling Guide →](./error-handling.md)
|
|
706
|
+
- [Components Reference →](./components.md)
|
|
707
|
+
- [Hooks Reference →](./hooks.md)
|