# Credit Token

> Increment-only, non-transferable credit token purchased with any ICS20 denom. Users pay X of a denom and receive Y tokens as credits/proof of payment. For a 1:1 backed token with on-chain transferability, use the Smart Token standard instead.

**Category:** Token Types

## Summary

Required standards: \["Credit Token"]

* Increment-only, non-transferable (soulbound) fungible token purchased with ICS20 denom
* validTokenIds: \[{ "start": "1", "end": "1" }] (single token ID)
* collectionApprovals: ONLY Mint approvals (no transferable/burnable approvals)
* Lock canUpdateCollectionApprovals (empty array = frozen)
* defaultBalances: autoApproveAllIncomingTransfers: true, autoApproveSelfInitiatedOutgoingTransfers: true, autoApproveSelfInitiatedIncomingTransfers: true
* All Mint approvals: overridesFromOutgoingApprovals: true, mustPrioritize: true
* Denomination tiers: create 8-10 approval tiers (credit-1, credit-5, credit-10, etc.) for greedy decomposition
* MUST include alias path for display
* All permissions locked (empty arrays)
* Key difference from Smart Token: one-way minting only, no backing/unbacking, no transferability

## Instructions

## Credit Token Configuration

### Concept

A Credit Token is an increment-only, non-transferable fungible token that users purchase with an ICS20 denom (USDC, ATOM, BADGE, etc.). Tokens serve as both proof of payment and consumable credits. This is a one-way system — tokens can only be minted (incremented), never sold back, burned, or transferred between users. For a 1:1 backed token with on-chain transferability, use the Smart Token standard instead.

### Payment Flow

When a user mints credit tokens, the payment (coinTransfers) goes directly to a **payout address** specified in the approval. The tokens are NOT redeemable post-mint — they are increment-only. The payout address receives the ICS20 denom immediately upon mint; there is no escrow or redemption mechanism.

### Usage Pattern: totalUsed / totalCreditsPaidFor

Credit tokens are designed for systems that track consumption off-chain. The on-chain token balance represents `totalCreditsPaidFor` — the total credits ever purchased. An off-chain system tracks `totalUsed`. The remaining budget is simply `balance - totalUsed`.

**Example: BitBadges API Credits (Collection 23 / 80, APITOKEN)**

* User purchases 10 USDC → receives 1,000,000 APITOKEN (on-chain balance = 1,000,000)
* User makes API calls (including the AI Builder) → backend tracks `totalUsed` (e.g., 250,000 APITOKEN used)
* Remaining budget = on-chain balance (1,000,000) - totalUsed (250,000) = 750,000
* User purchases 5 more USDC → on-chain balance increments to 2,000,000
* Remaining budget = 2,000,000 - 250,000 = 1,750,000
* The balance only ever goes up (increment-only). The off-chain `totalUsed` only ever goes up. Budget = balance - totalUsed.

### Required Structure

1. **Standards**: MUST include "Credit Token"
   * `"standards": ["Credit Token"]`
2. **validTokenIds**: Single fungible token ID
   * `"validTokenIds": [{ "start": "1", "end": "1" }]`
3. **Non-transferable (Soulbound)**: Only mint approvals allowed
   * `collectionApprovals` should ONLY contain approvals with `"fromListId": "Mint"`
   * No transferable-approval or burnable-approval
   * Lock `canUpdateCollectionApprovals` (empty array = frozen)
4. **Auto-approve incoming**: defaultBalances MUST have:
   * `"autoApproveAllIncomingTransfers": true`
   * `"autoApproveSelfInitiatedOutgoingTransfers": true`
   * `"autoApproveSelfInitiatedIncomingTransfers": true`
5. **Mint Approvals with coinTransfers**: Each approval mints tokens in exchange for payment
   * `"fromListId": "Mint"`
   * `"toListId": "All"`
   * `"initiatedByListId": "All"`
   * `"overridesFromOutgoingApprovals": true` (REQUIRED for all Mint approvals)
   * `"mustPrioritize": true`
   * `coinTransfers`: specifies payment amount and recipient

### Conversion Rate Pattern

The conversion rate is: X base units of ICS20 denom = Y tokens minted.

For example, if 1 USDC (1,000,000 base units) = 100,000 tokens:

* `coinTransfers.coins[0].amount`: "1000000" (1 USDC in base units)
* `predeterminedBalances.incrementedBalances.startBalances[0].amount`: "100000" (tokens minted)

### Denomination Tiers

Create multiple approval tiers for bulk purchases. Each tier has a unique `approvalId` (`credit-<multiplier>`) and mints a proportional amount of tokens. The frontend uses greedy decomposition to break any purchase amount into the fewest transactions.

**Choose 8-10 tiers** that make sense for the token's use case and expected purchase sizes. Cover a wide range from small to very large. The tiers are NOT hardcoded — pick the most applicable and efficient denominations for the specific token.

Example tiers (1 USDC = 100K tokens, but adapt to your use case):

| approvalId        | Payment      | Tokens Minted  |
| ----------------- | ------------ | -------------- |
| credit-1          | 1 USDC       | 100,000        |
| credit-5          | 5 USDC       | 500,000        |
| credit-10         | 10 USDC      | 1,000,000      |
| credit-50         | 50 USDC      | 5,000,000      |
| credit-100        | 100 USDC     | 10,000,000     |
| credit-500        | 500 USDC     | 50,000,000     |
| credit-1000       | 1,000 USDC   | 100,000,000    |
| credit-10000      | 10,000 USDC  | 1,000,000,000  |
| credit-100000     | 100,000 USDC | 10,000,000,000 |
| credit-1000000000 | 1B USDC      | 100T tokens    |

The multiplier in the approvalId (e.g., `credit-50`) represents the number of base payment units. Each tier's payment = multiplier × base payment amount, and tokens minted = multiplier × tokens per unit.

### Alias Path (REQUIRED)

MUST include an alias path so tokens display nicely:

```json
"aliasPathsToAdd": [{
  "denom": "u<symbol_lowercase>",
  "conversion": {
    "sideA": { "amount": "1" },
    "sideB": [{ "amount": "1", "ownershipTimes": [{"start":"1","end":"18446744073709551615"}], "tokenIds": [{"start":"1","end":"1"}] }]
  },
  "symbol": "<SYMBOL>",
  "denomUnits": [],
  "metadata": { "uri": "ipfs://METADATA_ALIAS_u<symbol_lowercase>", "customData": "" }
}]
```

### Approval Template

Each mint approval follows this structure:

```json
{
  "toListId": "All",
  "fromListId": "Mint",
  "initiatedByListId": "All",
  "transferTimes": [{ "start": "1", "end": "18446744073709551615" }],
  "tokenIds": [{ "start": "1", "end": "1" }],
  "ownershipTimes": [{ "start": "1", "end": "18446744073709551615" }],
  "approvalId": "credit-<amount>",
  "uri": "",
  "customData": "",
  "approvalCriteria": {
    "predeterminedBalances": {
      "manualBalances": [],
      "incrementedBalances": {
        "startBalances": [{ "amount": "<tokens_to_mint>", "tokenIds": [{"start":"1","end":"1"}], "ownershipTimes": [{"start":"1","end":"18446744073709551615"}] }],
        "incrementTokenIdsBy": "0",
        "incrementOwnershipTimesBy": "0",
        "allowOverrideTimestamp": false,
        "recurringOwnershipTimes": { "startTime": "0", "intervalLength": "0", "chargePeriodLength": "0" },
        "allowOverrideWithAnyValidToken": false
      },
      "orderCalculationMethod": { "useOverallNumTransfers": true, "usePerToAddressNumTransfers": false, "usePerFromAddressNumTransfers": false, "usePerInitiatedByAddressNumTransfers": false, "useMerkleChallengeLeafIndex": false, "challengeTrackerId": "" }
    },
    "approvalAmounts": { "overallApprovalAmount": "0", "perToAddressApprovalAmount": "0", "perFromAddressApprovalAmount": "0", "perInitiatedByAddressApprovalAmount": "0", "amountTrackerId": "credit-<amount>", "resetTimeIntervals": { "startTime": "0", "intervalLength": "0" } },
    "maxNumTransfers": { "overallMaxNumTransfers": "0", "perToAddressMaxNumTransfers": "0", "perFromAddressMaxNumTransfers": "0", "perInitiatedByAddressMaxNumTransfers": "0", "amountTrackerId": "credit-<amount>", "resetTimeIntervals": { "startTime": "0", "intervalLength": "0" } },
    "coinTransfers": [{
      "to": "<payment_recipient_address>",
      "coins": [{ "amount": "<payment_base_units>", "denom": "<ics20_denom>" }],
      "overrideFromWithApproverAddress": false,
      "overrideToWithInitiator": false
    }],
    "merkleChallenges": [],
    "mustOwnTokens": [],
    "overridesFromOutgoingApprovals": true,
    "overridesToIncomingApprovals": false,
    "mustPrioritize": true
  },
  "version": 0
}
```

### Permissions (All Locked)

All permissions should be locked (empty arrays = frozen):

```json
"collectionPermissions": {
  "canDeleteCollection": [],
  "canArchiveCollection": [],
  "canUpdateStandards": [],
  "canUpdateCustomData": [],
  "canUpdateManager": [],
  "canUpdateCollectionMetadata": [],
  "canUpdateValidTokenIds": [],
  "canUpdateTokenMetadata": [],
  "canUpdateCollectionApprovals": [],
  "canAddMoreAliasPaths": [],
  "canAddMoreCosmosCoinWrapperPaths": []
}
```

### Key Differences from Smart Token

* **Increment-only** — tokens can only be minted (purchased), never redeemed, burned, or decreased
* **Non-transferable** — soulbound, no peer-to-peer transfers. If users need transferability, use Smart Token instead
* **No backing/unbacking** — one-way minting only, no cosmosCoinBackedPath
* **Multiple tiers** — different approval amounts for bulk purchases via greedy decomposition
* **Credits never expire** — ownership times cover full range

### Frontend Integration

The Credit Token standard has a dedicated view page that shows:

1. User's current token balance (using the alias path for display)
2. Purchase form with DenomAmountSelectWithMax for the payment denom
3. Conversion rate display (X denom = Y tokens)
4. Multi-tier transaction decomposition for optimal gas usage

## Common Mistakes

* DON'T add transferable or burnable approvals — credit tokens are increment-only and non-transferable (soulbound). Only Mint approvals are allowed.
* DON'T forget mustPrioritize: true on all credit token Mint approvals — required for correct tier matching.
* DON'T forget the alias path — credit tokens will not display properly without one.
* DON'T use numbers instead of strings for amounts — all values must be string-encoded ("100000" not 100000).
* DON'T confuse credit tokens with smart tokens — credit tokens are one-way minting only with no backing/unbacking or transferability.

## Reference Collections

* [Collection 23](https://bitbadges.io/collections/23)


---

# 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/credit-token.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.
