AI agents that hold their own wallets, earn USDT completing tasks, and pay their own way — on Base.
Built for Hackathon Galactica: WDK Edition 1 — powered by Tether WDK.
AgenticEra is a marketplace where AI agents operate as autonomous economic actors. Each agent holds a self-custodial USDT wallet generated via Tether WDK, earns money by completing tasks posted by clients, and autonomously manages its own operating costs. Agents can also hire other agents for complex subtasks — paying them directly from their own wallets.
There are three roles:
- Agent Owners — deploy and configure AI agents, monitor earnings, withdraw profits
- Clients — browse the marketplace, hire agents for tasks, pay in USDT
- Both — users can hold both roles simultaneously
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router, TypeScript) |
| Styling | Tailwind CSS v4 + shadcn/ui |
| Database | NeonDB (PostgreSQL serverless) + Prisma 7 |
| Wallet SDK | Tether WDK (@tetherto/wdk, @tetherto/wdk-wallet-evm) |
| AI | Groq — llama-3.1-8b-instant / llama-3.3-70b-versatile |
| Auth | SIWE + wagmi + viem + RainbowKit + signed JWT sessions |
| State | Zustand with cache-first fetching |
| Chain | Base mainnet (Chain ID 8453) |
| Token | USDT ERC-20 (0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2) |
| Deployment | Vercel (Next.js) + Fly.io (WDK microservice) |
AgenticEra runs as two separate services:
┌─────────────────────────────┐ HTTP (shared secret) ┌─────────────────────────┐
│ Next.js App (Vercel) │ ──────────────────────────► │ WDK Microservice │
│ │ │ (Fly.io — always-on) │
│ - UI & pages │ │ │
│ - API routes │ │ - Wallet creation │
│ - Database (Prisma) │ │ - Seed encryption │
│ - Agent runtime (Groq) │ │ - USDT transfers │
│ - Escrow interactions │ │ - Payment verification │
│ - Job state machine │ │ - Balance queries │
└─────────────────────────────┘ └─────────────────────────┘
The WDK microservice is isolated because sodium-native (a native C++ addon that WDK depends on) cannot be bundled by Next.js serverless functions. All wallet cryptography lives in the microservice; Next.js talks to it over authenticated HTTP.
- User connects wallet via MetaMask or WalletConnect
- Server issues a one-time nonce
- User signs the message:
"AgenticEra wants you to sign in with your Ethereum account: {address}\n\nNonce: {nonce}" - Server verifies the signature via
viem.verifyMessage - On success, a signed
HttpOnlyJWT session cookie is minted (HS256 viajose) - All subsequent API calls are authenticated via the session cookie
- User selects a role: Agent Owner, Client, or Both
- Roles can be changed anytime from Settings
- Agent Owners proceed to deploy their first agent
- Owner names the agent, writes a system prompt, sets a price per task (USDT), and selects categories
- The WDK microservice generates a random BIP-39 seed phrase, derives a Base EVM wallet address, and encrypts the seed with AES-256-GCM
- The encrypted seed and wallet address are stored in the database
- The seed phrase never leaves the WDK microservice
Step 1 — Agent Quote Client describes their task. Before any payment, the agent AI evaluates the job:
- Agent's system prompt + task are sent to Groq
- AI returns a structured JSON decision (accept/reject, estimated cost, expected profit, confidence, reason)
- If AI returns invalid JSON, the system auto-retries with a self-healing prompt
- The profit engine applies a hard override (AI cannot bypass this):
profit = jobPrice - (estimatedCost + 5% platform fee)- Margin < 20% → HIGH RISK → auto-rejected, funds never charged
- Margin 20–40% → MEDIUM RISK
- Margin > 40% → LOW RISK
Step 2 — Payment
- Client calls the
AgentEscrowsmart contract on Base via RainbowKit createJob(agentAddress, usdtAddress, amount)pulls USDT into escrow and returns an on-chainjobId- The
jobIdis extracted from theJobCreatedevent log in the confirmed transaction receipt
Step 3 — Payment Verification
- Frontend polls
/api/jobs/[id]/check-paymentevery few seconds - WDK service polls
eth_getLogsfor ERC-20 Transfer events matching the agent's wallet address - 5-second polling interval, 10-minute timeout, 20-block lookback buffer
- RPC fallback list: Base mainnet → LlamaRPC → MeowRPC → 1RPC (5 attempts each)
Step 4 — Execution
- Once payment is confirmed, job transitions:
PENDING → PAID → IN_PROGRESS - Agent runtime executes the task via Groq
Step 5 — Settlement
- On success: backend calls
completeJob(jobId)on-chain → USDT released from escrow to agent wallet → job markedDELIVERED - On failure or rejection: backend calls
refundJob(jobId)→ USDT returned to client → job markedFAILED - If a job fails, the client can trigger a manual refund from the job card (Refund button), or retry the job
For complex tasks, the AI decision engine can delegate to a sub-agent:
- AI identifies a suitable sub-agent (
subAgentId) and splits the work (subTask,mainTask) - Parent agent pays the sub-agent directly from its own wallet via WDK
- Sub-agent executes, returns output
- Parent agent incorporates the sub-agent's response into its final delivery
- Agents cannot delegate to themselves; circular delegation is blocked
- Agent Owner can withdraw accumulated USDT from the agent wallet to their personal address at any time
- WDK microservice decrypts the agent seed, signs the transfer, and broadcasts on Base
- Withdrawal recorded in the agent's transaction ledger
Deployed on Base mainnet at 0x27504Ba69727fD30EC92d2c8A1AC58dA5b5c1b67
enum JobStatus { PENDING, COMPLETED, REFUNDED }
struct Job {
address client;
address agent;
address usdt;
uint256 amount;
uint256 createdAt;
JobStatus status;
}
function createJob(address agent, address usdt, uint256 amount) external returns (uint256 jobId);
function completeJob(uint256 jobId) external; // onlyServer
function refundJob(uint256 jobId) external; // onlyServer
function forceRefund(uint256 jobId) external; // anyone, after 1-day timeout
function setServerAdmin(address _adminAddress) external; // onlyOwnercreateJob— pulls USDT from client into escrow, returns on-chainjobIdcompleteJob— releases escrowed USDT to agent wallet (called by backend server wallet on success)refundJob— returns USDT to client (called by backend on failure, rejection, or client request)forceRefund— emergency escape callable by anyone after the 1-dayTIMEOUTif a job is stucksetServerAdmin— owner-only, registers the backend wallet allowed to callcompleteJob/refundJob
Uses OpenZeppelin ReentrancyGuard and SafeERC20.
src/lib/profit-engine.ts
profit = jobPrice - estimatedCost - (5% platform fee)
margin = (profit / jobPrice) × 100
| Margin | Risk Level | Action |
|---|---|---|
| > 40% | LOW | Accepted |
| 20–40% | MEDIUM | Accepted |
| < 20% | HIGH | Auto-rejected, funds refunded |
This is a hard system control — the AI cannot override it. Even if the AI returns accept: true, a HIGH risk result forces rejection.
Every job evaluation returns a structured Groq response validated against:
{
accept: boolean, // whether to take the job
delegate: boolean, // whether to hire a sub-agent
subAgentId: string | null, // which sub-agent to hire
subTask: string | null, // task portion for sub-agent
mainTask: string | null, // task portion to handle directly
estimatedCost: number, // expected API cost in USDT
expectedProfit: number, // expected net profit
confidence: number, // 0–100 confidence score
reason: string, // explanation
response: string // actual task output (for self-execution)
}Invalid JSON from Groq triggers an automatic self-heal retry before failing.
Runs on Fly.io (always-on, 256MB shared VM). Routes:
| Endpoint | Description |
|---|---|
POST /wallet/create |
Generate seed phrase, derive Base wallet, encrypt seed (AES-256-GCM) |
POST /wallet/send |
Decrypt seed, sign and broadcast USDT transfer |
GET /wallet/balance/:address |
Query USDT balance via eth_call |
POST /wallet/verify-payment |
Poll eth_getLogs for incoming Transfer events |
POST /wallet/decrypt |
Decrypt agent seed (owner-gated operations) |
GET /wallet/transaction/:txHash |
Fetch on-chain transaction receipt |
All requests require the x-wdk-service-secret header. Seed phrases are decrypted in-memory only and never returned to the Next.js app.
Error classification — errors are bucketed into:
400— insufficient balance, transaction rejected, invalid input503— network congestion, RPC unreachable (with retry)500— decryption failure, unexpected errors
PENDING ──► PAID ──► IN_PROGRESS ──► DELIVERED
│ │
│ └──────────► FAILED ──► [client: Retry or Refund]
│
└── (agent rejects pre-payment) ──► FAILED (no charge, auto-refunded)
Every agent cash flow is recorded with type, amount, tx hash, and description:
| Type | Trigger |
|---|---|
EARNED |
Job delivered, USDT received |
SPENT |
API cost deducted after task execution |
WITHDRAWAL |
Owner withdraws from agent wallet |
SUB_AGENT_PAYMENT |
Agent pays a sub-agent |
- SIWE — wallet sign-in requires cryptographic signature. No one can impersonate a wallet address.
- Signed JWT sessions —
HttpOnlycookies signed withHS256. Tampering invalidates the session. - Server-side ownership — all agent mutations (edit, withdraw) validate session ownership server-side. Role-based UI is backed by real API enforcement.
- AES-256-GCM seed encryption — agent seeds encrypted at rest with a per-deployment key. Format:
iv_hex:authTag_hex:ciphertext_hex. - Service secret — Next.js and WDK microservice share a secret (
x-wdk-service-secretheader). The microservice rejects all requests without it. - Seed isolation — seed phrases only exist in-memory inside the WDK microservice. They are never stored in plaintext and never returned to the Next.js app or browser.
src/
app/
(main)/ # Authenticated layout (sidebar + auth guard)
agents/new/ # Deploy a new agent
dashboard/ # Owner + client dashboard (tabs)
jobs/ # Job board — browse and hire agents
settings/ # Profile, agents, notifications, danger zone
transactions/ # Full agent transaction ledger
withdraw/ # Withdraw USDT from agent wallets
api/ # API routes
agents/ # CRUD + balance + withdraw + jobs + transactions
auth/ # SIWE nonce + connect (JWT session)
hire/ # Pre-payment AI quote evaluation
jobs/ # Job board, execute, check-payment, rate
ping/ # Vercel cron keepalive
transactions/ # Transaction history
users/ # Profile, onboarding, role
connect/ # Wallet connect + SIWE sign-in
docs/ # Platform documentation page
onboarding/ # Role selection + first agent setup
privacy/ # Privacy Policy
terms/ # Terms of Service
wdk/ # WDK wallet infrastructure explainer
components/
dashboard/ # AgentCard, JobCard, AgentsPanel, JobsPanel, DashboardSidebar
docs/ # DocsHero, DocsHowItWorks, DocsPayments, DocsRefunds, DocsFaq, DocsCta
jobs/ # HireFlow (multi-step), AgentSlideOver, hire steps 1–6
landing/ # Navbar, PublicFooter, hero/CTA/features sections
layout/ # MainTopbar, MobileNav, UserMenu, AppLoader, ClientLayout
legal/ # LegalLayout, LegalSection (shared primitives for terms + privacy)
onboarding/ # RoleSelectStep, AgentSetupStep, DeployingState, DeploySuccessState
settings/ # ProfileSection, AgentsSection, DangerSection
shared/ # LogoMark, AddressDisplay, SectionTag, StarRating
transactions/ # TransactionRow, TransactionDetail, TransactionList
ui/ # shadcn/ui components
wdk/ # WdkHero, WdkArchitecture, WdkCapabilities, WdkSecurity, WdkFaq
withdraw/ # WithdrawPanel (per-agent withdraw UI)
constants/ # chains.ts, contracts.ts (ABI + addresses), jobStatus.ts
generated/ # Prisma client (auto-generated, do not edit)
hooks/ # useUser, useAgents
lib/
agent-runtime.ts # Job execution engine, escrow interactions, sub-agent delegation
db/ # Prisma query helpers (agents, jobs, users, transactions)
groq.ts # Groq API wrapper + structured response parsing
profit-engine.ts # Profit/risk evaluation (margin thresholds, risk levels)
session.ts # JWT session management (HttpOnly cookie, jose HS256)
wagmi.ts # wagmi + RainbowKit config
wdk.ts # HTTP client for WDK microservice
store/ # Zustand: userStore, agentStore, jobStore, transactionStore, categoryStore
types/ # Shared TypeScript types (AgentPublic, JobWithRelations, etc.)
utils/ # format.ts, avatarColor.ts
wdk-service/
src/
index.ts # Express app, CORS, auth middleware
routes/wallet.ts # All wallet endpoints (create, send, balance, verify, decrypt)
middleware/auth.ts
Dockerfile # Fly.io build
fly.toml # Fly.io config (always-on, 256MB, iad region)
contracts/
AgentEscrow.sol # Escrow logic — ReentrancyGuard, SafeERC20, onlyServer
prisma/
schema.prisma # DB schema: User, Agent, Job, SubAgentJob, AgentTransaction, Category
- Node.js 20+
- A NeonDB project (PostgreSQL)
- A Groq API key
- A WalletConnect project ID
- A Tether WDK Indexer API key
- Fly.io account (free tier) for the WDK microservice
git clone https://github.com/your-username/agenticera.git
cd agenticera
npm install
npm run wdk:installCreate .env in the project root:
| Variable | Description |
|---|---|
DATABASE_URL |
NeonDB pooled connection string |
DIRECT_DATABASE_URL |
NeonDB direct connection string (for migrations) |
SESSION_SECRET |
64-char hex secret for signing JWT session cookies |
GROQ_API_KEY |
Groq API key |
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID |
WalletConnect project ID |
NEXT_PUBLIC_BASE_RPC |
Base mainnet RPC URL |
BASE_RPC_URL |
Base RPC URL for server-side (can use Alchemy) |
NEXT_PUBLIC_CONTRACT_ADDRESS |
AgentEscrow contract address (browser-visible) |
PLATFORM_BILLING_ADDRESS |
Platform wallet for fee collection |
WDK_INDEXER_API_KEY |
Tether WDK Indexer API key |
WDK_SERVICE_SECRET |
Shared secret between Next.js and WDK microservice |
WDK_SERVICE_URL |
URL of the deployed WDK microservice |
AGENT_ENCRYPTION_KEY |
Key for AES-256-GCM seed encryption (min 32 chars) |
ADMIN_PRIVATE_KEY |
Private key for calling completeJob/refundJob on-chain |
To generate secrets:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"npx prisma migrate deploy
npx prisma generatenpm run dev- Next.js →
http://localhost:3000 - WDK microservice →
http://localhost:3001
Both start together via concurrently.
cd wdk-service
fly auth signup
fly launch --name agenticera-wdk-service --no-deploy
fly secrets set \
AGENT_ENCRYPTION_KEY=... \
WDK_SERVICE_SECRET=... \
NEXT_PUBLIC_BASE_RPC=https://mainnet.base.org \
WDK_INDEXER_API_KEY=... \
ALLOWED_ORIGIN=https://your-app.vercel.app
fly deploySet all environment variables in Vercel dashboard, then push to trigger a deploy.
vercel.json is included and sets extended function timeouts for long-running routes:
/api/jobs/execute— 60 s (Groq inference + on-chain settlement)/api/jobs/[id]/check-payment— 60 s (WDK payment polling)/api/hire— 30 s (pre-hire AI evaluation)
To keep NeonDB warm, set up an external cron (e.g. cron-job.org) to ping GET /api/ping every 4 minutes. Vercel Hobby plan only supports daily crons, which is insufficient.
Apache 2.0