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

# Spending Allowance

> Delegated spending in Compact: transferFrom with allowance checks, the infinite-approval shortcut, and witness-derived spender authentication.

<Warning>
  Continues the witness-derived keypair pattern from
  [Approval](/basics/approval) and [Access Control](/basics/access-control).
  The spender authenticates by proving knowledge of their witness secret.
</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 and nested allowance map
  export ledger _balances: Map<UserPublicKey, Uint<128>>;
  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
          ])
      };
  }

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

  // transferFrom: caller authenticates as the *spender*, moves tokens
  // from `owner` to `to`, and pays for it with their allowance.
  export circuit transferFrom(
      owner: UserPublicKey,
      to: UserPublicKey,
      amount: Uint<128>
  ): Boolean {
      const spender = disclose(deriveUserPublicKey(getUserSecret()));
      _spendAllowance(owner, spender, amount);
      _transfer(owner, to, amount);
      return true;
  }

  // Internal: spend (decrement) the 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);
      }
      // current == MAX_UINT128 means "infinite approval"; leave allowance alone.
  }

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

  // Internal: move tokens from `sender` to `to`
  circuit _transfer(
      sender: UserPublicKey,
      to: UserPublicKey,
      amount: Uint<128>
  ): [] {
      const MAX_UINT128: Uint<128> = 340282366920938463463374607431768211455 as Uint<128>;
      const fromBalance = getBalance(sender);
      assert(fromBalance >= amount, "Insufficient balance");
      const toBalance = getBalance(to);
      assert(toBalance <= MAX_UINT128 - amount, "Balance overflow");
      _balances.insert(disclose(sender), disclose(fromBalance - amount));
      _balances.insert(disclose(to), disclose((toBalance + amount) as Uint<128>));
  }

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

## How It Works

### `transferFrom` authenticates the spender

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

The caller must produce a ZK proof that they hold the secret behind the
spender's stored `UserPublicKey`. The `owner` and `to` are explicit
parameters — they are *targets* of the operation, not authorities.

### Why this resists impersonation

`_spendAllowance(owner, spender, amount)` requires that the inner map
`_allowances[owner][spender]` already holds at least `amount`. Because the
allowance was written by `owner` against a specific `spender` public key, and
because `spender` here was just derived from the caller's witness secret, the
caller must actually hold the spender's secret. An attacker with no secret
cannot synthesise a passing proof, no matter what they read from the chain.

### Infinite approval

```compact theme={null}
if (current < MAX_UINT128) {
    assert(current >= amount, "Insufficient allowance");
    _approve(owner, spender, current - amount);
}
```

If the recorded allowance equals `MAX_UINT128`, treat it as unlimited and skip
the decrement. Useful for long-lived integrations (e.g. a DEX).

### Partial spending

```compact theme={null}
// Alice approves Bob for 1000 tokens (single transaction)
_approve(alice, bob, 1000);

// Bob calls transferFrom multiple times; the allowance decreases each call
//   transferFrom(alice, recipient1, 300)  → remaining: 700
//   transferFrom(alice, recipient2, 400)  → remaining: 300
//   transferFrom(alice, recipient3, 300)  → remaining: 0
//   transferFrom(alice, recipient4, 1)    → "Insufficient allowance"
```

## Privacy Note

<Warning>
  Each `transferFrom` writes the (owner, spender, amount) tuple to the public
  ledger. The participant graph is visible to chain observers.
</Warning>

## What's Next

<CardGroup cols={2}>
  <Card title="Token Approval" icon="check" href="/basics/approval">
    Back to granting approvals
  </Card>

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