---
name: poidh-bounty
description: "Post bounties, discover and inspect bounties, submit claims, evaluate submissions, and accept or nominate winning claims on poidh (pics or it didn't happen) on Ethereum Mainnet, Arbitrum, or Base. Use this skill when the user wants to create a bounty on poidh.xyz, find available bounties, post a task with an ETH reward on-chain, submit proof to an existing bounty, evaluate submissions using vision or other content tools, accept a winning claim on a solo bounty, initiate/resolve voting on an open bounty, query poidh data, or withdraw bounty winnings."
metadata:
  clawdbot:
    env:
      - PRIVATE_KEY
      - RPC_URL
      - POIDH_CHAIN
    bin:
      - cast
      - python3
---

## Overview

This skill interacts with poidh across Ethereum Mainnet, Arbitrum, and Base.

It can:

1. **Discover bounties**
2. **Read bounty and claim data**
3. **Post bounties** — solo or open
4. **Submit claims**
5. **Evaluate claim submissions**
6. **Accept winning claims** on solo bounties
7. **Initiate and resolve contributor voting** on open bounties
8. **Withdraw bounty winnings**

**poidh** ("pics or it didn't happen") is an onchain bounty protocol.

A bounty issuer escrows ETH and describes an outcome. Claimants submit proof that they completed the bounty. The issuer — or contributors through weighted voting — can accept a winning claim and release the bounty payout.

## Official Documentation

poidh's official documentation is available at:

```text
https://docs.poidh.xyz/
```

Use the documentation for deeper explanations of the poidh.xyz website, how it works, concepts, protocol behavior, reputation systems, and product terminology.

This `SKILL.md` is the primary operational guide for agent actions, data discovery, transaction safety, and interacting with the poidh.xyz website and the underlying poidh protocol smart contracts.

## MCP Server

poidh.xyz also runs a remote MCP server for agents that support the Model Context Protocol:

```text
https://poidh.xyz/api/mcp
```

Transport: Streamable HTTP, stateless, no authentication.

| Tool                    | Does                                                                                             |
| ----------------------- | ------------------------------------------------------------------------------------------------ |
| `find_bounties`         | List open bounties, or search all bounties by text                                               |
| `get_bounty`            | Complete bounty data: status, all claims, comments, winner, voting state and identities          |
| `get_account`           | Complete public profile/activity data: bounties, claims, NFTs, comments, score and related users |
| `get_leaderboard`       | Leaderboard by points                                                                            |
| `prepare_create_bounty` | Unsigned tx: post a solo or open bounty                                                          |
| `prepare_join_bounty`   | Unsigned tx: add funds to an open bounty                                                         |
| `prepare_submit_claim`  | Unsigned tx: submit a claim (see the URI rules below)                                            |
| `prepare_accept_claim`  | Unsigned tx: accept a claim, or start a vote when contributors exist                             |
| `prepare_vote`          | Unsigned tx: vote on the claim under vote                                                        |
| `prepare_resolve_vote`  | Unsigned tx: resolve a finished vote                                                             |
| `prepare_withdraw`      | Unsigned tx: withdraw winnings                                                                   |

The MCP server never signs or holds keys. `prepare_*` tools return:

- `transaction`: `{ chainId, to, data, value }` for `eth_sendTransaction`
- `walletSendCalls`: the same call as an EIP-5792 `wallet_sendCalls` request
- `simulation`: an `eth_call` dry run when you pass `from`, with decoded contract errors
- `signUrl`: a `https://poidh.xyz/tx?...` link to send a human who wants to review and approve the action on poidh.xyz with the wallet they already use there, including poidh's built-in wallet

The tools take the bounty ID shown in the poidh.xyz URL and convert it to the contract ID for you. They refuse to act on poidh v2 bounties (see Frontend IDs vs Contract IDs) and return an error instead of a transaction. The rules in this guide still apply: confirm with the user before signing, and only regular wallets (EOAs) can create bounties.

### WebMCP (in-browser agents)

When an agent runs inside a browser on poidh.xyz (Chrome's WebMCP, ChatGPT desktop site tools), the page registers the same tools through `document.modelContext`. `find_bounties` and `get_bounty` return data. The action tools (`create_bounty`, `join_bounty`, `submit_claim`, `accept_claim`, `vote`, `resolve_vote`, `withdraw`) open the `/tx` approval page, where the person approves in their own wallet.

## Open-Source Repository

The poidh.xyz application is open source.

Official repository:

```text
https://github.com/picsoritdidnthappen/poidh-app
```

poidh.xyz is the primary user interface and access point for the poidh protocol, but **poidh.xyz is not the poidh protocol itself**.

The poidh protocol consists of deployed onchain smart contracts and the resulting onchain state.

The poidh.xyz frontend provides a convenient interface for:

- discovering bounties
- creating and funding bounties
- submitting claims
- viewing claim media
- participating in bounty voting
- viewing profiles and account activity
- reading comments and other social context
- accessing structured `/data` endpoints

Agents, alternative frontends, scripts, applications, and other interfaces may interact with the poidh protocol without using poidh.xyz directly.

The open-source poidh.xyz repository is useful for understanding how the primary frontend:

- reads and presents protocol data
- resolves claim media
- displays bounty and voting state
- calculates and presents profile information
- exposes public `/data` endpoints
- integrates offchain social and identity information
- interacts with the poidh indexer and deployed contracts

Do not assume that behavior or data produced by the poidh.xyz frontend is itself authoritative protocol state.

For consensus-critical information such as:

- whether a bounty is active, canceled, or finalized
- accepted claims
- contributor voting state
- balances
- pending withdrawals
- payout state
- authorization
- transaction validity

the deployed smart contracts and current onchain state are authoritative.

The poidh.xyz frontend, its JSON endpoints, and the poidh indexer are interfaces and derived data sources built around the protocol.

## Claim Proof and the URI Field

A poidh claim contains:

1. a **name**
2. a **description**
3. a **proof URI**

The **proof URI is the value poidh.xyz uses to resolve the media attached to the claim**.

Do not treat the URI field as a generic evidence-link field.

Links to supporting material such as:

- X / Twitter posts
- Farcaster posts
- GitHub repositories or pull requests
- webpages
- benchmark pages
- documentation
- transaction explorer pages

should normally be placed in the **claim description**.

The smart contract accepts an arbitrary string for the URI field, but that does **not** mean an arbitrary URL will display properly on poidh.xyz.

### Media Types Supported by poidh.xyz

The poidh smart contract does not enforce a file type for the claim URI. The frontend determines whether the URI can actually be displayed.

#### Images uploaded through poidh.xyz

The normal poidh.xyz claim form currently accepts:

- `.jpg`
- `.jpeg`
- `.png`
- `.gif`
- `.webp`

The normal poidh.xyz upload flow:

1. uploads the image file to IPFS
2. creates metadata containing the uploaded image URI
3. uploads that metadata to IPFS
4. submits the resulting IPFS-hosted metadata URI to the poidh contract

Therefore, a claim created through the normal poidh.xyz interface will commonly have a URI pointing to **metadata**, not directly to the image file.

Do not invent or reconstruct this URI. Use the URI actually returned by the upload flow.

#### Claims submitted directly through the contract

Direct contract submissions do not have to use the poidh.xyz upload flow or IPFS.

The current poidh.xyz frontend knows how to display the following claim media:

**Direct images**

A directly accessible URL whose HTTP `Content-Type` begins with:

```text
image/
```

For maximum compatibility, prefer the same image formats accepted by the native poidh.xyz uploader:

```text
.jpg
.jpeg
.png
.gif
.webp
```

**Direct videos**

A directly accessible URL whose HTTP `Content-Type` begins with:

```text
video/
```

The frontend also explicitly recognizes these video extensions:

```text
.mp4
.mov
.webm
.ogg
```

**JSON metadata**

The frontend can resolve JSON metadata containing an `image` field:

```json
{
  "image": "<MEDIA_URL>"
}
```

The media URL referenced by that field should itself resolve to displayable image or video media.

The current frontend should **not** be assumed to render arbitrary PDFs, documents, GitHub pages, social posts, ordinary HTML pages, benchmark pages, or other generic URLs as claim media.

Those resources may still be valid supporting evidence, but their links belong in the claim description unless they resolve to supported media as described above.

### URI Rule

> **Contract-valid is not the same thing as frontend-displayable.**

Before submitting a claim directly through the contract:

1. choose a real, accessible URI
2. verify that it resolves to supported media or supported JSON metadata
3. verify that poidh.xyz can display the resulting claim media
4. put non-media supporting links in `CLAIM_DESCRIPTION`

Never invent, guess, or use a placeholder URI.

---

## Contract Immutability

The deployed poidh v3 contracts are **immutable**.

They cannot be upgraded or modified after deployment.

This has two important consequences:

1. integrations do not need to account for proxy upgrades or implementation changes at these addresses
2. known contract behavior — including known getter bugs — remains part of the deployed contract unless a new contract is deployed

Do not assume a documented contract bug has been fixed merely because the frontend behaves correctly.

Always verify which contract address is being used.

---

## Supported Networks

The actively supported poidh networks for this skill are:

- Ethereum Mainnet
- Arbitrum
- Base

All bounty funding and payouts on these networks use ETH.

---

## Retired Network: Degen Chain

Degen Chain (`chainId 666666666`) is retired and is **not a supported poidh network**.

Historical poidh activity from Degen Chain may still appear:

- on historical poidh.xyz bounty pages
- in the poidh indexer
- in profile and leaderboard data
- in analytics
- in historical datasets
- in old links or documentation

Treat all Degen Chain records as **historical, read-only data**.

> 🚨 **DO NOT CREATE BOUNTIES, SUBMIT CLAIMS, CONTRIBUTE FUNDS, ACCEPT CLAIMS, VOTE, WITHDRAW FUNDS, OR ATTEMPT ANY OTHER WRITE OPERATION ON DEGEN CHAIN.**

Do not attempt to switch the user's wallet to Degen Chain or request a Degen RPC URL.

If an indexer or frontend endpoint returns a Degen Chain bounty, do not treat it as an actionable bounty even if its historical status appears open.

If the user provides a historical Degen bounty URL, the skill may inspect available frontend or indexer data for historical purposes, but must clearly state that Degen Chain is retired and that no transaction can be performed through this skill.

---

## poidh v3 Core Contracts

| Chain            | Core Contract                                | Explorer                                                                  |
| ---------------- | -------------------------------------------- | ------------------------------------------------------------------------- |
| Ethereum Mainnet | `0xE731dFadBFf20542E10D09D26Fc71445C70d4232` | `https://etherscan.io/address/0xe731dfadbff20542e10d09d26fc71445c70d4232` |
| Arbitrum         | `0x5555Fa783936C260f77385b4E153B9725feF1719` | `https://arbiscan.io/address/0x5555fa783936c260f77385b4E153B9725fef1719`  |
| Base             | `0x5555Fa783936C260f77385b4E153B9725feF1719` | `https://basescan.org/address/0x5555fa783936c260f77385b4E153B9725fef1719` |

---

## poidh v3 NFT Contracts

Claim proof NFTs are minted through the poidh NFT contracts.

| Chain            | NFT Contract                                 |
| ---------------- | -------------------------------------------- |
| Ethereum Mainnet | `0x9c5f45d5e1382e4058d334d93c6c01442012a4d9` |
| Arbitrum         | `0x27e117cc9a8da363442e7bd0618939e3eeeacf6a` |
| Base             | `0x27e117cc9a8da363442e7bd0618939e3eeeacf6a` |

The NFT contract can also be resolved dynamically from the core contract:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "poidhNft()(address)" \
  --rpc-url $RPC_URL
```

Prefer resolving it dynamically when interacting with a live contract.

---

# 🚨 Never Use poidh v2 Contracts

poidh v3 is a security-focused rebuild of the protocol following an exploit affecting poidh v2 in December 2025.

Historical poidh v2 activity may still appear:

- on poidh.xyz
- in the indexer
- in analytics
- in historical datasets
- in old documentation

The v2 contracts are retained only as historical onchain records.

> 🚨 **DO NOT CREATE BOUNTIES, SUBMIT CLAIMS, SEND FUNDS, OR OTHERWISE INTERACT WITH POIDH V2 CONTRACTS.**

This skill must only perform write operations against the poidh v3 contracts listed above.

If a contract address does not match a supported v3 deployment, stop and verify it before sending a transaction.

---

# 🚨 Known poidh v3 Pagination Bug

The following convenience getters in the currently deployed poidh v3 contracts do **not** implement working offset pagination:

```solidity
getBounties(uint256 offset)

getClaimsByBountyId(
    uint256 bountyId,
    uint256 offset
)

getBountiesByUser(
    address user,
    uint256 offset
)

getClaimsByUser(
    address user,
    uint256 offset
)
```

Do not use these functions to exhaustively enumerate protocol data.

For collections larger than 10 rows, they may repeatedly return the same newest 10 records while older records remain unreachable through the getter.

This is especially dangerous when evaluating claims because older submissions may include:

- the earliest submission
- the first valid submission
- the actual winner under the bounty rules

### Never:

```text
getClaimsByBountyId(bountyId, 0)
getClaimsByBountyId(bountyId, 10)
getClaimsByBountyId(bountyId, 20)
```

and assume those are different pages.

They are not reliable pagination.

### Instead: walk the index

The public array getters are exact **when the RPC call succeeds**. Read indexes from `0`
upward and read each referenced record by id:

```solidity
bountyClaims(uint256 bountyId, uint256 index) returns (uint256 claimId)
claims(uint256 claimId) returns (
    uint256 id, address issuer, uint256 bountyId, address bountyIssuer,
    string name, string description, uint256 createdAt, bool accepted
)
```

Do **not** treat a failed indexed read as proof that the array ended.

A provider may surface an out-of-bounds revert, rate limit, transport failure, unsupported
method, or other RPC problem through the same generic client exception. A bare
`catch { break; }` can therefore silently truncate an array or turn a non-empty array into
an empty one.

Use this rule:

1. read `bountyClaims(bountyId, i)` from the current provider
2. if it returns a claim ID, record it and continue
3. if it fails, retry the **same exact `(bountyId, i)` read** through a genuinely independent RPC provider
4. verify the second provider is healthy with at least one known-good read
5. if the independent provider returns a claim ID, the first provider failed; continue the walk
6. only treat index `i` as the end when a healthy independent provider also reports that the same index is unavailable
7. if no independent provider is available, fail loudly and do not claim the enumeration was exhaustive

Conceptual example:

```js
async function walkBountyClaims(primary, secondary, bountyId) {
  const ids = [];

  for (let i = 0; ; i++) {
    try {
      ids.push(await primary.bountyClaims(bountyId, i));
      continue;
    } catch (primaryError) {
      // Confirm the exact same index through independent infrastructure.
      // `knownGoodRead` must be a call whose successful result is already known.
      await knownGoodRead(secondary);

      try {
        ids.push(await secondary.bountyClaims(bountyId, i));
        continue; // primary failed; this index exists
      } catch (secondaryError) {
        // Both providers report this exact index unavailable.
        // Treat this as the boundary only after the secondary provider passed
        // the known-good health check above.
        break;
      }
    }
  }

  return ids;
}
```

The independent provider must be genuinely independent infrastructure. A second URL or
transport that ultimately reaches the same RPC host is not a meaningful second opinion.

Apply the same boundary-confirmation rule to `userClaims(user, i)` and
`userBounties(user, i)`.

The same shape replaces all four broken getters:

| Do not use                              | Exact replacement                                                                            |
| --------------------------------------- | -------------------------------------------------------------------------------------------- |
| `getBounties(offset)`                   | `bountyCounter()`, then `bounties(id)` for each id                                           |
| `getClaimsByBountyId(bountyId, offset)` | `bountyClaims(bountyId, i)` walked to an independently confirmed boundary, then `claims(id)` |
| `getBountiesByUser(user, offset)`       | `userBounties(user, i)` walked to an independently confirmed boundary, then `bounties(id)`   |
| `getClaimsByUser(user, offset)`         | `userClaims(user, i)` walked to an independently confirmed boundary, then `claims(id)`       |

### A returned row is not necessarily a record

The same convenience getters allocate a fixed array of 10 and never shrink it, so short
results are **zero-padded**. Never treat `result.length` as the number of real records, and do
not attempt to repair these convenience getters by filtering their output.

Use the exact index-based replacements above for enumeration instead.

---

# Data Source Trust Hierarchy

poidh exposes several ways to obtain data.

Use the source appropriate to the task.

## 1. Onchain State — Authoritative

Use direct contract calls for:

- bounty status
- payout decisions
- claim acceptance
- voting state
- balances
- issuer authorization
- finalization state
- critical real-time checks
- anything immediately preceding a transaction

For critical decisions, the contract is the source of truth.

---

## 2. Onchain Indexes and Events — Authoritative Historical Discovery

For targeted exhaustive reads, prefer the contract's public index getters:

- `bountyClaims(bountyId, i)` for claims on one bounty
- `userBounties(user, i)` for bounties associated with one user
- `userClaims(user, i)` for claims associated with one user
- `bountyCounter()` + `bounties(id)` for the complete bounty registry
- `claimCounter()` + `claims(id)` for the complete claim registry

Walk array indexes from `0` upward. If an indexed read fails, confirm the **same exact
index** through a healthy independent RPC provider before treating the failure as the end
of the array. Then read each referenced record by id.

Use contract events for:

- proof URI discovery
- historical event metadata
- transaction history
- alternative exhaustive discovery when a public index is unavailable or inconvenient

For evaluating a single bounty, `bountyClaims(bountyId, i)` + `claims(id)` is the preferred
authoritative claim-enumeration path. `ClaimCreated` events are especially useful for retrieving
the submitted `imageUri`.

---

## 3. poidh Indexer — Convenient and Fast

poidh operates an offchain indexer populated from onchain events.

API documentation:

```text
https://indexer.poidh.xyz/swagger
```

The indexer is useful for:

- browsing bounties
- browsing claims
- relational queries
- building frontends
- finding candidate records quickly
- avoiding large numbers of RPC calls

The indexer is approximately **99.9% historically accurate**, but it is an offchain derived data source.

Therefore:

> Use the indexer for discovery and convenience, but verify critical state directly against the contract before transferring funds, accepting claims, submitting votes, or making other consensus-sensitive decisions.

---

## 4. poidh JSON Endpoints — Lightweight Discovery

poidh.xyz exposes lightweight frontend JSON endpoints.

These are useful when an agent wants structured bounty data without directly interacting with the indexer or parsing chain events.

They are convenience endpoints, not the ultimate authority for critical contract state.

### Common Resource Links

Individual bounty and account `/data` endpoints include top-level links to additional poidh resources:

```text
docsUrl
githubUrl
```

These fields have different purposes:

- `docsUrl` — official poidh documentation for deeper explanations of protocol concepts, product behavior, reputation systems, and terminology
- `githubUrl` — source code for the poidh.xyz frontend and its public data endpoints

Current resources:

```text
docsUrl:   https://docs.poidh.xyz/
githubUrl: https://github.com/picsoritdidnthappen/poidh-app
```

When an unfamiliar poidh concept appears in `/data`, prefer following `docsUrl` rather than guessing its meaning.

When implementation details of poidh.xyz or its `/data` endpoints matter, `githubUrl` points to the open-source frontend repository.

These resources are informational and operational aids. They do not replace direct onchain verification for consensus-critical state or transactions.

---

# Required Environment Variables

| Variable      | Description                                                           |
| ------------- | --------------------------------------------------------------------- |
| `PRIVATE_KEY` | Private key of the EOA signing transactions, hex with or without `0x` |
| `RPC_URL`     | RPC URL for the target chain                                          |
| `POIDH_CHAIN` | `mainnet`, `arbitrum`, or `base`                                      |

Do not expose `PRIVATE_KEY` in output, logs, URLs, command history shown to the user, or error reports.

---

# Resolve Chain Configuration

At the start of an onchain session:

```bash
case "$POIDH_CHAIN" in
  mainnet)
    POIDH_CONTRACT_ADDRESS="0xE731dFadBFf20542E10D09D26Fc71445C70d4232"
    POIDH_BASE_URL="https://poidh.xyz/mainnet"
    POIDH_V2_OFFSET=0
    ;;
  arbitrum)
    POIDH_CONTRACT_ADDRESS="0x5555Fa783936C260f77385b4E153B9725feF1719"
    POIDH_BASE_URL="https://poidh.xyz/arbitrum"
    POIDH_V2_OFFSET=180
    ;;
  base)
    POIDH_CONTRACT_ADDRESS="0x5555Fa783936C260f77385b4E153B9725feF1719"
    POIDH_BASE_URL="https://poidh.xyz/base"
    POIDH_V2_OFFSET=986
    ;;
  *)
    echo "Unknown POIDH_CHAIN: '$POIDH_CHAIN' (expected mainnet, arbitrum, or base)" >&2
    exit 1
    ;;
esac
```

---

# Frontend IDs vs Contract IDs

A poidh.xyz bounty ID is not always the same as its poidh v3 contract bounty ID.

Historical v2 bounties are included in frontend numbering.

Ethereum Mainnet never had poidh v2, so Mainnet IDs do not require an offset.

## Contract ID → Frontend ID

```text
frontend_id = contract_bounty_id + POIDH_V2_OFFSET
```

## Frontend ID → Contract ID

```text
contract_bounty_id = frontend_id - POIDH_V2_OFFSET
```

Current offsets:

| Chain            | Offset |
| ---------------- | -----: |
| Ethereum Mainnet |    `0` |
| Arbitrum         |  `180` |
| Base             |  `986` |

Example:

```text
Base contract bounty ID: 285

285 + 986 = 1271

Frontend:
https://poidh.xyz/base/bounty/1271
```

> 🚨 Never assume `/base/bounty/106` corresponds to `bounties(106)`.

## Frontend IDs at or below the offset are poidh v2 bounties

On Arbitrum and Base, a frontend ID less than or equal to the offset is a historical poidh v2 bounty. It is read-only: do not join it, claim it, accept a claim, vote, or resolve a vote.

The indexer still reports an `onChainId` for these bounties, but it is the **v2** contract ID. The same number is a different bounty on the v3 contract:

```text
Base frontend 176   → v2 bounty, onChainId 176 (read-only)
Base frontend 1162  → v3 bounty 176 (a different bounty)
```

> 🚨 Never pass a v2 bounty's `onChainId` to a v3 contract call. It targets someone else's bounty.

Convert the ID before making contract calls.

---

# Part 1: Discovering Bounties

Agents do not need to scan the entire contract registry just to discover current opportunities.

Use the frontend JSON feed or indexer first.

---

## Recent Open Bounties Feed

Endpoint:

```text
https://poidh.xyz/bounties/data
```

It returns recent open bounties across poidh.

Default limit:

```text
20
```

A larger limit can be requested:

```text
https://poidh.xyz/bounties/data?limit=100
```

Use this endpoint for tasks such as:

- "show me current poidh bounties"
- "find a bounty I can complete"
- "what bounties are open?"
- "find software bounties"
- "find photo bounties"
- "find bounties worth more than X"

After identifying a bounty, inspect its individual data endpoint and/or verify the relevant state onchain.

The frontend feed or indexer may contain historical Degen Chain records.

When discovering actionable bounties, only return bounties on:

- Ethereum Mainnet
- Arbitrum
- Base

Exclude Degen Chain (`chainId 666666666`) from actionable bounty results even if a historical record appears open.

---

## Individual Bounty JSON

Append:

```text
/data
```

to a bounty URL.

Format:

```text
https://poidh.xyz/[chain]/bounty/[frontend-bounty-id]/data
```

Example:

```text
https://poidh.xyz/base/bounty/1271/data
```

Typical response fields include:

```json
{
  "id": 1271,
  "onChainId": 285,
  "chainId": 8453,
  "title": "Best Bounty (July 2026) 🏆",
  "description": "Each month, we celebrate the bounty creators...",
  "amount": "67000000000000000",
  "issuer": "0x4200ac338555e25b20c8fe82ac02a5c8d4e5a5b4",
  "createdAt": "1782908709",
  "inProgress": true,
  "isJoinedBounty": false,
  "isCanceled": false,
  "isMultiplayer": true,
  "isVoting": false,
  "deadline": null,
  "ban": [],
  "extra": {
    "bountyId": 1271,
    "chainId": 8453,
    "album": "poidh"
  },
  "hasClaims": true,
  "hasParticipants": false,
  "priceUsd": 125.56135,
  "currency": "eth",
  "url": "https://poidh.xyz/base/bounty/1271"
}
```

The individual bounty `/data` endpoint also returns:

- `claims` — all non-banned claims, including `claimId`, the raw claim
  `uri`, claimant address, resolved name, Farcaster handle, Twitter
  handle, poidh profile URL, title, and description.

  `uri` is the claim's original proof/metadata URI. It is not necessarily
  a direct image or video URL and is not pre-resolved by this endpoint.
  It may point to JSON metadata, direct media, or an IPFS URI/gateway URL.
  Resolve it using the claim-media rules described below when the actual
  proof media needs to be inspected.

- `comments` — all publicly visible (non-deleted) bounty comments in
  chronological order, including author address, resolved name,
  Farcaster handle, Twitter handle, poidh profile URL, reply parent,
  timestamp, and reaction counts.

When evaluating or completing a bounty, always inspect `comments` for
clarifications, requirement changes, answers from the bounty creator,
or other context that may modify the original bounty description.

Of particular importance:

```text
id
```

is the frontend bounty ID.

```text
onChainId
```

is the v3 contract bounty ID.

When available, prefer the returned `onChainId` rather than manually calculating the offset.

Still verify critical state directly against the contract before a transaction.

## Individual Account JSON

Append:

```text
/data
```

to a poidh account URL.

Format:

```text
https://poidh.xyz/account/[address]/data
```

Example:

```text
https://poidh.xyz/account/0x4200ac338555e25b20c8fe82ac02a5c8d4e5a5b4/data
```

The account `/data` endpoint provides a public machine-readable view of a user's identity, reputation, activity, collection, and social history on poidh.

It may include:

- resolved name, Farcaster handle, Twitter handle, and profile image
- poidh score and aggregate ETH statistics
- historical Degen statistics
- bounties the user created
- bounties the user contributed funds to
- the user's contribution amount and relationship to each bounty
- the creator of each bounty the user interacted with
- current bounty status, including whether it is canceled, completed, or in progress
- whether an active bounty is currently accepting claims
- whether a voting round is currently unresolved
- whether the latest voting round has been resolved
- winning claim and winner information for completed bounties
- claims submitted by the user
- claim submission timestamps when available
- current claim outcome, such as won, pending, not selected, or bounty canceled
- the current state of the bounty associated with each claim
- claim NFTs currently held by the user
- original claimant information for held NFTs
- public, non-deleted comments authored by the user
- comment reply context when available
- users connected to the profile through bounties, claims, NFTs, and comments

`poidhScore` is poidh's reputation and activity score.

For deeper explanation of poidh concepts and product behavior, see:

```text
https://docs.poidh.xyz/
```

Bounty records may include fields such as:

```text
status
statusLabel
statusEmoji
acceptingClaims
votingInProgress
voteResolved
winningClaim
creator
url
dataUrl
```

`votingInProgress` means the current voting round has started and has not yet been resolved.

A voting deadline may have passed while:

```text
votingInProgress: true
```

if nobody has yet submitted the vote-resolution transaction.

```text
voteResolved: true
```

indicates that the latest voting round has been resolved.

Bounty status values may include:

```text
in_progress
completed
canceled
```

The status emoji mirrors the poidh frontend:

```text
💰 in progress
✅ completed
❌ canceled
```

The endpoint distinguishes between bounties a user created and bounties they merely funded.

For example:

```text
createdByProfile: false
fundedByProfile: true
```

means the user contributed funds to a bounty created by somebody else.

The bounty creator identity is included so an agent can follow that relationship to the creator's poidh account.

Completed bounties may include:

```text
winningClaim
```

with the winning claimant's identity.

Winner identity objects may include:

```text
address
name
farcasterHandle
twitterHandle
pfpUrl
profileUrl
profileDataUrl
```

This allows an agent to move from one user's activity to the public poidh profile of another user they interacted with.

Claim records may include:

```text
claimStatus
createdAt
currentOwner
bounty
```

Possible `claimStatus` values include:

```text
won
pending
not_selected
bounty_canceled
```

The nested `bounty` object contains the bounty's current status.

This allows an agent to determine, for example, whether a user submitted a claim to a bounty that:

- is still active and accepting additional claims
- currently has an unresolved voting round
- has been completed
- has been canceled

Full bounty descriptions are included only in the top-level:

```text
bounties
```

array, which represents bounties the profile created or funded.

Bounty references nested under:

```text
claims
nfts
comments
```

are intentionally compact.

They generally include the bounty title, current status, creator, winner when applicable, and bounty URLs rather than repeating the full bounty description.

If complete bounty details are needed, follow:

```text
dataUrl
```

to the bounty's individual `/data` endpoint.

The:

```text
nfts
```

array represents claim NFTs currently held by the account.

Do not interpret `nfts` as a complete historical NFT ownership or transfer record.

The current owner may differ from the original claimant, so NFT records may expose both identities when relevant.

Some claim NFTs may currently be held by a poidh protocol contract.

When this occurs, the identity object may include:

```text
accountType: protocol_contract
isProtocolContract: true
contractLabel
```

Protocol contracts are not social users.

They do not have poidh profile crawl targets and should not appear in:

```text
relatedUsers
```

The account endpoint may include:

```text
comments
```

containing public, non-deleted comments authored by the profile.

Comment records may include:

- comment body
- timestamp
- reply parent
- reply target identity
- upvote and downvote counts
- the related bounty
- the related bounty's current status
- the bounty creator
- the winning claimant when applicable

The endpoint may also include:

```text
relatedUsers
```

`relatedUsers` provides convenient crawl targets for the user's public poidh social graph.

Related users may include:

- creators of bounties the profile funded
- creators of bounties the profile submitted claims to
- winners of bounties the profile interacted with
- original claimants of NFTs the profile currently holds
- users the profile replied to in comments
- other publicly visible identities directly connected through poidh activity

Related-user objects may include:

```text
address
name
farcasterHandle
twitterHandle
pfpUrl
profileUrl
profileDataUrl
relationships
relatedBountyUrls
```

An agent may follow:

```text
profileDataUrl
```

to inspect another user's public poidh activity and continue traversing the public social graph.

A useful crawl pattern is:

```text
account /data
→ related bounty or related user
→ bounty dataUrl or profileDataUrl
→ additional public poidh context
```

Historical Degen Chain records may appear in account data.

Degen Chain is retired and must not be treated as an actionable network.

Records may include fields such as:

```text
networkStatus
protocolVersion
liveProtocolSupported
```

Treat:

```text
networkStatus: retired
```

or:

```text
liveProtocolSupported: false
```

as a signal that the record is historical or otherwise not part of the currently supported live protocol.

The account `/data` endpoint is a convenience representation derived from poidh frontend and indexed data.

Use it for:

- understanding a user's public poidh history
- reputation and activity inspection
- social-graph discovery
- finding bounties a user created or funded
- finding claims a user submitted
- understanding the outcome of those claims
- finding NFTs a user currently holds
- finding public comments authored by a user
- finding related poidh users
- determining the current state of bounties associated with historical user activity

For critical current protocol state, especially immediately before a transaction, verify the relevant state directly onchain.

---

# Part 2: Reading Bounties Directly Onchain

For authoritative bounty state:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "bounties(uint256)(uint256,address,string,string,uint256,address,uint256,uint256)" \
  <BOUNTY_ID> \
  --rpc-url $RPC_URL
```

Returns:

```text
id
issuer
name
description
amount
claimer
createdAt
claimId
```

Typical interpretation:

```text
claimer == 0x0000000000000000000000000000000000000000
```

Bounty has not been finalized.

```text
claimer == issuer
```

Bounty was cancelled.

```text
claimer == another address
```

Bounty has a winning claimant.

---

## Exhaustive Bounty Enumeration

Do **not** use:

```solidity
getBounties(offset)
```

for exhaustive enumeration.

Instead:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "bountyCounter()(uint256)" \
  --rpc-url $RPC_URL
```

Then:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "bounties(uint256)(uint256,address,string,string,uint256,address,uint256,uint256)" \
  <BOUNTY_ID> \
  --rpc-url $RPC_URL
```

for each valid registry index.

For ordinary bounty discovery, prefer the JSON feed or indexer rather than scanning the entire registry.

---

# Part 3: Posting a Bounty

## Verify Minimum Bounty

Do not hardcode assumptions immediately before a transaction.

Query:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "MIN_BOUNTY_AMOUNT()(uint256)" \
  --rpc-url $RPC_URL
```

Current expected minimum on Mainnet, Arbitrum, and Base:

```text
0.001 ETH
```

Minimum open-bounty contribution:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "MIN_CONTRIBUTION()(uint256)" \
  --rpc-url $RPC_URL
```

Current expected value:

```text
0.00001 ETH
```

---

## EOA Requirement

PoidhV3 enforces an EOA requirement for relevant write operations.

Smart contract wallets may revert.

The signing wallet should be an EOA.

Resolve its address with:

```bash
cast wallet address --private-key $PRIVATE_KEY
```

---

## Post a Solo Bounty

Solo bounties are funded only by the issuer.

```bash
cast send $POIDH_CONTRACT_ADDRESS \
  "createSoloBounty(string,string)" \
  "<BOUNTY_NAME>" \
  "<BOUNTY_DESCRIPTION>" \
  --value <AMOUNT> \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

Example:

```bash
cast send $POIDH_CONTRACT_ADDRESS \
  "createSoloBounty(string,string)" \
  "Brooklyn Bridge at sunset" \
  "High quality photo of the Brooklyn Bridge during golden hour. Must show the full span." \
  --value 0.001ether \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

---

## Post an Open Bounty

Open bounties allow other wallets to contribute ETH.

```bash
cast send $POIDH_CONTRACT_ADDRESS \
  "createOpenBounty(string,string)" \
  "<BOUNTY_NAME>" \
  "<BOUNTY_DESCRIPTION>" \
  --value <AMOUNT> \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

If external contributors join, claim acceptance uses contributor-weighted voting.

---

## Get the Bounty ID

After creation, inspect the transaction receipt and decode the bounty creation event.

```bash
cast receipt <TX_HASH> \
  --rpc-url $RPC_URL \
  --json
```

Do not blindly assume the first log containing multiple topics is the bounty creation event.

Decode the correct event emitted by `POIDH_CONTRACT_ADDRESS`.

Then:

```text
frontend_id = contract_bounty_id + POIDH_V2_OFFSET
```

Return both:

- contract bounty ID
- poidh.xyz frontend URL

---

# Part 4: Submitting a Claim

A claimant can submit proof to an eligible active bounty.

The signing wallet cannot be the bounty issuer.

---

## Inspect Bounty First

If given a poidh URL, use its `/data` endpoint for convenient metadata and `onChainId`.

Then verify onchain:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "bounties(uint256)(uint256,address,string,string,uint256,address,uint256,uint256)" \
  <BOUNTY_ID> \
  --rpc-url $RPC_URL
```

---

## Verify No Vote Is Active

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "bountyCurrentVotingClaim(uint256)(uint256)" \
  <BOUNTY_ID> \
  --rpc-url $RPC_URL
```

A return value of `0` indicates there is no current voting claim.

---

## Verify Claimant Is Not Issuer

Resolve signing wallet:

```bash
cast wallet address --private-key $PRIVATE_KEY
```

Compare against:

```text
bounty.issuer
```

The issuer cannot submit a claim on their own bounty.

---

## Submit Claim

```bash
cast send $POIDH_CONTRACT_ADDRESS \
  "createClaim(uint256,string,string,string)" \
  <BOUNTY_ID> \
  "<CLAIM_NAME>" \
  "<CLAIM_DESCRIPTION>" \
  "<PROOF_URI>" \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

Parameters:

### `CLAIM_NAME`

Short title for the submission.

### `CLAIM_DESCRIPTION`

Explanation of how the claimant completed the bounty.

### `PROOF_URI`

The URI used by poidh.xyz to resolve the media attached to the claim.

See **Media Types Supported by poidh.xyz** above before choosing this value.

The contract accepts an arbitrary string, but that does **not** mean an arbitrary URL will display properly on poidh.xyz.

#### Normal poidh.xyz uploads

The normal poidh.xyz claim form accepts:

```text
.jpg
.jpeg
.png
.gif
.webp
```

When using that flow, poidh.xyz:

1. uploads the image to IPFS
2. builds metadata containing the image URI, title, and description
3. uploads the metadata to IPFS
4. submits the resulting metadata URI to `createClaim`

Use the URI returned by that upload flow.

Do **not** manually invent an IPFS CID, metadata URI, or gateway URL.

#### Direct contract submissions

A caller interacting directly with:

```solidity
createClaim(
    uint256 bountyId,
    string name,
    string description,
    string imageUri
)
```

may provide another URI.

For the current poidh.xyz frontend to display it reliably, use one of these forms:

- a direct image URL returning `Content-Type: image/*`
- a direct video URL returning `Content-Type: video/*`
- a direct video URL ending in `.mp4`, `.mov`, `.webm`, or `.ogg`
- JSON metadata containing an `image` field whose value points to displayable media

For direct images, prefer:

```text
.jpg
.jpeg
.png
.gif
.webp
```

Do **not** use the URI field for generic supporting links.

If completion is demonstrated by a GitHub repository, social post, webpage, transaction, benchmark page, documentation, PDF, or another external resource, put that link in `CLAIM_DESCRIPTION` and use supported media for `PROOF_URI`.

Example:

```text
CLAIM_NAME:
Offline Android AI submission

CLAIM_DESCRIPTION:
Built and tested the app on GrapheneOS.
Repository: https://github.com/example/project
Demo notes: https://example.com/results

PROOF_URI:
https://example.com/proof.png
```

or:

```text
PROOF_URI:
https://example.com/claim-metadata.json
```

where the metadata contains:

```json
{
  "image": "https://example.com/proof.png"
}
```

> 🚨 **Never invent a URI.**
>
> Do not put placeholder text, a local filesystem path, an inaccessible temporary URL, or an arbitrary webpage into `PROOF_URI`.
>
> Before submitting the transaction, verify that the URI resolves to a supported media form that poidh.xyz can display.

---

## Decode Claim ID

After confirmation:

```bash
cast receipt <TX_HASH> \
  --rpc-url $RPC_URL \
  --json
```

Decode the `ClaimCreated` event emitted by the poidh core contract.

Do not infer the claim ID from an unrelated log.

---

# Part 5: Evaluating Claims

Winner selection is a high-integrity operation.

The agent must evaluate **all eligible claims**, not merely the newest ten.

Workflow:

1. identify the correct contract bounty ID
2. read the bounty
3. enumerate every claim
4. obtain the proof URI for every claim
5. inspect each submission
6. apply the bounty's explicit rules
7. recommend a winner
8. report uncertainty
9. obtain user confirmation
10. only then transact

> 🚨 Never recommend a winner until exhaustive claim enumeration has completed successfully.

---

## Step 1: Read Bounty Rules

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "bounties(uint256)(uint256,address,string,string,uint256,address,uint256,uint256)" \
  <BOUNTY_ID> \
  --rpc-url $RPC_URL
```

The onchain bounty name and description are the base selection rules.

Additional rules visible through the frontend or supplied by the user may also matter if they can be verified.

---

# Step 2: Enumerate Every Claim

## Never Page `getClaimsByBountyId`

This is **not** an exhaustive data source:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "getClaimsByBountyId(uint256,uint256)(tuple(uint256,address,uint256,address,string,string,uint256,bool)[])" \
  <BOUNTY_ID> 0 \
  --rpc-url $RPC_URL
```

It may show recent claims for convenience.

It must never be used as the sole candidate set for winner selection.

---

## Preferred Method: Walk `bountyClaims`

Enumerate the bounty's exact claim index:

```solidity
bountyClaims(uint256 bountyId, uint256 index) returns (uint256 claimId)
```

Read indexes starting at `0` and increasing by one.

For each returned claim ID, read current state with:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "claims(uint256)(uint256,address,uint256,address,string,string,uint256,bool)" \
  <CLAIM_ID> \
  --rpc-url $RPC_URL
```

A failed `bountyClaims(bountyId, i)` call is **not** by itself proof that index `i` is out of
bounds. RPC failures and genuine contract reverts may be indistinguishable through some
client libraries.

Before declaring the array complete:

1. retry the **same exact `bountyClaims(bountyId, i)`** through a genuinely independent RPC provider
2. verify that provider is healthy with a known-good contract read
3. if the second provider returns a claim ID, continue enumeration
4. only accept the boundary if the healthy second provider also reports that exact index unavailable
5. if an independent provider is unavailable or health cannot be verified, stop and report that exhaustive enumeration could not be confirmed

Do not replace this with a same-provider canary alone. A provider can successfully answer
one key while failing a different `(bountyId, index)` read.

This is the preferred authoritative method for enumerating every claim on a specific bounty.

Do not stop after ten results, and do not silently convert an RPC failure into a shorter
claim list.

---

## Alternative: `ClaimCreated` Events

The event is:

```solidity
event ClaimCreated(
    uint256 indexed id,
    address indexed issuer,
    uint256 indexed bountyId,
    address bountyIssuer,
    string title,
    string description,
    uint256 createdAt,
    string imageUri,
    uint256 round
);
```

`bountyId` is indexed, so `ClaimCreated` logs can be filtered for the target bounty.

Events are useful for:

- alternative exhaustive claim discovery
- retrieving the original proof URI (`imageUri`)
- claim creation metadata
- historical transaction context

Despite its historical name, `imageUri` may contain proof that is not an image.

RPC providers may restrict:

- block ranges
- returned log counts
- large `eth_getLogs` requests

If a log query fails, reduce the block range or use the public `bountyClaims` index.

Do not treat an RPC error as evidence that no claims exist.

---

## Last-Resort Fallback: Global Claim Registry

If targeted index reads and event queries are unavailable, get:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "claimCounter()(uint256)" \
  --rpc-url $RPC_URL
```

Then inspect:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "claims(uint256)(uint256,address,uint256,address,string,string,uint256,bool)" \
  <CLAIM_ID> \
  --rpc-url $RPC_URL
```

Retain every claim where:

```text
claim.bountyId == target_bounty_id
```

Do not stop after ten results.

Batch RPC calls where appropriate.

---

# Step 3: Verify Current Claim State

Claims discovered through events should be checked against current contract state when their current state matters. Claims read through `bountyClaims(...)` + `claims(id)` already use current contract state.

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "claims(uint256)(uint256,address,uint256,address,string,string,uint256,bool)" \
  <CLAIM_ID> \
  --rpc-url $RPC_URL
```

Confirm:

- correct bounty
- correct claimant
- accepted state
- other relevant current state

---

# Step 4: Obtain Proof URI

When `ClaimCreated` data is available, use:

```text
imageUri
```

as the submitted proof URI.

If the proof URI was not retrieved during enumeration, query the relevant `ClaimCreated` event or the NFT contract.

Resolve NFT contract:

```bash
NFT_ADDRESS=$(cast call $POIDH_CONTRACT_ADDRESS \
  "poidhNft()(address)" \
  --rpc-url $RPC_URL)
```

Then:

```bash
cast call $NFT_ADDRESS \
  "tokenURI(uint256)(string)" \
  <CLAIM_ID> \
  --rpc-url $RPC_URL
```

---

# Step 5: Validate and Resolve the Proof URI

Before evaluating a claim, distinguish between:

```text
claim.description
```

and:

```text
proof URI / imageUri
```

The description may contain arbitrary supporting links and explanatory text.

The proof URI is the value poidh.xyz uses to resolve the claim's display media.

The Solidity/event field is historically named `imageUri`, but supported media is not limited to images.

Do not assume:

- every claim URI uses IPFS
- every URL in the claim description is the claim URI
- a GitHub, social, benchmark, PDF, document, or webpage link belongs in the URI field
- every contract-valid URI is displayable by poidh.xyz
- `imageUri` must literally point directly to an image file

### Current frontend resolution behavior

The current poidh.xyz frontend can resolve:

1. direct responses with `Content-Type: image/*`
2. direct responses with `Content-Type: video/*`
3. URLs ending in `.mp4`, `.mov`, `.webm`, or `.ogg`
4. JSON containing an `image` field
5. poidh's normal IPFS-hosted claim metadata flow

The native poidh.xyz uploader accepts:

```text
.jpg
.jpeg
.png
.gif
.webp
```

When validating a direct contract submission, prefer these known-supported forms.

### Resolve the URI

The URI may be an IPFS gateway URL, another HTTP(S) URL, or another retrievable URI used by existing poidh claim metadata.

If an `ipfs://` URI is encountered, it may be resolved through an IPFS gateway for inspection:

```python
uri = "<URI>"

if uri.startswith("ipfs://"):
    url = uri.replace(
        "ipfs://",
        "https://ipfs.io/ipfs/",
        1
    )
else:
    url = uri
```

Then inspect the response.

Conceptually:

```python
import requests

response = requests.get(url, timeout=30)
content_type = response.headers.get("content-type", "").lower()

if content_type.startswith("image/"):
    content_url = url
    media_type = "image"

elif content_type.startswith("video/"):
    content_url = url
    media_type = "video"

elif url.lower().split("?", 1)[0].endswith(
    (".mp4", ".mov", ".webm", ".ogg")
):
    content_url = url
    media_type = "video"

else:
    text = response.text

    try:
        meta = response.json()
    except Exception:
        meta = None

    if isinstance(meta, dict) and meta.get("image"):
        content_url = meta["image"]
        media_type = "metadata"
    else:
        content_url = None
        media_type = None
```

If JSON metadata contains:

```json
{
  "image": "<MEDIA_URL>"
}
```

resolve and inspect that media URL as well.

Do not automatically treat an arbitrary successful HTTP response as valid claim media.

A `200 OK` response from a GitHub page, social post, PDF, documentation page, or ordinary HTML webpage does **not** mean poidh.xyz can display it as claim media.

If the URI cannot be resolved to a supported media form, report the claim media as invalid or inaccessible.

Do not assume:

- every URI is JSON metadata
- every proof URI is an image
- every claim uses IPFS
- PDFs or generic documents are supported claim media

---

# Step 6: Inspect Proof

Use the appropriate capability.

### Image

Inspect visually.

### Webpage / social post

Open and read it when it is supplied as supporting evidence in the claim description or otherwise referenced by the bounty/submission.

Do not infer that a webpage or social-post URL is a valid claim media URI merely because it is useful evidence.

### GitHub repository / PR

Inspect:

- code
- changes
- documentation
- tests
- deliverables
- reproducibility

### Video

Inspect available:

- video content
- representative frames
- transcript
- metadata
- linked evidence

### PDF / document

Read the document when it is linked as supporting evidence in the claim description or bounty rules.

Do not assume a PDF/document URL is a supported `PROOF_URI` for frontend display.

### Benchmark or dataset

Inspect:

- reported result
- methodology
- traces
- output
- configuration
- reproducibility requirements

Do not reject a claim solely because it is not photographic proof.

---

# Step 7: Apply the Actual Bounty Rules

Possible criteria include:

- eligibility
- completeness
- relevance
- quality
- authenticity
- originality
- reproducibility
- benchmark performance
- deadline
- submission order
- required social post
- required tags
- required links
- creativity
- issuer discretion

Only apply criteria that are present or reasonably implied by the actual bounty.

Do not invent additional requirements.

---

## Earliest-Valid Submission Rules

If the bounty states:

> first valid submission wins

or similar language such as:

> priority will be given to the earliest valid submission

then claim chronology matters.

Exhaustively enumerate claims and sort qualifying submissions using:

- `createdAt`
- claim ID / event order where useful as a secondary ordering signal

Never judge an earliest-valid bounty from only the newest 10 claims.

---

# Step 8: Present Evaluation

Before transacting, report:

- total claims discovered
- enumeration method
- claims evaluated
- inaccessible claims, if any
- disqualified claims and reasons
- qualifying finalists
- recommended winner
- reasoning
- whether chronology affected the decision
- uncertainty

If even one potentially eligible claim cannot be inspected, disclose that.

Do not silently discard inaccessible evidence.

---

# Part 6: Accepting a Solo Bounty Claim

For solo bounties — and open bounties where no external contributor ever participated — acceptance can occur directly.

First check:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "everHadExternalContributor(uint256)(bool)" \
  <BOUNTY_ID> \
  --rpc-url $RPC_URL
```

If `false`, direct acceptance may be used.

Before sending:

1. verify bounty is active
2. verify selected claim belongs to bounty
3. verify all claims were enumerated
4. verify winner satisfies bounty rules
5. verify signing wallet is issuer
6. obtain explicit user confirmation

Then:

```bash
cast send $POIDH_CONTRACT_ADDRESS \
  "acceptClaim(uint256,uint256)" \
  <BOUNTY_ID> \
  <CLAIM_ID> \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

Acceptance:

- finalizes the bounty
- credits claimant payout to `pendingWithdrawals`
- deducts protocol fee
- transfers the winning claim NFT to the issuer

---

# Part 7: Open Bounty Voting

If:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "everHadExternalContributor(uint256)(bool)" \
  <BOUNTY_ID> \
  --rpc-url $RPC_URL
```

returns:

```text
true
```

use contributor voting.

---

## Nominate Claim

Issuer:

```bash
cast send $POIDH_CONTRACT_ADDRESS \
  "submitClaimForVote(uint256,uint256)" \
  <BOUNTY_ID> \
  <CLAIM_ID> \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

The issuer's contribution weight is automatically cast as a YES vote.

Contributors then have **2 days** to vote YES or NO.

---

## Read Vote Status

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "bountyVotingTracker(uint256)(uint256,uint256,uint256)" \
  <BOUNTY_ID> \
  --rpc-url $RPC_URL
```

Returns:

```text
yes_weight
no_weight
deadline_timestamp
```

---

## Resolve Vote

After the deadline:

```bash
cast send $POIDH_CONTRACT_ADDRESS \
  "resolveVote(uint256)" \
  <BOUNTY_ID> \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

Resolution is permissionless.

---

# Part 8: Withdrawing Funds

Winning bounty payouts are credited to:

```solidity
pendingWithdrawals(address)
```

---

## Check Balance

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "pendingWithdrawals(address)(uint256)" \
  <ADDRESS> \
  --rpc-url $RPC_URL
```

---

## Withdraw to Signing Wallet

```bash
cast send $POIDH_CONTRACT_ADDRESS \
  "withdraw()" \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

---

## Withdraw to Another Address

```bash
cast send $POIDH_CONTRACT_ADDRESS \
  "withdrawTo(address)" \
  <RECIPIENT_ADDRESS> \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

Check the pending balance before sending.

---

# Complete Protocol Enumeration

For analytics or exhaustive historical work, do not rely on the broken convenience getters.

---

## Bounties

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "bountyCounter()(uint256)" \
  --rpc-url $RPC_URL
```

Then:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "bounties(uint256)(uint256,address,string,string,uint256,address,uint256,uint256)" \
  <ID> \
  --rpc-url $RPC_URL
```

---

## Claims

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "claimCounter()(uint256)" \
  --rpc-url $RPC_URL
```

Then:

```bash
cast call $POIDH_CONTRACT_ADDRESS \
  "claims(uint256)(uint256,address,uint256,address,string,string,uint256,bool)" \
  <ID> \
  --rpc-url $RPC_URL
```

For large scans, use batched JSON-RPC requests.

For ordinary application development, consider the poidh indexer instead.

---

# Broken Getter Technical Reference

The known faulty pattern is equivalent to:

```solidity
uint256 counter;

for (
    uint256 i = ids.length;
    i > offset && counter < 10;
    i--
) {
    result[counter] = items[ids[i - 1]];
    counter++;
}
```

The loop always starts at:

```text
ids.length
```

not:

```text
ids.length - offset
```

Therefore `offset` acts as a lower loop boundary rather than a page starting position.

For collections containing more than ten entries, the loop typically reaches:

```text
counter == 10
```

before the offset changes the returned records.

A large offset near the array length may shorten the returned result, creating the misleading appearance that offset is working.

A proper conceptual newest-first implementation would instead resemble:

```solidity
if (offset >= ids.length) {
    return new Claim[](0);
}

for (
    uint256 i = ids.length - offset;
    i > 0 && counter < 10;
    i--
) {
    result[counter] = claims[ids[i - 1]];
    counter++;
}
```

The deployed contracts are immutable.

Do not assume this bug will change at the existing addresses.

---

# Native Token Reference

poidh uses ETH on every network supported by this skill.

| Human amount | Cast value   |
| ------------ | ------------ |
| `0.001 ETH`  | `0.001ether` |
| `0.01 ETH`   | `0.01ether`  |
| `0.1 ETH`    | `0.1ether`   |
| `1 ETH`      | `1ether`     |

---

# Protocol Fee

PoidhV3 takes a **2.5% protocol fee** on accepted bounty payouts.

The fee is applied at settlement.

Before settlement, contributed funds remain in bounty escrow subject to the contract's rules.

The winner receives the bounty payout minus the protocol fee through the withdrawal mechanism.

---

# Agent Decision Flow

## Discovering a Bounty

1. Use:

   - `https://poidh.xyz/bounties/data`, or
   - the poidh indexer.

2. Filter opportunities according to the user's request.

3. Inspect the individual bounty `/data` endpoint for:

   - bounty description
   - amount
   - chain
   - status
   - issuer
   - claims
   - comments

4. Always inspect public comments for clarifications, requirement changes, answers from the bounty creator, or other context that may affect how the bounty should be completed.

5. Before submitting a claim or performing another transaction, verify current critical state directly onchain.

---

## Posting a Bounty

1. Determine:

   - chain
   - name
   - description
   - ETH amount
   - solo/open

2. Resolve v3 contract.
3. Verify `MIN_BOUNTY_AMOUNT()`.
4. Verify wallet is an EOA.
5. Verify sufficient ETH.
6. Explain transaction.
7. Obtain confirmation.
8. Send transaction.
9. Decode creation event.
10. Return:

    - transaction hash
    - contract bounty ID
    - frontend URL

---

## Submitting a Claim

1. Determine:

   - chain
   - bounty
   - claim name
   - claim description
   - proof URI

2. If given frontend URL:

   - use `/data`
   - obtain `onChainId`

3. Verify bounty onchain.
4. Verify no active vote.
5. Verify wallet is not issuer.
6. Explain transaction.
7. Obtain confirmation.
8. Submit claim.
9. Decode `ClaimCreated`.
10. Return claim ID and transaction hash.

---

## Evaluating a Bounty

1. Resolve contract bounty ID.
2. Read bounty rules.
3. Enumerate every claim.
4. Prefer `bountyClaims(bountyId, i)` walked from `0` upward.
5. If an indexed read fails, confirm the **same exact index** through a healthy independent RPC provider before treating it as the array boundary.
6. If no independent provider is available, do not claim exhaustive enumeration succeeded.
7. Read each returned claim ID with `claims(id)`.
8. Use `ClaimCreated` logs to retrieve proof URIs or as an alternative exhaustive discovery path.
9. Fall back to `claimCounter()` + `claims(id)` only when necessary.
10. Never use paginated `getClaimsByBountyId` as the complete candidate list.
11. Resolve every proof URI.
12. Inspect every potentially eligible submission.
13. Apply only the bounty's actual requirements.
14. Account for deadlines and chronology.
15. Present findings.
16. Obtain user confirmation before accepting or nominating.

---

## Accepting a Claim

1. Verify all claims were evaluated.
2. Verify selected claim.
3. Check external-contributor state.
4. If no external contributor:

   - `acceptClaim`

5. If external contributors exist:

   - `submitClaimForVote`

6. Explain result to user.

---

# Transaction Safety Rules

Before **every** write transaction:

1. verify network
2. verify v3 contract address
3. verify bounty ID
4. verify claim ID if applicable
5. verify frontend ID conversion if applicable
6. verify current onchain state
7. verify signing wallet
8. verify ETH amount
9. explain the exact effect of the transaction
10. obtain explicit user confirmation

Never send funds to an unverified contract address.

Never interact with a poidh v2 contract.

Never expose the private key.

Never execute a winner transaction solely because an automated evaluation produced a recommendation.

Evaluation and transaction execution are separate actions.

---

# Error Reference

| Error                             | Cause                         | Fix                                      |
| --------------------------------- | ----------------------------- | ---------------------------------------- |
| `ContractsCannotCreateBounties()` | Wallet is a smart contract    | Use an EOA                               |
| `MinimumBountyNotMet()`           | Bounty below minimum          | Increase `--value`                       |
| `MinimumContributionNotMet()`     | Contribution below minimum    | Increase contribution                    |
| `NoEther()`                       | No ETH sent                   | Add `--value`                            |
| `WrongCaller()`                   | Caller not authorized         | Use issuer wallet                        |
| `VotingOngoing()`                 | Vote active                   | Wait or resolve after deadline           |
| `VotingEnded()`                   | Voting window finished        | Resolve vote                             |
| `NotSoloBounty()`                 | Direct acceptance unavailable | Use voting flow                          |
| `ClaimAlreadyAccepted()`          | Claim already accepted        | No further action                        |
| `BountyClaimed()`                 | Bounty finalized              | No further action                        |
| `BountyClosed()`                  | Bounty cancelled              | No further action                        |
| `BountyNotFound()`                | Wrong contract bounty ID      | Verify ID                                |
| `ClaimNotFound()`                 | Invalid claim ID              | Verify claim                             |
| `IssuerCannotClaim()`             | Issuer attempted own bounty   | Use different claimant wallet            |
| `NotActiveParticipant()`          | Caller not active contributor | Verify contribution state                |
| `MaxParticipantsReached()`        | Contributor cap reached       | Cannot join until slot becomes available |
| `NothingToWithdraw()`             | No pending payout             | Check pending balance                    |
| `VoteWouldPass()`                 | Attempted invalid vote reset  | Cannot override passing vote that way    |

---

# Critical Rules

1. **Only interact with poidh v3 contracts.**
2. **Never interact with poidh v2 contracts.**
3. **Only use Ethereum Mainnet, Arbitrum, or Base for live poidh activity.**
4. **Degen Chain (`666666666`) is retired. Treat Degen records as historical and never attempt transactions or wallet switching on Degen.**
5. **Never treat a historical Degen bounty as actionable merely because frontend or indexer data reports it as open.**
6. **Treat poidh's deployed contracts as immutable.**
7. **Never trust the four broken `offset` getters for exhaustive enumeration.**
8. **Never evaluate only the newest 10 claims when choosing a winner.**
9. **Never treat a failed `bountyClaims`, `userClaims`, or `userBounties` indexed read as proof that the array ended without confirming the same exact index through a healthy independent RPC provider.**
10. **If independent boundary confirmation is unavailable, fail loudly and do not claim exhaustive enumeration succeeded.**
11. **Use `bountyClaims(bountyId, i)` + `claims(id)` as the preferred exhaustive claim-enumeration path. Use `ClaimCreated` logs as an alternative and for proof URI retrieval.**
12. **Use `/bounties/data`, individual `/data` endpoints, or the indexer for convenient discovery.**
13. **Treat indexer and frontend JSON data as derived convenience data, not final consensus state.**
14. **Verify critical state directly onchain before any transaction.**
15. **Always distinguish frontend bounty IDs from v3 contract bounty IDs.**
16. **Prefer `onChainId` from the individual JSON endpoint when available.**
17. **Respect rules such as "first valid submission wins."**
18. **Never silently ignore an inaccessible claim.**
19. **Confirm with the user before every state-changing transaction.**
20. **Never reveal or log the user's private key.**
21. **Treat the claim URI as the value poidh.xyz uses to resolve claim display media, not as a generic evidence-link field.**
22. **The native poidh.xyz claim uploader accepts `.jpg`, `.jpeg`, `.png`, `.gif`, and `.webp`, uploads the image to IPFS, then submits IPFS-hosted metadata containing the image URI.**
23. **For direct contract submissions, use media the current frontend can resolve: `image/*`, `video/*`, `.mp4`, `.mov`, `.webm`, `.ogg`, or JSON metadata containing an `image` field.**
24. **Do not assume arbitrary PDFs, documents, GitHub pages, social posts, ordinary webpages, benchmark pages, or other generic URLs will display as claim media. Put those links in the claim description.**
25. **Contract-valid is not the same thing as frontend-displayable. Verify the URI resolves to supported media before creating a claim.**
26. **Never invent, guess, or submit a placeholder proof URI.**
