The autonomous feature agent that pays. Built for the Audiera Participation Economy on BNB Chain.
Midas is an AI agent that ingests a creator's stem, writes a feature verse
(powered by the Audiera lyrics + music skills), mints a full vocal track,
and settles a 50/50 revenue split on-chain in a single flow. The creator
connects their wallet, uploads a stem, and watches their share of $BEAT
land on BSC Testnet — live.
Demo.Hacathon.mp4
🟣 Want to run it yourself? Follow Setup — full stack and try the exact same flow at
/try. Every tx hash in the demo is verifiable on BscScan Testnet.
- Architecture
- Pipeline flow
- Repository layout
- Setup — full stack
- Reference
- Deployed addresses (BSC Testnet)
- Design system
- Security notes
flowchart LR
User["Creator (wallet connected)"] -->|upload stem| FE["Next.js 16 Frontend<br/>(app/try)"]
FE -->|POST /api/midas/execute<br/>multipart: stem + creator| API["Next.js Route Handler<br/>app/api/midas/execute"]
API -->|POST /analyze| VPS["Analysis API<br/>(FastAPI + librosa)<br/>bpm · key · duration"]
API -->|POST /api/skills/lyrics| AUDL["Audiera Lyrics Skill"]
API -->|POST /api/skills/music<br/>poll every 5s| AUDM["Audiera Music Skill"]
API -->|ethers v6<br/>approve + executeFeatureSplit| RPC["BSC Testnet RPC"]
RPC --> SPL["MidasSplitter<br/>0xeab5…Ea994"]
SPL -->|50%| Creator["Creator Wallet"]
SPL -->|50%| Owner["Midas Protocol<br/>(contract owner)"]
API -->|JSON: lyrics, music URL,<br/>txHash, splits| FE
FE -->|tx row drop-in| Ledger["Live Ledger"]
classDef svc fill:#111113,stroke:#f59e0b,color:#f4f4f5
classDef chain fill:#17171a,stroke:#d4af37,color:#fcd34d
class FE,API,VPS,AUDL,AUDM svc
class RPC,SPL,Creator,Owner chain
Four layers, one flow:
- Frontend (Next.js 16 + React 19 + Tailwind v4) — hero landing,
/tryCreator Studio, Proof ledger. Reown AppKit for WalletConnect/MetaMask. - Agent API (Next.js route handler) — orchestrates analysis → Audiera → on-chain settlement. Returns one JSON payload.
- Analysis sidecar (FastAPI +
librosa) — real BPM / key / duration from the uploaded stem. Optional — the route falls back to a deterministic seed if unreachable. - Contracts (Foundry + OpenZeppelin) —
MockBEATERC-20 +MidasSplitterfor the 50/50 revenue routing.
sequenceDiagram
autonumber
participant U as Creator
participant FE as Frontend
participant API as /api/midas/execute
participant VPS as Analysis API
participant AUD as Audiera Skills
participant BSC as BSC Testnet
U->>FE: Connect wallet (AppKit)
U->>FE: Choose stem + Run
FE->>API: POST multipart (stem + creator)
API->>VPS: POST /analyze (stem)
VPS-->>API: { bpm, key, durationSec, genreHint }
API->>AUD: POST /api/skills/lyrics
AUD-->>API: 4-bar verse
API->>AUD: POST /api/skills/music (styles, artistId, lyrics)
loop every 5s, up to 60 attempts
API->>AUD: GET /api/skills/music/{taskId}
end
AUD-->>API: completed · music URL
API->>BSC: beatToken.approve(splitter, amount)
BSC-->>API: 1 confirmation
API->>BSC: splitter.executeFeatureSplit(creator, amount)
BSC-->>API: receipt.hash
API-->>FE: { lyrics, music, txHash, splits }
FE->>FE: animate genre badge → lyrics → split → confirmed
FE->>FE: drop tx into Live Ledger
midas/
├── contracts/ # Foundry — on-chain layer
│ ├── src/
│ │ ├── MockBEAT.sol # ERC-20, 1M initial supply to deployer
│ │ └── MidasSplitter.sol# 50/50 router (Ownable, SafeERC20)
│ ├── script/
│ │ └── DeployMidas.s.sol
│ ├── broadcast/ # Deployed addresses (committed)
│ ├── foundry.toml # Solc 0.8.24 · OZ remapping
│ └── .env.example
│
├── frontend/ # Next.js 16 · React 19 · Tailwind v4
│ ├── app/
│ │ ├── page.tsx # Landing: Hero · Engine · Proof
│ │ ├── try/page.tsx # Live Creator Studio
│ │ └── api/midas/execute/route.ts # The agent brain
│ ├── components/
│ │ ├── blocks/ # Hero, Engine, Proof, CreatorStudio
│ │ ├── layout/ # Navbar (wallet connect)
│ │ ├── midas/ # Agent state + wallet + upload/feed
│ │ └── ui/ # Atoms: GlowCard, CTAButton, TxFeed…
│ └── .env.example
│
├── analysis-api/ # FastAPI sidecar — real audio analysis
│ ├── app.py # /analyze + /health
│ ├── Dockerfile
│ ├── ecosystem.config.cjs # PM2
│ └── requirements.txt
│
├── .cursor/skills/ # Audiera skill definitions (lyrics + music)
└── CLAUDE.md # Design system spec (Obsidian + Gold)
| Tool | Version | Install |
|---|---|---|
| Node | ≥ 20 | nvm install 20 |
| npm | ≥ 10 | ships with Node |
| Foundry | latest | curl -L https://foundry.paradigm.xyz | bash && foundryup |
| MetaMask / any WalletConnect wallet | — | browser extension |
| Python | 3.11 (optional) | only needed for the analysis sidecar |
| tBNB | ~0.05 | BNB Testnet Faucet |
You'll also need accounts for:
- Audiera —
AUDIERA_API_KEY(lyrics + music skills) - Reown (formerly WalletConnect) —
NEXT_PUBLIC_REOWN_PROJECT_IDfrom cloud.reown.com
git clone <your-fork-url> midas
cd midas
# Contracts — pull submodules + fetch forge-std
git submodule update --init --recursive
cd contracts
forge install foundry-rs/forge-std --no-commit
cd ..
# Frontend
cd frontend && npm install && cd ..cd contracts
cp .env.example .env
# edit .env → PRIVATE_KEY=0x… (testnet-only, 0x prefixed, 66 chars total)
set -a; source .env; set +a
# Dry-run (simulates, no broadcast)
forge script script/DeployMidas.s.sol:DeployMidas \
--rpc-url $BSC_TESTNET_RPC
# Real broadcast
forge script script/DeployMidas.s.sol:DeployMidas \
--rpc-url $BSC_TESTNET_RPC \
--broadcastThe script logs both addresses; Foundry also writes them to
contracts/broadcast/DeployMidas.s.sol/97/run-latest.json.
After deploy, transfer some BEAT from the deployer wallet to the
MIDAS_AGENT_PRIVATE_KEY wallet so the agent has tokens to split. The agent
needs tBNB for gas too.
cd frontend
cp .env.example .envFill in frontend/.env:
AUDIERA_API_KEY=sk_audiera_...
AUDIERA_ARTIST_ID=i137z0bj0cwsbzrzd8m0c # Jason Miller (default)
MIDAS_AGENT_PRIVATE_KEY=0x... # broadcasts the split
NEXT_PUBLIC_BEAT_TOKEN_CONTRACT=0x3C4A79...efB901C4
NEXT_PUBLIC_SPLITTER_CONTRACT=0xeab5F2...Dc6Ea994
BSC_RPC_URL=https://data-seed-prebsc-1-s1.binance.org:8545
NEXT_PUBLIC_REOWN_PROJECT_ID=<from cloud.reown.com>
# Optional — only if you run the analysis sidecar
ANALYSIS_API_URL=http://localhost:8080/analyze
ANALYSIS_API_KEY=The frontend's agent route works without this — it'll use a deterministic fallback for BPM/key/duration. Run the sidecar for real audio analysis.
cd analysis-api
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
uvicorn app:app --host 0.0.0.0 --port 8080Or via Docker / PM2 — see analysis-api/README.md.
cd frontend
npm run devOpen http://localhost:3000:
- Landing page explains the 4-step Engine.
- Click Try Now → navigates to
/try. - Connect Wallet (MetaMask on BSC Testnet chain
0x61). - Choose Stem →
.wav/.mp3from disk. - Run Uploaded Stem → watch:
- Waveform pulse · "Midas is analyzing your track…"
- Genre badge + lyrics fade in line-by-line on the right
- Gold orb splits 50/50 between Your Wallet and Midas Protocol
- Settlement Confirmed with a live BscScan link
- Ledger at the bottom drops a new row with the real tx hash
classDiagram
direction LR
class MockBEAT {
+string name = "BEAT"
+uint256 INITIAL_SUPPLY = 1_000_000e18
+constructor() → mint to msg.sender
+approve(spender, amount)
+transferFrom(from, to, amount)
}
class MidasSplitter {
+IERC20 BEAT
+constructor(beatToken)
+executeFeatureSplit(originalCreator, amount)
+owner() // OpenZeppelin Ownable
event SplitExecuted
}
class Ownable { <<OpenZeppelin>> }
class SafeERC20 { <<OpenZeppelin lib>> }
MidasSplitter --> MockBEAT : pulls via safeTransferFrom
MidasSplitter --|> Ownable
MidasSplitter ..> SafeERC20 : uses
Pull pattern — the caller of executeFeatureSplit must have first
approved the splitter to spend amount BEAT. The frontend agent does both
within the same request:
function executeFeatureSplit(address originalCreator, uint256 amount) external {
uint256 creatorAmount = amount / 2;
uint256 ownerAmount = amount - creatorAmount; // odd wei → owner
BEAT.safeTransferFrom(msg.sender, originalCreator, creatorAmount);
BEAT.safeTransferFrom(msg.sender, owner(), ownerAmount);
emit SplitExecuted(originalCreator, owner(), creatorAmount, ownerAmount, amount);
}Accepts either JSON ({ creator }) or multipart/form-data (creator +
stem file field). Response shape:
Every step is try/catch'd — if the analysis VPS is down, fallback metadata is
used; if on-chain settlement fails, txHash falls back to a random 0x… and
onChain.error carries the message. The pipeline never crashes mid-flight.
multipart/form-data with field stem (.wav / .mp3). Optional
Authorization: Bearer <ANALYSIS_API_KEY> when the service was started with
that env var.
{
"bpm": 122,
"key": "A minor",
"durationSec": 184,
"genreHint": "Dark Drill"
}Uses librosa.beat.beat_track for BPM and a Krumhansl chroma-profile match
against all 24 major/minor keys. See analysis-api/app.py.
| Variable | Where | Required | Purpose |
|---|---|---|---|
PRIVATE_KEY |
contracts/.env |
✅ for deploy | Deployer wallet (needs tBNB) |
BSC_TESTNET_RPC |
contracts/.env |
✅ for deploy | Foundry --rpc-url |
BSCSCAN_API_KEY |
contracts/.env |
optional | --verify after deploy |
AUDIERA_API_KEY |
frontend/.env |
✅ | Lyrics + music skills bearer token |
AUDIERA_ARTIST_ID |
frontend/.env |
optional | Default feature artist (else derived from wallet hex) |
MIDAS_AGENT_PRIVATE_KEY |
frontend/.env |
✅ | Agent that pulls + splits BEAT |
NEXT_PUBLIC_BEAT_TOKEN_CONTRACT |
frontend/.env |
✅ | Deployed MockBEAT |
NEXT_PUBLIC_SPLITTER_CONTRACT |
frontend/.env |
✅ | Deployed MidasSplitter |
BSC_RPC_URL |
frontend/.env |
✅ | Ethers provider URL |
NEXT_PUBLIC_REOWN_PROJECT_ID |
frontend/.env |
✅ | WalletConnect / AppKit project id |
ANALYSIS_API_URL |
frontend/.env |
optional | Sidecar endpoint |
ANALYSIS_API_KEY |
frontend/.env & analysis-api |
optional | Bearer auth between them |
| Contract | Address | Explorer |
|---|---|---|
| MockBEAT | 0x3C4A79c3C45bcB31b892403181BFB558efB901C4 |
BscScan |
| MidasSplitter | 0xeab5F2f3308FC8c8a6f98119F7603451Dc6Ea994 |
BscScan |
Deploy receipts live in contracts/broadcast/DeployMidas.s.sol/97/run-latest.json.
Defined in CLAUDE.md. TL;DR:
- Canvas — Obsidian
#0A0A0A, elevated#111113, raised#17171A. - Accent — Amber-500
#F59E0Bprimary, Amber-300#FCD34Dhover, metallic gold#D4AF37reserved for revenue/high-value data. - Borders — 1px.
white/10default →white/20hover →amber-500/50active. - Type — Space Grotesk / Geist display, Geist Sans body, Geist Mono for every wallet, hash, amount, and JSON key.
- Motion — Framer Motion (
motion/react) with spring physics (stiffness 400 / damping 30). No CSSease-in-outfor interactive states. - Grid — 8-point spacing. No arbitrary pixel values.
- Geometry —
rounded-fullfor pills/CTAs,rounded-2xlmax for cards,rounded-nonefor terminals/code.
- Never commit a real
.env. Bothcontracts/.gitignoreandfrontend/.gitignorealready cover.env*. - The
MIDAS_AGENT_PRIVATE_KEYlives infrontend/.envbecause the route handler is a server component — it never reaches the browser. Do not prefix it withNEXT_PUBLIC_. - Use a dedicated testnet-only wallet for the agent. Rotate immediately if the key leaks into a logs, chat, or commit.
MidasSplitterusesSafeERC20+ checksamount > 0and non-zero addresses. Odd-wei remainder goes to the owner side so totals reconcile.- The analysis API writes uploaded stems to
/tmpand deletes them in thefinallyblock — no persistence.
MIT — do whatever, just credit Midas in the Audiera Participation Economy.
{ "status": "success", "genre": "Dark Drill", "metadata": { "bpm": 142, // from analysis API when available "key": "F# minor", "durationSec": 187, "analysisSource": "vps", // or "fallback" "sourceFileName": "demo.wav", "cid": "bafybe…" // mock IPFS CID }, "audiera_lyrics_status": "success", "lyrics": "…4 lines…", "audiera_music_status": "completed", // or "processing" | "error" "audiera_music_job_id": "tsk_…", "audiera_music_url": "https://ai.audiera.fi/music/12345", "audiera_music_file_url":"https://cdn.audiera.fi/…", "audiera_music_title": "…", "audiera_music_duration": 180, "audiera_artist_id": "i137z0bj0cwsbzrzd8m0c", "audiera_artist_name": "Jason Miller", "txHash": "0x…real BSC hash…", "bscScanUrl": "https://testnet.bscscan.com/tx/0x…", "onChain": { "status": "broadcast", "error": null }, "amount": "3421.0", "amountRaw": "3421000000000000000000", "splits": { "originalCreator": "0x…", // connected wallet or random "creatorAmount": "1710.5", "ownerAmount": "1710.5" }, "contracts": { "beatToken": "0x3C4A…01C4", "splitter": "0xeab5…Ea994", "rpc": "https://data-seed-prebsc-1-s1.binance.org:8545" } }