> ## 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.

# Token Approval

> ERC20-style token approvals in Compact: nested allowance maps, witness-derived owner authentication, and default map initialization.

<Warning>
  This page uses the witness-derived keypair pattern from
  [Access Control](/basics/access-control). The caller authenticates by
  proving knowledge of a witness secret whose hash matches the stored
  `UserPublicKey`. `ownPublicKey()` is **not** used for authentication.
</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;

  // Balances
  export ledger _balances: Map<UserPublicKey, Uint<128>>;

  // Allowances: owner → (spender → amount)
  export ledger _allowances: Map<UserPublicKey, Map<UserPublicKey, Uint<128>>>;

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

  // Approve spender to spend up to `amount` of the caller's tokens
  export circuit approve(spender: UserPublicKey, amount: Uint<128>): Boolean {
      const owner = disclose(deriveUserPublicKey(getUserSecret()));
      _approve(owner, spender, amount);
      return true;
  }

  // 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));
  }

  // 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));
  }
  ```
</Accordion>

## How It Works

### Authentication

```compact theme={null}
export circuit approve(spender: UserPublicKey, amount: Uint<128>): Boolean {
    const owner = disclose(deriveUserPublicKey(getUserSecret()));
    _approve(owner, spender, amount);
    return true;
}
```

The caller proves knowledge of a witness secret whose hash equals their
`UserPublicKey`. The contract then writes an allowance entry at
`_allowances[owner][spender] = amount`.

### Nested Map structure

```compact theme={null}
export ledger _allowances: Map<UserPublicKey, Map<UserPublicKey, Uint<128>>>;
```

A two-level map: the outer key is the owner's authenticated public key; the
inner map records how much each spender may spend.

**Reading the nested map** must be done inline — you cannot bind the inner map
to a `const`:

```compact theme={null}
// ✅ inline lookup is allowed
_allowances.lookup(disclose(owner)).lookup(disclose(spender));

// ❌ binding the inner Map to a const fails to compile
// const inner = _allowances.lookup(disclose(owner));  // ADT type error
```

### Initializing the inner Map

```compact theme={null}
if (!_allowances.member(disclose(owner))) {
    _allowances.insert(disclose(owner), default<Map<UserPublicKey, Uint<128>>>);
}
```

`default<Map<K, V>>` produces an empty `Map`, used to initialize the slot for
a new owner before the first allowance is recorded.

## Privacy Note

<Warning>
  Every `approve` call reveals the owner, the spender, and the amount on the
  public ledger. This is appropriate for *transparent* tokens (game items,
  public registries) but unsuitable for private finance. Midnight's native
  shielded primitives provide UTXO-based privacy that hides participants and
  amounts.
</Warning>

## What's Next

<CardGroup cols={2}>
  <Card title="Allowance & Spending" icon="wallet" href="/basics/allowance">
    Learn how to spend approved tokens
  </Card>

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