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

# Transfer Function

> Token transfers in Compact with witness-derived keypair authentication, balance and overflow checks, and explicit disclosure on Midnight.

<Warning>
  **`ownPublicKey()` is not authentication.** Anyone reading the chain can copy
  a public key and supply it as `ownPublicKey()` in a new transaction. To
  authenticate the caller, prove knowledge of a witness-held secret whose hash
  matches a stored public key — the [Access Control](/basics/access-control)
  pattern. This page builds the transfer function on top of that pattern.
</Warning>

## The Contract

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

  import CompactStandardLibrary;

  // Witness-derived keypair used to authenticate the caller.
  // The DApp generates the secret per user and keeps it in private state.
  struct UserSecretKey { bytes: Bytes<32>; }
  struct UserPublicKey { bytes: Bytes<32>; }
  witness getUserSecret(): UserSecretKey;

  // Balances keyed by the user's authenticated public key
  export ledger _balances: Map<UserPublicKey, Uint<128>>;

  // Derive the on-chain identifier from the witness secret
  export circuit deriveUserPublicKey(sk: UserSecretKey): UserPublicKey {
      return UserPublicKey {
          bytes: persistentHash<Vector<2, Bytes<32>>>([
              pad(32, "myapp:user:v1"),
              sk.bytes
          ])
      };
  }

  // Transfer tokens from the caller to `to`.
  // The caller proves they hold the secret behind their balance entry.
  export circuit transfer(to: UserPublicKey, amount: Uint<128>): Boolean {
      const sender = disclose(deriveUserPublicKey(getUserSecret()));
      _transfer(sender, to, amount);
      return true;
  }

  // Internal transfer logic (no caller authentication; only callable by
  // authenticated wrappers above and by the allowance flow).
  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: read a user's balance (returns 0 if unset)
  export circuit getBalance(account: UserPublicKey): Uint<128> {
      if (!_balances.member(disclose(account))) {
          return 0;
      }
      return _balances.lookup(disclose(account));
  }
  ```
</Accordion>

## How It Works

### Authentication via witness-derived keypair

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

To call `transfer`, the caller must produce a ZK proof that they know a
`UserSecretKey` whose hash matches the on-chain `UserPublicKey` they're
spending from. The on-chain balance is keyed by the **hash of the secret**;
reading the chain gives an attacker the hash, not the preimage.

Compare with the broken pattern (do **not** use):

```compact theme={null}
// ❌ Authentication does NOT work this way
const sender = left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());
```

`ownPublicKey()` returns whatever value the prover claims to know. An attacker
reads `sender` from the chain, supplies it as their own `ownPublicKey()`, and
produces a valid proof. See [Access Control](/basics/access-control#anti-pattern-ownpublickey-as-authorization).

### Balance and overflow checks

```compact theme={null}
const fromBalance = getBalance(sender);
assert(fromBalance >= amount, "Insufficient balance");

const toBalance = getBalance(to);
assert(toBalance <= MAX_UINT128 - amount, "Balance overflow");
```

Read both balances, assert the sender has enough, and assert the recipient's
new balance won't overflow `Uint<128>`. See [Overflow Protection](/basics/overflow-protection).

### Cast back to `Uint<128>` on store

```compact theme={null}
_balances.insert(disclose(to), disclose((toBalance + amount) as Uint<128>));
```

Compact arithmetic widens the result type. After the overflow assertion, the
sum is explicitly cast back to `Uint<128>` before being stored.

## Privacy Note

<Warning>
  **This contract publishes the full participant graph.** `_balances` is
  keyed by `UserPublicKey`, every transfer discloses both `sender` and `to`,
  and the inserted amount is also disclosed. Anyone reading the ledger sees
  the complete history of who paid whom and how much.

  This is the *account-model* privacy profile. For payments where senders,
  recipients, and amounts must be private, use Midnight's shielded token
  primitives (`sendShielded`, `mintShieldedToken`, etc.) — they're built on
  UTXOs and ZK commitments and don't leak the participant graph. The
  account-model pattern shown here is appropriate for *registries* where
  participants are intentionally public (game leaderboards, public reward
  programs, etc.).
</Warning>

## What's Next

<CardGroup cols={2}>
  <Card title="Token Approval" icon="check" href="/basics/approval">
    Grant permission for delegated transfers
  </Card>

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