Skip to main content

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.
Source code: SettlementSale.sol

Deployment

You can deploy a SettlementSale 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
Contract deployments work differently depending on sale type:
  • 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 a SettlementSale, the following is set at initialization:
ParameterDefault
claimRefundEnabledfalse. Only the REFUNDER_ROLE can process refunds. Can be changed to true after deployment to allow participants to self-claim via claimRefund().
reduceCommitmentEnabledfalse. Participants can only fully cancel via cancelBid(). Can be changed to true after deployment to allow partial reductions via reduceCommitment().
maxWalletsPerEntity30
Sonar opens the commitment stage automatically after deployment via a separate 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.
RoleAdmin (project multisig)Sonar management walletSonar 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) or replaceBidWithApproval() (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/closesAt window defined in the purchase permit
  • Forced lockup can be required for specific entities via the purchase permit
Transitions to: Cancellation or Settlement

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 reduceCommitmentEnabled is set, participants can partially reduce specific wallet/token commitments via reduceCommitment() without fully cancelling
  • Preliminary allocations are computed offchain and communicated to participants
  • Once a bid is fully cancelled it cannot be reinstated
Transitions to: Settlement

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()
Transitions to: Done

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 claimRefundEnabled is true, otherwise the refunder role processes them
  • Proceeds (accepted amounts) are withdrawn to the designated receiver via withdraw() or withdrawPartial()

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:
FieldDescription
priceThe maximum price the entity is willing to pay (for auctions)
amountTotal commitment amount across all payment tokens and wallets
lockupWhether 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.
Bid constraints:
  • 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 bytes16 entity ID
  • Wallet: A blockchain address used to commit funds
One entity can use multiple wallets (up to 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:
FieldDescription
bidTimestampTimestamp of the last bid placed by this entity
refundedWhether the entity has been refunded (prevents double refunding)
currentBidThe entity’s current active bid (price, amount, lockup)
walletsAll wallet addresses associated with this entity
Each wallet tracks:
FieldDescription
committedAmountByTokenAmount committed per payment token by this wallet
cancelledAmountByTokenAmount cancelled per payment token by this wallet (during Cancellation stage)
acceptedAmountByTokenAccepted allocation per payment token for this wallet (set during Settlement)
The refund for a wallet per token is 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
Not compatible with rebasing tokens (e.g., stETH) or fee-on-transfer tokens. Using incompatible tokens will result in incorrect accounting and potential loss of funds.
If a payment token depegs, the sale should be paused immediately using pause() for further assessment.

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_ROLE to 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 proceedsReceiver before calling withdraw(). Use setProceedsReceiver() to update if needed.
  • PURCHASE_PERMIT_SIGNER_ROLE must only be held by Sonar. Granting it to any other address would allow unauthorized participants to bypass eligibility checks.
  • TOKEN_RECOVERER_ROLE should 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

Use SettlementSale 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
Consider a custom contract when:
  • 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