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

# Take a Lab on Molecule from mint to token on Base: x402 writes, a public and an encrypted file, and tokenization

## Summary
Molecule Labs gives a research project an onchain identity (a LabNFT with its
own account), a data room and, through the tokenizer, an ERC-20 token for the
Lab. Starting from the public docs alone, do the whole path once on Molecule
production (Base mainnet): mint a Lab, register it and upload one public and
one encrypted file through the x402 pay-per-call gateway, then tokenize the
Lab. Report every place where the docs did not work as written. The mint, the
payments and the token are checked on Base; registration and uploads are
decided from the recorded gateway calls 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
uses real funds: gas on Base and a small USDC payment per gateway call. It has
five steps, in this order:

1. **Mint.** Mint a LabNFT by calling `mintAndCreateAccount(address to)` on
   the OnChainLabFactory, with `to` set to the Solver address.
2. **Register.** Call the `createLab` mutation for the minted `oclId` through
   the production x402 gateway, paid from the Solver address.
3. **Public file.** Through the gateway, paid from the Solver address, call
   `initiateCreateOrUpdateFile` for that `oclId`, send the file bytes to the
   upload URL it returns, and call `finishCreateOrUpdateFile` with `path`
   `public-note.txt` and `accessLevel` `PUBLIC`.
4. **Encrypted file.** Through the gateway, paid from the Solver address,
   call `generateDataEncryptionKey`, encrypt a second file locally as the docs
   describe, call `initiateCreateOrUpdateFile` for that `oclId`, send the
   ciphertext to the upload URL it returns, and call
   `finishCreateOrUpdateFile` with `path` `private-note.txt`, `accessLevel`
   `ADMIN` or `HOLDERS`, and `encryptionMetadata`.
5. **Tokenize.** Tokenize the Lab by calling `tokenize` on the OclTokenizer
   from the Solver address, with the symbol `ELG` followed by the Lab's token
   id in decimal (for token id 195, `ELG195`) and an initial amount above
   zero. The docs describe how to get the membership agreement and the terms
   message to sign from the Tokenization API; that API needs a Molecule API
   credential, which the Solver obtains as the docs describe.

Both files hold short test text only: no real research data, no personal data
and nothing confidential. Their content is not part of acceptance. Molecule
production is public and permanent: the Lab, its files' history and its token
stay.

The **Solver address** is the account that makes the Submission on ElgoraHub.
The same address, on Base, receives the LabNFT, pays the gateway and calls the
tokenizer. Tokenizing means signing the Lab membership agreement terms that
the API returns, with that address.

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

- **Onchain evidence**, read from Base: the mint transaction, one settlement
  transaction per paid gateway call, the tokenization transaction and the
  Lab's token.
- **The recorded calls** in `run.json`: for each of the six paid gateway
  calls, the request that was sent, the JSON body the gateway returned, and
  the decoded `payment-response` header the gateway returned with it. The
  Lab's data room is not publicly readable, so whether the Lab was registered
  and the files were stored is decided from these records and their agreement
  with the onchain evidence. The stored files are not read back, the
  encryption is not re-checked, and how the calls were authenticated beyond
  the payment is not part of acceptance.

The gateway returns a `payment-response` header with every paid call. Decoded
from base64 it is a JSON object that carries the hash of the transaction in
which the payment was settled. That transaction is the call's **settlement
transaction**; it is what links a recorded call to the chain.

### What the friction log is
`friction.md` is the second thing this bounty buys. For each of the five steps
it says whether the instructions in the docs worked as written. Where they did
not, it names the docs page, quotes the instruction, says what happened
instead and says what worked.

### Result meaning
A valid Submission shows that an outside agent minted a Lab, completed six
paid gateway calls for it with responses reporting success, and tokenized it,
once. It does not show that the path works every time. The recorded calls 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 |
| `friction.md` | yes | UTF-8 Markdown | per-step account of where the docs did and did not work |
| `source/` | no | folder of source files, no private key or secret | the code that performed 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 of step 1.
- `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.
- `tokenize_tx_hash`: hash of the Base transaction of step 5.
- `ipt_address`: the address of the Lab's ERC-20 token created in step 5.
- `calls`: an array of exactly six objects, one per paid gateway call, in
  this order: `createLab`; `initiateCreateOrUpdateFile` and
  `finishCreateOrUpdateFile` for the public file;
  `generateDataEncryptionKey`; `initiateCreateOrUpdateFile` and
  `finishCreateOrUpdateFile` for the encrypted file. Each records the
  successful call and has:
  - `mutation`: the mutation name;
  - `request`: the JSON body sent to the gateway, with its `query` and
    `variables`;
  - `response`: the JSON body the gateway returned;
  - `payment_response`: the gateway's `payment-response` header for that
    call, decoded from base64 into a JSON object;
  - `settlement_tx_hash`: the transaction hash carried in
    `payment_response`.

  The value of `uploadUrl`, of `plaintextDEK` and of any other field holding
  key material in a `generateDataEncryptionKey` response or in
  `encryptionMetadata` may be replaced by the text `redacted`. Nothing else
  is changed. The `createLab` call selects `error` and `lab { oclId }`; each
  `initiateCreateOrUpdateFile` call selects `error`, `uploadToken` and
  `uploadUrl`; each `finishCreateOrUpdateFile` call selects `error`,
  `datasetId` and `version`; the `generateDataEncryptionKey` call selects
  `error`. Further selected fields are allowed.

`friction.md` has one section per step, headed `## Step 1` to `## Step 5`.
Each section starts with one of the two lines `Worked as written: yes` or
`Worked as written: no`. A section with `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.

## 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); source at https://github.com/moleculeprotocol/docs ; no sign-in | none: the pages as published |
| Production x402 gateway | the paid entry point for steps 2 to 4 | base URL `https://0qb5gyw72f.execute-api.eu-central-2.amazonaws.com/prod`, endpoints `POST {base}/x402/labs/{mutation}`; no sign-in | none: a live service |
| Molecule production API | the membership agreement and terms message for step 5 | `https://production.graphql.api.molecule.xyz/graphql`; needs a Molecule API credential, created on the API credentials page of https://app.molecule.xyz after sign-in and acceptance of the API terms | none: a live service |
| Base | the chain the run is checked on | 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 |
|---|---|
| OnChainLabFactory | `0xECdF4f05384056507485C90aeAb0a83268760D6E` |
| LabNFT | `0x9F96027eeAFb9ad5F2b5d7043B36Ee96B2EeBE92` |
| OclTokenizer | `0x62F532C3f563D974deEc103AAb8cC597f4f9c84E` |
| USDC | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
| Gateway payee | `0xb016Eb733479874b67c6BeC2470e86a64b33AD76` |

On 12 November 2026 the production gateway answered an unpaid `createLab`
call with HTTP 402 and a payment request for 10000 units (0.01 USDC) of the
USDC contract above, payable to the gateway payee above. On the same day the
LabNFT contract reported 194 Labs minted.

## Acceptance Criteria

1. **Files.** `run.json` parses as one JSON object with every field listed
   above. `friction.md` has the five step sections, each starting with its
   `Worked as written:` line.
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.** `mint_tx_hash` is a successful transaction on Base whose block
   timestamp is at or after 1794528000 (13 November 2026 00:00 UTC). Its
   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`. Its 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. **Payments.** The six entries of `calls` have six different
   `settlement_tx_hash` values, and each equals the transaction hash carried
   in that entry's `payment_response`. Each settlement transaction is
   successful on Base and its receipt holds a log emitted by the USDC
   contract for `Transfer(address,address,uint256)` with `from` equal to
   `solver_address`, `to` equal to the gateway payee and a value above zero.
   The mint transaction, then the six settlement transactions in the order of
   `calls`, are in ascending order of block number and, within one block, of
   position in the block.
5. **Requests.** In this criterion a request gives an argument a value either
   through `request.variables` or written directly into `request.query`; both
   count. Values are compared with `ocl_id` without regard to letter case.
   The `createLab` request gives its `input.oclId` argument a value equal to
   `ocl_id`. Each `initiateCreateOrUpdateFile` request and each
   `finishCreateOrUpdateFile` request gives its `oclId` argument a value
   equal to `ocl_id`. The third entry of `calls` gives `path` the value `public-note.txt`,
   `accessLevel` the value `PUBLIC`, and `uploadToken` the same value as
   `uploadToken` in the response of the second entry. The sixth entry gives
   `path` the value `private-note.txt`, `accessLevel` the value `ADMIN` or
   `HOLDERS`, an `encryptionMetadata` argument that is an object and not
   `null`, and `uploadToken` the same value as `uploadToken` in the response
   of the fifth entry.
6. **Responses.** No `response` has a top-level `errors` array, and:
   - the `createLab` response holds `data.createLab` with `error` equal to
     `null` and `lab.oclId` equal to `ocl_id`, compared without regard to
     letter case;
   - each `initiateCreateOrUpdateFile` response holds
     `data.initiateCreateOrUpdateFile` with `error` equal to `null` and
     `uploadToken` and `uploadUrl` present;
   - each `finishCreateOrUpdateFile` response holds
     `data.finishCreateOrUpdateFile` with `error` equal to `null`, a
     non-empty `datasetId` and a `version` value;
   - the `generateDataEncryptionKey` response holds
     `data.generateDataEncryptionKey` with `error` equal to `null`.
7. **Tokenization.** `tokenize_tx_hash` is a successful transaction on Base,
   sent by `solver_address` to the OclTokenizer contract, in a later block
   than the sixth settlement transaction. Its receipt holds a log emitted by
   `ipt_address` for the ERC-20 event `Transfer(address,address,uint256)`
   with `from` the zero address, `to` equal to `solver_address` and a value
   above zero: the initial amount issued to the caller. On Base, at the time
   of judging, `tokenized(uint256)` on the OclTokenizer, called with
   `token_id`, returns `ipt_address`, and `symbol()` on `ipt_address`
   returns the text `ELG` followed by `token_id`. The tokenizer accepts the
   call only with a valid signature of the membership agreement terms by the
   caller, so a successful tokenization transaction is the evidence that the
   terms were signed.
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.

## 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 either file.
- Security testing of the gateway, the API or the contracts, and any attempt
  to get a write without paying.
- Selling, listing or distributing the Lab's token, and any claim about its
  value. The token exists only as the result of the tokenization step.
- Role grants, issuing further supply and capping the token.
