DEV Community

Nikhil Ranka
Nikhil Ranka

Posted on

The Complete Guide to Agent-to-Agent Marketplaces in 2026

The Complete Guide to Agent-to-Agent Marketplaces in 2026

By a senior engineer building autonomous AI agents


Introduction

Agent‑to‑agent (A2A) marketplaces have become the plumbing that lets one autonomous service discover, negotiate, and pay another for a discrete capability—think “micro‑service as a service”. By 2026 the ecosystem is mature enough that developers can treat another agent as a library call, but the underlying mechanics are still distributed, trust‑minimized, and cost‑sensitive. This guide walks through the architectural pieces you need to stitch together, shows concrete code for a minimal buyer and seller, and calls out the trade‑offs you’ll encounter in production.


Core Concepts

Term Meaning (2026) Typical Implementation
Agent A long‑running process that exposes a deterministic API (usually JSON‑over‑HTTP or gRPC) and can hold cryptographic keys for signing/payment. Built with any language; often wrapped in a lightweight runtime (e.g., WasmEdge, Micrium).
Marketplace A discovery + settlement layer that does not host the agent’s logic. It maintains an index of capabilities, verifies signatures, and escrows payment tokens. Usually a set of smart contracts on a low‑fee L2 (Base, Arbitrum Nova) plus an off‑chain gossip/DHT for metadata.
Capability Descriptor A machine‑readable schema (JSON‑Schema + OpenAPI‑like extensions) that lists input/output types, required auth, pricing, and SLAs. Stored on IPFS/Filecoin; hash referenced in the on‑chain registry.
Settlement Token The unit of value used for payment; in most A2A markets today it’s a stablecoin (USDC) on an L2 to keep gas < $0.001. ERC‑20 contract; approval‑and‑transfer pattern.
Reputation A lightweight score derived from on‑chain success/failure events and off‑chain attestations. Stored in a separate contract; queried before committing funds.

High‑Level Flow

  1. Register – Seller publishes a capability descriptor to IPFS, then calls Marketplace.register(bytes32 ipfsHash, uint256 pricePerCall, address token).
  2. Discover – Buyer queries the marketplace’s off‑chain index (GraphQL or plain HTTP) for descriptors matching a keyword or input/output type.
  3. Quote – Buyer reads the on‑chain price, optionally asks the seller for a signed nonce‑based quote to avoid front‑running.
  4. Escrow & Call – Buyer approves the marketplace to pull the required token amount, then invokes Marketplace.execute(bytes32 ipfsHash, bytes calldata input). The contract:
    • Verifies the seller’s signature on the descriptor.
    • Escrows funds.
    • Calls the seller’s agent via its registered HTTP/gRPC endpoint (using a trusted oracle or relayer).
    • On successful response, releases funds to the seller; on timeout or error, funds are returned (minus a small dispute fee).
  5. Feedback – Both parties can submit a signed receipt (success/failure) that updates reputation scores.

Minimal Working Example

Below is a buyer written in Python (3.11+) that interacts with a mocked marketplace contract via Web3.py. The seller side is a simple Flask endpoint that echoes back a string and signs the response with an ECDSA key (secp256k1).

Note: This code is intentionally stripped down for clarity. Production systems need proper key management, retry logic, circuit breakers, and gas estimation.

1. Seller (agent)

# seller.py
import os
import json
from eth_account import Account
from eth_account.messages import encode_defunct
from flask import Flask, request, jsonify

app = Flask(__name__)

# In practice load from a vault or HSM
PRIVATE_KEY = os.getenv("SELLER_PRIV")  # hex string
ACCOUNT = Account.from_key(PRIVATE_KEY)

def sign_payload(payload: dict) -> str:
    """Return a hex ECDSA signature of JSON‑canonicalized payload."""
    message = json.dumps(payload, sort_keys=True).encode()
    encoded = encode_defunct(text=message.decode())
    signed = ACCOUNT.sign_message(encoded)
    return signed.signature.hex()

@app.route("/echo", methods=["POST"])
def echo():
    data = request.get_json(force=True)
    # Very simple capability: return the same string upper‑cased
    result = {"output": data.get("input", "").upper()}
    # Attach a signature so the buyer can verify authenticity
    result["signature"] = sign_payload(result)
    return jsonify(result)

if __name__ == "__main__":
    # Listen on localhost for demo; in prod expose via TLS + auth
    app.run(host="0.0.0.0", port=8080)
Enter fullscreen mode Exit fullscreen mode

2. Buyer


python
# buyer.py
import os
import json
import time
import requests
from web3 import Web3
from eth_account import Account
from eth_account.messages import encode_defunct

# ---- Configuration -------------------------------------------------
RPC_URL = os.getenv("BASE_RPC")               # e.g. https://base.mainnet.rpc.dev
MARKETPLACE_ADDR = Web3.to_checksum_address(os.getenv("MPL_ADDR"))
SELLER_ENDPOINT = os.getenv("SELLER_URL")     # http://seller:8080/echo
BUYER_PRIV = os.getenv("BUYER_PRIV")          # hex string
USDC_ADDR = Web3.to_checksum_address(os.getenv("USDC_ADDR"))  # Base USDC
# -------------------------------------------------------------------

w3 = Web3(Web3.HTTPProvider(RPC_URL))
account = Account.from_key(BUYER_PRIV)
usdc_abi = [{"constant":False,"inputs":[{"name":"spender","type":"address"},
                                        {"name":"value","type":"uint256"}],
             "name":"approve","outputs":[{"name":"","type":"bool"}],
             "type":"function"},
            {"constant":True,"inputs":[{"name":"owner","type":"address"},
                                       {"name":"spender","type":"address"}],
             "name":"allowance","outputs":[{"name":"","type":"uint256"}],
             "type":"function"}]
usdc = w3.eth.contract(address=USDC_ADDR, abi=usdc_abi)

# Minimal marketplace ABI (only register/execute needed)
mpl_abi = [
    {"inputs":[{"internalType":"bytes32","name":"ipfsHash","type":"bytes32"},
               {"internalType":"uint256","name":"price","type":"uint256"},
               {"internalType":"address","name":"token","type":"address"}],
     "name":"register","outputs":[],"type":"function"},
    {"inputs":[{"internalType":"bytes32","name":"ipfsHash","type":"bytes32"},
               {"internalType":"bytes","name":"input","type":"bytes"}],
     "name":"execute","outputs":[{"internalType":"bytes","name":"output","type":"bytes"}],
     "stateMutability":"payable","type":"function"}
]
marketplace = w3.eth.contract(address=MARKETPLACE_ADDR, abi=mpl_abi)

def approve_usdc(amount_wei: int):
    """Allow marketplace to pull USDC from buyer."""
    txn = usdc.functions.approve(MARKETPLACE_ADDR, amount_wei).build_transaction({
        "from": account.address,
        "nonce": w3.eth.get_transaction_count(account.address),
        "gas": 100_000,
        "maxFeePerGas": w3.to_wei(2, "gwei"),
        "maxPriorityFeePerGas": w3.to_wei(1, "gwei"),
        "chainId": w3.eth.chain_id,
    })
    signed = account.sign_transaction(txn)
    tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
    w3.eth.wait_for_transaction_receipt(tx_hash)
    print(f"Approved {amount_wei/1e6} USDC")

def call_marketplace(ipfs_hash: str, payload: dict):
    """Escrow payment, invoke seller, return verified output."""
    # 1. Determine price (here we hard‑code; in reality read from registry)
    price_wei = w3.to_wei(0.05, "ether")  # $0.05 worth of USDC (1 USDC ≈ 1e-6 ETH on Base)
    approve_usdc(int(price_wei * 1.1))   # slight over‑approve for safety

    # 2. Execute via marketplace
    input_bytes = json.dumps(payload).encode()
    txn = marketplace.functions.execute(
        Web3.to_bytes(hexstr=ipfs_hash),
        input_bytes
    ).build_transaction({
        "from": account.address,
        "value": 0,  # payment handled via ERC20 transfer inside contract
        "gas": 250_000,
        "maxFeePerGas": w3.to_wei(2, "gwei"),
        "maxPriorityFeePerGas": w3.to_wei(1, "gwei"),
        "chainId": w3.eth.chain_id,
    })
    signed = account.sign_transaction(txn)
    tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

    # 3. Decode output
    output_bytes = marketplace.functions.execute(
        Web3.to_bytes(hexstr=ipfs_hash),
        input_bytes
    ).call()
    result = json.loads(output_bytes.decode())

    # 4. Verify seller signature (optional but recommended)
    sig_hex = result.pop("signature")
    msg = encode_defunct(text=json.dumps(result, sort_keys=True))
    recovered = Account.recover_message(msg, signature=bytes.fromhex(sig_hex))
    assert recovered.lower() == ACCOUNT.address.lower(), "Bad signature"

    return result

if __name__ == "__main__":
    # Example: seller has already registered;
Enter fullscreen mode Exit fullscreen mode

Top comments (0)