# Quest

> Quest/reward collection — users complete criteria and claim a badge + coin payout

**Category:** Token Types

## Summary

Required standards: \["Quests"]

* Single token only: validTokenIds = \[{start: "1", end: "1"}]
* Quest approval MUST be properly gated — typically via an off-chain claim (merkle challenge with claimConfig), but can also use on-chain criteria (mustOwnTokens, dynamicStoreChallenges, evmQueryChallenges, votingChallenges)
* Coin transfers with overrideFromWithApproverAddress: true + overrideToWithInitiator: true
* predeterminedBalances: amount 1, no increments, no recurring, no duration
* Escrow funded upfront via set\_mint\_escrow\_coins (rewardAmount \* maxClaims)
* invariants.noCustomOwnershipTimes: true
* Permissions: use "locked-approvals" preset (recommended)
* Default balances: empty balances, all auto-approve flags true

## Instructions

## Quest Configuration

### Mental Model

A quest collection rewards users for completing criteria. Users receive a quest badge (token 1) + coin payout.

The quest approval MUST be properly gated so that only eligible users can claim. Gating options:

* **Off-chain claim (most common)**: A merkle challenge with claimConfig containing plugins (password, codes, whitelist, etc.). The claim is verified off-chain by BitBadges, and a merkle proof is issued for on-chain redemption.
* **On-chain criteria**: mustOwnTokens (require holding specific tokens/badges), dynamicStoreChallenges, evmQueryChallenges, votingChallenges — these are checked directly on-chain during the transfer.
* **Both**: Combine off-chain claims with on-chain criteria for layered verification.

Choose the gating approach based on the user's request. If they mention passwords, codes, or whitelists, use an off-chain claim. If they mention token ownership or on-chain conditions, use the corresponding on-chain criteria.

### Build Steps (call ALL in parallel in one round)

1. `set_standards` → `["Quests"]`
2. `set_valid_token_ids` → `[{ "start": "1", "end": "1" }]`
3. `set_invariants` → `{ "noCustomOwnershipTimes": true }`
4. `set_permissions` → `{ "preset": "locked-approvals" }`
5. `set_default_balances` → empty balances, all auto-approve true
6. `set_collection_metadata` / `set_token_metadata` — descriptive content
7. `add_approval` — the quest approval (see exact structure below)
8. **`set_mint_escrow_coins`** — REQUIRED for coin rewards. Amount = rewardPerClaim × maxClaims.

### Quest Approval (add\_approval)

Use approvalId `"quest-approval"`. The EXACT approvalCriteria structure:

```json
{
  "approvalId": "quest-approval",
  "fromListId": "Mint",
  "toListId": "All",
  "initiatedByListId": "All",
  "tokenIds": [{"start":"1","end":"1"}],
  "approvalCriteria": {
    "overridesFromOutgoingApprovals": true,
    "maxNumTransfers": {
      "overallMaxNumTransfers": "<maxClaims>"
    },
    "predeterminedBalances": {
      "manualBalances": [],
      "incrementedBalances": {
        "startBalances": [{ "amount": "1", "tokenIds": [{"start":"1","end":"1"}], "ownershipTimes": [{"start":"1","end":"18446744073709551615"}] }],
        "incrementTokenIdsBy": "0",
        "incrementOwnershipTimesBy": "0",
        "durationFromTimestamp": "0",
        "allowOverrideTimestamp": false,
        "recurringOwnershipTimes": { "startTime": "0", "intervalLength": "0", "chargePeriodLength": "0" }
      },
      "orderCalculationMethod": {
        "useOverallNumTransfers": true,
        "usePerToAddressNumTransfers": false,
        "usePerFromAddressNumTransfers": false,
        "usePerInitiatedByAddressNumTransfers": false,
        "useMerkleChallengeLeafIndex": false,
        "challengeTrackerId": ""
      }
    },
    "coinTransfers": [{
      "to": "",
      "overrideFromWithApproverAddress": true,
      "overrideToWithInitiator": true,
      "coins": [{ "amount": "<rewardAmount>", "denom": "<rewardDenom>" }]
    }]
  }
}
```

**Gating** — add ONE OR MORE of these to approvalCriteria based on the user's request:

* **Off-chain claim**: `"merkleChallenges": [{ "root": "", "expectedProofLength": "0", "maxUsesPerLeaf": "1", "uri": "", "customData": "", "useCreatorAddressAsLeaf": false, "claimConfig": { "approach": "in-site", "label": "...", "plugins": [...] } }]`
* **Token ownership**: `"mustOwnTokens": [{ "collectionId": "...", "amountRange": {"start":"1","end":"18446744073709551615"}, ... }]` — Use collectionId "0" to self-reference this collection (e.g., require holding token 1 from this quest collection itself).
* **Dynamic store**: `"dynamicStoreChallenges": [...]`
* **EVM query**: `"evmQueryChallenges": [...]`

Off-chain claims are the most common for quests (passwords, codes, whitelists). On-chain criteria can be combined with or used instead of claims.

### Escrow Funding (REQUIRED)

Call `set_mint_escrow_coins` in the SAME round as the other tools. Example for 10 ATOM reward × 50 claims:

```
set_mint_escrow_coins({ coins: [{ denom: "ibc/A4DB...", amount: "500000000" }] })
```

Without this, the escrow has no funds and claims will fail.

### Common Mistakes

* DON'T add extra fields to coinTransfers — the ONLY fields are: to, overrideFromWithApproverAddress, overrideToWithInitiator, coins. NO startTime, NO other fields.
* DON'T omit `manualBalances: []` in predeterminedBalances — SDK crashes without it
* DON'T omit fields in orderCalculationMethod — include ALL boolean fields
* DON'T forget `set_mint_escrow_coins` — without it, the escrow is empty and rewards can't be paid
* DON'T set maxUsesPerLeaf to anything other than "1" — each user claims once
* DON'T set allowOverrideTimestamp: true — quests require false
* DON'T set useCreatorAddressAsLeaf: true — quests require false


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.bitbadges.io/token-standard/skills/quest.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
