Overview
SettlementSale is Sonar’s standard smart contract for token sales. It supports both fixed-price sales and English auctions, with settlement computed offchain and recorded onchain.
For standard sale configurations, Sonar deploys and manages this contract for you. You only need to deploy your own contract if you have custom requirements.
Sale Stages
The contract progresses through the following stages:PreOpen
Initial state before the sale opens. No commitments are accepted.- Used for setup and configuration
- Sale manager can prepare parameters before opening
Commitment
The active commitment phase where participants submit bids.- Participants with valid purchase permits can submit bids
- Bids specify price, amount, and lockup preference
- Bids can only increase (amounts and prices cannot be lowered)
- Each new bid replaces the previous bid for the same entity
- Automatically closes at a specified timestamp
Closed
The commitment phase has ended but settlement hasn’t begun.- No new bids accepted
- Sale manager can reopen the commitment phase if needed
- Can proceed to Cancellation or directly to Settlement
Cancellation
Optional cooling-off period for regulatory compliance (e.g., EU requirements).- Participants can cancel their bids and receive immediate refunds
- Preliminary allocations are computed offchain and shown to participants
- Once a bid is cancelled, it cannot be reinstated
Settlement
Final allocations are computed offchain and recorded onchain.- Settler role records allocations via
setAllocations() - Each entity’s accepted amount is recorded per payment token
- Settlement is finalized when all allocations are recorded
Done
Sale is complete. Refunds and proceeds can be processed.- Refunds equal commitment minus accepted allocation
- Participants can claim refunds (if enabled) or refunder role processes them
- Proceeds are withdrawn to the designated receiver
Roles & Permissions
The contract uses role-based access control. Multiple addresses can hold each role.| Role | Purpose | Typical Holder |
|---|---|---|
DEFAULT_ADMIN_ROLE | Full administrative access, can grant/revoke all roles | Project (always) |
SALE_MANAGER_ROLE | Manage sale stages (open, close, reopen) | Sonar or Project |
PURCHASE_PERMIT_SIGNER_ROLE | Sign purchase permits that authorize participation | Sonar (always) |
SETTLER_ROLE | Record final allocations during settlement | Sonar or Project |
SETTLEMENT_FINALIZER_ROLE | Finalize settlement and transition to Done | Sonar or Project |
REFUNDER_ROLE | Process refunds for participants | Sonar or Project |
PAUSER_ROLE | Emergency pause functionality | Sonar or Project |
TOKEN_RECOVERER_ROLE | Emergency token recovery (not granted by default) | Granted manually if needed |
The project always retains
DEFAULT_ADMIN_ROLE and full control over the contract. Sonar holds PURCHASE_PERMIT_SIGNER_ROLE to authorize eligible participants.Key Concepts
Bids
Each entity has a single active bid containing:| Field | Description |
|---|---|
price | The maximum price the entity is willing to pay (for auctions) |
amount | Total commitment amount across all payment tokens |
lockup | Whether the entity opts for token lockup |
- Amounts can only increase, never decrease
- Prices can only increase, never decrease
- Lockup can be enabled but not disabled once set
- Forced lockup can be required via purchase permit
Entity vs Wallet
- Entity: A compliance identity (individual or organization) verified by Sonar
- Wallet: A blockchain address used to commit funds
Multi-Token Support
The contract accepts multiple payment tokens (e.g., USDC and USDT):- All tokens assumed to have the same value (e.g., USD stablecoins)
- Commitments, allocations, and refunds tracked separately per token
- Each transaction uses a single payment token
When to Use
UseSettlementSale when:
- Running a fixed-price sale with pro-rata or iterative fill settlement
- Running an English auction
- You need offchain settlement computation
- Standard Sonar compliance and permit validation is sufficient
- You need first-come first-served with on-chain ordering
- You have custom allocation logic
- You need multiple commitments per entity (increase and decrease)