Skip to content
Open
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
6 changes: 0 additions & 6 deletions app/.eslintrc.json

This file was deleted.

25 changes: 16 additions & 9 deletions app/WARP.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,24 @@ pnpm lint
## Architecture

### Tech Stack
- **Framework**: Next.js (Pages Router)
- **Styling**: styled-components v6 with ThemeProvider
- **Font**: IBM Plex Mono (loaded via next/font)
- **Framework**: Next.js (App Router)
- **Styling**: styled-components v6 with ThemeProvider; `compiler: { styledComponents: true }` in `next.config.js`
- **Linting**: ESLint 9 flat config (`eslint.config.mjs`) with `eslint-config-next/core-web-vitals`
- **Font**: IBM Plex Mono (loaded via next/font in root layout)
- **React**: v19

### Project Structure
- `src/pages/` - Next.js pages (`_app.tsx` wraps all pages with theme/layout)
- `src/app/` - Next.js App Router
- `layout.tsx` - Root server layout; exports `metadata`/`viewport`; renders `<Providers>`
- `providers.tsx` - Client component wrapping `Web3Provider`, `ThemeProvider`, `GlobalStyle`, `Layout`
- `page.tsx` - Home page
- `mint/page.tsx` - Mint page (`"use client"`)
- `src/components/` - Reusable styled components
- `theme.tsx` - Design tokens (colors, fontSizes, lineHeights, borderRadius)
- `typography.tsx` - Text components (H1, H2, H3, P, etc.)
- `layout.tsx` - Main layout wrapper with Navbar and Footer
- `wrappers.tsx` - Layout containers
- `typography.tsx` - Text components (H1, H2, H3, P)
- `Layout.tsx` - Main layout wrapper with Navbar and Footer
- `wrappers.tsx` - Layout containers (FullWidth, Nav, Main, Heading, Content, StyledTable, Footer)
- `mint.tsx` - Mint-page styled components (MintLayout, Panel, StatusBox, ResponseBlock)

### Theming

Expand Down Expand Up @@ -81,7 +87,8 @@ The theme is defined in `src/components/theme.tsx` and typed in `styled.d.ts`. A
- Buttons → `buttons.tsx`
- Form inputs, labels, field layouts → `forms.tsx`
- Typography (headings, paragraphs) → `typography.tsx`
- Layout containers, wrappers, panels → `wrappers.tsx`
- Layout containers, generic wrappers → `wrappers.tsx`
- Mint-page containers (MintLayout, Panel, StatusBox, ResponseBlock) → `mint.tsx`
- Global styles → `globalstyle.tsx`
- Theme definition → `theme.tsx`
- Sample/seed data → `src/samples/`
Expand All @@ -93,7 +100,7 @@ The theme is defined in `src/components/theme.tsx` and typed in `styled.d.ts`. A
- Tab indentation, double quotes for strings.
- Named exports for styled-component files; `default export` for page/component files with logic.
- Use `type` keyword for type-only exports (`export type { ... }`).
- Keep page components in `src/pages/`, reusable UI in `src/components/`, config in `src/config/`, sample data in `src/samples/`.
- Keep page components in `src/app/`, reusable UI in `src/components/`, config in `src/config/`, sample data in `src/samples/`.
- Avoid creating `features/` directories for styled components or config — those belong in `components/` and `config/` respectively.

### Solidity
Expand Down
16 changes: 16 additions & 0 deletions app/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import prettier from "eslint-config-prettier";

const eslintConfig = defineConfig([
...nextVitals,
prettier,
{
rules: {
"react/no-unescaped-entities": "off",
},
},
globalIgnores([".next/**", "out/**", "build/**", "next-env.d.ts"]),
]);

export default eslintConfig;
2 changes: 1 addition & 1 deletion app/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
import "./.next/dev/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
5 changes: 1 addition & 4 deletions app/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8293';

const nextConfig = {
reactStrictMode: true,
turbopack: {},
compiler: {
styledComponents: true,
},
turbopack: {},
async rewrites() {
return [
{
Expand All @@ -26,9 +26,6 @@ const nextConfig = {
config.externals = [
...(Array.isArray(config.externals) ? config.externals : config.externals ? [config.externals] : []),
'@react-native-async-storage/async-storage',
'@gemini-wallet/core',
'porto',
'porto/internal',
];
return config;
},
Expand Down
35 changes: 17 additions & 18 deletions app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tap-app",
"version": "4.0.0",
"version": "0.2.1",
"private": true,
"license": "UNLICENSED",
"description": "TAP's frontend",
Expand All @@ -17,29 +17,28 @@
"generate:wagmi": "wagmi generate"
},
"dependencies": {
"@reown/appkit": "^1.8.17",
"@reown/appkit-adapter-wagmi": "^1.8.17",
"@tanstack/react-query": "^5.90.20",
"@wagmi/core": "^3.3.2",
"@reown/appkit": "^1.8.18",
"@reown/appkit-adapter-wagmi": "^1.8.18",
"@tanstack/react-query": "^5.90.21",
"@wagmi/core": "^3.4.0",
"next": "^16.1.6",
"next-sitemap": "^4.2.3",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-gtm-module": "^2.0.11",
"styled-components": "^6.3.8",
"viem": "^2.45.1",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"styled-components": "^6.3.11",
"viem": "^2.46.3",
"wagmi": "3.4.2"
},
"devDependencies": {
"@types/node": "^22.0.0",
"@types/react": "^19.1.0",
"@types/react-dom": "^19.1.0",
"@wagmi/cli": "^2.9.0",
"eslint": "^9.37.0",
"@types/node": "^22.19.13",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@wagmi/cli": "^2.10.0",
"eslint": "^9.39.3",
"eslint-config-next": "^16.1.6",
"eslint-config-prettier": "^9.1.0",
"prettier": "^3.4.0",
"typescript": "^5.7.0"
"eslint-config-prettier": "^9.1.2",
"prettier": "^3.8.1",
"typescript": "^5.9.3"
},
"browserslist": [
"defaults",
Expand Down
4 changes: 2 additions & 2 deletions app/public/sitemap-0.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url><loc>https://transferagentprotocol.xyz</loc><lastmod>2026-02-08T01:07:33.911Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
<url><loc>https://transferagentprotocol.xyz/mint</loc><lastmod>2026-02-08T01:07:33.912Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
<url><loc>https://transferagentprotocol.xyz/mint</loc><lastmod>2026-03-01T17:15:08.009Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
<url><loc>https://transferagentprotocol.xyz</loc><lastmod>2026-03-01T17:15:08.010Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
</urlset>
60 changes: 60 additions & 0 deletions app/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { Metadata, Viewport } from "next";
import { IBM_Plex_Mono } from "next/font/google";
import Providers from "./providers";

const plex = IBM_Plex_Mono({
weight: ["400", "500", "600", "700"],
style: ["normal", "italic"],
subsets: ["latin-ext"],
display: "swap",
preload: true,
});

export const metadata: Metadata = {
authors: [{ name: "Transfer Agent Protocol" }],
description:
"Mint equity cap tables onchain. Open-source and regulatory compliant infrastructure for tokenized capital markets and transfer agents.",
title: "Transfer Agent Protocol",
openGraph: {
type: "website",
siteName: "Transfer Agent Protocol",
url: "https://transferagentprotocol.xyz",
title: "Transfer Agent Protocol",
description:
"Mint equity cap tables onchain. Open-source and regulatory compliant infrastructure for tokenized capital markets and transfer agents.",
},
alternates: {
canonical: "https://transferagentprotocol.xyz",
},
manifest: "/manifest.json",
icons: {
icon: [
{ url: "/favicon.ico" },
{ url: "/favicon-16x16.png", sizes: "16x16", type: "image/png" },
{ url: "/favicon-32x32.png", sizes: "32x32", type: "image/png" },
],
apple: [{ url: "/icons/apple-touch-icon.png", sizes: "180x180" }],
other: [{ rel: "mask-icon", url: "/safari-pinned-tab.svg" }],
},
other: {
"msapplication-TileColor": "#0c0b0c",
},
};

export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
minimumScale: 1,
userScalable: true,
themeColor: "#fafafc",
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body className={plex.className}>
<Providers>{children}</Providers>
</body>
</html>
);
}
12 changes: 7 additions & 5 deletions app/src/pages/mint.tsx → app/src/app/mint/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { H2, P } from "../components/typography";
import { MintLayout, Panel } from "../components/wrappers";
import { IssuerForm } from "../components/IssuerForm";
import { MintActions } from "../components/MintActions";
import { useMintIssuer } from "../hooks/useMintIssuer";
"use client";

import { H2, P } from "../../components/typography";
import { MintLayout, Panel } from "../../components/mint";
import { IssuerForm } from "../../components/IssuerForm";
import { MintActions } from "../../components/MintActions";
import { useMintIssuer } from "../../hooks/useMintIssuer";

export default function MintPage() {
const mint = useMintIssuer();
Expand Down
6 changes: 3 additions & 3 deletions app/src/pages/index.tsx → app/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Link from "next/link";
"use client";

import { Heading, Content, StyledTable } from "../components/wrappers";
import { H1, H2, P } from "../components/typography";

Expand All @@ -8,9 +9,8 @@ export default function Home() {
<Heading>
<H1>Onchain cap tables.</H1>
<P>Tokenize RWAs, and handle post-trade settlement.</P>
<P>Based on the <a href="https://www.opencaptablecoalition.com/" target="_blank">Open Cap Table</a> data format, transfer agent protocol is being used by SEC-registered entities.</P>
<P>Based on the <a href="https://www.opencaptablecoalition.com/" target="_blank" rel="noopener noreferrer">Open Cap Table</a> data format, transfer agent protocol is being used by SEC-registered entities.</P>
<a href="https://paragraph.com/@thatalexpalmer/rwa-tokenization-protocol-stack-1" target="_blank" rel="noopener">Read why this exists</a>
Comment on lines +12 to 13
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit (Low): Inconsistent rel attributes on external links. Line 12 has rel="noopener noreferrer" (good), but line 13 only has rel="noopener" (missing noreferrer). Several links on lines 19 and 31 have target="_blank" with no rel at all. Worth standardizing to rel="noopener noreferrer" on all target="_blank" links.

Note: this inconsistency pre-dates this PR, but since the file was touched it's a good opportunity to clean up.


</Heading>
<H2>
Demo Deployments:
Expand Down
36 changes: 36 additions & 0 deletions app/src/app/providers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use client";

import { useEffect } from "react";
import { ThemeProvider } from "styled-components";
import theme from "../components/theme";
import GlobalStyle from "../components/globalstyle";
import Layout from "../components/Layout";
import Web3Provider from "../config/Web3Provider";

export default function Providers({ children }: { children: React.ReactNode }) {
// Suppress unhandled rejections from wallet SDK analytics blocked by ad blockers
useEffect(() => {
const handler = (e: PromiseRejectionEvent) => {
const msg = e.reason?.message || String(e.reason || "");
if (
msg.includes("Failed to fetch") ||
msg.includes("ERR_BLOCKED_BY_CLIENT") ||
Comment on lines +15 to +17
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution (Low): The "Failed to fetch" pattern is very broad — it will silently swallow any unhandled fetch rejection in the entire app, not just wallet analytics. This could mask real network errors (e.g. failed API calls to your server, failed contract reads).

Consider narrowing by also checking the error's stack or combining it with the other wallet-specific patterns:

if (
  msg.includes("ERR_BLOCKED_BY_CLIENT") ||
  msg.includes("AnalyticsSDK") ||
  msg.includes("pulse.walletconnect") ||
  (msg.includes("Failed to fetch") && (msg.includes("walletconnect") || msg.includes("reown")))
) {

Not blocking, but worth tightening if you notice swallowed errors during development.

msg.includes("AnalyticsSDK") ||
msg.includes("pulse.walletconnect")
) {
e.preventDefault();
}
};
window.addEventListener("unhandledrejection", handler);
return () => window.removeEventListener("unhandledrejection", handler);
}, []);

return (
<Web3Provider>
<ThemeProvider theme={theme}>
<GlobalStyle />
<Layout>{children}</Layout>
</ThemeProvider>
</Web3Provider>
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Consider (medium priority): The migration replaces the ServerStyleSheet-based SSR from the old _document.tsx with just the SWC compiler: { styledComponents: true } setting. That config handles display names and deterministic class generation, but the official Next.js App Router + styled-components docs recommend additionally creating a StyledComponentsRegistry using useServerInsertedHTML to properly collect and inject styles during React streaming SSR.

Without it, you may see a Flash of Unstyled Content (FOUC) in production — especially on first load or slow connections. If your build and dev testing look fine today, this is likely fine to ship, but keep it in mind as the root cause if FOUC is ever reported.

File renamed without changes.
2 changes: 1 addition & 1 deletion app/src/components/MintActions.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { MintButton } from "./buttons";
import { StatusBox, ResponseBlock } from "./wrappers";
import { StatusBox, ResponseBlock } from "./mint";
import { FieldGroup, FieldLabel } from "./forms";
import type { IssuerResponse } from "../services/registerIssuer";

Expand Down
6 changes: 4 additions & 2 deletions app/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"use client";

import styled from "styled-components";
import dynamic from "next/dynamic";
import { Nav } from "./wrappers";
import { LogoRouter, StyledA, WalletButtonStyled } from "./buttons";
import Link from "next/link";
import Image from "next/image";
import { useRouter } from "next/router";
import { usePathname } from "next/navigation";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug (Medium): With this migration to next/navigation, the external links further down (lines 37/40) still use <Link> for external URLs. Next.js <Link> with target="_blank" silently ignores the target — clicks navigate away instead of opening a new tab. Those should be plain <a> tags with rel="noopener noreferrer":

<a href="https://docs.transferagentprotocol.xyz" target="_blank" rel="noopener noreferrer">Docs</a>
<a href="https://github.com/transfer-agent-protocol/tap-cap-table" target="_blank" rel="noopener noreferrer">Github</a>

The Link import can also be removed if it's only used for the internal / route via LogoRouter (check if that's the case).


const NavActions = styled.span`
display: flex;
Expand All @@ -20,7 +22,7 @@ const WalletButton = dynamic(() => import("./WalletButtonClient"), {
});

export default function Navbar() {
const { pathname } = useRouter();
const pathname = usePathname();
const showWallet = pathname === "/mint";

return (
Expand Down
28 changes: 2 additions & 26 deletions app/src/components/buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import styled from "styled-components";
const LogoRouter = styled.button`
position: relative;
display: inline-flex;
flex-direction: row nowrap;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
min-width: 48px;
Expand Down Expand Up @@ -43,30 +43,6 @@ const StyledA = styled.button`
}
`;

const PrimaryButton = styled.button`
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
background: ${({ theme }) => theme.colors.main};
width: 9.875rem;
height: 3rem;
margin: 2rem 0 1rem 0;
border: none;
box-sizing: border-box;
border-radius: 4px;
font-size: 1rem;
font-weight: bold;
font-family: "IBM Plex Mono", sans-serif;
color: #FFFFFF;
cursor: pointer;
transition: all 0.168s cubic-bezier(0.211, 0.69, 0.313, 1);

&:hover {
opacity: 0.9;
}
`;

const WalletButtonStyled = styled.button`
display: flex;
flex-flow: row nowrap;
Expand Down Expand Up @@ -119,4 +95,4 @@ const MintButton = styled.button`
}
`;

export { LogoRouter, StyledA, PrimaryButton, WalletButtonStyled, MintButton }
export { LogoRouter, StyledA, WalletButtonStyled, MintButton }
Loading
Loading