Overview
SettlementSale is Sonar’s standard smart contract for token sales. It supports fixed-price sales and English auctions. Participants commit funds onchain during the commitment stage; clearing mechanics and final allocations are computed offchain by Sonar and recorded onchain during settlement.
The contract acts as an escrow: funds are held onchain and can only flow to the proceeds receiver (accepted amounts) or back to participants (refunds). With the exception of emergency token recovery, no other outflows are possible.
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.
Deployment
You can deploy aSettlementSale contract via the Founder Dashboard. Before deploying, you must have configured the following in the sale’s general settings:
- Chain and payment tokens
- Proceeds receiver address: the address that will receive the sale proceeds
- Contract admin address: see Roles & Permissions for details
- Test sales: deploy via the Dashboard or Integration page. Can be redeployed at any time.
- Production sales: deploy via the Dashboard. This action is irreversible.
Deployment Defaults
When Sonar deploys aSettlementSale, the following is set at initialization:
| Parameter | Default |
|---|---|
claimRefundEnabled | false. Only the REFUNDER_ROLE can process refunds. Can be changed to true after deployment to allow participants to self-claim via claimRefund(). |
reduceCommitmentEnabled | false. Participants can only fully cancel via cancelBid(). Can be changed to true after deployment to allow partial reductions via reduceCommitment(). |
maxWalletsPerEntity | 30 |
openCommitment() transaction. By the time the deployment is announced as live, the contract will already be in the Commitment stage.
Initial Role Grants
The following roles are granted at initialization. The admin address receives all operational roles. Sonar uses two distinct addresses: a management wallet (for operational roles) and a separate signing key (for signing purchase permits). See Roles & Permissions for details on each role.| Role | Admin (project multisig) | Sonar management wallet | Sonar signing key |
|---|---|---|---|
DEFAULT_ADMIN_ROLE | ✓ | — | — |
SALE_MANAGER_ROLE | ✓ | ✓ | — |
SETTLER_ROLE | ✓ | ✓ | — |
SETTLEMENT_FINALIZER_ROLE | ✓ | — | — |
PAUSER_ROLE | ✓ | ✓ | — |
REFUNDER_ROLE | ✓ | ✓ | — |
PURCHASE_PERMIT_SIGNER_ROLE | — | — | ✓ |
COMMITMENT_REDUCER_ROLE | — | — | — |
TOKEN_RECOVERER_ROLE | — | — | — |
COMMITMENT_REDUCER_ROLE and TOKEN_RECOVERER_ROLE are not granted at deployment. They can be granted by the admin if explicitly required.
Additional project team wallets can be granted roles after deployment via grantRole() (requires DEFAULT_ADMIN_ROLE).
Sale Stages
The contract progresses through the following stages:Sales start directly in the Commitment stage by default (Sonar opens commitment immediately after deployment).
The PreOpen stage exists but is not used in standard deployments.
Commitment
The active commitment phase where participants submit bids.- Participants with valid purchase permits can submit bids via
replaceBidWithPermit()(single transaction, recommended) orreplaceBidWithApproval()(requires prior ERC20 approval) - Bids specify price, amount, and lockup preference
- Each new bid replaces the previous bid for the same entity
- Bids can only increase: amounts and prices cannot be lowered; lockup cannot be disabled once set
- Bids are only accepted within the
opensAt/closesAtwindow defined in the purchase permit - Forced lockup can be required for specific entities via the purchase permit
Cancellation
Optional cooling-off period (e.g., for EU regulatory requirements).- Participants can fully cancel their bids via
cancelBid()and receive an immediate refund of all unaccepted committed amounts across all their wallets - If
reduceCommitmentEnabledis set, participants can partially reduce specific wallet/token commitments viareduceCommitment()without fully cancelling - Preliminary allocations are computed offchain and communicated to participants
- Once a bid is fully cancelled it cannot be reinstated
Settlement
Final allocations are computed offchain and recorded onchain.- The settler role records accepted amounts per wallet and payment token via
setAllocations() - Accepted amounts cannot exceed a wallet’s committed amount for that token
- Settlement is finalized by the settlement finalizer role via
finalizeSettlement()
Done
Sale is complete. Refunds and proceeds are processed.- Refunds equal the committed amount minus the accepted allocation, calculated separately per wallet per payment token
- Participants can claim their own refunds if
claimRefundEnabledis true, otherwise the refunder role processes them - Proceeds (accepted amounts) are withdrawn to the designated receiver via
withdraw()orwithdrawPartial()
Roles & Permissions
The contract uses role-based access control. Multiple addresses can hold each role.DEFAULT_ADMIN_ROLE
Full control over the contract, including role management, proceeds withdrawal, updating the proceeds receiver, and emergency stage override.
Granted to Sonar at deployment: No.
Recommended wallet: Multisig with at least a 2-of-3 threshold. This role controls funds and role management.
SALE_MANAGER_ROLE
Manages stage transitions and operational parameters (pause/unpause, claim refund toggle, max wallets limit).
Granted to Sonar at deployment: Yes.
Recommended wallet: Hardware wallet. Stage transitions are time-sensitive and a hardware wallet avoids multisig coordination delays.
SETTLER_ROLE
Records final allocations during the Settlement stage.
Granted to Sonar at deployment: Yes.
Recommended wallet: Software wallet. Settlement is run by Sonar, but a project-controlled wallet provides a backup.
SETTLEMENT_FINALIZER_ROLE
Finalizes settlement and transitions to Done, validating the total accepted amount.
Granted to Sonar at deployment: No.
Recommended wallet: Same multisig as DEFAULT_ADMIN_ROLE. Finalizing settlement is a high-stakes one-time action.
PAUSER_ROLE
Emergency pause that immediately halts all external user-facing functions.
Granted to Sonar at deployment: Yes.
Recommended wallet: One or more software wallets held by on-call team members. Speed is critical for an emergency pause.
REFUNDER_ROLE
Processes refunds for participants during the Done stage.
Granted to Sonar at deployment: Yes.
Recommended wallet: Software wallet. Refunds are run by Sonar, but a project-controlled wallet provides a backup.
PURCHASE_PERMIT_SIGNER_ROLE
Signs purchase permits that authorize participation. Must only ever be held by Sonar. Do not grant this to any other address.
Granted to Sonar at deployment: Yes.
COMMITMENT_REDUCER_ROLE
Force-reduces wallet commitments via forceReduceCommitment(), bypassing stage and pause restrictions. Correctly updates all accounting state.
Granted at deployment: No. Only grant if you need to adjust commitments outside the normal Cancellation stage flow.
TOKEN_RECOVERER_ROLE
Emergency recovery of tokens sent to the contract.
Granted at deployment: No. Only grant to a multisig if an actual recovery situation arises. Prefer forceReduceCommitment() for committed payment tokens.
The project always retains
DEFAULT_ADMIN_ROLE and full control over the contract, including the ability to grant and revoke all other roles. Sonar holds PURCHASE_PERMIT_SIGNER_ROLE exclusively 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 and wallets |
lockup | Whether the entity opts for token lockup (general information on lockup) |
The
price is denominated in the auction’s bid increment.
For example, if the bid increment is $0.01, then a price of 100 represents a willingness to pay $1.00.- 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 for specific entities via the purchase permit
Entity vs Wallet
- Entity: A compliance identity (individual or organization) verified by Sonar, identified by a
bytes16entity ID - Wallet: A blockchain address used to commit funds
maxWalletsPerEntity), but each wallet is tied to exactly one entity. The active bid and refund eligibility are tracked at the entity level. Committed amounts, accepted allocations, and refunds are tracked at the wallet level per payment token.
Entity State
Each entity has the following state tracked onchain:| Field | Description |
|---|---|
bidTimestamp | Timestamp of the last bid placed by this entity |
refunded | Whether the entity has been refunded (prevents double refunding) |
currentBid | The entity’s current active bid (price, amount, lockup) |
wallets | All wallet addresses associated with this entity |
| Field | Description |
|---|---|
committedAmountByToken | Amount committed per payment token by this wallet |
cancelledAmountByToken | Amount cancelled per payment token by this wallet (during Cancellation stage) |
acceptedAmountByToken | Accepted allocation per payment token for this wallet (set during Settlement) |
committedAmountByToken[token] - acceptedAmountByToken[token].
Use entityStatesByIDs(entityIDs) to read full entity state including all wallet states.
Multi-Token Support
The contract accepts multiple payment tokens (e.g., USDC and USDT):- All tokens must have the same number of decimals and are assumed to maintain 1:1 value parity
- Commitments, allocations, and refunds are tracked separately per token
- Each bid transaction uses a single payment token
- Allocations are set per wallet per token during settlement
Security Considerations
- Audit role assignments before and after deployment. Verify all role holders are correct and revoke any that are no longer needed.
- Grant
PAUSER_ROLEto hot wallets for fast emergency response. A multisig is too slow if you need to halt the contract quickly. - Simulate contract operations using tools like Tenderly before executing onchain, especially for settlement and withdrawal.
- Verify
proceedsReceiverbefore callingwithdraw(). UsesetProceedsReceiver()to update if needed. PURCHASE_PERMIT_SIGNER_ROLEmust only be held by Sonar. Granting it to any other address would allow unauthorized participants to bypass eligibility checks.TOKEN_RECOVERER_ROLEshould not be granted by default. The contract is designed so that tokens can only leave as refunds or proceeds withdrawals. Only grant this role if an actual recovery situation arises.
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)
See Also
Smart Contract Operations
Function reference and operational procedures
Custom Contracts
Build your own contract with Sonar permit validation
Settlement Strategies
How allocations are computed
Purchase Permits
How permits authorize participation
Sale Lifecycle
End-to-end sale process