dgf-prototype/ethereum/scripts/automatic-staking.js

224 lines
6.5 KiB
JavaScript
Raw Normal View History

2024-03-16 13:53:07 -05:00
const { ethers } = require('hardhat');
2024-04-09 05:09:27 -05:00
const { execSync } = require('child_process');
2024-03-20 16:30:27 -05:00
const { getContractAddressByNetworkName } = require('./contract-config');
const readFromApi = require('./util/read-from-api');
2024-03-16 13:53:07 -05:00
const network = process.env.HARDHAT_NETWORK;
2024-04-09 05:09:27 -05:00
let currentVersionProposalId;
2024-03-16 13:53:07 -05:00
let dao;
2024-03-20 16:30:27 -05:00
let work1;
let onboarding;
2024-03-16 13:53:07 -05:00
let account;
let validationPools;
let reputation;
let posts;
2024-04-09 05:09:27 -05:00
let proposalsContract;
let proposals;
const getCurrentVersion = () => {
const currentCommit = execSync('git rev-parse HEAD');
return currentCommit.toString();
};
const fetchCurrentVersionProposal = async () => {
2024-04-20 12:41:21 -05:00
// const p = await proposalsContract.
2024-04-09 05:09:27 -05:00
};
const getLatestVersion = () => {
const latestVersion = 'TBD';
return latestVersion;
};
2024-03-16 13:53:07 -05:00
const fetchReputation = async () => {
reputation = await dao.balanceOf(account);
console.log(`reputation: ${reputation}`);
};
const fetchPost = async (postIndex) => {
const {
id, sender, author, contentId,
} = await dao.posts(postIndex);
const { content, embeddedData } = await readFromApi(contentId);
const post = {
id,
sender,
author,
contentId,
content,
embeddedData,
};
posts[postIndex] = post;
return post;
};
2024-03-16 13:53:07 -05:00
const fetchValidationPool = async (poolIndex) => {
const {
id, postIndex, sender, stakeCount, fee, duration, endTime, resolved, outcome,
} = await dao.validationPools(poolIndex);
const pool = {
id, postIndex, sender, stakeCount, fee, duration, endTime, resolved, outcome,
};
pool.post = await fetchPost(pool.postIndex);
2024-03-16 13:53:07 -05:00
validationPools[poolIndex] = pool;
return pool;
};
const fetchValidationPools = async () => {
const count = await dao.validationPoolCount();
console.log(`validation pool count: ${count}`);
const promises = [];
validationPools = [];
for (let i = 0; i < count; i += 1) {
promises.push(fetchValidationPool(i));
}
await Promise.all(promises);
};
2024-04-09 05:09:27 -05:00
const fetchProposal = async (proposalIndex) => {
const proposal = await proposalsContract.proposals(proposalIndex);
proposals[proposalIndex] = proposal;
};
const fetchProposals = async () => {
const count = await proposalsContract.proposalCount();
console.log(`proposal count: ${count}`);
const promises = [];
proposals = [];
for (let i = 0; i < count; i += 1) {
promises.push(fetchProposal(i));
}
await Promise.all(promises);
};
2024-03-16 13:53:07 -05:00
const initialize = async () => {
2024-03-20 16:30:27 -05:00
const getContract = (name) => ethers.getContractAt(
name,
getContractAddressByNetworkName(network, name),
);
dao = await getContract('DAO');
work1 = await getContract('Work1');
onboarding = await getContract('Onboarding');
2024-04-09 05:09:27 -05:00
proposalsContract = await getContract('Proposals');
2024-03-16 13:53:07 -05:00
[account] = await ethers.getSigners();
const address = await account.getAddress();
console.log(`account: ${address}`);
posts = [];
2024-03-16 13:53:07 -05:00
await fetchReputation();
await fetchValidationPools();
2024-04-09 05:09:27 -05:00
await fetchProposals();
2024-03-16 13:53:07 -05:00
};
const poolIsActive = (pool) => {
if (new Date() >= new Date(Number(pool.endTime) * 1000)) return false;
if (pool.resolved) return false;
return true;
};
const poolIsValidWorkContract = (pool) => {
switch (pool.sender) {
case getContractAddressByNetworkName(network, 'Work1'): {
// If this is a valid work evidence
// TODO: Can we decode from the post, a reference to the work request?
// The work request does have its own contentId, the work contract has that
// under availabilityStakes
const expectedContent = 'This is a work evidence post';
return pool.post.content.startsWith(expectedContent);
}
case getContractAddressByNetworkName(network, 'Onboarding'): {
const expectedContent = 'This is an onboarding work evidence post';
return pool.post.content.startsWith(expectedContent);
}
default:
return false;
}
};
2024-03-18 14:03:53 -05:00
const poolIsProposal = (pool) => pool.sender === getContractAddressByNetworkName(network, 'Proposals');
2024-03-18 14:03:53 -05:00
const getPoolStatus = (pool) => {
if (poolIsActive(pool)) return 'Active';
if (!pool.resolved) return 'Ready to Evaluate';
if (pool.outcome) return 'Accepted';
return 'Rejected';
};
2024-03-18 14:03:53 -05:00
const stake = async (pool, amount, inFavor) => {
console.log(`staking ${amount} ${inFavor ? 'in favor of' : 'against'} pool ${pool.id.toString()}`);
await dao.stakeOnValidationPool(pool.id, amount, true);
2024-03-16 13:53:07 -05:00
await fetchReputation();
};
const conditionalStake = async (pool, amountToStake) => {
if (poolIsValidWorkContract(pool)) {
await stake(pool, amountToStake, true);
} else if (poolIsProposal(pool)) {
// We leave these alone at the moment.
// We could consider automatic followup staking,
// as a convenience if you decide early to favor a proposal
} else {
await stake(pool, amountToStake, false);
}
};
const conditionalStakeEach = async (pools, amountPerPool) => {
2024-03-16 13:53:07 -05:00
const promises = [];
pools.forEach(async (pool) => {
promises.push(conditionalStake(pool, amountPerPool));
2024-03-16 13:53:07 -05:00
});
await Promise.all(promises);
};
const printPool = (pool) => {
const dataStr = pool.post?.embeddedData ? `data: ${JSON.stringify(pool.post.embeddedData)},\n ` : '';
console.log(`pool ${pool.id.toString()}\n `
+ `status: ${getPoolStatus(pool)},\n `
+ `is valid work contract: ${poolIsValidWorkContract(pool)},\n `
+ `is proposal: ${poolIsProposal(pool)},\n `
+ `${dataStr}post content: ${pool.post?.content}`);
};
async function main() {
2024-04-09 05:09:27 -05:00
console.log('Current version:', getCurrentVersion());
await initialize();
validationPools.forEach(printPool);
2024-03-16 13:53:07 -05:00
2024-03-18 14:03:53 -05:00
// Stake half of available reputation on any active pools
const activePools = validationPools.filter(poolIsActive);
if (activePools.length && reputation > 0) {
const amountPerPool = reputation / BigInt(2) / BigInt(activePools.length);
await conditionalStakeEach(activePools, amountPerPool);
2024-03-16 13:53:07 -05:00
}
// Listen for new validation pools
dao.on('ValidationPoolInitiated', async (poolIndex) => {
2024-03-17 13:24:49 -05:00
const pool = await fetchValidationPool(poolIndex);
printPool(pool);
2024-03-16 13:53:07 -05:00
await fetchReputation();
2024-03-18 14:03:53 -05:00
if (!reputation) return;
const amountToStake = reputation / BigInt(2);
await conditionalStake(pool, amountToStake);
2024-03-16 13:53:07 -05:00
});
dao.on('ValidationPoolResolved', async (poolIndex, votePasses) => {
console.log(`pool ${poolIndex} resolved, status: ${votePasses ? 'accepted' : 'rejected'}`);
fetchValidationPool(poolIndex);
fetchReputation();
});
2024-03-20 16:30:27 -05:00
work1.on('AvailabilityStaked', async () => {
fetchReputation();
});
onboarding.on('AvailabilityStaked', async () => {
fetchReputation();
});
2024-03-16 13:53:07 -05:00
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});