Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 30 additions & 0 deletions CONTRIBUTION.md
Original file line number Diff line number Diff line change
@@ -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.

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
10 changes: 10 additions & 0 deletions frontend/src/components/ActivityTokenBalance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useAccount } from "wagmi";
import AddressTokenBalance from "@/components/AddressTokenBalance";

export function ActivityTokenBalance() {
const { address, isConnected } = useAccount();
if (!isConnected || !address) return null;
return <AddressTokenBalance address={address as `0x${string}`} />;
}

export default ActivityTokenBalance;
38 changes: 38 additions & 0 deletions frontend/src/components/AddressTokenBalance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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 raw = balanceQuery.data?.raw;
const decimals = balanceQuery.data?.decimals ?? 18;
const formatted = raw === undefined ? undefined : formatUnits(raw, decimals);

return (
<div className="mt-1 flex items-center gap-2 text-xs text-gray-600">
<Coins className="h-3 w-3" />
<span>{balanceQuery.isLoading ? "…" : (formatted ?? "0")} TGA</span>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<button aria-label="What is TGA?" className="cursor-help">
<HelpCircle className="h-3.5 w-3.5 text-gray-500" />
</button>
</TooltipTrigger>
<TooltipContent>
<p>The Guild Attestation token rewards activity on the DApp.</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
);
}

export default AddressTokenBalance;
7 changes: 6 additions & 1 deletion frontend/src/components/AppSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Smile, BadgeCheck, Home } from "lucide-react";
import { Smile, BadgeCheck, Home, BookOpen } from "lucide-react";

import {
Sidebar,
Expand All @@ -18,6 +18,11 @@ const items = [
url: "/",
icon: Home,
},
{
title: "Getting Started",
url: "/getting-started",
icon: BookOpen,
},
{
title: "Profiles",
url: "/profiles",
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/AppWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
SidebarInset,
} from "@/components/ui/sidebar";
import { AppSidebar } from "@/components/AppSidebar";
import { ActivityTokenBalance } from "@/components/ActivityTokenBalance";

const queryClient = new QueryClient();

Expand All @@ -37,6 +38,7 @@ export function AppWrapper({ children }: Web3ProviderProps) {
</div>

<div className="flex items-center space-x-4">
<ActivityTokenBalance />
<ConnectButton />
</div>
</div>
Expand Down
113 changes: 113 additions & 0 deletions frontend/src/components/pages/GettingStartedPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { AppWrapper } from "@/components/AppWrapper";
import React from "react";

export default function GettingStartedPage() {
return (
<section className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8 py-10 space-y-8">
<div className="flex items-center justify-between">
<h1 className="text-3xl font-bold tracking-tight">Getting Started</h1>
<a
href="https://discord.gg/pg4UgaTr"
target="_blank"
rel="noreferrer"
className="inline-flex items-center rounded-md bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
Join our Discord
</a>
</div>

<ol className="list-decimal pl-6 space-y-6 text-gray-700">
<li>
<div className="space-y-2">
<h2 className="text-xl font-semibold">
Install and set up MetaMask
</h2>
<p>
Install the MetaMask browser extension or mobile app and create a
wallet. Then add the Polygon Amoy test network in MetaMask (Chain
ID 80002).
</p>
<div className="space-x-3">
<a
href="https://metamask.io/download/"
target="_blank"
rel="noreferrer"
className="text-indigo-600 hover:underline"
>
Download MetaMask
</a>
<a
href="https://chainlist.org/chain/80002"
target="_blank"
rel="noreferrer"
className="text-indigo-600 hover:underline"
>
Add Polygon Amoy via Chainlist
</a>
</div>
</div>
</li>

<li>
<div className="space-y-2">
<h2 className="text-xl font-semibold">
Get Amoy testnet MATIC (gas)
</h2>
<p>
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.
</p>
<a
href="https://discord.gg/pg4UgaTr"
target="_blank"
rel="noreferrer"
className="text-indigo-600 hover:underline"
>
Request Amoy gas on Discord
</a>
</div>
</li>

<li>
<div className="space-y-2">
<h2 className="text-xl font-semibold">Create your Guild profile</h2>
<p>
Head to the Profiles page, connect your wallet, and create your
profile to start building your on‑chain reputation.
</p>
<a href="/profiles" className="text-indigo-600 hover:underline">
Go to Profiles
</a>
</div>
</li>

<li>
<div className="space-y-2">
<h2 className="text-xl font-semibold">Add a badge</h2>
<p>
Propose and create a new skill badge in the Badges page. Badges
represent skills or contributions recognized by the community.
</p>
<a href="/badges" className="text-indigo-600 hover:underline">
Go to Badges
</a>
</div>
</li>

<li>
<div className="space-y-2">
<h2 className="text-xl font-semibold">Give a badge to someone</h2>
<p>
After collaborating, issue a badge attestation to a peer to
acknowledge their work. This helps build credible, portable
developer profiles.
</p>
<a href="/profiles" className="text-indigo-600 hover:underline">
Find a profile to attest
</a>
</div>
</li>
</ol>
</section>
);
}
26 changes: 26 additions & 0 deletions frontend/src/components/pages/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,32 @@ export default function HomePage() {
can earn your first attestation.
</p>
</section>

<section className="space-y-3">
<h2 className="text-xl font-semibold">Learn more</h2>
<ul className="list-disc pl-6 space-y-2">
<li>
<a
href="https://medium.com/@antoineestienne/theguild-presentation-2b2b147d1fa5"
target="_blank"
rel="noreferrer"
className="text-indigo-600 hover:underline"
>
The Guild — Presentation
</a>
</li>
<li>
<a
href="https://medium.com/@antoineestienne/theguild-the-plan-25cd1ffd1e5b"
target="_blank"
rel="noreferrer"
className="text-indigo-600 hover:underline"
>
The Guild — The Plan
</a>
</li>
</ul>
</section>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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." }),
Expand All @@ -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<FormValues>({
resolver: zodResolver(formSchema),
Expand All @@ -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 (
<Dialog open={open} onOpenChange={setOpen}>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/profiles/list/ProfileCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +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 AddressTokenBalance from "@/components/AddressTokenBalance";

interface ProfileCardProps {
address: string;
Expand Down Expand Up @@ -79,6 +80,7 @@ export function ProfileCard({
{displayAddress}
</a>
</CardDescription>
<AddressTokenBalance address={address as `0x${string}`} />
</div>
{isOwner && (
<EditProfileDialog
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 ?? [];
Expand All @@ -24,6 +26,15 @@ export function ProfileAttestations({ address }: { address: string }) {
}));
}, [attestationsQuery.data, address]);

const profileNameByAddress = useMemo(() => {
const map = new Map<string, string>();
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,
Expand Down Expand Up @@ -77,7 +88,15 @@ export function ProfileAttestations({ address }: { address: string }) {
{it.justification}
</p>
<p className="text-xs text-gray-500 mt-1">
Issued by {it.issuer}
Issued by{" "}
<a
className="text-indigo-600 hover:underline"
href={`/profiles/${it.issuer}`}
>
{profileNameByAddress.get(
it.issuer.toLowerCase()
) || it.issuer}
</a>
</p>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { User } from "lucide-react";
import { useAccount } from "wagmi";
import { useMemo } from "react";
import { useGetProfiles } from "@/hooks/profiles/use-get-profiles";
import AddressTokenBalance from "@/components/AddressTokenBalance";

export function ProfileHeader({ address }: { address: string }) {
const profilesQuery = useGetProfiles();
Expand Down Expand Up @@ -44,6 +45,7 @@ export function ProfileHeader({ address }: { address: string }) {
{displayAddress ? (
<p className="font-mono text-sm text-gray-600">{displayAddress}</p>
) : null}
<AddressTokenBalance address={address as `0x${string}`} />
</div>
</header>
);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/ui/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Loading
Loading