Use ERC20 allowance to implement staking
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 36s
Details
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 36s
Details
This commit is contained in:
parent
ea6d068555
commit
bb26571779
|
@ -263,7 +263,7 @@ function App() {
|
|||
|
||||
const stake = useCallback(async (poolIndex, amount, inFavor) => {
|
||||
console.log(`Attempting to stake ${amount} ${inFavor ? 'for' : 'against'} pool ${poolIndex}`);
|
||||
await DAO.methods.stake(poolIndex, amount, inFavor).send({
|
||||
await DAO.methods.stakeOnValidationPool(poolIndex, amount, inFavor).send({
|
||||
from: account,
|
||||
gas: 999999,
|
||||
});
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -6,7 +6,6 @@ import Web3Context from '../../contexts/Web3Context';
|
|||
import WorkContractContext from '../../contexts/WorkContractContext';
|
||||
|
||||
const getAvailabilityStatus = (stake) => {
|
||||
if (stake.reclaimed) return 'Reclaimed';
|
||||
if (stake.assigned) return 'Assigned';
|
||||
if (new Date() < new Date(Number(stake.endTime) * 1000)) return 'Available';
|
||||
return 'Expired';
|
||||
|
@ -75,13 +74,6 @@ function AvailabilityStakes({
|
|||
setReputation(reputation / BigInt(2));
|
||||
}, [DAO, workContract, account, reputation, setReputation]);
|
||||
|
||||
const reclaimAvailabilityStake = useCallback(async (stakeIndex) => {
|
||||
await workContract.methods.reclaimAvailability(stakeIndex).send({
|
||||
from: account,
|
||||
gas: 999999,
|
||||
});
|
||||
}, [workContract, account]);
|
||||
|
||||
const extendAvailabilityStake = useCallback(async (stakeIndex, duration) => {
|
||||
await workContract.methods.extendAvailability(stakeIndex, duration).send({
|
||||
from: account,
|
||||
|
@ -139,7 +131,7 @@ function AvailabilityStakes({
|
|||
<td>{getAvailabilityStatus(s)}</td>
|
||||
{showActions && (
|
||||
<td>
|
||||
{s.currentUserIsWorker() && !s.assigned && !s.reclaimed && (
|
||||
{s.currentUserIsWorker() && !s.assigned && (
|
||||
<>
|
||||
<Button onClick={() => extendAvailabilityStake(s.id, 3600)}>
|
||||
Extend 1 Hr.
|
||||
|
@ -149,15 +141,6 @@ function AvailabilityStakes({
|
|||
</Button>
|
||||
</>
|
||||
)}
|
||||
{s.currentUserIsWorker() && s.timeRemaining <= 0
|
||||
&& !s.assigned && !s.reclaimed && (
|
||||
<>
|
||||
{' '}
|
||||
<Button onClick={() => reclaimAvailabilityStake(s.id)}>
|
||||
Reclaim
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x614fE39E47E48Ed39a078791917F6290C1C8D0cd",
|
||||
"Work1": "0x35d9024a19e970b1454Fa1C0e124dD2bd71E6360",
|
||||
"Onboarding": "0x861fD16fA5C26c53bf6C6E6210dD4d2364A26213",
|
||||
"Proposals": "0x918040581A6817fa2F3F6e73f7F10C3A3a7Bbc29"
|
||||
"DAO": "0x8d914D38dD301FC4606f5aa9fEcF8A76389020d3",
|
||||
"Work1": "0xfe58B9EB03F75A603de1B286584f5E9532ab8fB5",
|
||||
"Onboarding": "0x1d63FDe5B461106729fE1e5e38A02fc68C518Af5",
|
||||
"Proposals": "0x050C420Cc4995B41217Eba1B54B82Fd5687e9139"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0x58c41E768aCA55B39b5dC0618c0D0bE3f5519943",
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x614fE39E47E48Ed39a078791917F6290C1C8D0cd",
|
||||
"Work1": "0x35d9024a19e970b1454Fa1C0e124dD2bd71E6360",
|
||||
"Onboarding": "0x861fD16fA5C26c53bf6C6E6210dD4d2364A26213",
|
||||
"Proposals": "0x918040581A6817fa2F3F6e73f7F10C3A3a7Bbc29"
|
||||
"DAO": "0x8d914D38dD301FC4606f5aa9fEcF8A76389020d3",
|
||||
"Work1": "0xfe58B9EB03F75A603de1B286584f5E9532ab8fB5",
|
||||
"Onboarding": "0x1d63FDe5B461106729fE1e5e38A02fc68C518Af5",
|
||||
"Proposals": "0x050C420Cc4995B41217Eba1B54B82Fd5687e9139"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0x58c41E768aCA55B39b5dC0618c0D0bE3f5519943",
|
||||
|
|
|
@ -6,23 +6,19 @@ import "./core/Reputation.sol";
|
|||
import "./core/ValidationPools.sol";
|
||||
import "./core/Forum.sol";
|
||||
import "./interfaces/IAcceptAvailability.sol";
|
||||
|
||||
import "hardhat/console.sol";
|
||||
|
||||
// TODO: consider dynamically constructing contract instances rather than merging at build time
|
||||
contract DAO is Reputation, Forum, ValidationPools {
|
||||
/// Transfer REP to a contract, and call that contract's receiveTransfer method
|
||||
function stakeAvailability(
|
||||
address to,
|
||||
uint256 value,
|
||||
uint duration
|
||||
) external returns (bool transferred) {
|
||||
transferred = super.transfer(to, value);
|
||||
if (transferred)
|
||||
IAcceptAvailability(to).acceptAvailability(
|
||||
msg.sender,
|
||||
value,
|
||||
duration
|
||||
);
|
||||
) external returns (bool) {
|
||||
_approve(msg.sender, to, value);
|
||||
IAcceptAvailability(to).acceptAvailability(msg.sender, value, duration);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,9 @@ contract Onboarding is WorkContract, IOnValidate {
|
|||
true,
|
||||
abi.encode(requestIndex)
|
||||
);
|
||||
dao.stake(poolIndex, stake.amount, true);
|
||||
// We have an approval from stake.worker to transfer up to stake.amount
|
||||
dao.transferFrom(stake.worker, address(this), stake.amount);
|
||||
dao.stakeOnValidationPool(poolIndex, stake.amount, true);
|
||||
}
|
||||
|
||||
/// Callback to be executed when review pool completes
|
||||
|
|
|
@ -5,6 +5,7 @@ import "./DAO.sol";
|
|||
import "./Proposals.sol";
|
||||
import "./interfaces/IAcceptAvailability.sol";
|
||||
import "./interfaces/IOnProposalAccepted.sol";
|
||||
import "hardhat/console.sol";
|
||||
|
||||
abstract contract WorkContract is
|
||||
DAOContract,
|
||||
|
@ -16,7 +17,6 @@ abstract contract WorkContract is
|
|||
uint256 amount;
|
||||
uint endTime;
|
||||
bool assigned;
|
||||
bool reclaimed;
|
||||
}
|
||||
|
||||
enum WorkStatus {
|
||||
|
@ -89,7 +89,6 @@ abstract contract WorkContract is
|
|||
msg.sender == stake.worker,
|
||||
"Worker can only extend their own availability stake"
|
||||
);
|
||||
require(!stake.reclaimed, "Stake has already been reclaimed");
|
||||
require(!stake.assigned, "Stake has already been assigned work");
|
||||
if (block.timestamp > stake.endTime) {
|
||||
stake.endTime = block.timestamp + duration;
|
||||
|
@ -99,23 +98,6 @@ abstract contract WorkContract is
|
|||
emit AvailabilityStaked(stakeIndex);
|
||||
}
|
||||
|
||||
function reclaimAvailability(uint stakeIndex) external {
|
||||
AvailabilityStake storage stake = stakes[stakeIndex];
|
||||
require(
|
||||
msg.sender == stake.worker,
|
||||
"Worker can only reclaim their own availability stake"
|
||||
);
|
||||
require(
|
||||
block.timestamp > stake.endTime,
|
||||
"Stake duration has not yet elapsed"
|
||||
);
|
||||
require(!stake.reclaimed, "Stake has already been reclaimed");
|
||||
require(!stake.assigned, "Stake has already been assigned work");
|
||||
stake.reclaimed = true;
|
||||
dao.transfer(msg.sender, stake.amount);
|
||||
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;
|
||||
|
@ -204,7 +186,13 @@ abstract contract WorkContract is
|
|||
false,
|
||||
""
|
||||
);
|
||||
dao.stake(poolIndex, stake.amount, true);
|
||||
// We have an approval from stake.worker to transfer up to stake.amount
|
||||
dao.delegatedStakeOnValidationPool(
|
||||
poolIndex,
|
||||
stake.worker,
|
||||
stake.amount,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
function proposeNewPrice(
|
||||
|
|
|
@ -10,4 +10,3 @@ contract Reputation is ERC20("Reputation", "REP") {
|
|||
}
|
||||
|
||||
// TODO: try implementing as ERC721
|
||||
// If that doesn't work, try writing from scratch
|
||||
|
|
|
@ -3,10 +3,10 @@ pragma solidity ^0.8.24;
|
|||
|
||||
import "./Reputation.sol";
|
||||
import "./Forum.sol";
|
||||
|
||||
import "../interfaces/IOnValidate.sol";
|
||||
import "hardhat/console.sol";
|
||||
|
||||
struct Stake {
|
||||
struct ValidationPoolStake {
|
||||
uint id;
|
||||
bool inFavor;
|
||||
uint256 amount;
|
||||
|
@ -26,7 +26,7 @@ struct ValidationPool {
|
|||
uint id;
|
||||
uint postIndex;
|
||||
address sender;
|
||||
mapping(uint => Stake) stakes;
|
||||
mapping(uint => ValidationPoolStake) stakes;
|
||||
uint stakeCount;
|
||||
ValidationPoolParams params;
|
||||
uint256 fee;
|
||||
|
@ -45,9 +45,6 @@ contract ValidationPools is Reputation, Forum {
|
|||
uint public memberCount;
|
||||
mapping(address => bool) public isMember;
|
||||
|
||||
// TODO: possible parameter for minting ratio
|
||||
// TODO: possible parameter for stakeForAuthor
|
||||
|
||||
uint constant minDuration = 1; // 1 second
|
||||
uint constant maxDuration = 365_000_000 days; // 1 million years
|
||||
uint[2] minQuorum = [1, 10];
|
||||
|
@ -60,8 +57,9 @@ contract ValidationPools is Reputation, Forum {
|
|||
);
|
||||
|
||||
// TODO: Add forum parameters
|
||||
|
||||
/// Internal function to register a stake for/against a validation pool
|
||||
function _stake(
|
||||
function _stakeOnValidationPool(
|
||||
ValidationPool storage pool,
|
||||
address sender,
|
||||
uint256 amount,
|
||||
|
@ -69,9 +67,9 @@ contract ValidationPools is Reputation, Forum {
|
|||
bool fromMint
|
||||
) internal {
|
||||
require(block.timestamp <= pool.endTime, "Pool end time has passed");
|
||||
_update(sender, address(this), amount);
|
||||
//_update(sender, address(this), amount);
|
||||
uint stakeIndex = pool.stakeCount++;
|
||||
Stake storage s = pool.stakes[stakeIndex];
|
||||
ValidationPoolStake storage s = pool.stakes[stakeIndex];
|
||||
s.sender = sender;
|
||||
s.inFavor = inFavor;
|
||||
s.amount = amount;
|
||||
|
@ -80,9 +78,25 @@ contract ValidationPools is Reputation, Forum {
|
|||
}
|
||||
|
||||
/// Accept reputation stakes toward a validation pool
|
||||
function stake(uint poolIndex, uint256 amount, bool inFavor) public {
|
||||
function stakeOnValidationPool(
|
||||
uint poolIndex,
|
||||
uint256 amount,
|
||||
bool inFavor
|
||||
) public {
|
||||
ValidationPool storage pool = validationPools[poolIndex];
|
||||
_stake(pool, msg.sender, amount, inFavor, false);
|
||||
_stakeOnValidationPool(pool, msg.sender, amount, inFavor, false);
|
||||
}
|
||||
|
||||
/// Accept reputation stakes toward a validation pool
|
||||
function delegatedStakeOnValidationPool(
|
||||
uint poolIndex,
|
||||
address owner,
|
||||
uint256 amount,
|
||||
bool inFavor
|
||||
) public {
|
||||
ValidationPool storage pool = validationPools[poolIndex];
|
||||
_spendAllowance(owner, msg.sender, amount);
|
||||
_stakeOnValidationPool(pool, owner, amount, inFavor, false);
|
||||
}
|
||||
|
||||
/// Accept fee to initiate a validation pool
|
||||
|
@ -129,8 +143,8 @@ contract ValidationPools is Reputation, Forum {
|
|||
_mint(post.author, msg.value);
|
||||
// Here we assume a stakeForAuthor ratio of 0.5
|
||||
// TODO: Make stakeForAuthor an adjustable parameter
|
||||
_stake(pool, post.author, msg.value / 2, true, true);
|
||||
_stake(pool, post.author, msg.value / 2, false, true);
|
||||
_stakeOnValidationPool(pool, post.author, msg.value / 2, true, true);
|
||||
_stakeOnValidationPool(pool, post.author, msg.value / 2, false, true);
|
||||
emit ValidationPoolInitiated(poolIndex);
|
||||
}
|
||||
|
||||
|
@ -141,7 +155,7 @@ contract ValidationPools is Reputation, Forum {
|
|||
require(pool.resolved == false, "Pool is already resolved");
|
||||
uint256 stakedFor;
|
||||
uint256 stakedAgainst;
|
||||
Stake storage s;
|
||||
ValidationPoolStake storage s;
|
||||
for (uint i = 0; i < pool.stakeCount; i++) {
|
||||
s = pool.stakes[i];
|
||||
if (s.inFavor) {
|
||||
|
@ -164,11 +178,6 @@ contract ValidationPools is Reputation, Forum {
|
|||
// Refund fee
|
||||
// TODO: this could be made available for the sender to withdraw
|
||||
// payable(pool.sender).transfer(pool.fee);
|
||||
// Refund stakes
|
||||
for (uint i = 0; i < pool.stakeCount; i++) {
|
||||
s = pool.stakes[i];
|
||||
_update(address(this), s.sender, s.amount);
|
||||
}
|
||||
pool.resolved = true;
|
||||
emit ValidationPoolResolved(poolIndex, false, false);
|
||||
// Callback if requested
|
||||
|
@ -203,52 +212,55 @@ contract ValidationPools is Reputation, Forum {
|
|||
uint256 amountFromLosers = votePasses ? stakedAgainst : stakedFor;
|
||||
// Only bindingPercent % should be redistributed
|
||||
// Stake senders should get (100-bindingPercent) % back
|
||||
uint256 totalAllocated;
|
||||
// We have allowances for each stake. Time to collect from the losing stakes.
|
||||
uint totalRewards;
|
||||
uint totalAllocated;
|
||||
for (uint i = 0; i < pool.stakeCount; i++) {
|
||||
s = pool.stakes[i];
|
||||
uint bindingPercent = s.fromMint ? 100 : pool.params.bindingPercent;
|
||||
if (votePasses == s.inFavor) {
|
||||
// Winning stake
|
||||
// If this stake is from the minted fee, always redistribute it to the winners
|
||||
bool redistributeLosingStakes = s.fromMint ||
|
||||
pool.params.redistributeLosingStakes;
|
||||
uint reward = redistributeLosingStakes
|
||||
? ((s.amount * amountFromLosers) / amountFromWinners) *
|
||||
(bindingPercent / 100)
|
||||
: 0;
|
||||
_update(address(this), s.sender, s.amount + reward);
|
||||
totalAllocated += reward;
|
||||
} else {
|
||||
bool redistributeLosingStakes = s.fromMint ||
|
||||
pool.params.redistributeLosingStakes;
|
||||
if (votePasses != s.inFavor) {
|
||||
// Losing stake
|
||||
// If this stake is from the minted fee, don't burn it
|
||||
if (!s.fromMint) {
|
||||
uint refund = (s.amount * (100 - bindingPercent)) / 100;
|
||||
if (refund > 0) {
|
||||
_update(address(this), s.sender, refund);
|
||||
}
|
||||
if (!pool.params.redistributeLosingStakes) {
|
||||
uint amountToBurn = (s.amount *
|
||||
pool.params.bindingPercent) / 100;
|
||||
_burn(address(this), amountToBurn);
|
||||
totalAllocated += amountToBurn;
|
||||
}
|
||||
totalAllocated += refund;
|
||||
uint amount = (s.amount * bindingPercent) / 100;
|
||||
if (redistributeLosingStakes) {
|
||||
_update(s.sender, address(this), amount);
|
||||
totalRewards += amount;
|
||||
} else {
|
||||
_burn(s.sender, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Due to rounding, there may be some REP left over. Include this as a reward to the author.
|
||||
uint256 remainder = amountFromLosers - totalAllocated;
|
||||
// Issue rewards to the winners
|
||||
for (uint i = 0; i < pool.stakeCount; i++) {
|
||||
s = pool.stakes[i];
|
||||
uint bindingPercent = s.fromMint ? 100 : pool.params.bindingPercent;
|
||||
bool redistributeLosingStakes = s.fromMint ||
|
||||
pool.params.redistributeLosingStakes;
|
||||
if (redistributeLosingStakes && votePasses == s.inFavor) {
|
||||
// Winning stake
|
||||
// If this stake is from the minted fee, always redistribute it to the winners
|
||||
uint reward = (((amountFromLosers * s.amount) /
|
||||
amountFromWinners) * bindingPercent) / 100;
|
||||
totalAllocated += reward;
|
||||
_update(address(this), s.sender, reward);
|
||||
}
|
||||
}
|
||||
// Due to rounding, some reward may be left over. Let's give it to the author.
|
||||
uint remainder = totalRewards - totalAllocated;
|
||||
if (remainder > 0) {
|
||||
_update(address(this), post.author, remainder);
|
||||
}
|
||||
// Distribute fee proportionatly among all reputation holders
|
||||
|
||||
// Distribute fee proportionately among all reputation holders
|
||||
for (uint i = 0; i < memberCount; i++) {
|
||||
address member = members[i];
|
||||
uint256 share = (pool.fee * balanceOf(member)) / totalSupply();
|
||||
// TODO: For efficiency this could be modified to hold the funds for recipients to withdraw
|
||||
// TODO: Exclude encumbered reputation from totalSupply
|
||||
payable(member).transfer(share);
|
||||
}
|
||||
|
||||
// Callback if requested
|
||||
if (pool.callbackOnValidate) {
|
||||
IOnValidate(pool.sender).onValidate(
|
||||
|
|
|
@ -145,7 +145,7 @@ const getPoolStatus = (pool) => {
|
|||
|
||||
const stake = async (pool, amount, inFavor) => {
|
||||
console.log(`staking ${amount} ${inFavor ? 'in favor of' : 'against'} pool ${pool.id.toString()}`);
|
||||
await dao.stake(pool.id, amount, true);
|
||||
await dao.stakeOnValidationPool(pool.id, amount, true);
|
||||
await fetchReputation();
|
||||
};
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ describe('DAO', () => {
|
|||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(0);
|
||||
expect(await dao.validationPoolCount()).to.equal(1);
|
||||
expect(await dao.memberCount()).to.equal(0);
|
||||
expect(await dao.balanceOf(account1)).to.equal(0);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.totalSupply()).to.equal(POOL_FEE);
|
||||
});
|
||||
|
||||
|
@ -131,13 +131,14 @@ describe('DAO', () => {
|
|||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
await initiateValidationPool();
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
});
|
||||
|
||||
it('should be able to stake before validation pool has elapsed', async () => {
|
||||
await dao.stake(1, 10, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(110);
|
||||
await dao.stakeOnValidationPool(1, 10, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true, true);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
|
@ -146,13 +147,13 @@ describe('DAO', () => {
|
|||
|
||||
it('should not be able to stake after validation pool has elapsed', async () => {
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.stake(1, 10, true)).to.be.revertedWith('Pool end time has passed');
|
||||
await expect(dao.stakeOnValidationPool(1, 10, true)).to.be.revertedWith('Pool end time has passed');
|
||||
});
|
||||
|
||||
it('should be able to stake against a validation pool', async () => {
|
||||
await dao.stake(1, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(110);
|
||||
await dao.stakeOnValidationPool(1, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, false, true);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
|
@ -174,12 +175,12 @@ describe('DAO', () => {
|
|||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0));
|
||||
await initiateValidationPool({ fee: 100 });
|
||||
await dao.stake(1, 100, true);
|
||||
await dao.stakeOnValidationPool(1, 100, true);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true, true);
|
||||
});
|
||||
|
||||
it('should be able to evaluate outcome after duration has elapsed', async () => {
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true, true);
|
||||
expect(await dao.memberCount()).to.equal(1);
|
||||
|
@ -211,7 +212,7 @@ describe('DAO', () => {
|
|||
expect(await dao.validationPoolCount()).to.equal(2);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await expect(dao.evaluateOutcome(0)).to.emit(dao, 'ValidationPoolResolved').withArgs(0, true, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
});
|
||||
|
@ -241,11 +242,11 @@ describe('DAO', () => {
|
|||
it('Binding validation pool should redistribute stakes', async () => {
|
||||
const init = () => initiateValidationPool();
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stake(2, 10, true);
|
||||
await dao.connect(account2).stake(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(90);
|
||||
expect(await dao.balanceOf(account2)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(120);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(210);
|
||||
|
@ -256,11 +257,11 @@ describe('DAO', () => {
|
|||
it('Non binding validation pool should not redistribute stakes', async () => {
|
||||
const init = () => initiateValidationPool({ bindingPercent: 0 });
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stake(2, 10, true);
|
||||
await dao.connect(account2).stake(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(90);
|
||||
expect(await dao.balanceOf(account2)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(120);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
|
@ -271,11 +272,11 @@ describe('DAO', () => {
|
|||
it('Partially binding validation pool should redistribute some stakes', async () => {
|
||||
const init = () => initiateValidationPool({ bindingPercent: 50 });
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stake(2, 10, true);
|
||||
await dao.connect(account2).stake(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(90);
|
||||
expect(await dao.balanceOf(account2)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(120);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(205);
|
||||
|
@ -290,11 +291,11 @@ describe('DAO', () => {
|
|||
redistributeLosingStakes: false,
|
||||
});
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stake(2, 10, true);
|
||||
await dao.connect(account2).stake(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(90);
|
||||
expect(await dao.balanceOf(account2)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(120);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
|
@ -309,11 +310,11 @@ describe('DAO', () => {
|
|||
redistributeLosingStakes: false,
|
||||
});
|
||||
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.connect(account1).stake(2, 10, true);
|
||||
await dao.connect(account2).stake(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(90);
|
||||
expect(await dao.balanceOf(account2)).to.equal(90);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(120);
|
||||
await dao.connect(account1).stakeOnValidationPool(2, 10, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(2, 10, false);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
expect(await dao.balanceOf(account2)).to.equal(100);
|
||||
expect(await dao.balanceOf(dao.target)).to.equal(0);
|
||||
time.increase(POOL_DURATION + 1);
|
||||
await dao.evaluateOutcome(2);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
|
|
|
@ -139,7 +139,7 @@ describe('Onboarding', () => {
|
|||
await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE });
|
||||
await onboarding.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await expect(onboarding.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
await dao.stake(1, 60, false);
|
||||
await dao.stakeOnValidationPool(1, 60, false);
|
||||
await time.increase(86401);
|
||||
await expect(dao.evaluateOutcome(1)).not.to.emit(dao, 'ValidationPoolInitiated');
|
||||
expect(await dao.postCount()).to.equal(2);
|
||||
|
|
|
@ -141,7 +141,7 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
it('referendum retries if it fails to meet participation rate', async () => {
|
||||
await dao.stake(2, 200, true);
|
||||
await dao.stakeOnValidationPool(2, 200, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(2))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(2, true, true)
|
||||
|
@ -151,7 +151,7 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
it('referendum retries if it fails to meet win ratio', async () => {
|
||||
await dao.stake(2, 1000, false);
|
||||
await dao.stakeOnValidationPool(2, 1000, false);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(2))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(2, false, true)
|
||||
|
@ -169,7 +169,7 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
it('proposal fails if a referendum fails to meet participation rate 3 times', async () => {
|
||||
await dao.stake(2, 200, true);
|
||||
await dao.stakeOnValidationPool(2, 200, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(2))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(2, true, true)
|
||||
|
@ -177,7 +177,7 @@ describe('Proposal', () => {
|
|||
proposal = await proposals.proposals(0);
|
||||
expect(proposal.stage).to.equal(1);
|
||||
|
||||
await dao.stake(3, 200, true);
|
||||
await dao.stakeOnValidationPool(3, 200, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(3))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(3, true, true)
|
||||
|
@ -185,7 +185,7 @@ describe('Proposal', () => {
|
|||
proposal = await proposals.proposals(0);
|
||||
expect(proposal.stage).to.equal(1);
|
||||
|
||||
await dao.stake(4, 200, true);
|
||||
await dao.stakeOnValidationPool(4, 200, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(4))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(4, true, true);
|
||||
|
@ -194,7 +194,7 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
it('advances to next referendum if it meets participation rate and win ratio', async () => {
|
||||
await dao.stake(2, 1000, true);
|
||||
await dao.stakeOnValidationPool(2, 1000, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(2))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(2, true, true)
|
||||
|
@ -208,7 +208,7 @@ describe('Proposal', () => {
|
|||
beforeEach(async () => {
|
||||
await proposals.attest(0, 200);
|
||||
await expect(proposals.evaluateAttestation(0)).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.stake(2, 1000, true);
|
||||
await dao.stakeOnValidationPool(2, 1000, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(2))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(2, true, true)
|
||||
|
@ -230,7 +230,7 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
it('referendum retries if it fails to meet participation rate', async () => {
|
||||
await dao.stake(3, 200, true);
|
||||
await dao.stakeOnValidationPool(3, 200, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(3))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(3, true, true)
|
||||
|
@ -240,7 +240,7 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
it('referendum retries if it fails to meet win ratio', async () => {
|
||||
await dao.stake(3, 1000, false);
|
||||
await dao.stakeOnValidationPool(3, 1000, false);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(3))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(3, false, true)
|
||||
|
@ -250,7 +250,7 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
it('proposal fails if a referendum fails to meet participation rate 3 times', async () => {
|
||||
await dao.stake(3, 200, true);
|
||||
await dao.stakeOnValidationPool(3, 200, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(3))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(3, true, true)
|
||||
|
@ -258,7 +258,7 @@ describe('Proposal', () => {
|
|||
proposal = await proposals.proposals(0);
|
||||
expect(proposal.stage).to.equal(2);
|
||||
|
||||
await dao.stake(4, 200, true);
|
||||
await dao.stakeOnValidationPool(4, 200, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(4))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(4, true, true)
|
||||
|
@ -266,7 +266,7 @@ describe('Proposal', () => {
|
|||
proposal = await proposals.proposals(0);
|
||||
expect(proposal.stage).to.equal(2);
|
||||
|
||||
await dao.stake(5, 200, true);
|
||||
await dao.stakeOnValidationPool(5, 200, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(5))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(5, true, true);
|
||||
|
@ -275,7 +275,7 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
it('advances to next referendum if it meets participation rate and win ratio', async () => {
|
||||
await dao.stake(3, 1000, true);
|
||||
await dao.stakeOnValidationPool(3, 1000, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(3))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(3, true, true)
|
||||
|
@ -289,12 +289,12 @@ describe('Proposal', () => {
|
|||
beforeEach(async () => {
|
||||
await proposals.attest(0, 200);
|
||||
await expect(proposals.evaluateAttestation(0)).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
await dao.stake(2, 1000, true);
|
||||
await dao.stakeOnValidationPool(2, 1000, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(2))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(2, true, true)
|
||||
.to.emit(dao, 'ValidationPoolInitiated').withArgs(3);
|
||||
await dao.stake(3, 1000, true);
|
||||
await dao.stakeOnValidationPool(3, 1000, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(3))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(3, true, true)
|
||||
|
@ -316,7 +316,7 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
it('referendum retries if it fails to meet participation rate', async () => {
|
||||
await dao.stake(4, 200, true);
|
||||
await dao.stakeOnValidationPool(4, 200, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(4))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(4, true, true)
|
||||
|
@ -326,7 +326,7 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
it('referendum retries if it fails to meet win ratio', async () => {
|
||||
await dao.stake(4, 1000, false);
|
||||
await dao.stakeOnValidationPool(4, 1000, false);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(4))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(4, false, true)
|
||||
|
@ -336,7 +336,7 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
it('proposal fails if a referendum fails to meet participation rate 3 times', async () => {
|
||||
await dao.stake(4, 200, true);
|
||||
await dao.stakeOnValidationPool(4, 200, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(4))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(4, true, true)
|
||||
|
@ -344,7 +344,7 @@ describe('Proposal', () => {
|
|||
proposal = await proposals.proposals(0);
|
||||
expect(proposal.stage).to.equal(3);
|
||||
|
||||
await dao.stake(5, 200, true);
|
||||
await dao.stakeOnValidationPool(5, 200, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(5))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(5, true, true)
|
||||
|
@ -352,7 +352,7 @@ describe('Proposal', () => {
|
|||
proposal = await proposals.proposals(0);
|
||||
expect(proposal.stage).to.equal(3);
|
||||
|
||||
await dao.stake(6, 200, true);
|
||||
await dao.stakeOnValidationPool(6, 200, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(6))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(6, true, true);
|
||||
|
@ -361,8 +361,8 @@ describe('Proposal', () => {
|
|||
});
|
||||
|
||||
it('advances to accepted stage if it meets participation rate and win ratio', async () => {
|
||||
await dao.connect(account1).stake(4, 1000, true);
|
||||
await dao.connect(account2).stake(4, 1000, true);
|
||||
await dao.connect(account1).stakeOnValidationPool(4, 1000, true);
|
||||
await dao.connect(account2).stakeOnValidationPool(4, 1000, true);
|
||||
await time.increase(21);
|
||||
await expect(dao.evaluateOutcome(4))
|
||||
.to.emit(dao, 'ValidationPoolResolved').withArgs(4, true, true);
|
||||
|
|
|
@ -64,8 +64,9 @@ describe('Work1', () => {
|
|||
});
|
||||
|
||||
it('Should be able to stake availability', async () => {
|
||||
expect(await dao.balanceOf(account1)).to.equal(50);
|
||||
expect(await dao.balanceOf(work1.target)).to.equal(50);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(work1.target)).to.equal(0);
|
||||
expect(await dao.allowance(account1, work1.target)).to.equal(50);
|
||||
expect(await work1.stakeCount()).to.equal(1);
|
||||
const stake = await work1.stakes(0);
|
||||
expect(stake.worker).to.equal(account1);
|
||||
|
@ -77,29 +78,6 @@ describe('Work1', () => {
|
|||
await expect(dao.stakeAvailability(work1.target, 0, STAKE_DURATION)).to.be.revertedWith('No stake provided');
|
||||
});
|
||||
|
||||
it('should be able to reclaim staked availability after duration elapses', async () => {
|
||||
expect(await dao.balanceOf(account1)).to.equal(50);
|
||||
time.increase(STAKE_DURATION + 1);
|
||||
await expect(work1.reclaimAvailability(0)).to.emit(work1, 'AvailabilityStaked').withArgs(0);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
});
|
||||
|
||||
it('should not be able to reclaim staked availability twice', async () => {
|
||||
expect(await dao.balanceOf(account1)).to.equal(50);
|
||||
time.increase(STAKE_DURATION + 1);
|
||||
await work1.reclaimAvailability(0);
|
||||
await expect(work1.reclaimAvailability(0)).to.be.revertedWith('Stake has already been reclaimed');
|
||||
});
|
||||
|
||||
it('should not be able to reclaim staked availability before duration elapses', async () => {
|
||||
await expect(work1.reclaimAvailability(0)).to.be.revertedWith('Stake duration has not yet elapsed');
|
||||
});
|
||||
|
||||
it('should not be able to reclaim availability staked by another account', async () => {
|
||||
time.increase(STAKE_DURATION + 1);
|
||||
await expect(work1.connect(account2).reclaimAvailability(0)).to.be.revertedWith('Worker can only reclaim their own availability stake');
|
||||
});
|
||||
|
||||
it('should be able to extend the duration of an availability stake before it expires', async () => {
|
||||
await time.increase(STAKE_DURATION / 2);
|
||||
await expect(work1.extendAvailability(0, STAKE_DURATION)).to.emit(work1, 'AvailabilityStaked').withArgs(0);
|
||||
|
@ -115,12 +93,6 @@ describe('Work1', () => {
|
|||
await expect(work1.connect(account2).extendAvailability(0, STAKE_DURATION)).to.be.revertedWith('Worker can only extend their own availability stake');
|
||||
});
|
||||
|
||||
it('should not be able to extend a stake that has been reclaimed', async () => {
|
||||
await time.increase(STAKE_DURATION * 2);
|
||||
await work1.reclaimAvailability(0);
|
||||
await expect(work1.extendAvailability(0, STAKE_DURATION)).to.be.revertedWith('Stake has already been reclaimed');
|
||||
});
|
||||
|
||||
it('extending a stake before expiration should increase the end time by the given duration', async () => {
|
||||
await time.increase(STAKE_DURATION / 2);
|
||||
await work1.extendAvailability(0, STAKE_DURATION * 2);
|
||||
|
@ -152,16 +124,6 @@ describe('Work1', () => {
|
|||
expect(request.requestContentId).to.equal('req-content-id');
|
||||
});
|
||||
|
||||
it('should not be able to reclaim stake after work is assigned', async () => {
|
||||
const {
|
||||
dao, work1, account2,
|
||||
} = await loadFixture(deploy);
|
||||
await dao.stakeAvailability(work1.target, 50, STAKE_DURATION);
|
||||
const requestWork = () => work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await expect(requestWork()).to.emit(work1, 'WorkAssigned').withArgs(0, 0);
|
||||
await time.increase(STAKE_DURATION + 1);
|
||||
await expect(work1.reclaimAvailability(0)).to.be.revertedWith('Stake has already been assigned work');
|
||||
});
|
||||
it('should not be able to request work if there are no availability stakes', async () => {
|
||||
const {
|
||||
work1, account2,
|
||||
|
@ -242,9 +204,13 @@ describe('Work1', () => {
|
|||
it('should be able to submit work approval', async () => {
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await work1.submitWorkEvidence(0, 'evidence-content-id');
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
expect(await dao.balanceOf(work1.target)).to.equal(0);
|
||||
await expect(work1.submitWorkApproval(0, true))
|
||||
.to.emit(dao, 'ValidationPoolInitiated').withArgs(1)
|
||||
.to.emit(work1, 'WorkApprovalSubmitted').withArgs(0, true);
|
||||
expect(await dao.balanceOf(work1.target)).to.equal(0);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
const post = await dao.posts(1);
|
||||
expect(post.author).to.equal(account1);
|
||||
expect(post.sender).to.equal(work1.target);
|
||||
|
@ -256,6 +222,7 @@ describe('Work1', () => {
|
|||
expect(pool.stakeCount).to.equal(3);
|
||||
await time.increase(86401);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolResolved').withArgs(1, true, true);
|
||||
expect(await dao.balanceOf(account1)).to.equal(200);
|
||||
});
|
||||
|
||||
it('should be able to submit work disapproval', async () => {
|
||||
|
|
Loading…
Reference in New Issue