2024-04-28 15:06:10 -05:00
|
|
|
// 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;
|
2024-05-02 13:44:37 -05:00
|
|
|
mapping(address worker => uint stakeIndex) activeWorkerStakes;
|
2024-04-28 15:06:10 -05:00
|
|
|
uint public stakeCount;
|
|
|
|
|
|
|
|
event AvailabilityStaked(uint stakeIndex);
|
|
|
|
|
|
|
|
constructor(DAO dao) DAOContract(dao) {}
|
|
|
|
|
|
|
|
/// Accept availability stakes as reputation token transfer
|
|
|
|
function acceptAvailability(
|
2024-05-02 13:44:37 -05:00
|
|
|
address worker,
|
2024-04-28 15:06:10 -05:00
|
|
|
uint256 amount,
|
|
|
|
uint duration
|
2024-05-02 13:57:32 -05:00
|
|
|
) external returns (uint refund) {
|
2024-04-28 15:06:10 -05:00
|
|
|
require(
|
|
|
|
msg.sender == address(dao),
|
|
|
|
"acceptAvailability must only be called by DAO contract"
|
|
|
|
);
|
|
|
|
require(amount > 0, "No stake provided");
|
2024-05-02 13:44:37 -05:00
|
|
|
// 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;
|
2024-05-02 13:57:32 -05:00
|
|
|
} else {
|
|
|
|
// We are replacing an existing stake.
|
|
|
|
// That means we can refund some of the granted allowance
|
|
|
|
refund = stakes[stakeIndex].amount;
|
2024-05-02 13:44:37 -05:00
|
|
|
}
|
2024-04-28 15:06:10 -05:00
|
|
|
AvailabilityStake storage stake = stakes[stakeIndex];
|
2024-05-02 13:44:37 -05:00
|
|
|
stake.worker = worker;
|
2024-04-28 15:06:10 -05:00
|
|
|
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
|
2024-04-28 16:51:35 -05:00
|
|
|
function assignWork() internal returns (uint stakeIndex) {
|
2024-04-28 15:06:10 -05:00
|
|
|
stakeIndex = randomWeightedSelection();
|
|
|
|
AvailabilityStake storage stake = stakes[stakeIndex];
|
|
|
|
stake.assigned = true;
|
|
|
|
}
|
|
|
|
}
|