Skip to content

Repository files navigation

SELF Token Presale

Multi-round token presale with integrated vesting on Base.

Status: CANCELLED — recovery completed successfully

The SELF public presale will not proceed. The deployed contract was never initialized, contribute() was never callable, and no user ever contributed. No USDC was ever raised and no SELF was ever allocated to any buyer.

All SELF funded into the contract was recovered successfully through the contract's 7-day emergency withdrawal timelock. The presale contract now holds 0 SELF.

This repository remains published for transparency and to keep the audited source of the deployed bytecode available. The contracts below should be treated as retired. Nothing here is an offer to sell tokens.

Audit Scope

contracts/
├── SELFToken.sol         (~20 lines - Standard ERC20)
└── SELFPresale.sol        (~700 lines - Multi-round presale)

Technical Overview

SELFToken.sol

  • Standard OpenZeppelin ERC20
  • Fixed supply: 500,000,000 tokens
  • OpenZeppelin v4.9.6

SELFPresale.sol

  • 5 sequential rounds with role-managed round progression
  • Progressive pricing: $0.06 → $0.10 per token
  • Presale allocation: exactly 37,934,515 SELF; raise target up to approximately $2.5M
  • Contribution limits: $100 - $10,000 per wallet (cumulative)
  • Vesting: 40% TGE unlock + linear 12-month vesting
  • No round bonuses
  • Payment: USDC (native Circle) 6 decimals
  • OpenZeppelin v4.9.6: AccessControl, ReentrancyGuard, Pausable, SafeERC20

Deployment (retired)

The presale was deployed on Base mainnet and never initialized. initializeRounds was never executed, so contribute() reverted with RoundsNotInitialized for the contract's entire life.

Anyone can confirm the above independently on BaseScan by reading roundsInitialized() (false), totalAllocatedSELF() (0), and getPresaleStats() on the presale address.

Security Features

  • Role-based access control (5 roles)
  • Timelock delays (2-7 days on critical operations)
  • Circuit breaker ($500k daily withdrawal limit)
  • Flash loan protection (2-block cooldown)
  • Whale protection (10% max per tx)
  • Rate limiting ($100k/hour per wallet)
  • Custom errors (gas optimized)

Base USDC Configuration

Base USDC uses 6 decimals (native Circle USDC).

Contract: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913

uint256 constant MIN_CONTRIBUTION = 100 * 1e6;      // $100
uint256 constant MAX_CONTRIBUTION = 10_000 * 1e6;   // $10,000
uint256 constant HARD_CAP = 2_500_000 * 1e6;        // $2.5M

Token Distribution & Allocation

SELF Token Supply

Total Supply: 500,000,000 SELF (fixed, non-mintable)

Exactly 37,934,515 SELF was allocated to the presale contract and recovered after cancellation. Full token allocation information is published at https://docs.self.app/tokenomics.

Governance & Security

Timelock Operations (SEA-02)

All critical operations enforce mandatory timelock delays for community transparency:

Operation Timelock Role Required Guardrails
Enable TGE 2 days TGE_ENABLER_ROLE One-time enablement; TGE time immutable once set
Withdraw USDC 2 days TREASURY_ROLE Circuit breaker: $500k daily limit
Emergency SELF Withdrawal 7 days DEFAULT_ADMIN_ROLE Blocked if any user allocations exist
Update Rate Limit None DEFAULT_ADMIN_ROLE Bounded: $100 - $1M range
Pause/Unpause None PAUSER_ROLE Emergency circuit breaker only

Monitoring Events:

  • TimelockRequested(action, executionTime) - Initiates countdown
  • TimelockExecuted(action) - Confirms execution after delay
  • TimelockCancelled(action) - Operation cancelled before execution

All events are publicly visible on BaseScan for community monitoring.

Published source: https://docs.self.app/tokenomics

Security Guarantees

Hard On-Chain Invariants

The presale contract enforces critical security invariants in code, not operational policy:

1. Solvency Protection

  • USDC Withdrawals: Treasury withdrawals require privileged approval, a 2-day timelock, and a $500k/day circuit breaker.
  • SELF Token Claims: Contributions require sufficient SELF balance on-chain. Contract verifies balance >= outstandingClaims + newAllocation before accepting contributions.
  • Emergency Safeguards: Emergency SELF withdrawals blocked once any user allocations exist.

2. TGE Immutability

  • Token Generation Event can only be enabled once
  • Multiple layers prevent TGE time from being changed after activation:
    • Pending request check (prevents request overwrites)
    • Execution guard (prevents multiple executions)
    • Explicit cancellation required to replace pending requests

3. User Protections

  • Precision Math: All token calculations round up in favor of users
  • Dust Handling: Allows exact completion of rounds when remaining capacity < minimum contribution

Transparency & Monitoring

Public view functions for external verification:

  • getExcessSELFBalance() - Shows withdrawable excess vs. outstanding claims
  • getClaimableAmount(user) - Shows user's vested + unlocked tokens
  • getUserContribution(user) - Complete user allocation breakdown
  • getPresaleStats() - Aggregate presale state

All privileged operations emit events for on-chain monitoring.

Security Design

Administrative Controls:

  • Privileged roles are constrained by on-chain invariants; selected TGE and treasury actions also enforce timelocks and a circuit breaker

Unsold Token Recovery:

The contract protects against SELF tokens being locked while safeguarding buyer claims:

  • withdrawExcessSELF() allows the treasury to reclaim unsold SELF after TGE
  • Protected: cannot withdraw tokens needed for outstanding user claims
  • Formula: excess = balance - (totalAllocated - totalClaimed)
  • executeEmergencyWithdrawSELF() recovers SELF only before any user allocations exist (7-day timelock), so buyer claims can never be stranded

The cancellation uses the second mechanism. It is available only because totalAllocatedSELF == 0 — the same guard that would have made recovery impossible had even one buyer contributed. The design intent is that a cancelled-before-launch sale can be unwound, while a launched sale can never be rug-pulled.

Test Coverage: 49 passing tests, zero compiler warnings

Deployed Contracts (Base Mainnet):

Testing

npm install
npx hardhat test

Tests cover token functionality, presale logic, vesting, and edge cases.

contracts/test/MockUSDC.sol is a test utility only (6-decimal USDC simulator).

Repository Structure

contracts/
├── SELFToken.sol              # Audit scope
├── SELFPresale.sol            # Audit scope
└── test/MockUSDC.sol          # Test utility

test/
├── SELFToken.test.cjs
└── SELFPresale.test.cjs

scripts/
├── deploy-token.js
├── deploy-presale.js
├── initialize-rounds.js
└── verify-contracts.js

docs/
└── architecture.md

Deployment

Network: Base Mainnet
USDC: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Compiler: Solidity 0.8.20, optimizer enabled with 200 runs
Status: Deployed and exact-match verified. Never initialized. Cancelled — no contributions were ever taken, and recovery completed successfully with 0 SELF remaining in the presale contract.


Audit Ready: December 14, 2025
Skyharbor Updated V1.1: December 25, 2025
Skyharbor Updated V1.2: December 30, 2025
SEA-16 Fix V1.3: December 31, 2025
Per-Round Accounting Fix V1.4: January 1, 2026
Base Migration V1.5: March 13, 2026
Tokenomics Update V1.6: April 8, 2026 — Unified TGE unlock to 40%, removed bonuses, extended vesting to 12 months
Treasury Model Update V1.7: Streamlined treasury withdrawals with a 2-day timelock and $500k/day circuit breaker
Current Deployment V1.8: Base mainnet exact-match deployment at 0x9D762B5E519d6194aa829F31cF85317FE37Fe35d; no refund mechanism or minimum aggregate raise condition
Presale Cancelled V1.9: August 16, 2026 — sale will not proceed. Contract was never initialized and took zero contributions; emergency recovery requested via the 7-day timelock
Recovery Completed V1.10: August 23, 2026 — recovery completed successfully, leaving 0 SELF in the presale contract

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages