An autonomous fractional-reserve bank and financial intermediary for the agent economy, built for the Nevermined Autonomous Business Hackathon (March 5–6, 2026).
AgentBank accepts USDC deposits (via Nevermined plans), extends credit lines to agents that need working capital, and earns revenue by proxying upstream services at a markup — buying from seller agents on behalf of borrowers and pocketing the spread.
AgentBank implements real banking primitives adapted for the agent economy:
- Fractional reserve model — 20% reserve ratio, lends out the rest
- Capital adequacy enforcement — Basel-inspired minimum CAR of 12%
- Expected Credit Loss (ECL) provisioning — PD × LGD × EAD per loan
- Double-entry ledger — every transaction produces balanced debits and credits
- Dynamic interest rate pricing — cost of funds + risk premium + target margin
- Proxy intermediary layer — buys upstream services with USDC, resells to borrowers at markup via credit plan
- Collateralized lending — borrowers lock credits as collateral before receiving funds
- Solvency-gated redemptions — USDC redemptions approved only if bank stays above minimum reserve ratios
The credit peg is fixed: 1 credit = 0.01 USDC.
| Layer | Technology |
|---|---|
| Framework | Next.js 14 (App Router) |
| Language | TypeScript (strict mode) |
| Database | Neon (serverless Postgres) |
| ORM | Drizzle ORM |
| Payments | Nevermined SDK (@nevermined-io/payments) |
| On-chain | viem (Base testnet) |
| AI | OpenAI |
| Validation | Zod |
| Testing | Vitest |
| Styling | Tailwind CSS |
- Node.js 18+
- A Neon database
- A Nevermined API key and plan DID
- A Base testnet wallet with USDC
npm installCopy .env.example to .env.local and fill in your values:
cp .env.example .env.local| Variable | Description |
|---|---|
NVM_API_KEY |
Nevermined API key |
NVM_ENVIRONMENT |
testing or production |
NVM_PLAN_DID |
DID of your Nevermined credit plan (backward compat, uses Starter tier) |
NVM_PLAN_DID_STARTER |
DID for Starter tier (100 credits / $1.00 USDC) |
NVM_PLAN_DID_GROWTH |
DID for Growth tier (500 credits / $4.50 USDC) |
NVM_PLAN_DID_PRO |
DID for Pro tier (1000 credits / $8.00 USDC) |
NVM_AGENT_DID |
DID of your Nevermined agent (for observability) |
NEON_DATABASE_URL |
Neon Postgres connection string |
OPENAI_API_KEY |
OpenAI API key (for credit scoring LLM) |
USDC_CONTRACT_ADDRESS |
USDC contract on Base testnet |
BANK_WALLET_PRIVATE_KEY |
Bank's wallet private key (for USDC transfers) |
BANK_WALLET_ADDRESS |
Bank's wallet address |
npm run db:generate # generate migration files
npm run db:migrate # apply migrations to Neonnpm run devnpm test # single run
npm run test:watch # watch modeagentbank/
├── app/ # Next.js App Router
│ ├── api/ # API route handlers
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
│
├── db/ # Database layer
│ ├── schema.ts # Drizzle table definitions (7 tables)
│ ├── index.ts # Neon connection + Drizzle instance
│ └── __tests__/
│ └── schema.test.ts
│
├── lib/ # Core business logic
│ ├── types.ts # Zod schemas + TypeScript types
│ ├── config.ts # Risk limits, rate bounds, tier config, chart of accounts
│ ├── ledger.ts # Double-entry ledger (journal entries, trial balance, balance sheet)
│ ├── rate-engine.ts # Interest rate and deposit yield calculations
│ ├── risk-engine.ts # CAR, ECL, LCR, reserve ratio, stress testing
│ ├── banking-engine.ts # Loan issuance, repayment, default, deposits, withdrawals
│ ├── redemption-engine.ts # USDC redemption with solvency gates
│ ├── credit-scoring.ts # Credit scoring with external data acquisition
│ ├── provider-registry.ts # Data provider registry and ROI tracking
│ ├── proxy-service.ts # Proxy service layer (buy upstream, sell at markup)
│ ├── nevermined.ts # Nevermined SDK wrapper (mint, burn, balance, x402)
│ └── __tests__/ # Unit tests for all lib modules
│
├── PRD/ # Product requirements
│ ├── agent-bank-prd.md # Full PRD with banking model, risk framework, API contracts
│ └── agent-user-journey.md # User journey flows
│
├── ImplementationPlans/ # TDD implementation plan
│ └── agentbank-tdd-implementation-plan.md
│
├── scripts/
│ ├── register-plan.mjs # Register Nevermined credit plan
│ └── validate-env.mjs # Validate environment variables
│
├── .env.example # Environment variable template
├── drizzle.config.ts # Drizzle ORM configuration
├── next.config.js
├── tsconfig.json # Strict TypeScript config
├── vitest.config.ts
└── CHANGELOG.md # Full change history with test counts
Seven tables managed by Drizzle ORM:
| Table | Purpose |
|---|---|
agents |
Agent profiles, credit scores, tiers, blacklist status |
loans |
Active and historical loans with collateral and ECL data |
deposits |
Agent deposits with yield accrual |
redemptions |
USDC redemption records |
journal_entries |
Append-only double-entry ledger |
proxy_transactions |
Proxy service call records |
bank_snapshots |
Periodic balance sheet snapshots |
Credit tiers (based on credit score):
| Tier | Score | Rate | Max Loan | Collateral |
|---|---|---|---|---|
| A | 80–100 | 15% | 500 credits | 10% |
| B | 60–79 | 20% | 200 credits | 20% |
| C | 40–59 | 27% | 50 credits | 30% |
| Rejected | 0–39 | — | — | — |
Key risk limits (hard constraints, never violated):
- Reserve ratio ≥ 20%
- Capital Adequacy Ratio ≥ 12%
- Liquidity Coverage Ratio ≥ 100%
- Single borrower ≤ 25% of loan book
- Loan-to-deposit ratio ≤ 80%
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/health |
None | Balance sheet, key ratios, bank status |
| GET | /api/rates |
None | Current lending rates and deposit yield |
| GET | /api/stats |
None | Aggregate statistics |
| GET | /api/balance/[agentId] |
None | Agent's loans, deposits, redeemable balance |
| GET | /api/pricing |
None | Service pricing and USDC peg rate |
| POST | /api/deposit |
x402 | Accept a deposit |
| POST | /api/withdraw |
x402 | Withdraw a deposit |
| POST | /api/loan/apply |
x402 | Apply for a loan |
| POST | /api/loan/repay |
x402 | Repay a loan |
| POST | /api/redeem |
x402 | Redeem credits for USDC |
| POST | /api/proxy |
x402 | Proxy a service request |
Deployments are fully automated via GitHub Actions (.github/workflows/ci-cd.yml). Direct CLI deploys to production are not permitted.
Pipeline (push to master):
lint + test → deploy (Vercel) → db:push (Neon) → health check
Pipeline (pull request):
lint + test → build verification
| Secret | Description |
|---|---|
VERCEL_TOKEN |
Vercel personal access token |
VERCEL_ORG_ID |
From .vercel/project.json → orgId |
VERCEL_PROJECT_ID |
From .vercel/project.json → projectId |
NEON_DATABASE_URL |
Neon Postgres connection string |
VERCEL_DEPLOYMENT_URL |
Production URL for health checks (e.g. https://agentbank.vercel.app) |
| Job | Trigger | Purpose |
|---|---|---|
lint |
PR + push | ESLint + tsc --noEmit |
test |
PR + push | Vitest unit suite |
build |
PR only | Next.js build verification with dummy env vars |
deploy |
push to master |
Vercel CLI production deploy |
migrate |
after deploy | drizzle-kit push schema sync to Neon |
health-check |
after migrate | Hits /api/health and /api/.well-known/agent.json |
727 tests passing across all implemented modules (4 skipped — require live DB).
See CHANGELOG.md for a full breakdown by phase and task.
AgentBank integrates with the Nevermined request lifecycle and observability layer to provide:
- x402 payment validation on all paid API endpoints via
payment-signatureheader - Automatic credit settlement after successful request processing
- Custom event logging to the Nevermined hosted analytics dashboard (per-agent, per-service analytics)
This integration is optional. When the required environment variables are not set, the app degrades gracefully — all endpoints function normally without x402 validation or observability tracking.
| Variable | Description |
|---|---|
NVM_API_KEY |
Nevermined API key |
NVM_ENVIRONMENT |
testing, sandbox, or production |
NVM_PLAN_DID |
DID of your Nevermined credit plan |
NVM_AGENT_DID |
DID of your Nevermined agent |
| Endpoint | Credits |
|---|---|
POST /api/loan/apply |
5 |
POST /api/loan/repay |
1 |
POST /api/deposit |
1 |
POST /api/withdraw |
1 |
POST /api/redeem |
1 |
POST /api/proxy/[service] |
Dynamic (from service catalog) |
| All GET endpoints | Free |
When observability is enabled, analytics are available on the Nevermined Dashboard. No custom frontend work is required — the hosted dashboard surfaces per-agent, per-service metrics automatically.
AgentBank supports three pricing tiers on the Nevermined marketplace:
| Tier | Credits | Price | Discount |
|---|---|---|---|
| Starter | 100 | $1.00 USDC | — |
| Growth | 500 | $4.50 USDC | 10% |
| Pro | 1000 | $8.00 USDC | 20% |
To register all tiers:
node scripts/register-plan.mjsThis registers the agent (if needed) and all three plans, outputting the plan DIDs for your .env file.
To register a specific tier:
node scripts/register-plan.mjs --tier=GrowthSet the output DIDs in your environment:
NVM_PLAN_DID_STARTER=did:nvm:...
NVM_PLAN_DID_GROWTH=did:nvm:...
NVM_PLAN_DID_PRO=did:nvm:...AgentBank's proxy service dynamically discovers upstream agents from the Nevermined hackathon marketplace and routes requests to them using x402 authentication. This makes AgentBank a genuine buyer in the agent economy, generating real cross-team transactions.
Discovery API → Catalog Manager (TTL cache) → Proxy Service → Upstream Caller (x402) → Real Agent
- Discovery: Fetches sellers from
https://nevermined.ai/hackathon/register/api/discover - Catalog Manager: Merges dynamic sellers with static fallback catalog
- Proxy Service: Routes borrower requests to upstream agents
- Upstream Caller: Handles x402 authentication (
orderPlan → getX402AccessToken → POST with payment-signature)
The catalog merges two sources:
| Source | Description | Fields |
|---|---|---|
| Dynamic | Discovered from marketplace | planId, endpointUrl, creditCost, upstreamCostUsdc, source: 'dynamic' |
| Static | Hardcoded fallback | Basic pricing info, source: 'static' |
Dynamic entries take precedence when the same agent DID appears in both.
- TTL: 5 minutes (
DISCOVERY_CONFIG.refreshIntervalMs) - Manual Refresh:
GET /api/proxy/catalog?refresh=true - Stale-on-Error: If discovery fails, cached catalog is returned
| Failure Scenario | Behavior |
|---|---|
| Discovery API 401/500/timeout | Returns cached catalog or static fallback |
NVM_API_KEY not set |
Uses static catalog only |
| Upstream agent returns 402/500 | 0 credits charged to borrower |
orderPlan() or getX402AccessToken() fails |
Returns upstream_error to borrower |
# Get current catalog (merged dynamic + static)
GET /api/proxy/catalog
# Force cache refresh
GET /api/proxy/catalog?refresh=trueResponse includes source and intelligence fields per agent:
{
"services": [{
"service": "research",
"upstream_agents": [{
"agent": "did:nvm:real-seller",
"credit_cost": 8,
"upstream_cost_usdc": 0.05,
"availability": 1.0,
"plan_id": "plan-abc",
"endpoint_url": "https://seller.com/api",
"source": "dynamic",
"intelligence": {
"last_sampled_at": "2026-03-06T14:30:00Z",
"last_latency_ms": 150,
"samples_collected": 5
}
}]
}]
}The intelligence field is null when no market intelligence data exists for that agent+service pair.
All upstream agents (dynamic and static) are tracked in the provider registry:
queriesMade: Total proxy calls to this agentpredictionAccuracy: Success rate (for credit scoring providers)roiScore: Value generated per credit spent
Use selectProviders({ budget }) to rank agents by ROI score.
AgentBank is not just a passive proxy — it's an autonomous buyer in the agent economy. The Treasury Operations Engine proactively purchases services from marketplace sellers on a schedule, building counterparty relationships and deploying idle reserves productively.
Real banks don't wait for transactions. They:
- Establish correspondent relationships via small transactions with counterparties
- Make markets by buying services to resell at markup
- Perform due diligence by periodically sampling counterparties
AgentBank's treasury engine automates all three strategies.
| Strategy | Purpose | Budget Allocation |
|---|---|---|
| Correspondent | Build relationships with new sellers | 25% |
| Market-Making | Buy from sellers with positive spread (profit margin) | 30% |
| Due Diligence | Re-sample sellers not contacted in 3 minutes | 20% |
| Repeat Customer | Buy from proven sellers with successful history | 25% |
Correspondent Strategy: Targets sellers the bank has never transacted with. Buys 1 request from each, sorted by cost ascending (maximizes breadth per credit). Registers each seller in the provider registry for future credit scoring.
Market-Making Strategy: Targets sellers with positive spread (where creditCost × 0.01 > upstreamCostUsdc). Buys from highest-spread sellers first, building inventory for future proxy requests.
Due Diligence Strategy: Re-samples sellers not contacted in the last 3 minutes. Flags sellers with latency degradation (> 50% increase from historical average).
Repeat Customer Strategy: Prioritizes sellers with proven track record (queriesMade > 0, lastCallFailed = false). Buys from cheapest first to maximize call volume per credit.
The treasury budget is calculated from excess reserves:
excessReserves = cashReserves - (totalDeposits × minReserveRatio)
budgetCredits = min(excessReserves × 0.10, 50)
- Minimum threshold: Budget is 0 if excess reserves < 20 credits
- Hard cap: Budget never exceeds 50 credits per cycle
- Conservative: Only 10% of excess is spent per cycle
- Execution: Vercel cron job triggers
GET /api/treasury/cronevery minute - Bootstrap: Seeds 500 credits ($5 USDC) as initial equity on first run if balance sheet is empty
- Concurrency guard: No overlapping cycles
- Error isolation: One failing strategy doesn't abort others
# Get treasury status
GET /api/treasury
# Trigger a manual cycle
POST /api/treasury
# Vercel cron endpoint (called automatically every minute)
GET /api/treasury/cron
# Get last N cycle results (default: 10)
GET /api/treasury/history
GET /api/treasury/history?limit=5GET /api/treasury Response:
{
"status": "active",
"last_cycle": {
"cycle_id": "TC-2026030601",
"started_at": "2026-03-06T10:00:00Z",
"completed_at": "2026-03-06T10:00:05Z",
"budget_credits": 10,
"total_spent_credits": 8,
"sellers_contacted": 3,
"success_count": 3,
"failure_count": 0
},
"next_cycle_at": 1709715600000
}POST /api/treasury Response:
- 200: Cycle completed successfully
- 409: Cycle already in progress
- 422: Treasury operations disabled
Treasury behavior is configured in lib/config.ts:
TREASURY_CONFIG = {
enabled: true, // Set to false to disable
cycleLengthMs: 60_000, // 1 minute
maxBudgetPctOfExcessReserves: 0.20,
maxBudgetCreditsPerCycle: 150,
minExcessReserveCredits: 10,
strategyAllocation: {
correspondent: 0.25,
marketMaking: 0.30,
dueDiligence: 0.20,
repeatCustomer: 0.25,
},
dueDiligenceResampleAfterMs: 180_000, // 3 minutes
}To disable treasury operations, set TREASURY_CONFIG.enabled = false or remove NVM environment variables.
Treasury purchases record balanced journal entries:
| Account | Code | Debit | Credit |
|---|---|---|---|
| Credit Scoring Data | 1300 | ✓ | |
| Cash Reserves | 1000 | ✓ |
- Reference type:
treasury - Reference ID: Cycle ID (e.g.,
TC-2026030601) - Failed purchases: No ledger entry (no charge)
| Scenario | Behavior |
|---|---|
| Discovery API unreachable | Due diligence runs on known sellers |
| All upstream agents return errors | 0 credits spent, no ledger entries |
| Budget is 0 (reserves at minimum) | No purchases, cycle completes |
| Treasury disabled via config | Scheduler doesn't start |
| NVM credentials missing | Scheduler doesn't start |
| One strategy throws error | Other strategies continue |