> ## Documentation Index
> Fetch the complete documentation index at: https://compact-by-example.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Burning Tokens

> Burn tokens in Compact: self-burn and delegated burnFrom with allowance enforcement, supply invariants, and witness-derived authentication.

<Warning>
  Self-burn authenticates the caller as the account being burned. Delegated
  burn (`burnFrom`) authenticates the caller as the spender and enforces a
  prior allowance. Both use the witness-derived keypair pattern from
  [Access Control](/basics/access-control).
</Warning>

## The Contract

<Accordion title="View Contract Code">
  ```compact theme={null}
  pragma language_version >= 0.23;

  import CompactStandardLibrary;

  struct UserSecretKey { bytes: Bytes<32>; }
  struct UserPublicKey { bytes: Bytes<32>; }
  witness getUserSecret(): UserSecretKey;

  export ledger _balances: Map<UserPublicKey, Uint<128>>;
  export ledger _allowances: Map<UserPublicKey, Map<UserPublicKey, Uint<128>>>;
  export ledger _totalSupply: Uint<128>;

  export circuit deriveUserPublicKey(sk: UserSecretKey): UserPublicKey {
      return UserPublicKey {
          bytes: persistentHash<Vector<2, Bytes<32>>>([
              pad(32, "myapp:user:v1"),
              sk.bytes
          ])
      };
  }

  export circuit getTotalSupply(): Uint<128> {
      return _totalSupply;
  }

  // Burn your own tokens — the caller proves they hold the secret behind
  // the account whose balance is being burned.
  export circuit burn(amount: Uint<128>): [] {
      const account = disclose(deriveUserPublicKey(getUserSecret()));
      _burn(account, amount);
  }

  // Burn tokens from another account using an allowance.
  // The caller (spender) authenticates via their own witness secret;
  // the allowance from `account` to that spender is decremented.
  export circuit burnFrom(account: UserPublicKey, amount: Uint<128>): [] {
      const spender = disclose(deriveUserPublicKey(getUserSecret()));
      _spendAllowance(account, spender, amount);
      _burn(account, amount);
  }

  // Internal: burn tokens (assumes caller authentication happened above)
  circuit _burn(account: UserPublicKey, amount: Uint<128>): [] {
      const currentBalance = getBalance(account);
      assert(currentBalance >= amount, "Burn amount exceeds balance");
      assert(_totalSupply >= amount, "Burn amount exceeds total supply");

      _balances.insert(disclose(account), disclose(currentBalance - amount));
      _totalSupply = disclose(_totalSupply - amount);
  }

  // Internal: read & decrement allowance (with infinite-approval shortcut)
  circuit _spendAllowance(
      owner: UserPublicKey,
      spender: UserPublicKey,
      amount: Uint<128>
  ): [] {
      const current = allowance(owner, spender);
      const MAX_UINT128: Uint<128> = 340282366920938463463374607431768211455 as Uint<128>;
      if (current < MAX_UINT128) {
          assert(current >= amount, "Insufficient allowance");
          _approve(owner, spender, current - amount);
      }
  }

  // Internal: write the allowance entry
  circuit _approve(
      owner: UserPublicKey,
      spender: UserPublicKey,
      amount: Uint<128>
  ): [] {
      if (!_allowances.member(disclose(owner))) {
          _allowances.insert(disclose(owner), default<Map<UserPublicKey, Uint<128>>>);
      }
      _allowances.lookup(disclose(owner)).insert(disclose(spender), disclose(amount));
  }

  // Read remaining allowance
  export circuit allowance(owner: UserPublicKey, spender: UserPublicKey): Uint<128> {
      if (!_allowances.member(disclose(owner))) { return 0; }
      if (!_allowances.lookup(disclose(owner)).member(disclose(spender))) { return 0; }
      return _allowances.lookup(disclose(owner)).lookup(disclose(spender));
  }

  // Helper
  circuit getBalance(account: UserPublicKey): Uint<128> {
      if (!_balances.member(disclose(account))) { return 0; }
      return _balances.lookup(disclose(account));
  }
  ```
</Accordion>

## How It Works

### Self-burn authenticates the burner

```compact theme={null}
export circuit burn(amount: Uint<128>): [] {
    const account = disclose(deriveUserPublicKey(getUserSecret()));
    _burn(account, amount);
}
```

The caller proves knowledge of the secret behind the account whose balance is
being burned. Reading the chain gives an attacker the *hash* of the secret,
not the secret itself.

### `burnFrom` enforces an allowance

```compact theme={null}
export circuit burnFrom(account: UserPublicKey, amount: Uint<128>): [] {
    const spender = disclose(deriveUserPublicKey(getUserSecret()));
    _spendAllowance(account, spender, amount);
    _burn(account, amount);
}
```

Same idea as `transferFrom` but the destination is the burn (no `to`
parameter). The spender must have a non-zero `_allowances[account][spender]`
entry of at least `amount`.

### Invariants

* `currentBalance >= amount` — caller can't burn more than they hold.
* `_totalSupply >= amount` — defensive check; should always hold given the
  previous invariant, but cheap to assert.
* Both writes happen in sequence so `sum(balances) == _totalSupply` stays
  true.

## Privacy Note

<Warning>
  Each burn writes `account` and `amount` to the public ledger. The
  account-model design is deliberately transparent. For private deflationary
  mechanisms, use shielded primitives where amounts and identities stay in
  ZK-committed UTXOs.
</Warning>

## What's Next

<CardGroup cols={2}>
  <Card title="Minting Tokens" icon="plus" href="/basics/minting">
    Learn how to create new tokens
  </Card>

  <Card title="ERC20 Token" icon="coins" href="/applications/erc20-token">
    Complete token with burning
  </Card>
</CardGroup>
