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 (
+
+
+
+
+ -
+
+
+ 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).
+
+
+
+
+
+ -
+
+
+ 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
+
+
+
+
+ -
+
+
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
+
+
+
+
+ -
+
+
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
+
+
+
+
+ -
+
+
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
+
+
+
+
+
+ );
+}
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 (
+
+
);
}
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 (
);
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 (