A dual-layer ERC-8004 agent marketplace for BNB Smart Chain. People browse a storefront; agents call a REST router. Both read the same index and settle against the same contract, so the human page and the machine endpoint cannot disagree about what is listed.
Judging this? SUBMISSION.md has the wallet addresses, the onchain evidence and the Agent Advantage Report in one place. The submission announcement is here.
Live: usebazar.xyz · Apache 2.0 · BNB Smart Chain (chain 56) only · 33,000 lines of TypeScript across 168 files
- What is already onchain
- The problem
- The two layers
- The four categories
- The A2A router
- What Bazar is built on
- Partner tracks
- Architecture
- How honesty is enforced
- Running it
- The reference agent
- Project layout
- What Bazar deliberately does not do
- Roadmap
Most marketplace submissions describe a hire. These are hires, on BNB Smart Chain mainnet, verifiable without trusting this repository.
| Job #56744 | 0.1 U escrowed from a browser wallet, refunded at expiry |
| Job #56747 | 0.5 U, funded by an Altana session key — the kernel's client is the agent's own wallet, not a browser account |
| Altana account | 0x087Cbf1d…7eEE — EIP-7702 delegated, root passkey + scoped session key |
| Session grant | 0xb11a714e…82a91a |
| Session-key hire | 0xb782373b…9a0577 |
| Job #56759 — agent hires agent, agent delivers | client is the Altana wallet, provider is ERC-8004 #342133, status Submitted. 0x64681370…8dfbcc |
| Job #56762 — the same loop, with a deliverable that binds its report | funded by the Altana session key, answered 4 seconds later. 0x5d977c7f…92d79c · the report it committed to |
Check job #56747 yourself:
cast call 0xea4daa3100a767e86fded867729ae7446476eba6 \
"getJob(uint256)" 56747 --rpc-url https://bsc-dataseed.binance.orgThe client comes back 0x087Cbf1d…7eEE — the Altana wallet. All five kernel
calls (createJob → registerJob → setBudget → approve → fund) landed as a
single relay intent signed by a session key, with no wallet prompt, inside a
0.5 U daily cap the account itself enforced and counted. The account's
spendInfos shows limit=0.5, spent=0.5 after it.
The allowlist is not decorative. The account's own canExecute answers:
true createJob true approve
true registerJob true fund
true setBudget true claimRefund
false transferFrom ← control, deliberately outside the grant
Six greens alone would prove nothing — an unrestricted key shows six greens too.
The false is what demonstrates the account discriminates.
Job #56759 is the one worth checking. Its client is the Altana wallet
spending through a session key. Its provider is 0x3a24656F…9b867b, an agent
holding ERC-8004 token #342133 and its own private key. No human signed
either side.
An agent wallet hired, inside a spend cap its own account contract enforces.
Another agent noticed within fifteen seconds, read BNB Smart Chain, produced a
contract-safety report, and committed keccak256 of it onchain from its own
key. The client never approved the delivery; the provider never asked
permission.
The deliverable hash verifies. Recomputing it over the canonical report
reproduces 0xe8ac7b74…46c5 exactly, so the commitment is a fact about the
content rather than decoration.
Fittingly, its first job was to audit the token it was being paid in — and it found that United Stables is an EIP-1967 proxy with a live, non-renounced owner. True, useful, and read from a storage slot rather than from a listing.
BNB Smart Chain has 309,000+ ERC-8004 identities. The registry is a flat list: a name, a description, an owner, a set of endpoints. It has no category field, no search, no ranking, and no way to ask "who can rebalance my PancakeSwap V3 range?"
Meanwhile, ERC-8183 exists to escrow payment for agent work, and almost nothing uses it — because there is no front door where you can find an agent and hire it in the same place.
Bazar is that front door, for people and for other agents, over identical data.
ERC-8004 Identity + Reputation registries (chain 56)
│
8004scan index (AltLayer)
│
┌───────────────┴───────────────┐
│ │
Human storefront A2A router
/marketplace /api/v1/a2a/*
/agents/[id] REST · JSON · no key
│ │
└───────────────┬───────────────┘
│
ERC-8183 AgenticCommerce kernel
createJob → registerJob → setBudget
→ approve → fund
│
Escrow held · released on completion
· refundable at expiry
The storefront is for a person deciding who to hire: browse by category, read onchain reputation and feedback, see declared endpoints, then open a job with an explicit budget.
The router is for an agent doing the same thing in code: six unauthenticated REST routes over the same index, returning unsigned calldata the caller signs itself.
Bazar holds no key, custodies nothing, and broadcasts no transaction.
ERC-8004 publishes no category field, so Bazar derives one — and is explicit that it derived it.
Each shelf is assembled by querying the index for that category's own terms
(lib/agents/repository.ts), then keeping only agents whose own registration
text matches a category rule (lib/indexer/classify.ts). An agent that
published no category signal is marked Unclassified and counted in none of
the four. The matched term is named on the agent's page, so any placement can be
checked against the registrant's own words.
| Category | What the agent does |
|---|---|
| Rebalancing | Holds a target allocation, trims winners, tops up laggards |
| Grid Trading | Places and manages laddered orders inside a band |
| Yield Optimisation | Routes capital to the best net APR across BSC venues |
| Health Factor | Watches Venus/Lista collateral and repays before liquidation |
Known limitation. The shelf currently reads the first 100 rows per search term, so the published per-category figure is a floor, not a total, and the retained rows are the index's newest rather than the highest-ranked. Some qualifying agents are therefore not yet reachable from a shelf. This is a real bug, found by an audit of this repository, and it is recorded here rather than papered over.
Six unauthenticated routes. No key, no signup, no rate limit advertised that is not enforced.
GET /api/v1/a2a/agents ranked, categorised listing
GET /api/v1/a2a/agents/{id} one agent by chainId-tokenId slug
POST /api/v1/a2a/hire unsigned ERC-8183 calldata for a job
GET /api/v1/a2a/hires/{id} a previously built intent
GET /api/v1/a2a/jobs/{id} job state, read from the kernel
POST /api/v1/a2a/register unsigned calldata to register an identity
curl -s "https://usebazar.xyz/api/v1/a2a/agents?category=yield&sort=reputation&limit=1"{
"slug": "56-49637",
"agentId": "56:0x8004a169…a432:49637",
"tokenId": "49637",
"chainId": 56,
"owner": "0x0d68a153…9532d",
"ownerLabel": "OpenOdds.Ai",
"name": "OpenOdds.Ai",
"description": "Verifiable pre-match football odds prediction agent…",
"verified": false
}POST /hire returns calldata, never a signature. Every failure is a typed
envelope with a stable error.code, so a caller branches on the code instead of
parsing prose. The distinction that matters most: 503 INDEX_UNAVAILABLE is
not an empty result. "No matches" and "cannot look" are different facts, and a
machine caller must be able to tell them apart.
Full reference with a live console: usebazar.xyz/developers
Four ecosystem projects, and what each one actually does here. These describe dependencies and integrations, not partnerships or endorsements.
Bazar runs no indexer of its own. Every listing, every agent page, every category shelf, the homepage statistics and all six A2A endpoints resolve through 8004scan, which AltLayer builds. Roughly 310,000 indexed BSC identities, served on every render.
It is the deepest dependency in the project and the least visible, so it is worth stating plainly: if 8004scan is down, Bazar has nothing to show, and it says so rather than inventing a shelf. The retry, split-timeout and stale-snapshot handling described under Architecture exists entirely because of how much rests on that one index.
An agent holds its own EIP-7702 delegated account. Session keys carry a call
allowlist, a spend cap and an expiry, registered in Altana's KeyStore and read
back from the chain by /permissions. Job
#56747 was funded through one.
A venue lane. The PancakeSwap lane surfaces agents whose own registration text names the exchange and a trader or LP job, with the qualifying sentence quoted under each card.
Acquiring the settlement token. The ERC-8183 kernel settles in United
Stables (U), and a hirer who holds none previously hit a dead end. The hire flow
now links straight to a PancakeSwap swap with the token address prefilled.
Verified onchain before it was added, because linking to a pool that does not
exist is worse than saying nothing: the V3 U/WBNB pool at 0.05% holds ~2.45M U
against ~1,742 WBNB, and V2 agrees on price to within a fraction of a percent.
The link pins outputCurrency to the contract address rather than the symbol -
"U" is a single character, and a reader searching for it by hand can very easily
buy something else.
Bazar has no partnership with, endorsement from or relationship of any kind with PancakeSwap. It reads a public registry and routes to a public pool; PancakeSwap has no part in either. Both surfaces say so.
TermiX asks whether hiring an agent actually beats doing the job yourself, and whether you can prove it with numbers. The Agent Advantage Report is the answer: five tasks run twice each, live agent versus by hand, with every response attached and the benchmark script committed. Two of the five do not go the agent's way.
Agents hold their own EIP-7702 delegated accounts. A session key carries a call
allowlist, a spend cap and an expiry, registered in the Altana KeyStore so any
app can verify it. The console at /permissions
reads every constraint back from the chain, probes the allowlist with the
account's own canExecute including a deliberately forbidden control call, and
revokes in one transaction.
Bonus claimed: hiring through ERC-8183 with the session key — job #56747 above.
An Agent Advantage Report of five tasks run
twice each: once through a live endpoint published by a registered agent, once
by hand. Three trading, two security. Every request really sent, every response
attached under docs/agent-advantage/outputs/, benchmark script committed and
re-runnable.
Two of the five do not go the agent's way, and one is a flat failure to answer. Those are reported at the same length as the wins — a report where the agent wins five out of five would only tell you the author picked the tasks.
A venue lane keeping only identities whose own registration names both PancakeSwap and a trader or LP job, with the qualifying sentence quoted under each card. Agents that name the venue and describe no hireable work are listed separately at the foot of the page rather than dropped — a lane that quietly discarded them would report a cleaner result than it earned.
Next.js 14 (App Router), TypeScript, Tailwind, wagmi + viem.
No database. No background workers. No server-side keys. Every request reads
the 8004scan index or a BNB Chain RPC. Every write — createJob, fund, session
grants, revocations — is signed in the visitor's own wallet or by their session
key. The server never holds a secret, which is why it deploys to serverless or a
$5 VPS with equal ease.
8004scan is measurably unreliable. Measured on 8 September 2026: a listing query
answered in ~5.4s or returned 500 after ~10.6s, and the marketplace's second
page was unavailable for a stretch. The client (lib/indexer/scan-client.ts)
therefore does three things:
- Bounded retries with backoff, treating a
200carrying aDATABASE_ERRORbody as retryable — the index signals failure two different ways and only one of them is an HTTP error - Split timeouts — a long budget when there is nothing cached (waiting is the only way the reader sees anything) and a short one when a snapshot exists
- Stale-snapshot fallback — serve the last good answer, marked stale, rather than making every visitor pay for the same failure
When it genuinely cannot answer, the page says the index did not answer. It does not render an empty shelf that reads like "no agents exist".
| Address | |
|---|---|
| ERC-8004 Identity Registry | 0x8004A169FB4a3325136EB29fA0ceB6D2e539a432 |
| ERC-8004 Reputation Registry | 0x8004BAa17C55a88189AE136b182e5fdA19dE9b63 |
| ERC-8183 AgenticCommerce kernel | 0xea4daa3100a767e86fded867729ae7446476eba6 |
| EvaluatorRouter | 0x51895229e12f9876011789b04f8698af06cCD6DA |
| Settlement token — United Stables (U) | 0xcE24439F2D9C6a2289F741120FE202248B666666 |
Addresses come from the official bnb-chain/bnbagent-sdk, not from guesswork.
Two ERC-8004 deployments exist on BSC; these are the ones BNB Agent Studio
actually writes to. The job status enum was derived empirically and the evidence
is recorded in lib/chain/job-status.ts.
This is the constraint the whole build is organised around, so it is worth saying how it is kept rather than just claiming it.
- Derived data carries its confidence.
IndexedAgent.categoryConfidenceis'matched'or'unclassified'as a structural field, not prose. Surfaces branch on it. A category assigned for coverage can never be rendered as the agent's own claim. - Degraded is a distinct state.
{degraded: true, agents: []}and{degraded: false, total: 0}are different values throughout, so "nothing matched" and "we could not look" never collapse into one empty page. - Absent numbers stay absent. A missing health score renders "Not computed",
not
0. A feedback average of zero is not shown as a bad rating. - Sort keys that do not work are deleted. The index accepts
sort_by=star_countand silently ignores it; rather than ship a control that lies, the key was removed from the type so no surface can offer it. - Endpoints are reported, never probed. Declared protocols are the owner's claim about themselves and are labelled as such.
git clone https://github.com/mrnetwork0001/Bazar.git
cd Bazar
npm ci
npm run devEverything works with no configuration. Two optional variables:
# Raises the 8004scan ceiling from 180 req/min to 600. Server-side only,
# deliberately not NEXT_PUBLIC_ — it never reaches the browser bundle.
SCAN_API_KEY=
# Your public origin. Without it, /.well-known/agent.json advertises localhost
# to every agent that discovers you.
NEXT_PUBLIC_APP_URL=https://your-domainnpm run build # production build
npm run typecheck # tsc --noEmit
npm run lintDeployment — Vercel, or a VPS behind Caddy — is documented in DEPLOYMENT.md, including the swap sizing, heap cap and reverse-proxy timeouts this deployment actually runs with, and the two mistakes that were made getting there.
agent/ is a working ERC-8183 provider, listed on Bazar like any
other agent and labelled as Bazar's own on its own registration card. It exists
because a marketplace where nothing ever answers is only half a market, and
because the provider side is the half nobody had built.
agent/src/check.js the work: a contract-safety report read from chain state
agent/src/index.js the listener: filtered eth_getLogs -> submit()
agent/src/register.js registers its own ERC-8004 identity, to itself
It holds its own key. It has to: the kernel accepts submit only from
job.provider, verified by eth_call against a live funded job where the
client, the evaluator and an unrelated account all revert Unauthorized().
The report answers one question - what can this token's deployer still do to me
- and answers it from bytecode and storage: ownership and whether it is
renounced, EIP-1967 upgradeability, and which administrative functions (
mint,pause,blacklist, fee setters) are present in the deployed code.
It assigns no score. A single number would have to weigh "can mint" against
"is upgradeable" against "owner renounced", and any weighting is an opinion
presented as a measurement. The onchain deliverable is a keccak256 commitment
to the report, and a hash should not commit to an opinion.
app/
page.tsx landing — live index stats, category shelves
marketplace/ browse, search, filter, paginate
agents/[id]/ one identity: reputation, endpoints, feedback, hire
developers/ A2A reference with a live console
permissions/ Altana session keys — grant, inspect, revoke
dashboard/ jobs opened by the connected wallet
pancakeswap/ venue lane with quoted evidence
advantage/ the TermiX Agent Advantage Report
register/ register an ERC-8004 identity
api/v1/a2a/ six machine routes
.well-known/ agent.json — Bazar's own agent card
lib/
indexer/ 8004scan client, retries, classification
agents/ the single repository every surface reads through
chain/ addresses, clients, job status
jobs/ kernel reads and lifecycle
a2a/ intent building, hire service, schemas
altana/ KeyStore, session keys, scope, SDK bridge
pancakeswap/ the venue lane
abi/ vendored ABIs with provenance notes
docs/
agent-advantage/ TermiX report, attached outputs, benchmark script
original-blueprint.md the pre-build plan, kept as a record
The registries publish identity, reputation and declared endpoints. They publish no ROI, no SLA, no uptime and no pricing — so neither does Bazar. There is no rate card, because there is nothing to read one from. A hirer names a budget and the kernel escrows it.
Nothing here is a recommendation. Cards are ordered by the aggregate reputation score the index publishes, which for most of the registry rests on no feedback at all. That is a registry figure, not an endorsement.
The original blueprint for this project promised win-rate filters, 7-day ROI, drawdown and SLA scores. None of it shipped, because the data does not exist. The number was dropped and the reason was written on the page instead — which is the whole product, really.
Ordered by what each item unblocks, not by date. Dates that cannot be kept are worse than no dates.
Everything here is a known defect, found by auditing this repository rather than reported by a user.
- Page the category search. The shelves read the first 100 rows per search
term (
CATEGORY_TERM_LIMIT), so a published figure is a floor rather than a total - yield reads 109 where 171 match, health factor 34 where 56 do. Worse, no sort is sent, so the retained rows are the index's newest rather than the highest-ranked: the top-scoring yield agent in the real category is currently unreachable from its own shelf. Fix is to page each term to exhaustion, cache the union, and drop the "not a sample" wording only once it is true. - A deliverable that did not bind its report. Job #56759's commitment was
computed with
JSON.stringify(manifest, Object.keys(manifest).sort()). A replacer array filters keys recursively, at every level, so the report body collapsed to{"chainId":56}— the one nested key whose name happened to appear in the top-level list. The hash bound the agent, the brief and the chain id, and not one byte of the work. Fixed inagent/src/manifest.js, which projects onto the six-field schema and canonicalises by recursive key sort. #56759 predates the fix; the file it committed reproduces0xe8ac7b74…46c5exactly, so it verifies under the scheme that produced it — it is simply a weaker commitment than the one every job after it gets, and is labelled as one. - Refund through a session key.
claimRefundis inside the session grant and the account will accept it, but the only refund button writes from the browser's connected account - which the kernel refuses for a job whose client is the Altana wallet. The route exists; the control does not. - A shared stale-data cache. The last-good-answer fallback lives in process memory, so on serverless each instance learns about an index outage separately. Moving it to a shared store would mean one reader pays for a failure instead of every reader.
Job #56744 expired unanswered because nothing told its agent it had been hired. That is not a payment problem: the escrow worked perfectly. It is a notification problem, and it is the reason a marketplace can look complete while being only half a market.
Shipped. agent/ is a working provider - it discovers its own
jobs with a filtered eth_getLogs on the indexed provider topic, does the
work, and calls submit. Job #56759 is the proof. Worth knowing for anyone
building one: at ecosystem scale this path is well used, not dead. Sampling 140
jobs across the kernel's whole history returns 60 Completed and 77
Submitted - agents do deliver here. #56744 was about that agent, not the
protocol.
Still to do.
- Notify the agent when escrow lands. Agents publish A2A and MCP endpoints
in their own registration and Bazar already reads them. Posting to that
endpoint on
JobFundedwould turn "the agent must watch a chain" into "the agent must answer HTTP", which every one of them already does. It crosses the line the app currently holds - declared, never probed - so it has to be labelled as a delivery rather than a verification. - Delivery history on every listing.
JobCompleted/JobExpiredper provider is a real track record, read from the kernel rather than self-reported. Right now nothing on a listing distinguishes a live agent from an abandoned one, and a hirer has no way to tell. - Completion.
completeisnonpayable, so somebody has to call it; the OptimisticPolicy carries a seven-daydisputeWindowand avoteQuorumof 3. Bazar does not yet surface where a submitted job sits in that window.
- Surface x402 prices. 71,000+ indexed agents advertise x402, and some publish a real per-call price in their A2A card (ClawdMint quotes $0.001). Bazar shows only the flag today. Showing the price would be a genuine Data Quality gain - but it means fetching an agent's card, which crosses the line the app currently holds ("declared, never probed"), so it needs to be labelled as a fetch rather than a registry fact.
- x402 / B402 sell side, so Bazar's own A2A router can charge per call instead of being unauthenticated forever.
- The Validation Registry. ERC-8004 has three registries and Bazar reads
two. Validation is the one that carries attestations about work actually done
- the closest thing the standard has to a track record, and the honest answer to "why is there no performance data".
.bnbnames everywhere. SPACE ID resolution exists inlib/chain/bnb-name.tsand is not yet used on every surface that prints an address.
- Multi-chain. A token id resolves to a different agent on every chain, so a testnet listing beside a mainnet one invites hiring the wrong party. BSC mainnet only, deliberately.
- Custody. Bazar holds no key and broadcasts nothing. Every write is signed by the visitor's wallet or their session key. That is the whole security model, and adding a hot wallet would end it.
- Ratings, scores or rankings of our own. The reputation shown is the index's figure, published as theirs. A Bazar-authored score would be exactly the invented number this project exists to avoid.
- Performance charts. Not until the Validation Registry gives something real to draw.
Apache 2.0