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.
| Property | Value |
|---|---|
| Name | PRIV Token |
| Symbol | PRIV |
| Decimals | 18 |
| Initial Supply | 100,000,000 (100M) |
| Maximum Supply | 1,000,000,000 (1B) |
| Network | Base (Chain ID: 8453) |
Deployed Addresses
Base Sepolia (Testnet)
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) externalControlled Minting
Only the contract owner can mint new tokens, and minting is capped at the maximum supply.
function mint(address to, uint256 amount) external onlyOwnerMinting 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| Parameter | Type | Description |
|---|---|---|
to | address | Recipient address |
amount | uint256 | Amount 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| Parameter | Type | Description |
|---|---|---|
delegatee | address | Address 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:
- Hold PRIV tokens to participate in governance
- Delegate voting power (to yourself or a representative)
- Create proposals if you hold >= 100,000 PRIV
- Vote on proposals during the voting period (7 days)
- 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
-
Delegation Required: Voting power is not automatic. Token holders must delegate to themselves or others to participate in governance.
-
Max Supply Enforced: The 1 billion token cap is enforced at the contract level and cannot be bypassed.
-
Owner Control: Only the contract owner can mint new tokens. Consider transferring ownership to a multisig or the PRIVTimelock for decentralization.
-
Permit Security: When using permit signatures, always validate the deadline and spender address before signing.