// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.24; import "./core/DAO.sol"; import "./interfaces/IAcceptAvailability.sol"; contract Availability is IAcceptAvailability, DAOContract { struct AvailabilityStake { address worker; uint256 amount; uint endTime; bool assigned; } mapping(uint => AvailabilityStake) public stakes; mapping(address worker => uint stakeIndex) activeWorkerStakes; uint public stakeCount; event AvailabilityStaked(uint stakeIndex); constructor(DAO dao) DAOContract(dao) {} /// Accept availability stakes as reputation token transfer function acceptAvailability( address worker, uint256 amount, uint duration ) external returns (uint refund) { require( msg.sender == address(dao), "acceptAvailability must only be called by DAO contract" ); require(amount > 0, "No stake provided"); // If we already have a stake for this worker, replace it uint stakeIndex = activeWorkerStakes[worker]; if (stakeIndex == 0 && stakes[stakeIndex].worker != worker) { // We don't have an existing stake for this worker stakeIndex = stakeCount++; activeWorkerStakes[worker] = stakeIndex; } else if (stakes[stakeIndex].assigned) { // Stake has already been assigned; We need to create a new one stakeIndex = stakeCount++; activeWorkerStakes[worker] = stakeIndex; } else { // We are replacing an existing stake. // That means we can refund some of the granted allowance refund = stakes[stakeIndex].amount; } AvailabilityStake storage stake = stakes[stakeIndex]; stake.worker = worker; stake.amount = amount; stake.endTime = block.timestamp + duration; emit AvailabilityStaked(stakeIndex); } /// Select a worker randomly from among the available workers, weighted by amount staked function randomWeightedSelection() internal view returns (uint stakeIndex) { uint totalStakes; for (uint i = 0; i < stakeCount; i++) { if (stakes[i].assigned) continue; if (block.timestamp > stakes[i].endTime) continue; totalStakes += stakes[i].amount; } require(totalStakes > 0, "No available worker stakes"); uint select = block.prevrandao % totalStakes; uint acc; for (uint i = 0; i < stakeCount; i++) { if (stakes[i].assigned) continue; if (block.timestamp > stakes[i].endTime) continue; acc += stakes[i].amount; if (acc > select) { stakeIndex = i; break; } } } /// Assign a random available worker function assignWork() internal returns (uint stakeIndex) { stakeIndex = randomWeightedSelection(); AvailabilityStake storage stake = stakes[stakeIndex]; stake.assigned = true; } }