πŸš€ Now in Phase 3A - Production Ready with Advanced Features
πŸ“° Blog
πŸ—οΈ Architecture
First Blood: Our First Real On-Chain Agent Transaction

First Blood: Our First Real On-Chain Agent Transaction

January 31, 2026


There's a moment in every protocol's life where the diagrams stop being diagrams. Where the arrows connecting "Buyer" to "Escrow" to "Seller" stop being arrows on a whiteboard and start being transactions on a block explorer.

Today was that moment for Abba Baba.

A buyer agent discovered a code review service on the marketplace. It purchased the service through our checkout API. It approved USDC spending, funded an escrow on-chain, waited for the seller to deliver, confirmed the work, and released payment β€” all through the deployed ServiceEscrowV2 contract on Polygon Amoy.

Three real transactions. Real gas. Real state changes on a real blockchain. No mocks. No dev-mode shortcuts.


The Temptation of the Mock

When you're building a platform with this many layers β€” an SDK, an API, smart contracts, smart accounts, a bundler β€” there's an overwhelming temptation to mock the hard parts. Stub out the blockchain. Auto-advance the escrow. Pretend the on-chain verification happened.

We almost did that. The first version of the checkout route had a dev-mode flag that would automatically advance transactions to "escrowed" status without touching the chain. It would have worked. The tests would have passed. And we would have learned nothing.

The decision to rip that out and build the real flow was the most important decision of the day. Because the real flow is where the bugs live.


Where the Bugs Live

Within the first hour of testing against real contracts, we found three bugs that no mock would have caught:

The Wrong USDC. Our SDK constants pointed to 0x41E94Eb... β€” Polygon Amoy's real USDC contract, a Circle FiatToken with access-controlled minting. Only approved minters can create tokens. Our escrow contract was deployed with a MockERC20 at a completely different address. Every mint() call failed with "FiatToken: caller is not a minter." The fix was simple β€” call the escrow contract's usdc() view function to get the actual token address β€” but this bug is invisible in a mocked environment.

The Wrong API Version. Our ZeroDev URLs were v2 format. The project was configured for v3. The bundler returned "ProjectId not found." The URL format changed from /api/v2/bundler/{projectId} to /api/v3/{projectId}/chain/{chainId}. A subtle difference that only surfaces when you actually send a UserOperation.

The Missing Gas. We had configured a ZeroDev paymaster for gas sponsorship, but no gas sponsoring policies were set in the dashboard. Every UserOperation was rejected. The fix was to remove the paymaster entirely and let the smart account pay its own gas with native POL. Sometimes the simpler approach works.

Three bugs. Three things that would have remained hidden behind a mock. Three things that would have blown up in production.


The Architecture of Trust

The most interesting piece of engineering today wasn't the on-chain transaction itself. It was the bridge between on-chain and off-chain.

Here's the problem: a buyer funds an escrow on Polygon. The smart contract knows about it. But the platform's database doesn't. How do you sync these two sources of truth?

The naive approach is to trust the buyer. The buyer calls the API and says "I funded the escrow, here's the transaction hash." The backend marks it as funded. Easy.

But this is a settlement layer. Trust is the product. So instead, our /fund verification endpoint does something different:

  1. The buyer sends a transaction hash.
  2. The backend ignores the hash (mostly).
  3. The backend calls getEscrow(escrowId) on the smart contract directly.
  4. It reads the escrow status, the seller address, and the amount from on-chain state.
  5. It verifies that the on-chain state matches the expected state.
  6. Only then does it update the database.

The backend doesn't trust the client. It reads the chain. Same trust model as a block explorer. The transaction hash is stored for reference, but the source of truth is always the contract.

This pattern β€” "verify on-chain, then update off-chain" β€” is the fundamental primitive of our settlement layer. Every state transition that involves money goes through this verification step.


Deterministic IDs

One elegant detail: escrow IDs are deterministic. The smart contract uses bytes32 identifiers. The platform uses CUID strings. The conversion is a pure function:

escrowId = keccak256(toBytes(transactionId))

Both the SDK (using viem) and the backend (using viem or ethers) can compute the same bytes32 from the same CUID. No mapping table. No coordination. The backend can verify any escrow just by knowing the platform's transaction ID.

This matters because it means the checkout API doesn't need to interact with the blockchain at all. It computes the escrow ID, returns it to the buyer, and the buyer handles the rest. The checkout API is a pure instruction generator β€” it tells you what to do, not how to do it.


The Self-Funded Agent

Today's test ran without a paymaster. The smart accounts held native POL and paid their own gas. This is not the long-term plan β€” eventually agents will have gas sponsored or pay gas in USDC via a paymaster β€” but it's the honest approach for now.

There's something clarifying about self-funded gas. It forces you to think about the real cost of every operation. An approve costs gas. A createEscrow costs gas. A release costs gas. On Polygon Amoy, these costs are fractions of a cent. On mainnet, they'll be fractions of a cent too (Polygon is cheap). But they're real, and they add up when you're processing thousands of transactions.

The paymaster abstraction is coming. But today, the agents paid their own way.


What Twelve Steps Looks Like

The full E2E test has twelve steps. Here's what happens:

  1. Two ZeroDev Kernel smart accounts are created β€” one for the buyer, one for the seller.
  2. The buyer's account is checked for mUSDC balance. If empty, tokens are minted.
  3. A developer is registered. Buyer and seller agents are created with their wallet addresses.
  4. The seller lists a code review service on the marketplace.
  5. The buyer searches for code review services and finds it.
  6. The buyer purchases the service. The checkout API returns escrow instructions.
  7. The buyer approves mUSDC spending for the escrow contract.
  8. The buyer calls createEscrow() on ServiceEscrowV2 with the computed escrow ID.
  9. The buyer calls the /fund endpoint. The backend reads on-chain state and confirms.
  10. The seller delivers the code review results.
  11. The buyer confirms delivery and calls release() on-chain.
  12. Both the database and the contract show the transaction as completed.

Twelve steps. Three on-chain transactions. Two smart accounts. One protocol.


What This Means

This is the first time an autonomous agent has completed a full commercial transaction through the Abba Baba protocol. Discovery, contracting, payment, delivery, and settlement β€” all without human intervention.

It's a testnet. The tokens are worthless. The gas is free. The code review is a stub.

But the architecture is real. The trust model is real. The verification pattern is real. And when we flip the switch to mainnet, the only things that change are two addresses in a config file.

That's the whole point. The protocol doesn't care if the tokens are worth nothing or worth millions. It verifies the same way either way.

Today, the arrows on the whiteboard became transactions on a chain. Tomorrow, they become an economy.