> Documentation index: https://docs.squeekchain.com/llms.txt. Use it to find every page.

# SqueekToken

> SqueekToken (SQK) function by function — the ERC-20 and permit interface, SqueekMining-only minting, the lifetime cap, events and errors, and the published ABI.

Web version: https://docs.squeekchain.com/developers/token/

## TL;DR

- SQK is a standard ERC-20 with EIP-2612 permit, 18 decimals and zero starting supply.
- Only SqueekMining can mint, always to itself, and total supply can never exceed 2.1 billion SQK.
- The token itself does not check for work; that rule lives in SqueekMining.

## Overview

!!! warning "In active development"

    The Squeekchain contracts are in active development and are likely to change. Functions, events, errors, limits and behavior described here may change before a mainnet release, and the Sepolia deployment may be upgraded or replaced. Pin the ABI you integrate against, and re-verify the deployment before relying on it.

SqueekToken holds SQK balances and allowances. It has no owner, no burn, no pause and no upgrade path. `mintForCheckpoint` is the only way supply grows, and only the fixed SqueekMining proxy can call it.

| Network | Address | Kind |
|---|---|---|
| Sepolia | `0x6872bfa5ccc3e9e440c6c16e5ffdabb8dba4cc76` | Immutable; not a proxy |

## Functions

### Permissionless

#### Standard ERC-20 and EIP-2612

```solidity
function name() external view returns (string);                 // "Squeek"
function symbol() external view returns (string);               // "SQK"
function decimals() external view returns (uint8);              // 18
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function eip712Domain() external view returns (bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions);
```

These are OpenZeppelin 5.6.1's [ERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.6.1/contracts/token/ERC20/ERC20.sol), [ERC20Capped](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.6.1/contracts/token/ERC20/extensions/ERC20Capped.sol) and [ERC20Permit](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.6.1/contracts/token/ERC20/extensions/ERC20Permit.sol), unmodified. Amounts are SQK base units (`10^-18` SQK). The permit domain is name `Squeek`, version `1`, this chain and this token. A permit sets an allowance only; it does not authorize claims or merges.

### Views

#### `cap`

```solidity
function cap() external view returns (uint256);
```

The lifetime supply cap in SQK base units: `2,100,000,000 × 10^18` on Sepolia, fixed at construction. SqueekMining refuses to bootstrap against any other cap.

#### `mining`

```solidity
function mining() external view returns (address);
```

The SqueekMining proxy: the only minter and the only mint recipient. [Verify the deployment](https://docs.squeekchain.com/developers/quickstart.md#verify-the-deployment) checks it.

### Admin

#### `mintForCheckpoint`

```solidity
function mintForCheckpoint(uint64 throughRound, uint256 amount) external returns (uint256);
```

SqueekMining only. Mints `amount` SQK base units to SqueekMining and emits `CheckpointMinted`. A zero amount mints nothing but still emits the event.

| Name | Type | Unit | Meaning |
|---|---|---|---|
| `throughRound` | `uint64` | round | Last SqueekCore round the checkpoint processed; attribution only |
| `amount` | `uint256` | SQK base units | Amount to mint |

Reverts with `UnauthorizedMining` for any other caller and `ERC20ExceededCap` when supply would pass the cap.

## Events

```solidity
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
event CheckpointMinted(uint64 indexed throughRound, uint256 amount, uint256 totalMinted);
event EIP712DomainChanged();
```

| Event | Emitted when |
|---|---|
| `Transfer` | Any transfer; a mint is a `Transfer` from the zero address to SqueekMining |
| `Approval` | `approve`, `permit`, or an allowance spent by `transferFrom` |
| `CheckpointMinted` | Every checkpoint mint, including zero; `totalMinted` is supply afterwards |
| `EIP712DomainChanged` | Declared by the inherited interface; never emitted, since the domain is fixed |

## Errors

| Error | Cause | Fix |
|---|---|---|
| `UnauthorizedMining` | A caller other than SqueekMining tried to mint | Only SqueekMining mints |
| `ERC20ExceededCap(increasedSupply, cap)` | A mint would pass the cap | SqueekMining clips issuance at the cap |
| `ERC20InsufficientBalance(sender, balance, needed)` | Transfer larger than the balance | Transfer less |
| `ERC20InsufficientAllowance(spender, allowance, needed)` | `transferFrom` beyond the allowance | Approve or permit more |
| `ERC20InvalidSender(sender)`, `ERC20InvalidReceiver(receiver)`, `ERC20InvalidApprover(approver)`, `ERC20InvalidSpender(spender)` | A zero address where one is not allowed | Use a real address |
| `ERC2612ExpiredSignature(deadline)` | Permit after its deadline | Sign a new permit |
| `ERC2612InvalidSigner(signer, owner)`, `ECDSAInvalidSignature`, `ECDSAInvalidSignatureLength(length)`, `ECDSAInvalidSignatureS(s)` | Permit not signed by `owner` over these fields, or malformed | Sign with the owner's key for the current nonce |
| `InvalidAccountNonce(account, currentNonce)` | Permit nonce already used | Read `nonces(owner)` and sign again |
| `InvalidMining`, `ERC20InvalidCap(cap)` | Invalid constructor arguments | Setup only |
| `InvalidShortString`, `StringTooLong(str)` | Inherited EIP-712 name or version encoding failure | Setup only |

## Integrator patterns

- **Use base units.** Keep amounts as `bigint` base units and format with 18 decimals; never convert to `number`.
- **Count supply once.** A checkpoint mint emits both `CheckpointMinted` and a `Transfer`; count one.
- **Supply equals what SqueekMining funded.** `totalSupply()` equals SqueekMining `state().minted`, whether or not owners have claimed. Unclaimed SQK sits in SqueekMining's balance.
- **Do not infer work from the token.** The cap limits supply, but the work-backed issuance rule lives in SqueekMining's code, which governance can upgrade.
- **Treat it as a plain ERC-20 otherwise.** There are no transfer hooks, fees, pauses or blocklists.

## Usage example

Check that SQK supply matches what SqueekMining has funded and what it still holds for owners.

```ts
// sqk-supply.ts
import { formatUnits, parseAbi } from "viem";
import { blockNumber, client } from "./setup";
import { verifyGraph } from "./verify";

const { mining, token } = await verifyGraph();
const tokenAbi = parseAbi([
  "function totalSupply() view returns (uint256)",
  "function cap() view returns (uint256)",
  "function balanceOf(address account) view returns (uint256)",
]);
const miningAbi = parseAbi([
  "function state() view returns ((uint256 minted, uint256 paid, uint256 totalWeight, uint256 rewardIndex, uint64 closedThroughBlock, uint64 lastActivationRound, uint256 weightActivationCursor, uint256 weightActivationCount, bool bootstrapped, uint64 processedThroughRound, uint64 closedCumulativeWork, uint96 closedCumulativeBlockUnits, uint32 closedWorkPerThousandBlocks))",
]);
const read = { address: token, abi: tokenAbi, blockNumber } as const;
const [supply, cap, held, state] = await Promise.all([
  client.readContract({ ...read, functionName: "totalSupply" }),
  client.readContract({ ...read, functionName: "cap" }),
  client.readContract({ ...read, functionName: "balanceOf", args: [mining] }),
  client.readContract({ address: mining, abi: miningAbi, functionName: "state", blockNumber }),
]);
if (supply !== state.minted) throw new Error("Supply differs from SqueekMining's funded total");
if (held < state.minted - state.paid) throw new Error("SqueekMining holds less than it owes");
console.log({
  supplySQK: formatUnits(supply, 18),
  remainingToCapSQK: formatUnits(cap - supply, 18),
  unclaimedSQK: formatUnits(state.minted - state.paid, 18),
});
```

## ABI

[`SqueekToken.json`](https://docs.squeekchain.com/assets/abi/SqueekToken.json). Any ERC-20 ABI also works for transfers and balances.

## Related pages

- [SqueekMining](https://docs.squeekchain.com/developers/mining.md)
- [The SQK token](https://docs.squeekchain.com/rewards/token.md)
- [Supply limits and rounding](https://docs.squeekchain.com/rewards/supply-and-rounding.md)
