diff --git a/.env.example b/.env.example
index f5eef2c..a73c123 100644
--- a/.env.example
+++ b/.env.example
@@ -1,7 +1,7 @@
# Supabase
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY=
-SUPABASE_SERVICE_ROLE_KEY=
+SUPABASE_SECRET_KEY=
# Circle
CIRCLE_API_KEY=
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8895994..10df36d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,12 +18,12 @@ jobs:
- name: Install dependencies
run: npm install
- scan:
- needs: lint-and-test
- if: ${{ github.event_name == 'pull_request' && github.event.repository.private == false }}
- uses: circlefin/circle-public-github-workflows/.github/workflows/pr-scan.yaml@v1
+ # scan:
+ # needs: lint-and-test
+ # if: ${{ github.event_name == 'pull_request' && github.event.repository.private == false }}
+ # uses: circlefin/circle-public-github-workflows/.github/workflows/pr-scan.yaml@v1
- release-sbom:
- needs: lint-and-test
- if: github.event_name == 'push'
- uses: circlefin/circle-public-github-workflows/.github/workflows/attach-release-assets.yaml@v1
\ No newline at end of file
+ # release-sbom:
+ # needs: lint-and-test
+ # if: github.event_name == 'push'
+ # uses: circlefin/circle-public-github-workflows/.github/workflows/attach-release-assets.yaml@v1
\ No newline at end of file
diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 0000000..2bd5a0a
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+22
diff --git a/README.md b/README.md
index a2a1ea4..50ca285 100644
--- a/README.md
+++ b/README.md
@@ -4,13 +4,20 @@ Integrate USDC as a payment method for purchasing credits on Arc. This sample ap
+## Table of Contents
+
+- [Prerequisites](#prerequisites)
+- [Clone and Run Locally](#clone-and-run-locally)
+- [Environment Variables](#environment-variables)
+- [User Accounts](#user-accounts)
+
## Prerequisites
-- Node.js 20.x or newer
-- npm (automatically installed when Node.js is installed)
-- Docker (for running Supabase locally)
-- [ngrok](https://ngrok.com/) (for local webhook testing)
-- Circle Developer Controlled Wallets [API key](https://console.circle.com/signin) and [Entity Secret](https://developers.circle.com/wallets/dev-controlled/register-entity-secret)
+- **Node.js v22+** — Install via [nvm](https://github.com/nvm-sh/nvm) (`nvm use` will read the `.nvmrc` file)
+- **Supabase CLI** — Install via `npm install -g supabase` or see [Supabase CLI docs](https://supabase.com/docs/guides/cli/getting-started)
+- **Docker Desktop** (only if using the local Supabase path) — [Install Docker Desktop](https://www.docker.com/products/docker-desktop/)
+- **[ngrok](https://ngrok.com/)** - for local webhook testing)
+- Circle Developer Controlled Wallets **[API key](https://console.circle.com/signin)** and **[Entity Secret](https://developers.circle.com/wallets/dev-controlled/register-entity-secret)**
## Getting Started
@@ -22,37 +29,44 @@ Integrate USDC as a payment method for purchasing credits on Arc. This sample ap
npm install
```
-2. Create a `.env.local` file in the project root:
+2. Set up the database — Choose one of the two paths below:
+
+
+ Path 1: Local Supabase (Docker)
+
+ Requires Docker Desktop installed and running.
```bash
- cp .env.example .env.local
+ npx supabase start
+ npx supabase migration up
```
- Required variables:
+ The output of `npx supabase start` will display the Supabase URL and API keys needed in the next step.
+
+
+
+
+ Path 2: Remote Supabase (Cloud)
+
+ Requires a [Supabase](https://supabase.com/) account and project.
```bash
- # Supabase
- NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
- NEXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY=your_anon_key
- SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
-
- # Circle
- CIRCLE_API_KEY=your_circle_api_key
- CIRCLE_ENTITY_SECRET=your_entity_secret
- CIRCLE_BLOCKCHAIN=ARC-TESTNET
- CIRCLE_USDC_TOKEN_ID=15dc2b5d-0994-58b0-bf8c-3a0501148ee8
-
- # Misc
- ADMIN_EMAIL=admin@admin.com
+ npx supabase link --project-ref
+ npx supabase db push
```
-3. Start Supabase locally:
+ Retrieve your project URL and API keys from the Supabase dashboard under **Settings → API**.
+
+
+
+3. Set up environment variables:
```bash
- npx supabase start
- npx supabase migration up
+ cp .env.example .env.local
```
+ Then edit `.env.local` and fill in all required values. Use the Supabase URL and keys from the previous step's output (see [Environment Variables](#environment-variables) section below).
+
4. Start the development server:
```bash
@@ -69,10 +83,10 @@ Integrate USDC as a payment method for purchasing credits on Arc. This sample ap
ngrok http 3000
```
- Add the webhook endpoint in Circle Console:
+ Copy the HTTPS URL from ngrok (e.g., `https://your-ngrok-url.ngrok.io`) and add it to your Circle Console webhooks section:
- Navigate to Circle Console → Webhooks
- - Add endpoint: `https://your-ngrok-url.ngrok.io/api/circle/webhook`
- - Keep ngrok running to receive webhook events
+ - Add a new webhook endpoint: `https://your-ngrok-url.ngrok.io/api/circle/webhook`
+ - Keep ngrok running while developing to receive webhook events
## How It Works
@@ -84,26 +98,54 @@ Integrate USDC as a payment method for purchasing credits on Arc. This sample ap
## Environment Variables
+Copy `.env.example` to `.env.local` and fill in the required values:
+
+```bash
+# Supabase
+NEXT_PUBLIC_SUPABASE_URL=
+NEXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY=
+SUPABASE_SECRET_KEY=
+
+# Circle
+CIRCLE_API_KEY=
+CIRCLE_ENTITY_SECRET=
+CIRCLE_BLOCKCHAIN=ARC-TESTNET
+CIRCLE_USDC_TOKEN_ID=
+
+# Misc
+ADMIN_EMAIL=admin@admin.com
+```
+
| Variable | Scope | Purpose |
| ------------------------------------- | ----------- | ------------------------------------------------------------------------ |
-| `NEXT_PUBLIC_SUPABASE_URL` | Public | Supabase project URL |
-| `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY` | Public | Supabase anonymous/public key |
-| `SUPABASE_SERVICE_ROLE_KEY` | Server-side | Service role for privileged database operations |
-| `CIRCLE_API_KEY` | Server-side | Circle API key for webhook signature verification |
-| `CIRCLE_ENTITY_SECRET` | Server-side | Circle entity secret for wallet operations |
-| `CIRCLE_BLOCKCHAIN` | Server-side | Blockchain network identifier (e.g., "ARC-TESTNET") |
-| `CIRCLE_USDC_TOKEN_ID` | Server-side | USDC token ID for the specified blockchain |
-| `ADMIN_EMAIL` | Server-side | Email address that determines which user gets admin dashboard access |
+| `NEXT_PUBLIC_SUPABASE_URL` | Public | Supabase project URL. |
+| `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY` | Public | Supabase anonymous/public key. |
+| `SUPABASE_SECRET_KEY` | Server-side | Secret key for privileged writes (e.g., transaction inserts). |
+| `CIRCLE_API_KEY` | Server-side | Used to fetch Circle webhook public keys for signature verification. |
+| `CIRCLE_ENTITY_SECRET` | Server-side | Circle entity secret for wallet operations. |
+| `CIRCLE_BLOCKCHAIN` | Server-side | Blockchain network identifier (e.g., "ARC-TESTNET"). |
+| `CIRCLE_USDC_TOKEN_ID` | Server-side | USDC token ID for the specified blockchain. Pre-filled for ARC-TESTNET. |
+| `ADMIN_EMAIL` | Server-side | Admin user email address. |
+
+## User Accounts
+
+### Admin Account
+
+On first startup, an admin user is automatically created with the following credentials:
+
+- **Email:** `admin@admin.com`
+- **Password:** `123456`
+
+The admin account has access to the **Admin Dashboard**, which provides an overview of all users, wallets, and transactions in the system.
-## Usage Notes
+Regular users who sign up will see the **User Dashboard**, which allows them to purchase credits with USDC and view their own transaction history.
-- Designed for Arc testnet only
-- Requires valid Circle API credentials and Supabase configuration
-- Admin wallet must have sufficient USDC and gas fees
-- ngrok (or similar) required for local webhook testing
+### Signup Rate Limits
-## Scripts
+Supabase limits email signups to **2 per hour** by default (unless custom SMTP is configured). If you hit an "email rate limit exceeded" error during testing:
+- **Local Supabase (Docker):** Email verification is handled by the built-in [Inbucket](http://127.0.0.1:54324) mail server — check it to confirm signups. The rate limit can be adjusted in `supabase/config.toml` under `[auth.rate_limit]`.
+- **Remote Supabase (Cloud):** Use real email addresses (disposable emails may fail verification). If you hit the limit, you can manually add users via the Supabase dashboard under **Authentication → Users**.
- `npm run dev`: Start Next.js development server with auto-reload
- `npx supabase start`: Start local Supabase instance
- `npx supabase migration up`: Apply database migrations
@@ -116,4 +158,4 @@ This sample application:
- Verifies webhook signatures for security
- Is not intended for production use without modification
-See `SECURITY.md` for vulnerability reporting guidelines. Please report issues privately via Circle's bug bounty program.
\ No newline at end of file
+See `SECURITY.md` for vulnerability reporting guidelines. Please report issues privately via Circle's bug bounty program.
diff --git a/app/api/circle/webhook/route.ts b/app/api/circle/webhook/route.ts
index 150ec75..37b64c8 100644
--- a/app/api/circle/webhook/route.ts
+++ b/app/api/circle/webhook/route.ts
@@ -21,8 +21,6 @@ import { NextRequest, NextResponse } from "next/server";
import { supabaseAdminClient } from "@/lib/supabase/admin-client";
import { circleDeveloperSdk } from "@/lib/circle/developer-controlled-wallets-client";
import { convertToSmallestUnit } from "@/lib/utils/convert-to-smallest-unit";
-import { encodeFunctionData } from "viem";
-import type { Abi } from "viem";
import {
CHAIN_IDS_TO_MESSAGE_TRANSMITTER,
CHAIN_IDS_TO_TOKEN_MESSENGER,
@@ -418,7 +416,7 @@ async function updateAdminTransactionStatus(
});
if (insertError) {
- if ((insertError as any).code === "23505") {
+ if ("code" in insertError && insertError.code === "23505") {
// Unique constraint hit (e.g., idempotency_key). Treat as success and continue.
console.log("[CCTP] Mint insert deduped by unique constraint.");
} else {
diff --git a/app/dashboard/[txHash]/page.tsx b/app/dashboard/[txHash]/page.tsx
index 4c818d6..04494c0 100644
--- a/app/dashboard/[txHash]/page.tsx
+++ b/app/dashboard/[txHash]/page.tsx
@@ -203,7 +203,7 @@ export default async function TransactionDetailsPage(
{event.new_status.charAt(0).toUpperCase() + event.new_status.slice(1)}
- {format(new Date(event.created_at), "PP")}
+ {format(new Date(event.created_at), "PPpp")}
))
) : (
diff --git a/app/dashboard/layout.tsx b/app/dashboard/layout.tsx
index 007fb03..69d0c21 100644
--- a/app/dashboard/layout.tsx
+++ b/app/dashboard/layout.tsx
@@ -36,7 +36,7 @@ export default function ProtectedLayout({
- arc-commerce
+ Arc Commerce
{/* The wallet buttons have been removed from here */}
@@ -51,4 +51,4 @@ export default function ProtectedLayout({
);
-}
\ No newline at end of file
+}
diff --git a/app/layout.tsx b/app/layout.tsx
index e5c3eff..db59ea4 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -30,7 +30,7 @@ const defaultUrl = process.env.VERCEL_URL
export const metadata: Metadata = {
metadataBase: new URL(defaultUrl),
title: "Arc Commerce",
- description: "Platform credit purchases using USDC and Circle Wallets",
+ description: "Purchase platform credits using USDC on Arc.",
};
export default function RootLayout({
@@ -58,4 +58,4 @@ export default function RootLayout({