PRIV ProtocolPRIV Docs
Contracts

PRIV Token

The native ERC-20 token of PRIV Protocol with governance voting capabilities.

Overview

PRIVToken is an ERC-20 token that serves as the native currency of the PRIV Protocol ecosystem. It includes governance capabilities through ERC20Votes, enabling on-chain voting for protocol decisions.

PropertyValue
NamePRIV Token
SymbolPRIV
Decimals18
Initial Supply100,000,000 (100M)
Maximum Supply1,000,000,000 (1B)
NetworkBase (Chain ID: 8453)

Deployed Addresses

Base Sepolia (Testnet)

Base Sepolia0x04eb...4Bd4

Features

ERC20Votes Extension

The token implements OpenZeppelin's ERC20Votes extension, enabling:

  • Voting Power: Token holders can delegate their voting power to themselves or others
  • Checkpoints: Historical snapshots of voting power for governance proposals
  • Delegation: Gasless delegation via EIP-712 signatures
// Delegate voting power to yourself
token.delegate(msg.sender);

// Delegate to another address
token.delegate(delegatee);

// Check voting power at a specific block
uint256 votes = token.getPastVotes(account, blockNumber);

EIP-2612 Permit

Gasless approvals via signed messages. Users can approve token spending without paying gas.

// Sign permit off-chain
const permit = await signTypedData({
  domain: {
    name: 'PRIV Token',
    version: '1',
    chainId: 8453,
    verifyingContract: PRIV_TOKEN_ADDRESS,
  },
  types: {
    Permit: [
      { name: 'owner', type: 'address' },
      { name: 'spender', type: 'address' },
      { name: 'value', type: 'uint256' },
      { name: 'nonce', type: 'uint256' },
      { name: 'deadline', type: 'uint256' },
    ],
  },
  message: {
    owner: userAddress,
    spender: contractAddress,
    value: amount,
    nonce: await token.nonces(userAddress),
    deadline: Math.floor(Date.now() / 1000) + 3600,
  },
})

Burnable

Token holders can burn their tokens to permanently reduce the supply.

// Burn your own tokens
function burn(uint256 amount) external

// Burn tokens from another account (requires allowance)
function burnFrom(address account, uint256 amount) external

Controlled Minting

Only the contract owner can mint new tokens, and minting is capped at the maximum supply.

function mint(address to, uint256 amount) external onlyOwner

Minting Rules:

  • Only callable by contract owner
  • Cannot exceed MAX_SUPPLY (1 billion tokens)
  • Reverts with ExceedsMaxSupply() if limit exceeded
  • Reverts with InvalidAddress() if recipient is zero address

Contract Interface

Constants

/// @notice Initial token supply: 100 million tokens
uint256 public constant INITIAL_SUPPLY = 100_000_000 * 10 ** 18;

/// @notice Maximum total supply cap: 1 billion tokens
uint256 public constant MAX_SUPPLY = 1_000_000_000 * 10 ** 18;

Functions

mint

Mint new tokens to an address.

function mint(address to, uint256 amount) external onlyOwner
ParameterTypeDescription
toaddressRecipient address
amountuint256Amount to mint (18 decimals)

Reverts if:

  • Caller is not owner
  • Recipient is zero address (InvalidAddress)
  • Would exceed max supply (ExceedsMaxSupply)

remainingMintableSupply

Get the remaining tokens that can be minted before reaching max supply.

function remainingMintableSupply() external view returns (uint256)

Returns: The amount of tokens that can still be minted.


delegate

Delegate voting power to an address.

function delegate(address delegatee) external
ParameterTypeDescription
delegateeaddressAddress to delegate voting power to

Note: To vote on governance proposals, you must delegate to yourself or another address.


getVotes

Get the current voting power of an address.

function getVotes(address account) external view returns (uint256)

getPastVotes

Get the voting power of an address at a specific past block.

function getPastVotes(address account, uint256 blockNumber) external view returns (uint256)

nonces

Get the current nonce for an address (used in permit signatures).

function nonces(address owner) public view returns (uint256)

Events

TokensMinted

event TokensMinted(address indexed to, uint256 amount)

Emitted when tokens are minted via the mint() function.

DelegateChanged

event DelegateChanged(
    address indexed delegator,
    address indexed fromDelegate,
    address indexed toDelegate
)

Emitted when an account changes their delegate.

DelegateVotesChanged

event DelegateVotesChanged(
    address indexed delegate,
    uint256 previousBalance,
    uint256 newBalance
)

Emitted when a delegate's voting power changes.


Errors

/// @notice Thrown when minting would exceed the maximum supply
error ExceedsMaxSupply();

/// @notice Thrown when an invalid address is provided
error InvalidAddress();

Governance Integration

The PRIVToken is designed to work with PRIVGovernor for on-chain governance:

  1. Hold PRIV tokens to participate in governance
  2. Delegate voting power (to yourself or a representative)
  3. Create proposals if you hold >= 100,000 PRIV
  4. Vote on proposals during the voting period (7 days)
  5. Proposals execute through the PRIVTimelock (2-day delay)
// Delegate to yourself to enable voting
await token.delegate(myAddress)

// Check your voting power
const votes = await token.getVotes(myAddress)
console.log(`Voting power: ${formatEther(votes)} PRIV`)

Usage Examples

Check Balance

import { useReadContract } from 'wagmi'
import { privTokenAbi } from '@priv/contracts'

function usePrivBalance(address: string) {
  return useReadContract({
    address: PRIV_TOKEN_ADDRESS,
    abi: privTokenAbi,
    functionName: 'balanceOf',
    args: [address],
  })
}

Transfer Tokens

import { useWriteContract } from 'wagmi'

function useTransferPriv() {
  const { writeContract } = useWriteContract()

  return (to: string, amount: bigint) => {
    writeContract({
      address: PRIV_TOKEN_ADDRESS,
      abi: privTokenAbi,
      functionName: 'transfer',
      args: [to, amount],
    })
  }
}

Approve Spending

function useApprovePriv() {
  const { writeContract } = useWriteContract()

  return (spender: string, amount: bigint) => {
    writeContract({
      address: PRIV_TOKEN_ADDRESS,
      abi: privTokenAbi,
      functionName: 'approve',
      args: [spender, amount],
    })
  }
}

Delegate Voting Power

function useDelegateVotes() {
  const { writeContract } = useWriteContract()

  return (delegatee: string) => {
    writeContract({
      address: PRIV_TOKEN_ADDRESS,
      abi: privTokenAbi,
      functionName: 'delegate',
      args: [delegatee],
    })
  }
}

Check Remaining Mintable Supply

function useRemainingSupply() {
  return useReadContract({
    address: PRIV_TOKEN_ADDRESS,
    abi: privTokenAbi,
    functionName: 'remainingMintableSupply',
  })
}

Security Notes

  1. Delegation Required: Voting power is not automatic. Token holders must delegate to themselves or others to participate in governance.

  2. Max Supply Enforced: The 1 billion token cap is enforced at the contract level and cannot be bypassed.

  3. Owner Control: Only the contract owner can mint new tokens. Consider transferring ownership to a multisig or the PRIVTimelock for decentralization.

  4. Permit Security: When using permit signatures, always validate the deadline and spender address before signing.


Source Code

View on GitHub