What you’ll be able to do
- Submit a claim, backing it by a bond.
- Challenge an incorrect claim with proof.
- Settle a claim and withdraw available funds.
MetalAssertions at 0x000000000000000000000000000000000000018c records claims
backed by a token deposit called a bond. Each claim belongs to a domain,
which sets who can participate, the required bond, and how challenges are
decided. This guide uses an objective domain, where the contract decides
challenges by checking proofs. Here, you’ll use a Merkle proof to check a value
against a Merkle root.
Use a test account for the false claim and obtain the domain operator’s
permission. A successful challenge permanently prevents that account from
submitting further claims in the domain.
Obtain a domain and two permitted accounts
Get the domain name, its publishedrulesHash, and permission to use the
two-leaf test tree from the operator. The network configures domains at genesis.
Use separate signing accounts for the asserter and challenger, and reserve them
for this exercise so you can track changes to their balances.
Check for runtime bytecode at the assertions address.
Install the TypeScript SDK for transaction signing.
Use the
token metadata and spending approval procedure
to verify the bond token, read balances, and grant exact allowances to the
assertions contract. Fund each signing account with the bond token for bonds and
the gas token for transaction fees, using
test funding where supported. Each
approval, assertion, challenge, settlement, and withdrawal is a separate
transaction.
Load the interface and validate the domain
domainName and rulesHash from the operator, and asserter and
challenger from the two signing accounts’ addresses. Read
suspended(domainId, asserter) at the same block and stop if true. Confirm a
nonzero bond floor and a window long enough to submit a challenge. Save
claimable for both accounts and burned so you can measure their changes
during the exercise.
For a declared value in the domain bond token’s smallest units, the minimum bond
is max(bondFloor, floor(declaredValue * bondBps / 10000)). Read
requiredBond(domainId, declaredValue) and use that exact amount B. A
challenge posts the assertion’s recorded bond, even if the asserter chose more
than the minimum. Account for transaction fees separately from bonds.
Construct a claim and its Merkle proof
Construct the agreed test tree with twobytes32 leaf values. Prefix leaves
with 0x00 before hashing and internal nodes with 0x01. Order siblings
according to the leaf index bits, starting with the least significant bit.
keccak256(abi.encode(kind, subjectA, subjectB, value)), using kind 2 for a
Merkle leaf.
Submit and identify the claim
ApproveB of the bond token to the assertions contract and verify the
resulting allowance. If replacing an existing allowance, first reconcile any
outstanding uses. Simulate
assertClaim(domainId, correctHash, rulesHash, declaredValue, B) from the
asserter, estimate its fee, and prepare it with the SDK. Use the asserter’s
wallet client as wallet, the verified gas token address as gasToken, and
gasLimit and maxFeePerGas from the fee estimate.
attempt.txHash, attempt.raw, the signing account’s address, nonce, and
all claim inputs in protected application storage before calling
attempt.send(). If submission times out, query the saved transaction hash to
recover its receipt.
Use
Verify and reconcile payments
to verify successful execution, the canonical block hash, and the finalized
block height. Filter logs by the assertions address before decoding:
B, status
1 (Asserted), and verified == false. expiresAt must equal the receipt
block’s timestamp plus windowSeconds. Independently compute the ID as
keccak256(abi.encode(domainId, asserter, claimHash, rulesHash, receipt.blockNumber)),
using types bytes32,address,bytes32,bytes32,uint256. Save the ID with the
signed attempt.
Settle and withdraw the correct claim
Once the chain timestamp reachesexpiresAt, submit settle(id) from either
signing account using a new saved signed transaction. An earlier call reverts
with WindowActive. Verify the finalized Settled(id) event, status 2, and
an increase of exactly B in claimable(bondToken, asserter).
Submit withdraw(bondToken) from the asserter to withdraw its full claimable
balance for that token. Verify the Withdrawn(bondToken, asserter, amount)
event, a matching bond-token Transfer from the assertions address to the
asserter, and zero remaining claimable balance. Follow
settlement and finality for each transaction. If the
bond token also pays gas, account for transaction fees separately from the
withdrawal amount.
ReceivePolicy checks the withdrawal’s recipient. A
refusal in REVERT mode reverts the withdrawal and preserves the claimable
balance. In QUARANTINE mode, it sends the tokens to quarantine while the
withdrawal emits Withdrawn and clears the claimable balance. Save the
finalized quarantine receipt and matching token transfer to ReceivePolicy, then
mark the withdrawal as awaiting delivery. Recover the tokens through that
receipt.
Challenge a false claim
After the first withdrawal completes, approve another bond and submit a second claim using the same root and index with a false leaf value oftoHex(99n, { size: 32 }). Save the signed transaction and claim inputs. The
challenge encodes those inputs followed by the true leaf and its sibling path:
B, then submits challenge(secondId, proof) through its wallet client.
Leave enough time for the transaction to execute while
block.timestamp < expiresAt, and stop if the window has elapsed.
Verify a finalized Voided(secondId, challenger) event, status 3, and
suspended(domainId, asserter) == true. With H = floor(B / 2), the challenger
gains B + H claimable units and burned(bondToken) increases by B - H. This
returns the challenger’s bond and awards it half of the asserter’s forfeited
bond, rounded down. Withdraw the challenger’s funds and verify delivery as
above.
Check the event to determine the challenge’s outcome. ChallengeFailed means
the challenger forfeited its bond and the assertion remains Asserted, even
though the transaction executed successfully. This occurs when the proof fails
to reach the root or proves the claimed leaf correct. Claim inputs that fail to
reproduce the claim hash cause BadClaim and revert the transaction.
Verify recovery and other claim classes
After a restart, use the saved transaction hashes and assertion IDs to recover receipts and state. Check finality, the assertion’s status, suspension, claimable balances, and the change in burned funds before deciding whether another transaction is needed. See the Assertions reference for other claim classes. UseassertOracleClaim to verify a retained oracle write immediately.
Evidence domains resolve challenges through validator verdicts, while subjective
domains use a named arbiter.
Test mismatched claim inputs, invalid paths, unknown or non-objective domains,
early settlement, and challenges at or after expiry before submitting
transactions. Include an odd bond amount to check rounding, and test withdrawal
refusal, quarantine, and recovery after a restart.