Skip to content

Conversation

@marcdhi
Copy link
Collaborator

@marcdhi marcdhi commented Oct 16, 2025

Summary

This PR adds first-class support for team names across the smart contracts, backend attestations, and frontend UI. Users can now create teams with a name (required, max 20 chars), see the team name throughout the app (Hunt details and leaderboard), and include the team name in attestations for consistent display. The UI presents a minimal header showing the team name and an “Invite more” button when the team is not full and the viewer is the team owner.

Motivation

  • With team play enabled and a leaderboard (feat: Hunt Leaderboard using Sign Protocol attestations #120), team names are more user-friendly than numeric IDs or raw addresses.
  • Adding team names to attestations ensures consistent display regardless of where data is sourced from (on-chain vs off-chain attestations).

Changes

  • Contract (Solidity)

    • Added name to Team and TeamInfo structs in contracts/src/Khoj.sol.
    • Updated createTeam signature to createTeam(uint256 _huntId, string _teamName).
    • Updated getTeam to return name via TeamInfo.
    • Kept all invariants: checks for teams enabled, uniqueness per hunt, team size, membership mapping/array.
    • Added event parsing in contracts/scripts/deploy.js to reliably retrieve huntId/teamId in ethers v6.
    • Added Base Sepolia network to contracts/hardhat.config.js (uses BASE_SEPOLIA_RPC_URL, PRIVATE_KEY).
  • Contract Tests

    • Updated contracts/test/Khoj.test.js to use new createTeam(_huntId, _teamName) signature.
    • Added assertions confirming the team name is set/read correctly via getTeam.
    • All tests passing locally.
  • Backend

    • backend/src/services/sign-protocol.js
      • Extended the schema to include teamName: string.
      • Updated attestClueSolved to accept and include teamName (fallback to wallet address for solo users).
    • backend/src/server.js
      • /attest-clue now accepts teamName and forwards to the service.
    • backend/src/services/leaderboard.js
      • Carries through teamName in leaderboard entries for display.
  • Frontend

    • ABI
      • Updated frontend/src/assets/hunt_abi.ts to reflect new createTeam signature and TeamInfo order: huntId, teamId, owner, name, maxMembers, memberCount, members.
    • Types
      • Updated frontend/src/types/hunt.ts Team to include optional name.
    • Components
      • HuntDetails.tsx:
        • Added required team name input (max 20 characters).
        • Contract call updated to createTeam(huntId, teamName).
        • Minimal header shows “Team {name}”.
        • Removed team ID display.
        • Replaced copy action with a minimal “Invite more” button (shown only if viewer is owner and team isn’t full). Re-uses existing invite generation flow.
        • Character counter and label to improve input UX.
      • Leaderboard.tsx:
        • Displays teamName (with truncation) if present; falls back to previous logic (solo address or Team #ID).
      • Clue.tsx:
        • Sends teamName to /attest-clue. Solo fallback to wallet address retained.

Breaking changes

  • Contract ABI: createTeam now requires a second parameter (_teamName). Frontend and any scripts must pass this.
  • TeamInfo tuple order updated to include name. Any consumer reading team info must use the new ABI.

Deployment and configuration

  • Contracts
    • Deploy the updated Khoj contract to your target network (e.g., Base Sepolia).
    • Example (with env configured): npx hardhat run contracts/scripts/deploy.js --network baseSepolia
  • Frontend
    • Set VITE_PUBLIC_BASE_CONTRACT_ADDRESS to the new contract address.
    • Restart the frontend dev server to pick up env changes.
  • Backend (Sign Protocol)
    • The schema now includes teamName. If using a static schema ID, create a new schema and update SIGN_SCHEMA_ID in env, or re-run the schema creation step and update the ID.

Testing

  • Contracts: npx hardhat test — all tests pass.
  • Manual QA
    • Create a hunt with teams enabled.
    • From Hunt Details:
      • Enter a team name (<=20 chars).
      • Click “Create Team & Generate Invite.”
      • Confirm the team header shows “Team {name}”.
      • If not full and owner, “Invite more” visible and generates a QR/invite.
    • Solve a clue:
      • Attestation body includes teamName.
      • Leaderboard displays the team name (truncated for long names).
    • Solo user flow:
      • Attestation includes the user’s address in teamName fallback, so leaderboard still displays a meaningful identifier.

Related issues

Screenshot 2025-10-16 at 18 39 03

Copy link
Owner

@mittal-parth mittal-parth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

Left some comments.

  • Another point is that the name of the team looks bigger than the Team Management heading. Reduce the size as its breaking the visual hierarchy
  • Resolve conflicts

teamData.set(teamIdentifier, {
teamIdentifier,
teamLeaderAddress: data.teamLeaderAddress,
teamName: data.teamName || undefined,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
teamName: data.teamName || undefined,
teamName: data.teamName || teamIdentifier,


const attestationData = {
teamIdentifier: teamIdentifier.toString(),
teamName: (teamName || (teamIdentifier?.startsWith?.('0x') ? teamIdentifier : '')).toString(),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
teamName: (teamName || (teamIdentifier?.startsWith?.('0x') ? teamIdentifier : '')).toString(),
teamName: (teamName || (teamIdentifier).toString(),

Team storage newTeam = teams[teamId];
newTeam.owner = msg.sender;
newTeam.huntId = _huntId;
newTeam.name = _teamName;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a validation for teamName length to be not more than 20 chars

try {
const attestationData = {
teamIdentifier: teamData?.teamId?.toString() || userWallet.toString(), // team id for teams, user wallet for solo users
teamName: teamData?.name || (teamData?.teamId ? undefined : userWallet.toString()),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
teamName: teamData?.name || (teamData?.teamId ? undefined : userWallet.toString()),
teamName: teamData?.name || userWallet.toString(),

Comment on lines +643 to +659
{Number(teamData?.memberCount || 0) < Number(teamData?.maxMembers || 0) && teamData?.owner === userWallet && (
<Button
size="sm"
onClick={async () => {
try {
const teamIdStr = teamData?.teamId?.toString?.();
if (teamIdStr) {
await generateInviteAfterTeamCreation(teamIdStr);
}
} catch (e) {
toast.error('Failed to generate invite');
}
}}
>
Invite more
</Button>
)}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this.

Not having an Invite More button is meant to be.

We explicitly ask the user to save the screenshot of the QR as invites expire after a certain interval.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Support specifying team names

3 participants