Add work request/stake events and deployed to sepolia
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 42s Details

This commit is contained in:
Ladd Hoffman 2024-03-14 16:05:02 -05:00
parent e6599ec1f1
commit 40fbe2e18f
5 changed files with 168 additions and 23 deletions

View File

@ -10,12 +10,12 @@ import work1Artifact from './assets/Work1.json';
const contracts = {
'0x539': { // Hardhat
DAO: '0x8d914D38dD301FC4606f5aa9fEcF8A76389020d3',
Work1: '0x050C420Cc4995B41217Eba1B54B82Fd5687e9139',
DAO: '0x635F46Ea745a14431B27c5dd5838306Be289B747',
Work1: '0xEAefe601Aad7422307B99be65bbE005aeA966012',
},
'0xaa36a7': { // Sepolia
DAO: '0x8F00038542C87A5eAf18d5938B7723bF2A04A4e4',
Work1: '0x42b79f8d8408c36aD4347ab72f826684440a7a8F',
DAO: '0x38AE4ABD47B10f6660CD70Cc8FF3401341E13d9e',
Work1: '0x358A07B26F4c556140872ecdB69c58e8807E7178',
},
};
@ -152,7 +152,6 @@ function App() {
const promises = [];
dispatchValidationPool({ type: 'refresh' });
for (let i = 0; i < count; i += 1) {
// promises.push(DAOContract.methods.validationPools(i).call());
promises.push(fetchValidationPool(i));
}
await Promise.all(promises);
@ -163,8 +162,15 @@ function App() {
Object.assign(s, {
id: Number(stakeIndex),
currentUserIsWorker: () => s.worker.toLowerCase() === account.toString().toLowerCase(),
timeRemaining: new Date(Number(s.endTime) * 1000) - new Date(),
});
dispatchAvailabilityStake({ type: 'update', item: s });
if (s.timeRemaining > 0) {
setTimeout(() => {
s.timeRemaining = 0;
dispatchAvailabilityStake({ type: 'update', item: s });
}, s.timeRemaining);
}
return s;
};
@ -231,6 +237,12 @@ function App() {
fetchValidationPool(event.returnValues.poolIndex);
});
work1Contract.events.AvailabilityStaked({ fromBlock: 'latest' }).on('data', (event) => {
console.log('event: availability staked', event);
fetchAvailabilityStake(event.returnValues.stakeIndex);
fetchReputation();
});
work1Contract.events.WorkAssigned({ fromBlock: 'latest' }).on('data', (event) => {
console.log('event: work assigned', event);
const r = fetchWorkRequest(event.returnValues.requestIndex);
@ -241,6 +253,11 @@ function App() {
console.log('event: work evidence submitted', event);
fetchWorkRequest(event.returnValues.requestIndex);
});
work1Contract.events.WorkApprovalSubmitted({ fromBlock: 'latest' }).on('data', (event) => {
console.log('event: work approval submitted', event);
fetchWorkRequest(event.returnValues.requestIndex);
});
}, [provider, account, chainId, balance, setReputation, dispatchAvailabilityStake,
dispatchValidationPool, dispatchWorkRequest]);
@ -313,8 +330,7 @@ function App() {
});
}, [DAO, account]);
const stakeAvailability = useCallback(async () => {
const duration = 300; // 5 minutes
const stakeAvailability = useCallback(async (duration) => {
const target = contracts[chainId].Work1;
await DAO.methods.stakeAvailability(target, reputation, duration).send({
from: account,
@ -324,6 +340,20 @@ function App() {
setReputation(0);
}, [DAO, account, chainId, reputation, setReputation]);
const reclaimAvailabilityStake = useCallback(async (stakeIndex) => {
await work1.methods.reclaimAvailability(stakeIndex).send({
from: account,
gas: 1000000,
});
}, [work1, account]);
const extendAvailabilityStake = useCallback(async (stakeIndex, duration) => {
await work1.methods.extendAvailability(stakeIndex, duration).send({
from: account,
gas: 1000000,
});
}, [work1, account]);
const requestWork = useCallback(async () => {
const web3 = new Web3(provider);
const priceWei = BigInt(web3.utils.toWei(work1Price, 'ether'));
@ -482,13 +512,19 @@ function App() {
<div>
<h2>Work Contract 1</h2>
<div>
Price:
{work1Price}
{' '}
ETH
{`Price: ${work1Price} ETH`}
</div>
<div>
<Button onClick={() => stakeAvailability()}>Stake Availability</Button>
Stake Availability:
{' '}
{!reputation && <>No reputation available to stake</>}
{reputation > 0 && (
<>
<Button onClick={() => stakeAvailability(300)}>5 Min.</Button>
{' '}
<Button onClick={() => stakeAvailability(7200)}>2 Hr.</Button>
</>
)}
</div>
<div>
Availability Stake Count:
@ -505,6 +541,7 @@ function App() {
<th>End Time</th>
<th>Assigned</th>
<th>Reclaimed</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@ -516,6 +553,22 @@ function App() {
<td>{new Date(Number(s.endTime) * 1000).toLocaleString()}</td>
<td>{s.assigned.toString()}</td>
<td>{s.reclaimed.toString()}</td>
<td>
{s.currentUserIsWorker() && (
<Button onClick={() => extendAvailabilityStake(s.id, 3600)}>
Extend 1 Hr.
</Button>
)}
{s.currentUserIsWorker() && s.timeRemaining <= 0
&& !s.assigned && !s.reclaimed && (
<>
{' '}
<Button onClick={() => reclaimAvailabilityStake(s.id)}>
Reclaim
</Button>
</>
)}
</td>
</tr>
))}
</tbody>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -39,8 +39,10 @@ contract Work1 is IAcceptAvailability {
// TODO: Make parameters configurable
uint constant POOL_DURATION = 1 days;
event AvailabilityStaked(uint stakeIndex);
event WorkAssigned(address worker, uint requestIndex);
event WorkEvidenceSubmitted(uint requestIndex);
event WorkApprovalSubmitted(uint requestIndex, bool approval);
constructor(DAO dao_, uint price_) {
dao = dao_;
@ -53,10 +55,13 @@ contract Work1 is IAcceptAvailability {
uint256 amount,
uint duration
) external {
AvailabilityStake storage stake = stakes[stakeCount++];
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 {
@ -66,6 +71,7 @@ contract Work1 is IAcceptAvailability {
"Worker can only extend their own availability stake"
);
stake.endTime = block.timestamp + duration;
emit AvailabilityStaked(stakeIndex);
}
function reclaimAvailability(uint stakeIndex) external {
@ -79,8 +85,10 @@ contract Work1 is IAcceptAvailability {
"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
@ -151,6 +159,7 @@ contract Work1 is IAcceptAvailability {
request.approval = approval;
// Make work evidence post
uint postIndex = dao.addPost(stake.worker);
emit WorkApprovalSubmitted(requestIndex, approval);
// Initiate validation pool
request.poolIndex = dao.initiateValidationPool{value: request.fee}(
postIndex,

View File

@ -49,7 +49,7 @@ describe('Work1', () => {
work1 = setup.work1;
account1 = setup.account1;
account2 = setup.account2;
await dao.stakeAvailability(work1.target, 50, STAKE_DURATION);
await expect(dao.stakeAvailability(work1.target, 50, STAKE_DURATION)).to.emit(work1, 'AvailabilityStaked').withArgs(0);
});
it('Should be able to stake availability', async () => {
@ -62,13 +62,24 @@ describe('Work1', () => {
expect(stake.endTime).to.equal(await time.latest() + STAKE_DURATION);
});
it('should not be able to stake availability without reputation value', async () => {
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 work1.reclaimAvailability(0);
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');
});
@ -80,7 +91,7 @@ describe('Work1', () => {
it('should be able to extend the duration of an availability stake before it expires', async () => {
await time.increase(STAKE_DURATION / 2);
await work1.extendAvailability(0, STAKE_DURATION);
await expect(work1.extendAvailability(0, STAKE_DURATION)).to.emit(work1, 'AvailabilityStaked').withArgs(0);
});
it('should be able to extend the duration of an availability stake after it expires', async () => {
@ -107,6 +118,16 @@ describe('Work1', () => {
expect(request.customer).to.equal(account2);
});
it('should not be able to reclaim stake after work is assigned', async () => {
const {
dao, work1, account1, account2,
} = await loadFixture(deploy);
await dao.stakeAvailability(work1.target, 50, STAKE_DURATION);
const requestWork = () => work1.connect(account2).requestWork({ value: WORK1_PRICE });
await expect(requestWork()).to.emit(work1, 'WorkAssigned').withArgs(account1, 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,
@ -129,7 +150,7 @@ describe('Work1', () => {
} = await loadFixture(deploy);
await dao.stakeAvailability(work1.target, 50, STAKE_DURATION);
const requestWork = () => work1.connect(account2).requestWork({ value: WORK1_PRICE });
await time.increase(61);
await time.increase(STAKE_DURATION + 1);
await expect(requestWork()).to.be.revertedWith('No available worker stakes');
});
@ -178,7 +199,9 @@ describe('Work1', () => {
it('should be able to submit work approval', async () => {
await work1.connect(account2).requestWork({ value: WORK1_PRICE });
await work1.submitWorkEvidence(0);
await expect(work1.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
await expect(work1.submitWorkApproval(0, true))
.to.emit(dao, 'ValidationPoolInitiated').withArgs(1)
.to.emit(work1, 'WorkApprovalSubmitted').withArgs(0, true);
const post = await dao.posts(1);
expect(post.author).to.equal(account1);
expect(post.sender).to.equal(work1.target);
@ -187,7 +210,9 @@ describe('Work1', () => {
it('should be able to submit work disapproval', async () => {
await work1.connect(account2).requestWork({ value: WORK1_PRICE });
await work1.submitWorkEvidence(0);
await expect(work1.submitWorkApproval(0, false)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
await expect(work1.submitWorkApproval(0, false))
.to.emit(dao, 'ValidationPoolInitiated').withArgs(1)
.to.emit(work1, 'WorkApprovalSubmitted').withArgs(0, false);
});
it('should not be able to submit work approval/disapproval twice', async () => {