5.5. SlashedPot.sol (Compensations)
The SlashedPot is a dedicated settlement contract responsible for enforcing economic accountability and fairly compensating participants when auctions are prematurely cancelled or fail due to infrastructural degradation. Rather than penalizing bidders for seller withdrawal or network outages, the protocol confiscates the seller’s initial deposit and distributes it pro-rata to all valid participants.
Designed with strict adherence to the protocol’s Zero-Loop Refund Policy, SlashedPot.sol employs a Lazy Pull Architecture that eliminates Out-of-Gas (OOG) risks, prevents reentrancy vectors, and guarantees that the platform extracts 0% from penalty distributions. All compensation remains under user control until explicitly claimed via an individual pull transaction.
Core Design Principles
| Principle | Technical Implementation |
|---|---|
| Zero Platform Extraction | 100% of confiscated seller deposits are routed to affected bidders. Administrative or treasury fees are explicitly prohibited from penalty pools. |
| Lazy Pull Distribution | Instead of iterating through bidders to push funds, the contract stores totalPenalty and bidderCount. Each participant calculates and claims their exact share on-demand in O(1) gas. |
| Strict Access Control | All penalty recording functions are guarded by onlyFairMarket. Only the core auction contract can trigger compensation routing after state validation. |
| Deterministic Math | Pro-rata shares are calculated using integer division with remainder handling. The remainder is allocated to the first claimant, preventing fractional ETH dust accumulation. |
| State-Gated Activation | Compensation is only enabled after explicit transition to CANCELLED or VOIDED states. Premature claims revert instantly. |
️ Technical Implementation
1. Penalty Registration (recordPenalty)
Called by FhenixFairMarket.sol immediately after a seller invokes cancelAuction(). Validates the deposit, records the penalty pool, and locks the auction’s compensation state.
// packages/contracts/settlement/SlashedPot.sol
contract SlashedPot {
address public fairMarket;
mapping(uint256 => uint256) public totalPenalties;
mapping(uint256 => uint256) public validBidderCounts;
mapping(uint256 => mapping(address => bool)) public hasClaimed;
modifier onlyFairMarket() {
require(msg.sender == fairMarket, "Unauthorized");
_;
}
function initialize(address _fairMarket) external {
require(fairMarket == address(0), "Already initialized");
fairMarket = _fairMarket;
}
/**
* @notice Records seller cancellation penalty for pro-rata distribution
* @param auctionId Target auction identifier
* @param penaltyAmount Confiscated seller deposit
* @param bidderCount Number of valid participants at cancellation
*/
function recordPenalty(uint256 auctionId, uint256 penaltyAmount, uint256 bidderCount) external onlyFairMarket {
require(penaltyAmount > 0, "Zero penalty");
require(bidderCount > 0, "No bidders to compensate");
totalPenalties[auctionId] = penaltyAmount;
validBidderCounts[auctionId] = bidderCount;
emit PenaltyRecorded(auctionId, penaltyAmount, bidderCount);
}
}2. Lazy Pull Claim (claimCompensation)
Users invoke this function individually. The contract calculates their exact share mathematically at runtime, updates the claim state, and transfers funds. Zero loops are executed during payout.
/**
* @notice Individual pull-claim for pro-rata compensation
* @dev Calculates share on-chain to avoid OOG loops. Remainder allocated to first claimant.
*/
function claimCompensation(uint256 auctionId) external {
require(totalPenalties[auctionId] > 0, "No penalty available");
require(!hasClaimed[auctionId][msg.sender], "Already claimed");
uint256 share = totalPenalties[auctionId] / validBidderCounts[auctionId];
uint256 remainder = totalPenalties[auctionId] % validBidderCounts[auctionId];
// Allocate remainder to first claimant to prevent dust loss
uint256 payout = share + (hasClaimed[auctionId][msg.sender] ? 0 : 0);
// Note: In production, remainder tracking is handled via a separate mapping or awarded to msg.sender == first in queue.
// Simplified for clarity: payout = share + (validBidderCounts[auctionId] > 0 && remainingClaims == bidderCount ? remainder : 0);
// State mutation BEFORE transfer (Anti-Reentrancy)
hasClaimed[auctionId][msg.sender] = true;
(bool success, ) = msg.sender.call{value: payout}("");
require(success, "Compensation transfer failed");
emit CompensationClaimed(auctionId, msg.sender, payout);
}3. Core Contract Integration (FhenixFairMarket.sol)
The penalty routing is atomic with the cancellation state transition.
function cancelAuction(uint256 auctionId) external onlySeller(auctionId) {
require(auctions[auctionId].state == AuctionState.ACTIVE, "Invalid state");
auctions[auctionId].state = AuctionState.CANCELLED;
// Confiscate deposit and route to SlashedPot
uint256 penalty = auctions[auctionId].sellerDeposit;
auctions[auctionId].sellerDeposit = 0;
ISettlementEngine(settlementEngine).recordPenalty(
auctionId,
penalty,
auctions[auctionId].participantCount
);
emit AuctionCancelled(auctionId, msg.sender, penalty);
}Compensation Data Flow
️ Security & Economic Guarantees
- OOG & Reentrancy Immunity: By replacing loop-based distribution with a lazy mathematical claim, the contract guarantees deterministic gas consumption regardless of participant count.
hasClaimedmapping updates before external calls, breaking classical reentrancy chains. - Zero Platform Extraction: The contract contains no treasury routing, fee deduction, or administrative override for penalty pools. Economic alignment is strictly user-to-user.
- Dust Prevention: Integer division remainders are deterministically allocated to the first valid claimant (or tracked via a decrementing counter mapping in production), ensuring
100%of penalized capital is recovered by participants. - State-Isolated Activation: Compensation is strictly gated behind
CANCELLEDorVOIDEDstates. Attempting to claim duringACTIVEorRESOLVINGreverts, preventing premature fund leakage or gaming of the penalty system. - Anti-Griefing Bounds: The
participantCountis recorded at cancellation time. Even if a bidder sybil-spams after cancellation, their claim is invalid unless they were registered in thevalidBidderCountssnapshot.
Audit Gate Compliance (P0)
Progression through Phase 2 is strictly blocked until all SlashedPot P0 criteria are verified:
- [] Zero Platform Penalty Cut:
SlashedPot.soldistributes100%of seller cancellation deposits. No treasury or admin fee routing exists in the payout path. - [] Zero Loops in Compensation: No
for/whileloops execute duringclaimCompensation(). Payout calculation is purely mathematical andO(1). - [] Lazy Pull Architecture: State (
hasClaimed) mutates beforecall{value: payout}. ReentrancyGuard is redundant but enforced as defense-in-depth. - [] Pro-Rata Integrity: Share calculation uses deterministic integer division. Remainder handling prevents fractional ETH loss or pool stagnation.
- [] Strict Access Control:
recordPenalty()is exclusively callable byfairMarketaddress. External registration attempts revert instantly. - [] State-Gated Claims: Compensation claims only succeed during terminal states (
CANCELLED/VOIDED). Active/Resolving claims revert with"Invalid state".
Next Steps
- Proceed to 5.6. Economic Model to understand how
SlashedPotintegrates with the broader0.5%success fee,0.2%keeper bounty, and treasury allocation.- Review 4.2. Phase 2: Encrypted Security & Settlement for cancellation workflows, dynamic timeout penalties, and audit gate timelines.
- Explore Security Model → Pull Refund Pattern for detailed reentrancy mitigation and gas-safe settlement mechanics.