Docs

Publish and fund

What one publish-fund run actually does, which values land on-chain versus in Elgora's storage, and every way it can stop.

Publishing and funding is one command in the CLI and one button on the website, but four distinct things happen inside it. Knowing which is which tells you exactly what a failure cost you. The examples below use the CLI; the website does the same four steps with a browser wallet, and an API client does them itself.

elgora-cli poster:review ./bounty_challenge.md
# Approve the exact reviewed text, then fund:
elgora-cli poster:publish-fund ./bounty_challenge.md
# or read the page from stdin
cat ./bounty_challenge.md | elgora-cli poster:publish-fund -

The four steps

Publish the bytes

The client signs one request over the exact page bytes and posts them as text/markdown. Elgora checks current terms, then reuses its saved approval of those exact bytes. It stores the exact bytes, derives spec_commitment, and returns prepared createBounty arguments plus the Hub's current fee policy.

The AI is not called again for bytes that already passed review in the last seven days. Publishing itself reviews bytes without a ready approval from the last seven days on this site, under the same quota and rules; if they are not ready, nothing is published. That review can take up to about three minutes and returns no warnings. Run poster:review first so you see the issues and warnings before you publish; a changed file or an approval older than seven days needs another review.

Nothing has been spent. Emits publish_fund.publication_prepared.

Verify the response locally

The client re-derives spec_commitment from the bytes it just sent and compares. It then requires the returned chain id, Hub address, and escrow token to match its own local configuration.

This matters: a faulty or hostile API could otherwise talk your wallet into signing a call you did not intend. Any client you write should do the same, and should build the transaction from its own copy of the contract interface rather than from a to/data pair the server handed it.

Authorize the escrow token

The client checks you hold enough USDC and some native gas, then signs a short-lived USDC authorization for the exact escrow amount to the Hub. No new allowance is required; previously granted allowances are not revoked.

Contract wallets, EIP-7702 accounts, and every other CLI signer use the approval path instead: an approve for exactly the escrow amount, sent with creation, as one batch where the wallet supports it. It emits publish_fund.usdc_approved only when an approval is sent. Cancelling a signature stops publication, without switching to approval.

Create the bounty

createBountyWithAuthorization spends the authorization and opens the bounty in one funding transaction. You sign the authorization and the transaction, and still pay gas. If creation fails, the authorization remains unspent. The allowance fallback uses createBounty; a successful exact approval and transfer consumes that approval. The client reads the BountyCreated event back and prints your bounty_id.

Emits publish_fund.bounty_created.

What ends up where

The short version

The chain gets hashes, money, deadlines, and roles. Elgora's storage gets the bytes those hashes commit to. Nothing readable lives on-chain, and nothing authoritative lives off-chain.

On-chain, in ElgoraHub, at creation:

ValueNote
bountyIdAssigned sequentially
specCommitmentkeccak256 of your exact page bytes
escrowAmountBase units, transferred into the contract
posterThe wallet that called createBounty
submissionDeadlineFrom your frontmatter
judgingDeadlineAtDerived: deadline + half the review window
settlementTimeoutAtDerived: deadline + the full review window
guardianRosterHashThe roster live at creation, pinned for this bounty's whole life
treasuryFeeBps, guardianFeeBpsSnapshotted, so a later fee change cannot touch your bounty
treasuryRecipient, guardianFeeRecipientRecorded at creation for the record only; fees go to whoever holds each role when they are claimed
payoutSchemeSnapshotted, so no Verdict can redirect settlement through a different one

In Elgora's storage:

  • the exact approved bounty_challenge.md bytes, addressed by spec_commitment;
  • later, each Submission's canonical envelope JSON, and the encrypted artifact bytes in private object storage;
  • later, each written Verdict's Markdown, addressed by report_commitment;
  • later, delivery key-wrap rows and the advisory VerificationRecord.

Nowhere, ever: plaintext artifacts, artifact locations on-chain, content encryption keys, or any Guardian's private key.

Because commitments live on-chain and preimages live off it, storage is an availability layer rather than a trust root: any reader re-hashes what they fetched and compares. See Commitments.

What can go wrong, and what it cost you

FailureMoney spentWhat to do
Frontmatter or body rejectedNoneFix the page — see the challenge contract
Page not ready (bounty_challenge_not_ready)NoneFix the listed issues, run poster:review again, then publish the exact approved bytes
spec_commitment mismatch after publishNoneStop. Your client and the API disagree about your bytes; do not sign
Deployment mismatch (chain, Hub, or token)NoneStop. Your configuration points at a different deployment than the API does
Poster has no ETH on chain <id>NoneFund the wallet with native gas
insufficient escrow token balanceNoneThe message prints required and available base units
approve revertedGas for one failed transactionCheck the token address and allowance; retry
createBounty reverted with InvalidEscrowAmountGasYour amount is outside the Hub's configured escrow bounds — see Limits
createBounty reverted with InvalidDeadlineGasThe deadline is outside the Hub's configured bounty-duration bounds — too close, or too far out
createBounty reverted with ContractPausedGasThe protocol owner has paused the Hub; nothing to fix on your side
No BountyCreated event from the configured HubGasYour client is pointed at a different contract than the transaction reached

A published page whose funding transaction never lands is harmless: the bytes are stored, but no bounty exists. Re-running the command on the same file produces the same spec_commitment and simply tries again.

Publishing against another deployment

Every route defaults to Base mainnet, where funding escrows real USDC. To try a bounty on the Base Sepolia testnet deployment first, use https://testnet.elgora.ai on the website. An API client switches every value to the testnet deployment's: API host, signing audience, chain id, Hub, and escrow token together. The CLI selects it per invocation:

elgora-cli poster:publish-fund --network base-sepolia ./bounty_challenge.md
elgora-cli poster:publish-fund --api-base-url https://your-api.example ./bounty_challenge.md

--network takes a name or a chain id and sets the chain, and with it the API, for that run. --api-base-url overrides that API — point it at one that actually serves the chain you selected. For a deployment the CLI does not know about, supply a complete and coherent set:

elgora-cli poster:publish-fund --network <chain id> \
  --hub-address 0x… --rpc-url https://… --api-base-url https://… \
  ./bounty_challenge.md

The escrow token is read from that Hub.

Never mix an address from one deployment with an endpoint from another. See Deployment and parameters.

Keep these three values

{"event":"publish_fund.bounty_created","tx_hash":"0x…","block_number":"…","bounty_id":"12","spec_commitment":"0x…"}
  • bounty_id — how every other party and every other command refers to your bounty.
  • spec_commitment — proof of which bytes you approved. Anyone can recompute it with elgora-cli spec-commitment and compare against the chain.
  • tx_hash — the creation transaction.

Store them anywhere you like; they are all public. Nothing else you need is secret, and nothing needs to be kept in a browser.

Read this page as Markdown

On this page