Discovery — service_market
Search date: 2026-09-22 Objective: a decentralized service marketplace — providers register services, customers purchase with on-chain payment, completed work is resolved, and escrowed payment is released to the provider only after successful resolution. Must resist unauthorized settlement, double payment, and stuck funds.
This record follows docs/DISCOVERY_AND_REUSE.md. It is the gate, not a summary written afterwards.
1. What was searched
| Step | Source | Method |
|---|---|---|
| 1 | Cosmic Bull catalog | catalog/README.md, catalog/applications.md, catalog/primitives.md read in full |
| 2 | Cosmic Bull /p/ and /r/ sources | all 33 .gno files under pearl/p/ and pearl/r/ read as source, not via catalog summaries |
| 3 | On-chain pearl-1 | exhaustive enumeration, 500 packages at height 626979 — 167 under /p/, 333 under /r/ |
| 4 | Public Gno / GitHub | pinned examples tree (all 1,503 .gno files; 144 kept + 180 quarantined package dirs); GitHub code search; gnolang/tx-exports by keyword |
The chain listing exceeds the query tool's single-response budget, so step 3 was run as an alphabet slice — gno.land/p/{a..z}, gno.land/r/{a..z}, and gno.land/r/g1{0..9,a..z} for the address-namespace bulk. No prefix was left unqueried. vm/qpaths is HEAD-only, so this is a snapshot at that height.
Two properties were treated as the decisive filter, because they are the heart of the objective:
- Real coin custody — a
chain/bankerand anOriginSendenvelope, versus accounting that only moves integers. - A resolution step gating payout — value that stays immobile across transactions until a separate resolving act releases it.
2. Relevant implementations found
Cosmic Bull's own portfolio
| Package | Custody | Resolution gate | Classification |
|---|---|---|---|
r/market | real (coinio+feeledger) | none | RELATED |
r/bounty_panel | real, pooled openTotal | yes (M-of-N panel) | RELATED |
r/grants | real, pooled | yes (funder selects) | RELATED |
r/bounties | real, pooled | yes (funder awards) | RELATED |
r/service_registry | none | n/a | RELATED (complementary) |
p/coinio | — | — | REUSABLE PRIMITIVE |
p/feeledger | — | — | REUSABLE PRIMITIVE |
p/duebook, p/permbook | — | — | REUSABLE, not needed (see §4) |
External, live on pearl-1
| Package | Classification | Concrete reason it is not reused |
|---|---|---|
r/samcrew/escrow_v3 | RELATED — closest functional neighbour | Genuinely custodial and resolution-gated, with timeout valves. But it is a bilateral contract, not a marketplace: CreateContract(cur realm, freelancer address, …) means the client names the freelancer, so there is no provider registration and no service catalog — no discoverable supply side, and no standing offer a customer can purchase against. Authority is a hardcoded AdminAddress with no rotation function, and that key is simultaneously sole dispute arbiter, a unilateral ReleaseFunds path, and a permanent Pause() kill switch over every fund path. It derives identity via unsafe.PreviousRealm() inside crossing functions rather than cur.Previous() (security.md Class 2 shape). It hard-imports r/samcrew/memba_market_config, so adopting it means inheriting a third party's live DAO fee spine. |
r/samcrew/agent_registry_v2 | RELATED | Provider registration + prepaid credits, genuinely custodial — but settlement is provider-asserted: UseCredit is callable by the agent creator, so the provider unilaterally debits the customer. That is precisely the unauthorized-settlement property this objective must resist. |
r/samcrew/memba_nft_market_v3_2, nsmarket/v1–v4 (×3 namespaces), g1hx4z2k…/nftmarket* (28 realms), gnomads_market, memba_token_otc_v2 | RELATED | All settle a transferable token asset atomically against coins. There is no delivery phase, so there is no resolution step to gate. nsmarket/v4's own package doc is explicit that holding either side "is a different kind of business". |
r/g16m0r7…/{escrow, bountyboard, subscription, orderbook, splitpayment}, r/g1nyjym92…/escrow | RELATED | Two disqualifying defects throughout. No custody: no chain/banker import anywhere in the family; Payout returns a formatted string and moves nothing. Designation forgery (security.md Class 2): Approve(caller, dealID string), Resolve(arbiter, …), Claim(hunter, …) take the actor's claimed identity as a string parameter, so any address can act as any party. g1nyjym92…/escrow additionally lets a caller-supplied id overwrite an existing deal. |
r/moul/x/daily/escrow/v1 | RELATED | Its own doc says it: "Accounting only: no funds move." Models the mutual-confirmation handshake correctly and derives identity correctly, but with no custody it cannot resist double payment or stuck funds — there are no funds. |
Public ecosystem
r/demo/defi/atomicswap (HTLC, hashlock-gated rather than acceptance-gated), p/demo/subscription, r/jjoptimist/eventix (instant delivery, and carries an in-source SECURITY XXX note that its paymentToken any is unvalidated) — all RELATED, and none of the three is deployed on pearl-1.
3. The delta
No package found combines all four of: provider registration, an open catalog a customer purchases against, real coin custody, and an acceptance-gated release.
escrow_v3has custody and resolution, but no provider registry and a non-rotatable third-party arbiter over funds.service_registryhas registration, but holds nothing — and its own INTEGRATOR CONTRACT forbids routing a payment off one of its resolutions.markethas purchase and custody, butBuycredits the seller in the same transaction as payment (market.gno:203-207) and goes terminal, so value never enters an escrow pool.
Stated precisely: every existing Cosmic Bull escrow realm escrows funder-first — the party who will later decide escrows their own money and then solicits work. This objective requires customer-first escrow against a standing provider offer. Four consequences follow, and they are the new work:
- A standing offer that survives N orders.
market's listing is single-shot (Active → Soldis terminal), so one listing is one sale. A service offering must be purchasable repeatedly. - Escrow on the purchase path. No existing package has both pay-against-a-pre-existing-offer and hold-pending-resolution.
- A per-order state machine —
Purchased → Delivered → Released, with escrow immobile until the acceptance edge. - Valve polarity inverts. Existing valves refund the funder on inaction. Here, after delivery, customer silence must eventually release to the provider — otherwise a non-responsive customer traps completed work. Copying an existing valve unchanged would ship a live griefing vector.
4. Reuse decisions
Imported as-is:
| Package | Contribution |
|---|---|
p/g1ut6…/coinio | Receive (escrow-in at purchase), Payout (release), Sweep (surplus), HeldAt. Honours the once-per-transaction Receive contract and debit-before-payout. |
p/g1ut6…/feeledger | Claimable balances and fee pot after resolution; guarantees credited + fee == amount; overflow-checked. |
gno.land/p/nt/avl/v0 | Ordered service/order trees with deterministic Iterate for Render. |
gno.land/p/nt/markdown/sanitize/v0 | InlineText over provider-supplied titles and descriptions — the service_registry Y5 fix, live-verified. |
All four confirmed present on pearl-1 by live enumeration. A §7a closure check proves byte equivalence separately; path existence is not byte equivalence.
Adopted as a pattern, with credit, not as a dependency:
escrow_v3.ClaimDisputeTimeout— resolving a timeout according to the pre-timeout status rather than always refunding. This is the correct answer to delta point 4 and was arrived at independently by that realm first.grantsmaxFeeBpsceiling (audit Y1) — a provider's published offer carries the maximum fee it consents to, so an admin fee change cannot retroactively alter an in-flight offer's economics.market/bounty_panelrejectStraySend, fee snapshotting, two-step admin handover, and theH == …conservation identity.
Considered and not used:
| Package | Reason |
|---|---|
p/duebook | Its consume-then-act Claim would make a timeout replay-proof, but the realm's own terminal status transitions already give that property without a second state store to keep consistent. Its cross-realm consumption path also has no live consumer yet (audit Y1). |
p/permbook | Roles are not in scope. Per-order buyer and seller identity must come from cur.Previous().Address() directly, never from a permission set. |
r/service_registry | Deliberately not used to resolve providers. Its INTEGRATOR CONTRACT states that a name is not an authorization and must not route a payment. |
p/samcrew/memba_market_core_v2 | SplitProceedsBPS is clean money math, but it is three-way NFT-shaped and frozen to the Memba generation. feeledger covers the two-way split without coupling to a third party's live config realm. |
p/nt/treasury/v0 | Heavier multi-banker dispatch surface, marked v0 Unaudited; coinio is the minimal guard-trio contract already audited and pearl-validated here. |
5. Novelty statement
"No relevant existing implementation was found in the searched sources."
This is bounded by §6 and is deliberately not a claim that nobody has built this.
6. Search-confidence limits
Exhaustive: pearl-1 package paths at height 626979 (500 packages, every prefix queried); the pinned examples tree by filename and by content keyword; the coin-custody set in that tree (banker.NewBanker/SendCoins/OriginSend, 28 files) enumerated and reviewed by path.
Sampled, not exhaustive: GitHub code search — escrow extension:gno alone reports ~980 results; roughly 40 were read by relevance and 6 in full. GitHub code search indexes incompletely and ranks opaquely. gnolang/tx-exports was searched by keyword, not enumerated.
Not searched at all: AmozPay/gbnty (GitHub API rate limit exhausted before it could be read — an unresolved gap); non-GitHub hosts; the Gno Playground corpus; forum- and chat-posted code. Other chains — sapphire-1 and local — were not enumerated; this is pearl-1 only.
Depth limit: most external classifications rest on outlines plus targeted full reads. Per the skill's own warning, an outline is navigation, not evidence; names and doc comments are realm-authored claims. escrow_v3, g16m0r7…/escrow, and market were read in full because they were load-bearing.
A correction recorded against this search's own reliability. One search strand reported samcrew/escrow_v3 as existing only on topaz, and its dependency memba_market_config as absent from pearl-1. Both claims were wrong — a direct gno_packages query on gno.land/r/samcrew/ returns both. Had that gone uncorrected, the closest functional neighbour on the target chain would have been dismissed as not deployed. The lesson is the standing one: a negative deployment claim is only as good as the query that produced it, and the query is cheap enough that it should always be run.