Overview
First-come first-served (FCFS) sales allow broad public participation while enforcing user purchase constraints through per-wallet caps and total supply limits. This pattern is ideal for standard token launches.
Use Case
- Open public sale with no allowlist requirements
- Per-wallet caps to prevent individual dominance
- Multiple wallets per entity allowed for convenience
Contract Implementation
The following example illustrates how a first-come first-served (FCFS) sale contract might be implemented by extending the base ExampleSale contract (see also Smart Contract Integration Guide).
This code is for educational purposes only and is not ready for production use.
contract FCFSSale is ExampleSale {
uint256 public constant TOTAL_SALE_CAP = 10_000_000_000_000; // $10M total
uint256 public totalAmount = 0;
error SaleCapExceeded(uint256 attempted, uint256 remaining);
function purchase(
uint256 amount,
PurchasePermitV2 calldata purchasePermit,
bytes calldata purchasePermitSignature
) external override {
_validatePurchasePermit(purchasePermit, purchasePermitSignature);
// Check total sale cap
uint256 newGlobalTotalAmount = totalAmount + amount;
if (newGlobalTotalAmount > TOTAL_SALE_CAP) {
revert SaleCapExceeded(amount, TOTAL_SALE_CAP - totalRaised);
}
// Validate permit purchase limits
uint256 newUserTotalAmount = amountByAddress[msg.sender] + amount;
if (newUserTotalAmount < purchasePermit.minAmount) {
revert AmountBelowMinimum(newUserTotalAmount, purchasePermit.minAmount);
}
if (newUserTotalAmount > purchasePermit.maxAmount) {
revert AmountExceedsMaximum(newUserTotalAmount, purchasePermit.maxAmount);
}
_trackEntity(purchasePermit.entityID, msg.sender);
amountByAddress[msg.sender] = newUserTotalAmount;
totalRaised = newGlobalTotalAmount;
emit Purchased(msg.sender, purchasePermit.entityID, amount, newTotalAmount);
// Note: If you need to transfer tokens as part of the purchase, you would do that here.
}
}
See Also