From c3efc65ba69c648ff5e0e3ab3b93b41541ad33a6 Mon Sep 17 00:00:00 2001 From: Antoine Estienne Date: Mon, 22 Sep 2025 14:18:40 +0200 Subject: [PATCH 1/9] add getting started, issue #51 --- frontend/src/components/AppSidebar.tsx | 7 +- .../components/pages/GettingStartedPage.tsx | 113 ++++++++++++++++++ frontend/src/pages/getting-started.astro | 16 +++ 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/pages/GettingStartedPage.tsx create mode 100644 frontend/src/pages/getting-started.astro diff --git a/frontend/src/components/AppSidebar.tsx b/frontend/src/components/AppSidebar.tsx index 9336fc2..d5ade67 100644 --- a/frontend/src/components/AppSidebar.tsx +++ b/frontend/src/components/AppSidebar.tsx @@ -1,4 +1,4 @@ -import { Smile, BadgeCheck, Home } from "lucide-react"; +import { Smile, BadgeCheck, Home, BookOpen } from "lucide-react"; import { Sidebar, @@ -18,6 +18,11 @@ const items = [ url: "/", icon: Home, }, + { + title: "Getting Started", + url: "/getting-started", + icon: BookOpen, + }, { title: "Profiles", url: "/profiles", diff --git a/frontend/src/components/pages/GettingStartedPage.tsx b/frontend/src/components/pages/GettingStartedPage.tsx new file mode 100644 index 0000000..f5d3af1 --- /dev/null +++ b/frontend/src/components/pages/GettingStartedPage.tsx @@ -0,0 +1,113 @@ +import { AppWrapper } from "@/components/AppWrapper"; +import React from "react"; + +export default function GettingStartedPage() { + return ( +
+
+

Getting Started

+ + Join our Discord + +
+ +
    +
  1. +
    +

    + Install and set up MetaMask +

    +

    + Install the MetaMask browser extension or mobile app and create a + wallet. Then add the Polygon Amoy test network in MetaMask (Chain + ID 80002). +

    + +
    +
  2. + +
  3. +
    +

    + Get Amoy testnet MATIC (gas) +

    +

    + You need a small amount of Amoy MATIC to make on‑chain actions. + Ask in our Discord and a member will send you some testnet funds. +

    + + Request Amoy gas on Discord + +
    +
  4. + +
  5. +
    +

    Create your Guild profile

    +

    + Head to the Profiles page, connect your wallet, and create your + profile to start building your on‑chain reputation. +

    + + Go to Profiles + +
    +
  6. + +
  7. +
    +

    Add a badge

    +

    + Propose and create a new skill badge in the Badges page. Badges + represent skills or contributions recognized by the community. +

    + + Go to Badges + +
    +
  8. + +
  9. +
    +

    Give a badge to someone

    +

    + After collaborating, issue a badge attestation to a peer to + acknowledge their work. This helps build credible, portable + developer profiles. +

    + + Find a profile to attest + +
    +
  10. +
+
+ ); +} diff --git a/frontend/src/pages/getting-started.astro b/frontend/src/pages/getting-started.astro new file mode 100644 index 0000000..85a6277 --- /dev/null +++ b/frontend/src/pages/getting-started.astro @@ -0,0 +1,16 @@ +--- +import Layout from "../layouts/Layout.astro"; +import GettingStartedPage from "@/components/pages/GettingStartedPage"; +import { AppWrapper } from "@/components/AppWrapper"; +--- + + +
+ + + +
+ +
+ + From 1f4962095e62fef47bda78407d81582203e97ca2 Mon Sep 17 00:00:00 2001 From: Antoine Estienne Date: Mon, 22 Sep 2025 14:31:40 +0200 Subject: [PATCH 2/9] Display Activity token balance(s) on the app #27 and #54 --- .../src/components/ActivityTokenBalance.tsx | 26 ++++++++++++ frontend/src/components/AppWrapper.tsx | 2 + .../action-buttons/AddAttestationDialog.tsx | 15 +++++-- .../components/profiles/list/ProfileCard.tsx | 18 ++++++++- .../profiles/profile-page/ProfileHeader.tsx | 18 ++++++++- .../use-get-activity-token-balance.ts | 40 +++++++++++++++++++ frontend/src/lib/abis/erc20Abi.ts | 16 ++++++++ 7 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 frontend/src/components/ActivityTokenBalance.tsx create mode 100644 frontend/src/hooks/attestations/use-get-activity-token-balance.ts create mode 100644 frontend/src/lib/abis/erc20Abi.ts diff --git a/frontend/src/components/ActivityTokenBalance.tsx b/frontend/src/components/ActivityTokenBalance.tsx new file mode 100644 index 0000000..33a8cbd --- /dev/null +++ b/frontend/src/components/ActivityTokenBalance.tsx @@ -0,0 +1,26 @@ +import React, { useMemo } from "react"; +import { useAccount } from "wagmi"; +import { formatUnits } from "viem"; +import useGetActivityTokenBalance from "@/hooks/attestations/use-get-activity-token-balance"; + +export function ActivityTokenBalance() { + const { address, isConnected } = useAccount(); + const { data, isLoading } = useGetActivityTokenBalance( + address as `0x${string}` | undefined + ); + + const formatted = useMemo(() => { + if (!data) return undefined; + return formatUnits(data.raw, data.decimals); + }, [data]); + + if (!isConnected) return null; + + return ( +
+ {isLoading ? "…" : (formatted ?? "0")} TGA +
+ ); +} + +export default ActivityTokenBalance; diff --git a/frontend/src/components/AppWrapper.tsx b/frontend/src/components/AppWrapper.tsx index 8455957..77c6e11 100644 --- a/frontend/src/components/AppWrapper.tsx +++ b/frontend/src/components/AppWrapper.tsx @@ -11,6 +11,7 @@ import { SidebarInset, } from "@/components/ui/sidebar"; import { AppSidebar } from "@/components/AppSidebar"; +import { ActivityTokenBalance } from "@/components/ActivityTokenBalance"; const queryClient = new QueryClient(); @@ -37,6 +38,7 @@ export function AppWrapper({ children }: Web3ProviderProps) {
+
diff --git a/frontend/src/components/profiles/action-buttons/AddAttestationDialog.tsx b/frontend/src/components/profiles/action-buttons/AddAttestationDialog.tsx index e5a2b4c..6365c1e 100644 --- a/frontend/src/components/profiles/action-buttons/AddAttestationDialog.tsx +++ b/frontend/src/components/profiles/action-buttons/AddAttestationDialog.tsx @@ -31,6 +31,7 @@ import { SelectValue, } from "@/components/ui/select"; import { useGetAttestations } from "@/hooks/attestations/use-get-attestations"; +import { useGetActivityTokenBalance } from "@/hooks/attestations/use-get-activity-token-balance"; const formSchema = z.object({ badgeName: z.string().min(1, { message: "Badge name is required." }), @@ -56,7 +57,8 @@ export function AddAttestationDialog({ reset, } = useCreateAttestation(); const { data: badges, isLoading: badgesLoading } = useGetBadges(); - const { refetch } = useGetAttestations(); + const { refetch: refetchAttestations } = useGetAttestations(); + const { refetch: refetchActivityTokenBalance } = useGetActivityTokenBalance(); const form = useForm({ resolver: zodResolver(formSchema), @@ -73,12 +75,19 @@ export function AddAttestationDialog({ useEffect(() => { if (isConfirmed) { - refetch(); + refetchAttestations(); + refetchActivityTokenBalance(); setOpen(false); form.reset(); reset(); } - }, [isConfirmed, refetch, form, reset]); + }, [ + isConfirmed, + refetchAttestations, + refetchActivityTokenBalance, + form, + reset, + ]); return ( diff --git a/frontend/src/components/profiles/list/ProfileCard.tsx b/frontend/src/components/profiles/list/ProfileCard.tsx index 5a1c475..ba83143 100644 --- a/frontend/src/components/profiles/list/ProfileCard.tsx +++ b/frontend/src/components/profiles/list/ProfileCard.tsx @@ -1,4 +1,4 @@ -import { Badge, Award, User, Edit, Trash, Plus } from "lucide-react"; +import { Badge, Award, User, Edit, Trash, Plus, Coins } from "lucide-react"; import { Card, CardContent, @@ -11,6 +11,8 @@ import { EditProfileDialog } from "../action-buttons/EditProfileDialog"; import { useAccount } from "wagmi"; import DeleteProfileDialog from "../action-buttons/DeleteProfileDialog"; import { AddAttestationDialog } from "../action-buttons/AddAttestationDialog"; +import { formatUnits } from "viem"; +import useGetActivityTokenBalance from "@/hooks/attestations/use-get-activity-token-balance"; interface ProfileCardProps { address: string; @@ -37,6 +39,14 @@ export function ProfileCard({ const { address: connectedAddress } = useAccount(); const isOwner = connectedAddress?.toLowerCase() === address.toLowerCase(); + const balanceQuery = useGetActivityTokenBalance(address as `0x${string}`); + const formattedBalance = (() => { + const raw = balanceQuery.data?.raw; + const decimals = balanceQuery.data?.decimals ?? 18; + if (raw === undefined) return undefined; + return formatUnits(raw, decimals); + })(); + const displayName = name || `${address.slice(0, 6)}...${address.slice(-4)}`; const displayAddress = `${address.slice(0, 6)}...${address.slice(-4)}`; return ( @@ -79,6 +89,12 @@ export function ProfileCard({ {displayAddress} +
+ + + {balanceQuery.isLoading ? "…" : (formattedBalance ?? "0")} TGA + +
{isOwner && ( { + const raw = balanceQuery.data?.raw; + const decimals = balanceQuery.data?.decimals ?? 18; + if (raw === undefined) return undefined; + return formatUnits(raw, decimals); + }, [balanceQuery.data]); + return (
@@ -44,6 +54,12 @@ export function ProfileHeader({ address }: { address: string }) { {displayAddress ? (

{displayAddress}

) : null} +
+ + + {balanceQuery.isLoading ? "…" : (formattedBalance ?? "0")} TGA + +
); diff --git a/frontend/src/hooks/attestations/use-get-activity-token-balance.ts b/frontend/src/hooks/attestations/use-get-activity-token-balance.ts new file mode 100644 index 0000000..9bb1e78 --- /dev/null +++ b/frontend/src/hooks/attestations/use-get-activity-token-balance.ts @@ -0,0 +1,40 @@ +import { useMemo } from "react"; +import { useReadContract } from "wagmi"; +import { erc20Abi } from "@/lib/abis/erc20Abi"; +import { ACTIVITY_TOKEN_ADDRESS } from "@/lib/constants/blockchainConstants"; + +export function useGetActivityTokenBalance(address?: `0x${string}`) { + const token = ACTIVITY_TOKEN_ADDRESS; + + const balanceQuery = useReadContract({ + abi: erc20Abi, + address: token, + functionName: "balanceOf", + args: address ? [address] : undefined, + query: { enabled: Boolean(token && address) }, + }); + + const decimalsQuery = useReadContract({ + abi: erc20Abi, + address: token, + functionName: "decimals", + query: { enabled: Boolean(token) }, + }); + + const value = useMemo(() => { + const raw = balanceQuery.data as bigint | undefined; + const decimals = Number((decimalsQuery.data as number | undefined) ?? 18); + if (raw === undefined) return undefined; + return { raw, decimals }; + }, [balanceQuery.data, decimalsQuery.data]); + + const isLoading = balanceQuery.isLoading || decimalsQuery.isLoading; + const error = + (balanceQuery.error as Error | null) || + (decimalsQuery.error as Error | null) || + null; + + return { data: value, isLoading, error, refetch: balanceQuery.refetch }; +} + +export default useGetActivityTokenBalance; diff --git a/frontend/src/lib/abis/erc20Abi.ts b/frontend/src/lib/abis/erc20Abi.ts new file mode 100644 index 0000000..74058ae --- /dev/null +++ b/frontend/src/lib/abis/erc20Abi.ts @@ -0,0 +1,16 @@ +export const erc20Abi = [ + { + type: "function", + name: "balanceOf", + stateMutability: "view", + inputs: [{ name: "account", type: "address" }], + outputs: [{ name: "", type: "uint256" }], + }, + { + type: "function", + name: "decimals", + stateMutability: "view", + inputs: [], + outputs: [{ name: "", type: "uint8" }], + }, +] as const; From a9ef4e73afc656b865d07cbce7bd548750968c91 Mon Sep 17 00:00:00 2001 From: Antoine Estienne Date: Mon, 22 Sep 2025 15:01:46 +0200 Subject: [PATCH 3/9] a few tweaks seem to improve #47 --- .../attestations/use-create-attestation.ts | 43 ++++++++++++++++--- frontend/src/lib/wagmi.ts | 5 +++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/frontend/src/hooks/attestations/use-create-attestation.ts b/frontend/src/hooks/attestations/use-create-attestation.ts index 69f5912..79a23c5 100644 --- a/frontend/src/hooks/attestations/use-create-attestation.ts +++ b/frontend/src/hooks/attestations/use-create-attestation.ts @@ -1,5 +1,11 @@ -import { useMemo } from "react"; -import { useWriteContract, useWaitForTransactionReceipt } from "wagmi"; +import { useMemo, useRef } from "react"; +import { + useWriteContract, + useWaitForTransactionReceipt, + useConfig, + useAccount, +} from "wagmi"; +import { simulateContract } from "@wagmi/core"; import { encodeAbiParameters } from "viem"; import { easAbi } from "@/lib/abis/easAbi"; import { @@ -31,6 +37,8 @@ function encodeBadgeData( } export function useCreateAttestation() { + const config = useConfig(); + const { address: account, chainId } = useAccount(); const { writeContractAsync, data: hash, @@ -38,6 +46,7 @@ export function useCreateAttestation() { error, reset, } = useWriteContract(); + const isBusyRef = useRef(false); const createAttestation = useMemo(() => { return async ( @@ -45,14 +54,20 @@ export function useCreateAttestation() { badgeName: string, justification: string ) => { + if (isBusyRef.current || isPending) { + throw new Error( + "Previous attestation is still pending. Please wait..." + ); + } + isBusyRef.current = true; // Convert strings to bytes32 const badgeNameBytes = stringToBytes32(badgeName); const justificationBytes = stringToBytes32(justification); // Encode data according to schema const encodedData = encodeBadgeData(badgeNameBytes, justificationBytes); - // Call EAS.attest via wagmi - const uid = await writeContractAsync({ + // 1) Simulate with exact sender/chain to catch reverts and get prepared request + const simulation = await simulateContract(config, { abi: easAbi, address: EAS_CONTRACT_ADDRESS, functionName: "attest", @@ -70,12 +85,26 @@ export function useCreateAttestation() { }, }, ], - // value: 0n, // no ETH + account: account as `0x${string}` | undefined, + chainId, }); - return uid; + // 2) Bump gas params slightly and send + const bump = (v?: bigint) => + typeof v === "bigint" ? (v * 12n) / 10n : v; + const req: any = { ...simulation.request }; + req.gas = bump(req.gas); + req.maxFeePerGas = bump(req.maxFeePerGas); + req.maxPriorityFeePerGas = bump(req.maxPriorityFeePerGas); + + try { + const txHash = await writeContractAsync(req); + return txHash; + } finally { + isBusyRef.current = false; + } }; - }, [writeContractAsync]); + }, [account, chainId, config, isPending, writeContractAsync]); const wait = useWaitForTransactionReceipt({ hash: hash as `0x${string}` | undefined, diff --git a/frontend/src/lib/wagmi.ts b/frontend/src/lib/wagmi.ts index e2b7afb..6fce4e3 100644 --- a/frontend/src/lib/wagmi.ts +++ b/frontend/src/lib/wagmi.ts @@ -1,4 +1,5 @@ import { getDefaultConfig } from "@rainbow-me/rainbowkit"; +import { http } from "wagmi"; import { polygonAmoy } from "wagmi/chains"; const projectId = import.meta.env.PUBLIC_WALLET_CONNECT_PROJECT_ID as @@ -10,4 +11,8 @@ export const config = getDefaultConfig({ projectId: projectId ?? "", chains: [polygonAmoy], ssr: false, + syncConnectedChain: true, + transports: { + [polygonAmoy.id]: http(), + }, }); From 00f2320d4720798068049f2cf98fa62c88e2af6f Mon Sep 17 00:00:00 2001 From: Antoine Estienne Date: Mon, 22 Sep 2025 15:29:56 +0200 Subject: [PATCH 4/9] Update contribution guide #45 --- CONTRIBUTION.md | 30 ++++++++++++++++++++++++++++++ README.md | 2 ++ 2 files changed, 32 insertions(+) create mode 100644 CONTRIBUTION.md diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md new file mode 100644 index 0000000..5a5a9be --- /dev/null +++ b/CONTRIBUTION.md @@ -0,0 +1,30 @@ +Contributing to The Guild Genesis + +Thank you for your interest in contributing! Please follow these steps before working on an issue or feature: + +1. Join our Discord: https://discord.gg/pg4UgaTr +2. Create your profile on https://theguild.dev +3. Edit your Guild profile to include your Discord username (so we can verify your identity and attribute rewards). +4. Discuss the terms for fulfilling the ticket with Antoine (scope, deliverables, timeline). + +Rewards and recognition + +- Rule of thumb: 10 points per hour, 80 points per full day. +- Contribution tokens have no monetary value. They represent reputation and will grant voting rights in the Guild in the future. +- Your contributions will also be recognized with badges on https://theguild.dev. + +Process + +- Pick an open issue and comment that you’d like to work on it, or propose a new one. +- Align requirements with Antoine in Discord before starting. +- Submit a PR referencing the issue. Include clear commit messages and a brief changelog in the PR description. +- Be responsive to review feedback; we’ll merge once acceptance criteria are met. + +Development quick links + +- Frontend (Astro + React): `frontend/` +- Backend (Rust + Axum + SQLx): `backend/` +- Smart Contracts (Foundry): `the-guild-smart-contracts/` + +If anything is unclear, ping us in Discord and we’ll help you get unblocked quickly. + diff --git a/README.md b/README.md index 345bd35..1bbdf82 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,8 @@ event BadgeCreated(bytes32 indexed name, bytes32 description, address indexed cr This is a community-driven project. Join our [Discord](https://discord.gg/pg4UgaTr) to discuss features, propose changes, and contribute to the codebase. +See detailed steps in [CONTRIBUTION.md](CONTRIBUTION.md). + ## License See [LICENSE](LICENSE) file for details. From af288995ccb576e53d9635131be8612eaff20a42 Mon Sep 17 00:00:00 2001 From: Antoine Estienne Date: Mon, 22 Sep 2025 15:35:36 +0200 Subject: [PATCH 5/9] add links to the medium articles --- frontend/src/components/pages/HomePage.tsx | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/frontend/src/components/pages/HomePage.tsx b/frontend/src/components/pages/HomePage.tsx index 69fd1e1..3fde3f7 100644 --- a/frontend/src/components/pages/HomePage.tsx +++ b/frontend/src/components/pages/HomePage.tsx @@ -62,6 +62,32 @@ export default function HomePage() { can earn your first attestation.

+ +
+

Learn more

+ +
); } From 4201b62821d24638a47e39372bd6f0bfcdb81762 Mon Sep 17 00:00:00 2001 From: Antoine Estienne Date: Mon, 22 Sep 2025 15:48:04 +0200 Subject: [PATCH 6/9] Issuer should link to profile (and use name) #32 --- .../profile-page/ProfileAttestations.tsx | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/profiles/profile-page/ProfileAttestations.tsx b/frontend/src/components/profiles/profile-page/ProfileAttestations.tsx index c1ce2d4..e9fae57 100644 --- a/frontend/src/components/profiles/profile-page/ProfileAttestations.tsx +++ b/frontend/src/components/profiles/profile-page/ProfileAttestations.tsx @@ -7,9 +7,11 @@ import { AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; +import { useGetProfiles } from "@/hooks/profiles/use-get-profiles"; export function ProfileAttestations({ address }: { address: string }) { const attestationsQuery = useGetAttestations(); + const profilesQuery = useGetProfiles(); const attestations = useMemo(() => { const list = attestationsQuery.data ?? []; @@ -24,6 +26,15 @@ export function ProfileAttestations({ address }: { address: string }) { })); }, [attestationsQuery.data, address]); + const profileNameByAddress = useMemo(() => { + const map = new Map(); + const list = profilesQuery.data ?? []; + for (const p of list) { + if (p.address) map.set(p.address.toLowerCase(), p.name || ""); + } + return map; + }, [profilesQuery.data]); + const grouped = useMemo(() => { const map = new Map< string, @@ -77,7 +88,15 @@ export function ProfileAttestations({ address }: { address: string }) { {it.justification}

- Issued by {it.issuer} + Issued by{" "} + + {profileNameByAddress.get( + it.issuer.toLowerCase() + ) || it.issuer} +

From ee562afd88288d77f49c2935cddeba4817065146 Mon Sep 17 00:00:00 2001 From: Antoine Estienne Date: Mon, 22 Sep 2025 16:04:40 +0200 Subject: [PATCH 7/9] add tooltip for TGA balance --- .../src/components/ActivityTokenBalance.tsx | 22 ++-------- .../src/components/AddressTokenBalance.tsx | 42 +++++++++++++++++++ .../components/profiles/list/ProfileCard.tsx | 20 ++------- .../profiles/profile-page/ProfileHeader.tsx | 20 ++------- frontend/src/components/ui/tooltip.tsx | 2 +- 5 files changed, 52 insertions(+), 54 deletions(-) create mode 100644 frontend/src/components/AddressTokenBalance.tsx diff --git a/frontend/src/components/ActivityTokenBalance.tsx b/frontend/src/components/ActivityTokenBalance.tsx index 33a8cbd..633fe8d 100644 --- a/frontend/src/components/ActivityTokenBalance.tsx +++ b/frontend/src/components/ActivityTokenBalance.tsx @@ -1,26 +1,10 @@ -import React, { useMemo } from "react"; import { useAccount } from "wagmi"; -import { formatUnits } from "viem"; -import useGetActivityTokenBalance from "@/hooks/attestations/use-get-activity-token-balance"; +import AddressTokenBalance from "@/components/AddressTokenBalance"; export function ActivityTokenBalance() { const { address, isConnected } = useAccount(); - const { data, isLoading } = useGetActivityTokenBalance( - address as `0x${string}` | undefined - ); - - const formatted = useMemo(() => { - if (!data) return undefined; - return formatUnits(data.raw, data.decimals); - }, [data]); - - if (!isConnected) return null; - - return ( -
- {isLoading ? "…" : (formatted ?? "0")} TGA -
- ); + if (!isConnected || !address) return null; + return ; } export default ActivityTokenBalance; diff --git a/frontend/src/components/AddressTokenBalance.tsx b/frontend/src/components/AddressTokenBalance.tsx new file mode 100644 index 0000000..8056ffc --- /dev/null +++ b/frontend/src/components/AddressTokenBalance.tsx @@ -0,0 +1,42 @@ +import React, { useMemo } from "react"; +import { Coins, HelpCircle } from "lucide-react"; +import { formatUnits } from "viem"; +import useGetActivityTokenBalance from "@/hooks/attestations/use-get-activity-token-balance"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; + +export function AddressTokenBalance({ address }: { address: `0x${string}` }) { + const balanceQuery = useGetActivityTokenBalance(address); + + const formatted = useMemo(() => { + const raw = balanceQuery.data?.raw; + const decimals = balanceQuery.data?.decimals ?? 18; + if (raw === undefined) return undefined; + return formatUnits(raw, decimals); + }, [balanceQuery.data]); + + return ( +
+ + {balanceQuery.isLoading ? "…" : (formatted ?? "0")} TGA + + + + + + +

The Guild Attestation token rewards activity on the DApp.

+
+
+
+
+ ); +} + +export default AddressTokenBalance; diff --git a/frontend/src/components/profiles/list/ProfileCard.tsx b/frontend/src/components/profiles/list/ProfileCard.tsx index ba83143..eae0318 100644 --- a/frontend/src/components/profiles/list/ProfileCard.tsx +++ b/frontend/src/components/profiles/list/ProfileCard.tsx @@ -1,4 +1,4 @@ -import { Badge, Award, User, Edit, Trash, Plus, Coins } from "lucide-react"; +import { Badge, Award, User, Edit, Trash, Plus } from "lucide-react"; import { Card, CardContent, @@ -11,8 +11,7 @@ import { EditProfileDialog } from "../action-buttons/EditProfileDialog"; import { useAccount } from "wagmi"; import DeleteProfileDialog from "../action-buttons/DeleteProfileDialog"; import { AddAttestationDialog } from "../action-buttons/AddAttestationDialog"; -import { formatUnits } from "viem"; -import useGetActivityTokenBalance from "@/hooks/attestations/use-get-activity-token-balance"; +import AddressTokenBalance from "@/components/AddressTokenBalance"; interface ProfileCardProps { address: string; @@ -39,14 +38,6 @@ export function ProfileCard({ const { address: connectedAddress } = useAccount(); const isOwner = connectedAddress?.toLowerCase() === address.toLowerCase(); - const balanceQuery = useGetActivityTokenBalance(address as `0x${string}`); - const formattedBalance = (() => { - const raw = balanceQuery.data?.raw; - const decimals = balanceQuery.data?.decimals ?? 18; - if (raw === undefined) return undefined; - return formatUnits(raw, decimals); - })(); - const displayName = name || `${address.slice(0, 6)}...${address.slice(-4)}`; const displayAddress = `${address.slice(0, 6)}...${address.slice(-4)}`; return ( @@ -89,12 +80,7 @@ export function ProfileCard({ {displayAddress} -
- - - {balanceQuery.isLoading ? "…" : (formattedBalance ?? "0")} TGA - -
+ {isOwner && ( { - const raw = balanceQuery.data?.raw; - const decimals = balanceQuery.data?.decimals ?? 18; - if (raw === undefined) return undefined; - return formatUnits(raw, decimals); - }, [balanceQuery.data]); - return (
@@ -54,12 +45,7 @@ export function ProfileHeader({ address }: { address: string }) { {displayAddress ? (

{displayAddress}

) : null} -
- - - {balanceQuery.isLoading ? "…" : (formattedBalance ?? "0")} TGA - -
+
); diff --git a/frontend/src/components/ui/tooltip.tsx b/frontend/src/components/ui/tooltip.tsx index 3bec14b..3530569 100644 --- a/frontend/src/components/ui/tooltip.tsx +++ b/frontend/src/components/ui/tooltip.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import * as TooltipPrimitive from "@radix-ui/react-tooltip"; -import { cn } from "@/lib/utils"; +import { cn } from "@/lib/utils/index"; function TooltipProvider({ delayDuration = 0, From 4a90c69e2aa517a2981266dc056733ef4679dff3 Mon Sep 17 00:00:00 2001 From: Antoine Estienne Date: Mon, 22 Sep 2025 16:08:05 +0200 Subject: [PATCH 8/9] fix fe tests --- frontend/src/test/ProfileCard.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/test/ProfileCard.test.tsx b/frontend/src/test/ProfileCard.test.tsx index a62c979..9d1804a 100644 --- a/frontend/src/test/ProfileCard.test.tsx +++ b/frontend/src/test/ProfileCard.test.tsx @@ -29,6 +29,7 @@ vi.mock("wagmi", () => ({ useWaitForTransactionReceipt: () => ({ data: vi.fn() }), useReadContract: () => ({ data: undefined }), useReadContracts: () => ({ data: undefined }), + useConfig: () => ({}), })); // Mock RainbowKit From 18709440973091a55424610b02f2f11f9a2a1d7d Mon Sep 17 00:00:00 2001 From: Antoine Estienne Date: Mon, 22 Sep 2025 16:43:21 +0200 Subject: [PATCH 9/9] remove use memeo --- frontend/src/components/AddressTokenBalance.tsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/AddressTokenBalance.tsx b/frontend/src/components/AddressTokenBalance.tsx index 8056ffc..afa106e 100644 --- a/frontend/src/components/AddressTokenBalance.tsx +++ b/frontend/src/components/AddressTokenBalance.tsx @@ -1,4 +1,3 @@ -import React, { useMemo } from "react"; import { Coins, HelpCircle } from "lucide-react"; import { formatUnits } from "viem"; import useGetActivityTokenBalance from "@/hooks/attestations/use-get-activity-token-balance"; @@ -12,12 +11,9 @@ import { export function AddressTokenBalance({ address }: { address: `0x${string}` }) { const balanceQuery = useGetActivityTokenBalance(address); - const formatted = useMemo(() => { - const raw = balanceQuery.data?.raw; - const decimals = balanceQuery.data?.decimals ?? 18; - if (raw === undefined) return undefined; - return formatUnits(raw, decimals); - }, [balanceQuery.data]); + const raw = balanceQuery.data?.raw; + const decimals = balanceQuery.data?.decimals ?? 18; + const formatted = raw === undefined ? undefined : formatUnits(raw, decimals); return (