Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add wallet_getAssets support + Chain Abstraction demo changes #816

Merged
merged 25 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b98224c
added sample implementation of wallet_getAssets rpc
KannuSingh Jan 31, 2025
70d286d
handle native token in wallet_getAssets
KannuSingh Feb 3, 2025
c468be8
refactor and display balances
KannuSingh Feb 3, 2025
f3323ca
chore: handing donut increment, token selection
KannuSingh Feb 3, 2025
fd8118d
handle direct rpc call to wallet for wallet_getAssets
KannuSingh Feb 5, 2025
3791eeb
chore:run prettier
KannuSingh Feb 6, 2025
9bd291e
added wallet capability check for 'wallet_getAssets' method
KannuSingh Feb 11, 2025
e1be264
Merge branch 'main' into feat/wallet_getAssets
KannuSingh Feb 12, 2025
c9dc10e
chore:deps update
KannuSingh Feb 12, 2025
6e2af69
chore: refactor and rename methods
KannuSingh Feb 12, 2025
b4bd434
remove storage from wagmi adapter
KannuSingh Feb 12, 2025
7c63241
chore:update deps
KannuSingh Feb 12, 2025
ae7e05f
chore:remove cookies from AppkitProvider
KannuSingh Feb 12, 2025
de08dba
handle wallet connecting state in button
KannuSingh Feb 12, 2025
eb8ea2f
chore: update AK to 1.6.8 on all examples
tomiir Feb 13, 2025
1f9f81c
chore:refactor
KannuSingh Feb 13, 2025
d055b45
fix:add node-version file for other project build
KannuSingh Feb 13, 2025
644f6b2
fix:update node-version to 22.x.x for other project build
KannuSingh Feb 13, 2025
72f978c
replace .node-version file to .nvmrc
KannuSingh Feb 13, 2025
e01894b
added node version on package.json
KannuSingh Feb 13, 2025
94568de
fix token aggregation logic on fallback
KannuSingh Feb 13, 2025
635b136
delete pnpm.lock and update yarn.lock
KannuSingh Feb 13, 2025
4466b60
chore:run prettier
KannuSingh Feb 13, 2025
ab1029d
refactor: update payment flow UI and balance fetching logic
KannuSingh Feb 15, 2025
bb1446f
Merge branch 'main' into feat/wallet_getAssets
KannuSingh Feb 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 28 additions & 20 deletions advanced/dapps/chain-abstraction-demo/app/hooks/useGiftDonut.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,32 @@ import { getAccount, getWalletClient, getPublicClient } from "wagmi/actions";
import { useState } from "react";
import { TransactionToast } from "@/components/TransactionToast";

type TransactionStatus = 'waiting-approval' | 'pending' | 'success' | 'error';
type TransactionStatus = "waiting-approval" | "pending" | "success" | "error";

export default function useGiftDonut() {
const [isPending, setIsPending] = useState(false);

const updateToast = (
toastId: ReturnType<typeof toast>,
status: TransactionStatus,
{ elapsedTime, hash, networkName }: {
toastId: ReturnType<typeof toast>,
status: TransactionStatus,
{
elapsedTime,
hash,
networkName,
}: {
elapsedTime?: number;
hash?: string;
networkName?: string;
} = {}
} = {},
) => {
toast(
<TransactionToast
<TransactionToast
status={status}
elapsedTime={elapsedTime}
hash={hash}
networkName={networkName}
/>,
{ id: toastId }
{ id: toastId },
);
};

Expand All @@ -39,10 +43,12 @@ export default function useGiftDonut() {

const account = getAccount(config);
const connectedChainId = account.chain?.id;

if (!connectedChainId) throw new Error("Chain undefined");
if (connectedChainId !== network.chainId) {
throw new Error("Please switch chain, connected chain does not match network");
throw new Error(
"Please switch chain, connected chain does not match network",
);
}

return { client, publicClient };
Expand Down Expand Up @@ -73,15 +79,15 @@ export default function useGiftDonut() {
// Validate chain and get clients
const { client, publicClient } = await validateTransaction(network);
const chainId = getAccount(config).chain?.id!;

// Get token contract
const contract = getTokenContract(token, chainId);
const tokenAmount = donutCount * 1 * 10 ** 6;

// Start tracking elapsed time
updateInterval = setInterval(() => {
updateToast(toastId, 'waiting-approval', {
elapsedTime: Math.floor((Date.now() - startTime) / 1000)
updateToast(toastId, "waiting-approval", {
elapsedTime: Math.floor((Date.now() - startTime) / 1000),
});
}, 1000);

Expand All @@ -94,29 +100,31 @@ export default function useGiftDonut() {
});

// Update to pending status
updateToast(toastId, 'pending', { hash: tx, networkName: network.name });
updateToast(toastId, "pending", { hash: tx, networkName: network.name });

// Wait for transaction
const receipt = await publicClient.waitForTransactionReceipt({ hash: tx });
const receipt = await publicClient.waitForTransactionReceipt({
hash: tx,
});
clearInterval(updateInterval);

// Update final status
const finalElapsedSeconds = Math.floor((Date.now() - startTime) / 1000);
const finalStatus = receipt.status === 'success' ? 'success' : 'error';
const finalStatus = receipt.status === "success" ? "success" : "error";

updateToast(toastId, finalStatus, {
elapsedTime: finalElapsedSeconds,
hash: tx,
networkName: network.name
networkName: network.name,
});

return tx;
} catch (e) {
clearInterval(updateInterval!);
const finalElapsedSeconds = Math.floor((Date.now() - startTime) / 1000);

if (e instanceof Error) {
updateToast(toastId, 'error', { elapsedTime: finalElapsedSeconds });
updateToast(toastId, "error", { elapsedTime: finalElapsedSeconds });
}
console.error(e);
} finally {
Expand All @@ -125,4 +133,4 @@ export default function useGiftDonut() {
};

return { giftDonutAsync, isPending };
}
}
4 changes: 1 addition & 3 deletions advanced/dapps/chain-abstraction-demo/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import AppKitProvider from "@/context";
import { headers } from "next/headers";
import { cn } from "@/lib/utils";
import { ThemeProvider } from "@/components/theme-provider";
import { Toaster } from "@/components/ui/sonner";
Expand All @@ -22,7 +21,6 @@ export default function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
const cookies = headers().get("cookie");
return (
<html lang="en" suppressHydrationWarning>
<head />
Expand All @@ -32,7 +30,7 @@ export default function RootLayout({
inter.className,
)}
>
<AppKitProvider cookies={cookies}>
<AppKitProvider>
<ThemeProvider
attribute="class"
defaultTheme="dark"
Expand Down
73 changes: 36 additions & 37 deletions advanced/dapps/chain-abstraction-demo/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,50 @@
"use client";

import * as React from "react";
import Image from "next/image";
import { ConnectWalletButton } from "@/components/ConnectWalletButton";
import { useAppKitAccount } from "@reown/appkit/react";
import { BalanceDisplay } from "@/components/BalanceDisplay";
import { DonutImage } from "@/components/DonutImage";
import { DonutInfo } from "@/components/DonutInfo";
import { GiftDonutButton } from "@/components/GiftDonutButton";
import Navbar from "@/components/Navbar";
import { GiftDonutModalTrigger } from "@/components/GiftDonutModalTrigger";
import { useWalletAssets } from "@/context/WalletAssetsProvider";
import { useAppKitAccount } from "@reown/appkit/react";
import React from "react";

const DONUT_PRICE = 1.0;

export default function Home() {
const { status, address } = useAppKitAccount();
const { status } = useAppKitAccount();
const { balances, isLoading, getBalanceBySymbol } = useWalletAssets();

const hasEnoughBalance = React.useMemo(() => {
const usdcBalance = parseFloat(getBalanceBySymbol("USDC"));
const usdtBalance = parseFloat(getBalanceBySymbol("USDT"));
return usdcBalance >= DONUT_PRICE || usdtBalance >= DONUT_PRICE;
}, [getBalanceBySymbol]);

return (
<div className="sm:w-1/2 flex flex-col sm:mx-10">
<Navbar />
<div className="flex flex-col justify-center gap-4 mt-8">
<div className="flex items-center justify-center h-64 relative ">
<Image
src="/donut-cover.png"
alt="Gift Donut"
className="object-cover"
fill={true}
/>
</div>
<div className="flex flex-col gap-5">
<div className="flex flex-col text-left">
<p className=" font-bold text-primary">Donut #1</p>
<p className=" text-secondary">Lorem ipsum dolor sit...</p>
</div>
<div className="flex justify-between items-center w-full">
<div className="flex flex-col text-left">
<p className=" text-secondary">Price</p>
<p className=" font-bold text-primary">$1.00</p>
</div>
{status === "connected" || address ? (
<div>
<GiftDonutModalTrigger
triggerText="Gift Donut"
initialView="Checkout"
className="bg-blue-500 hover:bg-blue-700 text-invert"
/>
<DonutImage />
<div className="flex flex-col gap-5 justify-end w-full">
<div className="flex flex-col justify-between gap-4 w-full">
<DonutInfo />
<div className="flex items-center justify-between gap-4">
<div className="flex flex-col items-center">
<p className="text-secondary">Price</p>
<p className="font-bold text-primary">
${DONUT_PRICE.toFixed(2)}
</p>
</div>
) : (
<div>
<ConnectWalletButton />
</div>
)}
<GiftDonutButton
isConnected={status === "connected"}
isLoading={isLoading}
hasEnoughBalance={hasEnoughBalance}
/>
</div>
</div>
{status === "connected" && (
<BalanceDisplay balances={balances} isLoading={isLoading} />
)}
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { type TokenBalance } from "@/utils/BalanceFetcherUtil";

interface BalanceDisplayProps {
balances: TokenBalance[];
isLoading: boolean;
}

export const BalanceDisplay: React.FC<BalanceDisplayProps> = ({
balances,
isLoading,
}) => {
if (isLoading) {
return null;
}

return (
<div className="flex flex-col items-center gap-1 text-sm text-secondary mt-2">
{balances.map((token) => (
<div key={token.address}>
Available {token.symbol} Balance: {token.balance} {token.symbol}
</div>
))}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from 'react';
import { Loader2 } from 'lucide-react';
import React from "react";
import { Loader2 } from "lucide-react";

export const CheckWalletToast = () => {
return (
<div className="flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
<p className="text-sm font-medium">Check your wallet to approve transaction</p>
<p className="text-sm font-medium">
Check your wallet to approve transaction
</p>
</div>
);
};
};
Original file line number Diff line number Diff line change
@@ -1,19 +1,47 @@
import React from "react";
import { useAppKit } from "@reown/appkit/react";
import { useAppKit, useAppKitAccount } from "@reown/appkit/react";
import { Button } from "./ui/button";

export function ConnectWalletButton() {
const { open } = useAppKit();
const { status } = useAppKitAccount();
const [mounted, setMounted] = React.useState(false);

React.useEffect(() => {
setMounted(true);
}, []);

if (!mounted) {
return (
<Button
type="button"
style={{
background:
"var(--foreground-foreground-secondary, hsla(0, 0%, 16%, 1))",
}}
className="w-full text-primary rounded-full"
size="lg"
>
Connect Wallet
</Button>
);
}

return (
<Button
type="button"
style={{background: "var(--foreground-foreground-secondary, hsla(0, 0%, 16%, 1))" }}
style={{
background:
"var(--foreground-foreground-secondary, hsla(0, 0%, 16%, 1))",
}}
className="w-full text-primary rounded-full"
size="lg"
onClick={() => open({ view: "Connect" })}
disabled={status === "connecting" || status === "reconnecting"}
>
Connect Wallet
{status === "connecting" || status === "reconnecting"
? "Connecting..."
: "Connect Wallet"}
</Button>
);
}
12 changes: 12 additions & 0 deletions advanced/dapps/chain-abstraction-demo/components/DonutImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Image from "next/image";

export const DonutImage = () => (
<div className="flex items-center justify-center h-64 relative">
<Image
src="/donut-cover.png"
alt="Gift Donut"
className="object-cover"
fill={true}
/>
</div>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
interface DonutInfoProps {}

export const DonutInfo: React.FC<DonutInfoProps> = ({}) => (
<div className="flex flex-col text-left">
<p className="font-bold text-primary">Donut #1</p>
<p className="text-secondary">Lorem ipsum dolor sit...</p>
</div>
);
Loading
Loading