quest
Quest/reward collection: users complete criteria and claim a badge + coin payout
Quest
Instruction text for agents that use the Quest skill, loaded by bb dev skills quest and the MCP get_skill_instructions tool.
Try it
Paste this into Claude Code, Codex, or Cursor with the BitBadges MCP server wired. The agent loads the skill, builds, verifies, and hands back a link to review and sign.
Load the quest skill and build me one: quest/reward collection: users complete criteria and claim a badge + coin payout. Use the session tools, run validate, review, and simulate in parallel, fix anything critical, then call get_review_url and give me the link.From a shell, bb dev skills quest prints the same instructions.
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)
set_standards→["Quests"]set_valid_token_ids→[{ "start": "1", "end": "1" }]set_invariants→{ "noCustomOwnershipTimes": true }set_permissions→{ "preset": "locked-approvals" }set_default_balances→ empty balances, all auto-approve trueset_collection_metadata/set_token_metadata: descriptive contentadd_approval: the quest approval (see exact structure below)set_mint_escrow_coins: required for coin rewards. Amount = rewardPerClaim × maxClaims.
Quest Approval (add_approval)
Use approvalId "quest-approval". The exact approvalCriteria structure:
{
"approvalId": "quest-approval",
"fromListId": "Mint",
"toListId": "All",
"initiatedByListId": "All",
"tokenIds": [{"start":"1","end":"1"}],
"approvalCriteria": {
"overridesFromOutgoingApprovals": true,
"maxNumTransfers": {
"overallMaxNumTransfers": "<maxClaims>"
},
"predeterminedBalances": {
"incrementedBalances": {
"startBalances": [{ "amount": "1", "tokenIds": [{"start":"1","end":"1"}], "ownershipTimes": [{"start":"1","end":"18446744073709551615"}] }],
"recurringOwnershipTimes": { "startTime": "0", "intervalLength": "0", "chargePeriodLength": "0" }
},
"orderCalculationMethod": {
"useOverallNumTransfers": true
}
},
"coinTransfers": [{
"overrideFromWithApproverAddress": true,
"overrideToWithInitiator": true,
"coins": [{ "amount": "<rewardAmount>", "denom": "<rewardDenom>" }]
}]
}
}{
"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