Skip to content

Commit dbf26dc

Browse files
authored
implement ca demo wallet checkout (#866)
* chore: update appkit deps * add wallet checkout integration * chore: update yarn.lock * add purchase donut modal view and receipt view * chore: display asset amount and name * handle dialog tile console error * rename payment methods to payment options * add visual assets * add options to select/deselect payment options * handle error message in modal * chore: fix handling error.message * chore: visual assets type changes * chore: refactor PurchaseDonutButton * chore: remove unused imports and comments * chore: remove registering PaymentOptionsModalView from PaymentOptions component * chore: refactor to use existing getChainLogoUrl method * chore: refactored paymentOptionsView * chore: remove +/- donut quantity for wallet_checkout usecase * chore: handle all defined errors * chore: updated donut price to 1 USD * chore: add optimismSepolia in network array
1 parent 022bd96 commit dbf26dc

27 files changed

+2596
-139
lines changed

advanced/dapps/chain-abstraction-demo/app/globals.css

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,16 @@
3131
--popover: 0 0% 100%;
3232
--popover-foreground: 240 10% 3.9%;
3333
--primary: 240 5.9% 10%;
34-
--primary-foreground: 0 0% 15%;
34+
--primary-foreground: 0 0% 98%;
3535
--secondary: 240 4.8% 95.9%;
3636
--secondary-foreground: 240 5.9% 10%;
37+
--tertiary-foreground: rgba(244, 244, 244, 1);
3738
--muted: 240 4.8% 95.9%;
3839
--muted-foreground: 240 3.8% 46.1%;
3940
--accent: 240 4.8% 95.9%;
4041
--accent-foreground: 240 5.9% 10%;
4142
--destructive: 0 84.2% 60.2%;
42-
--destructive-foreground: 0 0% 15%;
43+
--destructive-foreground: 0 0% 98%;
4344
--border: 240 5.9% 90%;
4445
--input: 240 5.9% 90%;
4546
--ring: 240 10% 3.9%;
@@ -49,26 +50,33 @@
4950
--chart-4: 43 74% 66%;
5051
--chart-5: 27 87% 67%;
5152
--radius: 0.5rem;
53+
54+
/* Custom variables from component */
55+
--foreground-foreground-secondary: rgba(230, 230, 230, 1);
56+
--foreground-foreground-accent-primary-010: rgba(9, 136, 240, 0.1);
57+
--text-text-accent-primary: rgba(9, 136, 240, 1);
58+
--border-border-secondary: rgba(200, 200, 200, 1);
5259
}
60+
5361
.dark {
5462
--background: 0 0% 13%;
5563
--foreground: 0 0% 15%;
5664
--invert: 0 0% 13%;
5765
--card: 240 10% 3.9%;
58-
--card-foreground: 0 0% 15%;
66+
--card-foreground: 0 0% 98%;
5967
--popover: 240 10% 3.9%;
60-
--popover-foreground: 0 0% 15%;
68+
--popover-foreground: 0 0% 98%;
6169
--primary: 0 0% 100%;
6270
--primary-foreground: 0 0% 15%;
6371
--secondary: 0 0% 60%;
64-
--secondary-foreground: 0 0% 15%;
72+
--secondary-foreground: 0 0% 98%;
6573
--tertiary-foreground: rgba(54, 54, 54, 1);
6674
--muted: 240 3.7% 15.9%;
6775
--muted-foreground: 240 5% 64.9%;
6876
--accent: 240 3.7% 15.9%;
69-
--accent-foreground: 0 0% 15%;
77+
--accent-foreground: 0 0% 98%;
7078
--destructive: 0 62.8% 30.6%;
71-
--destructive-foreground: 0 0% 15%;
79+
--destructive-foreground: 0 0% 98%;
7280
--border: 240 3.7% 15.9%;
7381
--input: 240 3.7% 15.9%;
7482
--ring: 207, 93%, 49%, 0.2;
@@ -77,6 +85,12 @@
7785
--chart-3: 30 80% 55%;
7886
--chart-4: 280 65% 60%;
7987
--chart-5: 340 75% 55%;
88+
89+
/* Custom variables from component */
90+
--foreground-foreground-secondary: rgba(42, 42, 42, 1);
91+
--foreground-foreground-accent-primary-010: rgba(9, 136, 240, 0.1);
92+
--text-text-accent-primary: rgba(9, 136, 240, 1);
93+
--border-border-secondary: rgba(79, 79, 79, 1);
8094
}
8195
}
8296

@@ -88,3 +102,29 @@
88102
@apply bg-background text-foreground;
89103
}
90104
}
105+
106+
/* Additional utility classes for common patterns in the component */
107+
@layer components {
108+
.rounded-button {
109+
@apply h-8 w-8 rounded-full p-0;
110+
background-color: var(--tertiary-foreground);
111+
}
112+
113+
.payment-option-badge {
114+
@apply flex gap-1 p-1 rounded-full text-sm items-center;
115+
background-color: var(--tertiary-foreground);
116+
}
117+
118+
.icon-container {
119+
@apply w-10 h-10 rounded-full flex items-center justify-center;
120+
background-color: var(--foreground-foreground-secondary);
121+
}
122+
123+
.accent-button {
124+
background: var(--foreground-foreground-accent-primary-010);
125+
}
126+
127+
.accent-text {
128+
color: var(--text-text-accent-primary);
129+
}
130+
}

advanced/dapps/chain-abstraction-demo/app/page.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ import { ConnectWalletButton } from "@/components/ConnectWalletButton";
66
import { useAppKitAccount } from "@reown/appkit/react";
77
import Navbar from "@/components/Navbar";
88
import { GiftDonutModalTrigger } from "@/components/GiftDonutModalTrigger";
9+
import PurchaseDonutButton from "@/components/PurchaseDonutButton";
10+
import { useWalletCheckout } from "@/hooks/useWalletCheckout";
911

1012
export default function Home() {
1113
const { status, address } = useAppKitAccount();
14+
const { isWalletCheckoutSupported, } = useWalletCheckout()
1215

1316
return (
1417
<div className="sm:w-1/2 flex flex-col sm:mx-10">
@@ -33,12 +36,13 @@ export default function Home() {
3336
<p className=" font-bold text-primary">$1.00</p>
3437
</div>
3538
{status === "connected" || address ? (
36-
<div>
39+
<div className="flex gap-2">
3740
<GiftDonutModalTrigger
3841
triggerText="Gift Donut"
3942
initialView="Checkout"
4043
className="bg-blue-500 hover:bg-blue-700 text-invert"
4144
/>
45+
{isWalletCheckoutSupported && <PurchaseDonutButton />}
4246
</div>
4347
) : (
4448
<div>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { useWalletCheckout } from "@/hooks/useWalletCheckout"
2+
import { Button } from "@/components/ui/button"
3+
import { useAppKitProvider } from "@reown/appkit/react"
4+
import UniversalProvider from '@walletconnect/universal-provider'
5+
import { toast } from "sonner"
6+
import { useEffect, useRef } from "react"
7+
import { walletCheckoutManager } from "@/controllers/WalletCheckoutModalManager"
8+
import { WalletCheckoutModal } from "./WalletCheckoutModal"
9+
import { registerCheckoutViews } from "@/config/checkoutViews"
10+
11+
12+
const initializeCheckoutManager = () => {
13+
registerCheckoutViews();
14+
}
15+
16+
const PurchaseDonutButton = () => {
17+
const { isWalletCheckoutSupported, getPreConfiguredPaymentsOptions } = useWalletCheckout()
18+
const { walletProvider } = useAppKitProvider<UniversalProvider>('eip155')
19+
20+
// Initialize checkout manager when the component mounts
21+
useEffect(() => {
22+
initializeCheckoutManager();
23+
24+
// Configure payment options when component mounts
25+
const configurePaymentsOptions = async () => {
26+
if (isWalletCheckoutSupported) {
27+
try {
28+
const options = await getPreConfiguredPaymentsOptions();
29+
walletCheckoutManager.setPaymentOptions(options);
30+
} catch (error) {
31+
console.error("Failed to configure payment options:", error);
32+
}
33+
}
34+
};
35+
36+
configurePaymentsOptions();
37+
}, [getPreConfiguredPaymentsOptions, isWalletCheckoutSupported]);
38+
39+
const handlePurchaseDonut = async () => {
40+
if (!isWalletCheckoutSupported || !walletProvider) {
41+
toast.error('Wallet checkout not supported', {
42+
description: 'Your wallet does not support the checkout feature',
43+
});
44+
return;
45+
}
46+
walletCheckoutManager.resetTransactionState();
47+
walletCheckoutManager.setWalletProvider(walletProvider);
48+
walletCheckoutManager.open("checkout");
49+
}
50+
51+
return (
52+
<>
53+
<Button onClick={handlePurchaseDonut} disabled={!isWalletCheckoutSupported}>
54+
Purchase Donut
55+
</Button>
56+
<WalletCheckoutModal />
57+
</>
58+
);
59+
}
60+
61+
export default PurchaseDonutButton
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"use client";
2+
3+
import React from "react";
4+
import { useSnapshot } from "valtio";
5+
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
6+
import { walletCheckoutManager } from "../controllers/WalletCheckoutModalManager";
7+
import * as VisuallyHidden from "@radix-ui/react-visually-hidden";
8+
9+
export const WalletCheckoutModal: React.FC = () => {
10+
const snap = useSnapshot(walletCheckoutManager.getState());
11+
12+
const handleClose = () => {
13+
walletCheckoutManager.close();
14+
};
15+
16+
if (!snap.isOpen) return null;
17+
18+
const CurrentView = snap.views[snap.currentView]?.component;
19+
20+
if (!CurrentView) return null;
21+
22+
return (
23+
<Dialog open={snap.isOpen} onOpenChange={handleClose}>
24+
<DialogContent
25+
aria-describedby={undefined}
26+
className="sm:max-w-[435px] bg-background"
27+
>
28+
<VisuallyHidden.Root asChild>
29+
<DialogTitle>Modal Title</DialogTitle>
30+
</VisuallyHidden.Root>
31+
<CurrentView
32+
onClose={handleClose}
33+
onViewChange={(viewKey) => walletCheckoutManager.switchView(viewKey)}
34+
/>
35+
</DialogContent>
36+
</Dialog>
37+
);
38+
};

advanced/dapps/chain-abstraction-demo/components/gift-donut-modal-views/CheckoutView.tsx

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,7 @@ function GiftDonutForm({
9494
<Button
9595
variant="outline"
9696
onClick={() => setDonutCount(count - 1)}
97-
style={{ backgroundColor: "var(--tertiary-foreground)" }}
98-
className="h-8 w-8 rounded-full p-0"
97+
className="rounded-button"
9998
disabled={count <= 0}
10099
>
101100
-
@@ -104,33 +103,25 @@ function GiftDonutForm({
104103
<Button
105104
variant="outline"
106105
onClick={() => setDonutCount(count + 1)}
107-
style={{ backgroundColor: "var(--tertiary-foreground)" }}
108-
className="h-8 w-8 rounded-full p-0"
106+
className="rounded-button"
109107
>
110108
+
111109
</Button>
112110
</div>
113111
</div>
114-
{/* Removed inline error message block */}
115112
</div>
116113
</div>
117114
</div>
118115
<div className="flex items-center flex-col gap-2 w-full text-primary">
119116
<div className="flex w-full items-center gap-2">
120-
<div
121-
className="w-10 h-10 rounded-full flex items-center justify-center"
122-
style={{
123-
background: "var(--foreground-foreground-secondary, rgba(42, 42, 42, 1))",
124-
}}
125-
>
117+
<div className="icon-container">
126118
<CoinSVG />
127119
</div>
128120
<div className="flex flex-1 items-center justify-between">
129121
<p>Pay with</p>
130122
<div className="flex gap-4 items-center justify-between cursor-pointer">
131123
<div
132-
className="flex gap-1 p-1 rounded-full text-sm items-center"
133-
style={{ backgroundColor: "var(--tertiary-foreground)" }}
124+
className="payment-option-badge"
134125
onClick={() => onViewChange("PayWith")}
135126
>
136127
{selectedToken && (
@@ -151,20 +142,14 @@ function GiftDonutForm({
151142
</div>
152143
</div>
153144
<div className="flex w-full items-center gap-2">
154-
<div
155-
className="w-10 h-10 rounded-full flex items-center justify-center"
156-
style={{
157-
background: "var(--foreground-foreground-secondary, rgba(42, 42, 42, 1))",
158-
}}
159-
>
145+
<div className="icon-container">
160146
<NetworkSVG />
161147
</div>
162148
<div className="flex flex-1 items-center justify-between">
163149
<p>Choose Network</p>
164150
<div className="flex gap-4 items-center justify-between cursor-pointer">
165151
<div
166-
className="flex gap-1 p-1 rounded-full text-sm items-center"
167-
style={{ backgroundColor: "var(--tertiary-foreground)" }}
152+
className="payment-option-badge"
168153
onClick={() => onViewChange("ChooseNetwork")}
169154
>
170155
{selectedNetwork && (
@@ -216,17 +201,14 @@ function GiftDonutForm({
216201
<button
217202
onClick={onClose}
218203
type="button"
219-
style={{
220-
border: "1px solid var(--border-border-secondary, rgba(79, 79, 79, 1))",
221-
}}
222-
className="flex flex-1 text-primary items-center justify-center border-secondary rounded-lg"
204+
className="flex flex-1 text-primary items-center justify-center border border-border-secondary rounded-lg"
223205
>
224206
Cancel
225207
</button>
226208
<Button
227-
style={{
228-
background: "var(--foreground-foreground-accent-primary-010, rgba(9, 136, 240, 0.1))",
229-
}}
209+
style={{
210+
background: "var(--foreground-foreground-accent-primary-010, rgba(9, 136, 240, 0.1))",
211+
}}
230212
onClick={() => {
231213
// If token and network are incompatible, redirect to network selection
232214
if (!giftDonutModalManager.isTokenNetworkCompatible()) {
@@ -252,4 +234,4 @@ function GiftDonutForm({
252234
);
253235
}
254236

255-
export default CheckoutView;
237+
export default CheckoutView;

0 commit comments

Comments
 (0)