> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metalntwx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Assertions

> Bonded claim domains, proof encoding, resolution classes, and payouts

The `MetalAssertions` genesis contract at
`0x000000000000000000000000000000000000018c` records claims backed by a bond
under a domain's published rules. A permitted challenger can dispute a claim
during the domain's challenge window. Once the window ends, anyone can call
`settle(id)` to settle an unchallenged assertion.
[Verified oracle claims](#verified-oracle-claims) are checked and settled when
submitted.

Each domain sets one of three challenge classes:

| Class | A challenge is decided by |
| - | - |
| Objective | A proof the challenger submits. The proof determines the outcome |
| Evidence | A verdict the validators attest through the [oracle](https://docs.metalntwx.com/build/data-and-signatures/use-oracle-data.md) |
| Subjective | A named arbiter address |

When a bond is forfeited, half goes to the winner as claimable balance, rounded
down. The remainder is recorded as burned and held permanently by the assertions
contract.

## Before you start

Prepare the bond token and its spending approval using the
[token and spending approval instructions](https://docs.metalntwx.com/reference/assets-and-payments/mip20.md#approve-a-spender).
Fund the signing account's balance in the gas token using
[Estimate and record fees](https://docs.metalntwx.com/build/prepare/estimate-and-record-fees.md), then verify
[execution and finality](https://docs.metalntwx.com/reference/network-fundamentals/settlement.md) for each transaction.

## Domains

Domains are fixed at genesis. `domainIdOf(name)` computes the domain ID as
`keccak256(name)`, and `domain(id)` returns its configuration:

| Field | Meaning |
| - | - |
| `asserter` | The one address that may assert. Zero permits anyone |
| `challenger` | The one address that may challenge. Zero permits anyone |
| `bondToken` | The MIP20 token bonds are posted in |
| `bondFloor` | The least bond, in the bond token |
| `bondBps` | The bond as basis points of the assertion's declared value |
| `windowSeconds` | The challenge window, in block time |
| `binding` | Whether a settled assertion binds the domain's consumers |
| `evidence` | An evidence domain: challenges are questions validators answer |
| `arbiter` | A subjective domain: the named address decides challenges. Zero means no arbiter. Exclusive with `evidence` |

`requiredBond(domainId, declaredValue)` returns the minimum bond:
`max(bondFloor, floor(declaredValue * bondBps / 10000))`. A challenge posts the
assertion's recorded bond, which can exceed that minimum.

## Claims

An objective claim identifies a subject whose value can be checked onchain and
records the asserter's proposed value:

```text theme={null}
claimHash = keccak256(abi.encode(kind, subjectA, subjectB, value))
```

`claimHashOf(kind, subjectA, subjectB, value)` computes it on chain.

| `kind` | Subject | Truth |
| - | - | - |
| `1` (`CLAIM_ORACLE_AT`) | `subjectA` = oracle item id, `subjectB` = block height | `0xFD.at(itemId, height).value` |
| `2` (`CLAIM_MERKLE_LEAF`) | `subjectA` = Merkle root, `subjectB` = leaf index | The leaf at that index under the root, in a tree whose leaves are `keccak256(0x00 ‖ leaf)` and whose nodes are `keccak256(0x01 ‖ left ‖ right)` |

`rulesHash` identifies the rules under which the assertion is made. The
assertion ID is
`keccak256(abi.encode(domainId, asserter, claimHash, rulesHash, block.number))`,
allowing one assertion with those same fields per block. Submit a new assertion
if the claim or rules change.

## Asserting and settling

* `assertClaim(domainId, claimHash, rulesHash, declaredValue, bond)` pulls
  `bond` of the domain's token from the caller (approve first) and returns the
  assertion id, also emitted in `Asserted`.
* `settle(id)` settles an assertion once its window elapsed. Anyone may call
  it.
* `withdraw(token)` pays out everything claimable to the caller.

`Settled` and `Voided` are final states. Consumers check `status` when using an
assertion, and later disputes require a new assertion.

## Verified oracle claims

Use `assertOracleClaim(domainId, itemId, height, value, rulesHash)` for oracle
claims (kind `1`). It reads `0xFD.at(itemId, height)` in the same call and
reverts with `Unreadable` if that write is unavailable, `WrongValue` if the
value differs, or `Flagged` if any flag is set.

A successful call records the assertion as `Settled` with `verified = true`, a
zero bond and declared value, and `expiresAt` set to the current block time. It
emits `Asserted` followed by `Settled`. The claim hash and assertion ID use the
same formulas as `assertClaim`.

The verified assertion remains after the oracle's history ring overwrites the
original write. Save its item ID, height, and value so a consumer can recompute
the claim hash and compare it with the verified record.

The domain's `asserter` allowlist and suspensions apply across all three
challenge classes. Check `verified == true` when relying on immediate oracle
verification. A claim submitted through `assertClaim` is checked only if
challenged.

## Objective challenges

`challenge(id, proof)` takes
`proof = abi.encode(kind, subjectA, subjectB, value, actual, siblings)`: the
claim's preimage, the value the challenger says the subject has, and for a
Merkle claim the sibling path from `actual` to the root. `siblings` is empty for
an oracle claim.

* **Contradiction.** The assertion is voided, the asserter's bond is forfeited
  (half burned, half to the challenger, whose own bond is returned), and the
  asserter is suspended in the domain.
* **Failed proof.** An invalid path or proof of the claimed value forfeits the
  challenger's bond, with half burned and half claimable by the asserter. The
  assertion remains open to further challenges until its window ends.
* **Unreadable oracle claim.** If a claim submitted through `assertClaim` names
  an unavailable oracle write, a challenge voids it and returns both bonds in
  full as claimable balances. The challenger must still post its bond, and the
  asserter remains permitted in the domain. If unchallenged, the assertion can
  settle after the window ends.

> [!WARNING]
>
> Use `assertOracleClaim` to verify an oracle value while the write is available.
> The history ring keeps one write per `height % 64`, so an item written every
> block loses its earlier write after 64 blocks. A bonded claim through
> `assertClaim` then becomes voidable before its challenge window may have ended.

## Evidence challenges

In an evidence domain a challenge is a question for the validators:

* `challengeEvidence(id, evidenceURI)` posts a bond equal to the asserter's,
  names the evidence, marks the assertion `Challenged`, and lists it in
  `pendingEvidence()`.
* Validators that answer evidence questions add the verdict item
  `verdictItemOf(id)` to the oracle catalog through a `catalog/1` statement, and
  attest the verdict there. The item's name, `verdictNameOf(id)`, is `v/`
  followed by the first 14 bytes of `id` in hex. The row leaves the catalog once
  the challenge closes.
* `resolveEvidence(id)` applies an undisputed verdict written after the
  challenge. If none is available after one challenge window, anyone can resolve
  it as `TOO_EARLY`.

| Verdict | Outcome |
| - | - |
| `1` TRUE | Settles for the asserter. The challenger's bond is half burned, half to the asserter |
| `2` FALSE | Voids and suspends the asserter. Its bond is half burned, half to the challenger |
| `3` TOO\_EARLY | Reopens the assertion with a fresh window. The challenger's bond is returned and the asserter's stays held. The third TOO\_EARLY on an assertion voids it instead, with both bonds returned and no suspension |
| `4` INVALID\_RULES | Voids it with both bonds returned and no suspension |

One challenge per assertion runs at a time. `tooEarly` in `assertion(id)` counts
`TOO_EARLY` resolutions, including those caused by a missing verdict. The third
such resolution (`MAX_TOO_EARLY`) voids the assertion and returns both bonds.

## Subjective challenges

In a domain that names an `arbiter`, that address decides challenges.

* `challengeSubjective(id)` posts a bond equal to the asserter's and marks the
  assertion `Challenged`. A challenge from the arbiter reverts with
  `ArbiterCannotChallenge`.
* `resolveSubjective(id, verdict)` lets the arbiter apply a verdict from the
  table above. After one challenge window, anyone else can call it to resolve
  the challenge as `TOO_EARLY`, regardless of the supplied verdict.

A challenge of the wrong class reverts with `WrongChallengeClass`: `challenge`
outside an objective domain, `challengeEvidence` outside an evidence domain, and
`challengeSubjective` outside a subjective domain.

## Interface

Reads:

| Function | Returns |
| - | - |
| `domain(domainId)` | The domain's fields |
| `assertion(id)` | The assertion. `status` is `1` Asserted, `2` Settled, `3` Voided, `4` Challenged. `tooEarly` counts TOO\_EARLY verdicts. `verified` marks a claim `assertOracleClaim` checked |
| `suspended(domainId, account)` | Whether the account is suspended in the domain |
| `claimable(token, account)` | What `withdraw(token)` pays the account |
| `burned(token)` | Forfeited halves held at `0x018C` |
| `requiredBond(domainId, declaredValue)` | The bond an assertion or challenge posts |
| `domainIdOf(name)`, `claimHashOf(...)` | Id helpers |
| `pendingEvidence()` | Open evidence challenges |
| `evidence(id)` | The evidence URI an open challenge names |
| `verdictNameOf(id)`, `verdictItemOf(id)` | The verdict item's catalog name and oracle id |

Events: `Asserted`, `Settled`, `Voided`, `ChallengeFailed`, `Withdrawn`,
`EvidenceChallenged`, `EvidenceResolved`, `SubjectiveChallenged`,
`SubjectiveResolved`.

Errors: `UnknownDomain`, `WrongChallengeClass`, `NotChallenged`, `NoVerdict`,
`NotArbiter`, `ArbiterCannotChallenge`, `BadVerdict`, `NotAsserter`,
`NotChallenger`, `Suspended`, `BondTooSmall`, `TransferFailed`,
`AlreadyAsserted`, `NotAsserted`, `WindowActive`, `WindowElapsed`, `BadClaim`,
`Unreadable`, `WrongValue`, `Flagged`, `NothingToWithdraw`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.