---
profile: elgora_markdown_bounty_challenge_v1
escrow_amount: "20000000"
submission_deadline: 1795219140
payout_policy: winner_take_all
---

# Stand up an agent-run Lab on Molecule: an AI owner, with Claude, Codex and Grok joined as contributors through Agent Connect

## Summary
Molecule Labs gives a research project an onchain identity (a LabNFT with its
own account) and a data room. Agent Connect lets an outside AI agent join a
Lab: the Lab's owner approves the connection, and the agent gets its own
wallet and a role on the Lab. Starting from the public docs alone, set up one
Lab on Molecule production (Base mainnet) that is run by agents: an AI agent
owns it, and three further AI agents, one each from Claude, Codex and Grok,
are joined to it as Contributors through Agent Connect, upload a file each
and read each other's files. Report where the docs did not work as written
and where a human had to step in. The Lab and the agents' roles are checked
against Base; the connections and the file work are decided from the recorded
tool results in the Submission. Among valid Submissions, the one submitted
first wins.

## Challenge details

### The run
Everything happens on Molecule production, on Base (chain id 8453). The run
has three steps:

1. **Lab.** Mint a LabNFT to the Solver address and register the Lab so that
   it has a data room, in any way the docs describe.
2. **Contributors.** Connect three AI agents to the Lab through Agent Connect
   at `https://mcp.molecule.xyz`, each with the Contributor role: one from a
   Claude app (claude.ai or Claude Code), one from Codex and one from Grok.
   The Lab's owner, the Solver address, approves each connection as the docs
   describe. To Agent Connect each app is its own agent with its own wallet,
   so the three agents have three different wallets.
3. **Work.** Each agent uploads one file to the Lab with the
   `data_room_upload` tool, and then reads one other agent's file with the
   `data_room_read` tool:

   | Agent | Uploads | Reads |
   |---|---|---|
   | Claude | `agent-claude.md` | `agent-codex.md` |
   | Codex | `agent-codex.md` | `agent-grok.md` |
   | Grok | `agent-grok.md` | `agent-claude.md` |

Each uploaded file holds the text `written by ` followed by the uploading
agent's wallet address in lowercase, starting with `0x` and with all 40 hex
digits. No real research data, no personal data and nothing confidential
goes into the Lab. Molecule production is public and permanent: the Lab and
its files' history stay.

The **Solver address** is the account that makes the Submission on ElgoraHub.
The same address, on Base, receives the LabNFT and is the Lab's owner.

The aim is a Lab in which every step is done by AI agents, including the
owner's approval of each connection. Whether a human helped is not part of
acceptance; it is reported, step by step, in `friction.md`.

### What decides that the run happened
Two kinds of evidence decide acceptance, and nothing else does:

- **Onchain evidence, submitted as files.** The Submission holds the mint
  transaction's receipt and, for one Base block the Solver chooses (the
  **check block**), the Lab's owner and each agent wallet's role as read from
  Base at that block. These submitted values decide the onchain conditions.
  Base, a public source named under Inputs, is the authority they are
  compared with: a submitted value that differs from Base at the stated
  transaction or block is incorrect. A connection made through Agent Connect
  leaves the agent's wallet with the Contributor role and the agent flag set.
- **The recorded tool results** in `run.json`: for each of the three agents,
  the result of `agent_connect_whoami`, of its `data_room_upload` call and of
  its `data_room_read` call, as the Agent Connect server returned them. The
  Lab's data room is not publicly readable, so whether the agents were
  connected through Agent Connect and whether the files were stored and read
  is decided from these records and their agreement with the onchain
  evidence. The stored files are not read back, and the order in which the
  tool calls were made is not part of acceptance.

### What the friction log is
`friction.md` is the second thing this bounty buys. For each of the three
steps it says whether the instructions in the docs worked as written and
whether the step was done without a human. Where the docs did not work, it
names the docs page, quotes the instruction, says what happened instead and
says what worked. Where a human stepped in, it says what the human did.

### Result meaning
A valid Submission shows that a Lab owned by the Solver address has three
agent wallets holding the Contributor role with the agent flag, that the
recorded connections are from a Claude app, Codex and Grok, and that the
recorded tool results show each agent reading a file another agent uploaded.
It does not show that no human took part, and it does not show that the path
works every time. The recorded tool results and `friction.md` are the
Solver's own record.

## What you need to submit (Deliverables)

| File | Required | Format | Purpose |
|---|---:|---|---|
| `run.json` | yes | UTF-8 JSON, one object, fields below | the record of the run |
| `chain/mint_receipt.json` | yes | UTF-8 JSON | the `result` object of the JSON-RPC call `eth_getTransactionReceipt` for `mint_tx_hash` on Base, unchanged |
| `friction.md` | yes | UTF-8 Markdown | per-step account of where the docs did not work and where a human stepped in |
| `source/` | no | folder of source files, no private key or secret | code and agent instructions used for the run; informational only, not scored |

`run.json` fields:

- `solver_address`: the Solver address, `0x` followed by 40 hex digits.
- `mint_tx_hash`: hash of the Base transaction that minted the LabNFT.
- `token_id`: the LabNFT token id minted, as a decimal string.
- `ocl_id`: the Lab's `oclId`, `0x` followed by 64 hex digits.
- `lab_account`: the Lab's account address created by the mint.
- `check_block`: the number of the check block, a decimal string.
- `owner_at_check_block`: the address `ownerOf(uint256)` on the LabNFT
  contract returns for `token_id` at the check block.
- `agents`: an object with exactly the three keys `claude`, `codex` and
  `grok`. Each value has:
  - `agent_wallet`: the agent's wallet address;
  - `role_at_check_block`: an object with `role`, `expiry` and `is_agent`,
    the three values `getRole` on the AccessResolver returns for `ocl_id`
    and this wallet at the check block;
  - `whoami`: the JSON result of that agent's `agent_connect_whoami` call,
    made after it was connected to the Lab;
  - `upload`: an object with `arguments` (the arguments of that agent's
    `data_room_upload` call) and `result` (what the tool returned);
  - `read`: an object with `arguments` (the arguments of that agent's
    `data_room_read` call) and `result` (what the tool returned).

`friction.md` has one section per step, headed `## Step 1` to `## Step 3`.
Each section starts with these two lines, each ending in `yes` or `no`:
`Worked as written:` and `Done without a human:`. A section with
`Worked as written: no` holds one or more entries, each with the docs page
path or URL, a quote of the instruction, what happened instead, and what
worked. A section with `Done without a human: no` says what the human did.

## Inputs, Materials and References

| Input | Why it is needed | How to get it | SHA-256 content hash |
|---|---|---|---|
| Molecule docs | the only instructions for the run | https://docs.molecule.xyz (index for agents: https://docs.molecule.xyz/llms.txt); Agent Connect page https://docs.molecule.xyz/ai-tooling/agent-connect ; source at https://github.com/moleculeprotocol/docs ; no sign-in | none: the pages as published |
| Agent Connect server | how the three agents join the Lab and work in it | `https://mcp.molecule.xyz`; each connection is approved by the Lab's owner in the Molecule app, https://app.molecule.xyz | none: a live service |
| Base | the chain the submitted onchain values are compared with | any Base RPC, for example https://mainnet.base.org ; explorer https://basescan.org ; no sign-in | none: public chain state |

Contracts on Base used by the acceptance criteria:

| Name | Address |
|---|---|
| LabNFT | `0x9F96027eeAFb9ad5F2b5d7043B36Ee96B2EeBE92` |
| AccessResolver | `0x89a14Be8f7824d4775053Edad0f2fA2d6767b72B` |

The AccessResolver function used is
`getRole(bytes32 oclId, address account) returns (uint8 role, uint64 expiry, bool isAgent)`,
where role 2 is Contributor. On 12 November 2026 the Poster read the role of
an agent wallet that had been connected to a Lab through Agent Connect from
Claude Code: the call returned role 2 and `isAgent` true, and that agent's
`agent_connect_whoami` result was a JSON object with `host` equal to
`claude-code`, an `agentWallet`, and a `grants` array whose entries hold
`oclId`, `role`, `status` and a `lab` object with `name` and `shortname`.
The Poster has not observed the `host` value of a Codex or a Grok
connection; the host rule in the acceptance criteria is written to hold for
any value that names the app.

## Acceptance Criteria

1. **Files.** `run.json` parses as one JSON object with every field listed
   above. `friction.md` has the three step sections, each starting with its
   `Worked as written:` and `Done without a human:` lines.
2. **Solver address.** `solver_address` in `run.json` equals the account that
   made the Submission on ElgoraHub, compared without regard to letter case.
3. **Mint.** `chain/mint_receipt.json` is the receipt of `mint_tx_hash` on
   Base: its `transactionHash` equals `mint_tx_hash` and its content equals
   the receipt Base returns for that hash. The transaction is successful and
   its block timestamp is at or after 1794528000 (13 November 2026 00:00
   UTC). The receipt holds a log emitted by the LabNFT contract for the
   ERC-721 event `Transfer(address,address,uint256)` with `from` the zero
   address, `to` equal to `solver_address` and `tokenId` equal to
   `token_id`. The receipt also holds a log emitted by the LabNFT contract
   for the event
   `OclIdentityCreated(address indexed account, bytes32 indexed oclId, uint256 indexed tokenId, bytes32 salt, uint256 canonicalChainId)`
   with `account` equal to `lab_account`, `oclId` equal to `ocl_id` and
   `tokenId` equal to `token_id`.
4. **Owner.** `check_block` is a block of Base later than the mint
   transaction's block. `owner_at_check_block` equals `solver_address`, and
   equals what `ownerOf(uint256)` on the LabNFT contract returns for
   `token_id` on Base at the check block.
5. **Agent roles.** The three `agent_wallet` values differ from each other
   and from `solver_address`. For each agent, `role_at_check_block` holds
   `role` 2 and `is_agent` true, and its three values equal what `getRole`
   on the AccessResolver returns for `ocl_id` and that `agent_wallet` on
   Base at the check block.
6. **Connections.** For each agent, `whoami` holds an `agentWallet` equal to
   that agent's `agent_wallet` and a `grants` entry whose `oclId` equals
   `ocl_id` and whose `role` is `contributor`. Addresses and ids are compared
   without regard to letter case. The `host` value in the `whoami` of the
   agent under key `claude` contains the letters `claude`, that of the agent
   under key `codex` contains `codex`, and that of the agent under key `grok`
   contains `grok`, each without regard to letter case.
7. **Work.** Each of the six `arguments` objects names the Lab either by an
   `oclId` equal to `ocl_id`, or by a shortname equal to the `lab.shortname`
   of the `grants` entry for `ocl_id` in the same agent's `whoami`. Each
   agent's `upload.arguments` gives the path in the Uploads column of the
   table in Challenge details for that agent, and its `read.arguments` gives
   the path in the Reads column. Each agent's `read.result` contains the
   text `written by ` followed, in lowercase, by the `agent_wallet` of the
   agent that uploads that path. That read is what establishes that the
   other agent's upload succeeded; an `upload.result` is recorded but no
   further condition is placed on it.
8. **Friction log.** Every step section of `friction.md` marked
   `Worked as written: no` holds one or more entries, each with a docs page,
   a quote of the instruction, what happened instead and what worked. Every
   section marked `Done without a human: no` says what the human did.

## How is the winner selected?
- Valid Submissions are those meeting every acceptance criterion.
- Among valid Submissions, the active Submission with the lowest
  `submission_call_count` wins. `submission_call_count` is ElgoraHub's
  bounty-wide count of accepted submissions at that call; it rises with every
  call, so no two Submissions share one, and resubmitting replaces the active
  Submission and gives it a later count.
- If none is valid, the outcome is `no_valid_submission`.

## Out Of Scope
- Real research data, personal data or anything confidential in any file.
- Security testing of Agent Connect, the API or the contracts.
- Granting a role by calling the AccessResolver directly instead of
  connecting through Agent Connect.
- Tokenization and payments.
