ACP Agent — Episode 2: How I Built a Crypto Price Verification Agent Before ACP Graduation
I'd never deployed anything to the cloud before this project. Never pushed code to GitHub. Never even heard of Railway. So when I say "I built this," what I really mean is: I described what I wanted to Claude, fixed the parts that broke, and somehow ended up with a working agent.
This post covers the actual build process — picking the right APIs, writing the price verification logic, getting the thing running on Railway, and registering it on ACP. If you've got zero coding background like me, this is probably the episode you'll want to bookmark.
What This Post Covers
The full technical setup behind PriceVerifier — from choosing Kraken and Coinbase over four API candidates, to the core verification logic, Railway deployment at $5/month, and the ACP registration process that turned out to be trickier than expected. Everything a non-developer needs to go from "I have an idea" to "it's running online."
Picking the Right APIs (I Tested Four)
PriceVerifier's whole point is cross-checking prices from two independent sources. So the first decision was: which exchanges?
I tested four options before landing on Kraken + Coinbase:
Kraken — Fast response, generous rate limits, about 200 trading pairs, completely free. This became my primary source.
Coinbase — Also fast, also generous, 400+ coins, also free. Perfect secondary source.
CoinGecko — Covers 10,000+ coins, but the free tier caps you at 30 requests per minute and the response time is noticeably slower. I kept this as a fallback only — if Coinbase goes down, CoinGecko steps in.
CryptoCompare — Limited rate allowance on the free tier, fewer coins than I expected. Dropped it early.
The deciding factor was dead simple: Kraken and Coinbase are both free, both fast, and neither has meaningful rate limits at the polling frequency I needed (one check every 20 seconds). When you're charging $0.01 per verification, you can't afford API costs eating into that.
The Price Verification Logic
The core function is probably the most straightforward part of the whole project. Fetch price from Kraken, fetch price from Coinbase, compare:
The real-world version has more going on — a three-tier fallback system (Kraken+Coinbase primary, Kraken+CoinGecko if Coinbase fails, single-source warning if only one API responds), error handling for when exchanges go offline, and a mapping table because Kraken uses weird ticker symbols. BTC isn't "BTC" on Kraken — it's "XXBTZUSD." I didn't know that until the first test returned nothing.
A sample response looks like this:
0.0027% deviation on BTC. Kraken and Coinbase almost always agree within a fraction of a percent on major coins. The WARN and FAIL verdicts are more relevant for lower-liquidity tokens where price discrepancies actually happen.
Deploying on Railway (First Time Ever)
This was the part I was most nervous about. I'd heard "deploy to the cloud" so many times and always pictured some nightmare of server configuration and SSH keys. Railway turned out to be... surprisingly painless.
Here's what I actually did:
Created a GitHub repository. Pushed four files: main.py (the seller agent), buyer.py (a test agent for generating jobs), requirements.txt (just two libraries), and runtime.txt (tells Railway which Python version to use).
Connected Railway to the GitHub repo. Every time I push changes to main, Railway automatically redeploys. That part felt like magic the first time it worked.
Created two services inside Railway: one running main.py (the actual PriceVerifier agent), another running buyer.py (my test buyer that sends jobs to the seller). Total cost: $5/month for 24/7 operation on the Starter plan.
The requirements.txt is literally two lines:
I'll be honest — the first deploy failed because I forgot runtime.txt. Railway defaulted to an older Python version that didn't like some of the syntax. Added python-3.11.0 to the file, pushed again, and it worked. Small thing, but it took me 40 minutes to figure out why the logs looked wrong.
Registering on ACP (Where It Got Confusing)
With the code running on Railway, the next step was registering PriceVerifier on the ACP marketplace. This part has more moving pieces than I expected.
You go to app.virtuals.io/acp, connect your MetaMask wallet, and register a new agent. You pick a name, set a role, upload a profile image, and link your X/Telegram accounts. The platform generates a smart wallet for your agent automatically — this is separate from your MetaMask wallet.
That distinction tripped me up. Your MetaMask wallet signs operations (like registering and whitelisting). Your agent's smart wallet holds USDC and processes transactions. They're different addresses, different purposes. I initially deposited USDC into my MetaMask wallet thinking the agent would use it. It didn't. The funds need to go into the agent's smart wallet.
After registration, you define a Job Offering — the service your agent provides. For PriceVerifier, I set up:
Name: verifyPrice
Price: $0.01 USDC
Description: Cross-validates crypto prices from Kraken and Coinbase
Requirements schema: What the buyer needs to send (a coin symbol)
Deliverables schema: What the agent returns (verdict, prices, deviation)
Then you whitelist your developer wallet so you can manage the agent, fund the test buyer's wallet with some USDC on Base network, and you're technically ready to start processing jobs.
Technically.
There's one decision during registration that seems trivial but will haunt me for 12 debugging sessions. When you register an agent, you pick a role: Provider, Evaluator, Hybrid, or Requestor. I picked the wrong one. That story is Episode 3, and honestly, it's the most expensive single click I've ever made in terms of time wasted.
The Two-Agent Architecture
Something that confused me at first: why do I need two agents?
In ACP's sandbox, your agent can't get jobs from real users. So you need a second agent — a buyer — to create test jobs for your seller. It's basically your agent talking to itself.
The Seller (main.py) is PriceVerifier. It listens for incoming jobs, verifies prices, and delivers results.
The Buyer (buyer.py) is a simple loop that creates jobs for the seller. During graduation testing, it cycles through 10 different coins — ETH, BTC, SOL, XRP, ADA, DOGE, AVAX, DOT, LINK, MATIC — one after another. It's only used for hitting the graduation milestones (10 total jobs, 3 consecutive).
Both run as separate services on Railway, sharing the same GitHub repo. The buyer costs almost nothing to run since it only fires a job every 30 seconds or so.
Key Takeaways
- Kraken + Coinbase won the API comparison: both free, fast, and reliable enough for a $0.01 service. CoinGecko stays as fallback only.
- Railway deployment is genuinely beginner-friendly — connect GitHub, push code, it runs. $5/month for 24/7.
- The agent wallet and MetaMask wallet are different things. USDC goes in the agent wallet, not MetaMask. This will save you a confused hour.
- You need two agents for sandbox testing — a seller (your actual service) and a buyer (test job generator). Both run on Railway.
What's Next
Everything I just described — the API logic, the deployment, the registration — went relatively smoothly. Episode 3 is where things fell apart. I made a single wrong choice during agent registration that silently broke everything for 12 sessions. The logs showed success. The on-chain transactions showed nothing. And I had no idea why.
← Previous: Episode 1: Why I'm Building Toward ACP Graduation Next: Episode 3: The Mistake That Blocked Graduation for 12 Sessions →
More updates on the way. If you're working on something similar or found a smarter way to do it, drop it in the comments — the more we share, the faster we all move.
Disclaimer: This blog documents my personal learning journey. Nothing here is financial advice.
Comments
Post a Comment