87 lines
2.9 KiB
Solidity
87 lines
2.9 KiB
Solidity
|
// 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;
|
||
|
uint public stakeCount;
|
||
|
|
||
|
event AvailabilityStaked(uint stakeIndex);
|
||
|
event WorkAssigned(uint requestIndex, uint stakeIndex);
|
||
|
|
||
|
constructor(DAO dao) DAOContract(dao) {}
|
||
|
|
||
|
/// Accept availability stakes as reputation token transfer
|
||
|
function acceptAvailability(
|
||
|
address sender,
|
||
|
uint256 amount,
|
||
|
uint duration
|
||
|
) external {
|
||
|
require(
|
||
|
msg.sender == address(dao),
|
||
|
"acceptAvailability must only be called by DAO contract"
|
||
|
);
|
||
|
require(amount > 0, "No stake provided");
|
||
|
uint stakeIndex = stakeCount++;
|
||
|
AvailabilityStake storage stake = stakes[stakeIndex];
|
||
|
stake.worker = sender;
|
||
|
stake.amount = amount;
|
||
|
stake.endTime = block.timestamp + duration;
|
||
|
emit AvailabilityStaked(stakeIndex);
|
||
|
}
|
||
|
|
||
|
function extendAvailability(uint stakeIndex, uint duration) external {
|
||
|
AvailabilityStake storage stake = stakes[stakeIndex];
|
||
|
require(
|
||
|
msg.sender == stake.worker,
|
||
|
"Worker can only extend their own availability stake"
|
||
|
);
|
||
|
require(!stake.assigned, "Stake has already been assigned work");
|
||
|
if (block.timestamp > stake.endTime) {
|
||
|
stake.endTime = block.timestamp + duration;
|
||
|
} else {
|
||
|
stake.endTime = stake.endTime + 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(uint requestIndex) internal returns (uint stakeIndex) {
|
||
|
stakeIndex = randomWeightedSelection();
|
||
|
AvailabilityStake storage stake = stakes[stakeIndex];
|
||
|
stake.assigned = true;
|
||
|
emit WorkAssigned(requestIndex, stakeIndex);
|
||
|
}
|
||
|
}
|