add automatic staking script
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled Details

This commit is contained in:
Ladd Hoffman 2024-03-16 13:53:07 -05:00
parent 6df1ad4c70
commit eaa81c17ec
8 changed files with 140 additions and 39 deletions

1
ethereum/.env.example Normal file
View File

@ -0,0 +1 @@
SEPOLIA_PRIVATE_KEY=

View File

@ -33,11 +33,6 @@ struct ValidationPool {
bool outcome;
}
struct StakeData {
uint poolIndex;
bool inFavor;
}
/// This contract must manage validation pools and reputation,
/// because otherwise there's no way to enforce appropriate permissions on
/// transfer of value between reputation NFTs.

View File

@ -1,4 +1,5 @@
require('@nomicfoundation/hardhat-toolbox');
require('@nomicfoundation/hardhat-verify');
require('dotenv').config();
/** @type import('hardhat/config').HardhatUserConfig */
@ -16,4 +17,12 @@ module.exports = {
accounts: [process.env.SEPOLIA_PRIVATE_KEY],
},
},
etherscan: {
apiKey: {
sepolia: process.env.ETHERSCAN_API_KEY,
},
},
sourcify: {
enabled: false,
},
};

View File

@ -15,6 +15,7 @@
"devDependencies": {
"@nomicfoundation/hardhat-chai-matchers": "^2.0.6",
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.5",
"chai": "^4.4.1",
"eslint": "^8.57.0",
"eslint-config-airbnb": "^19.0.4",
@ -1693,11 +1694,10 @@
}
},
"node_modules/@nomicfoundation/hardhat-verify": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.4.tgz",
"integrity": "sha512-B8ZjhOrmbbRWqJi65jvQblzjsfYktjqj2vmOm+oc2Vu8drZbT2cjeSCRHZKbS7lOtfW78aJZSFvw+zRLCiABJA==",
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.5.tgz",
"integrity": "sha512-Tg4zu8RkWpyADSFIgF4FlJIUEI4VkxcvELsmbJn2OokbvH2SnUrqKmw0BBfDrtvP0hhmx8wsnrRKP5DV/oTyTA==",
"dev": true,
"peer": true,
"dependencies": {
"@ethersproject/abi": "^5.1.2",
"@ethersproject/address": "^5.0.2",
@ -2389,7 +2389,6 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"dev": true,
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@ -2740,7 +2739,6 @@
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
"dev": true,
"peer": true,
"engines": {
"node": ">=8"
}
@ -3120,7 +3118,6 @@
"resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz",
"integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==",
"dev": true,
"peer": true,
"dependencies": {
"nofilter": "^3.1.0"
},
@ -6396,8 +6393,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
@ -6556,8 +6552,7 @@
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
@ -6576,8 +6571,7 @@
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
"integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/log-symbols": {
"version": "4.1.0",
@ -7118,7 +7112,6 @@
"resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz",
"integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==",
"dev": true,
"peer": true,
"engines": {
"node": ">=12.19"
}
@ -8286,7 +8279,6 @@
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
"integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
"dev": true,
"peer": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"astral-regex": "^2.0.0",
@ -8304,7 +8296,6 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"peer": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@ -8320,7 +8311,6 @@
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"peer": true,
"dependencies": {
"color-name": "~1.1.4"
},
@ -8332,8 +8322,7 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/solc": {
"version": "0.7.3",
@ -8751,7 +8740,6 @@
"resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz",
"integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==",
"dev": true,
"peer": true,
"dependencies": {
"ajv": "^8.0.1",
"lodash.truncate": "^4.4.2",

View File

@ -4,13 +4,16 @@
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "hardhat test",
"automatic-staking-local": "hardhat run --network localhost scripts/automatic-staking.js localhost",
"automatic-staking-sepolia": "hardhat run --network sepolia scripts/automatic-staking.js sepolia"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@nomicfoundation/hardhat-chai-matchers": "^2.0.6",
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.5",
"chai": "^4.4.1",
"eslint": "^8.57.0",
"eslint-config-airbnb": "^19.0.4",

View File

@ -0,0 +1,111 @@
const { ethers } = require('hardhat');
const DAOAddress = {
localhost: '0x76Dfe9F47f06112a1b78960bf37d87CfbB6D6133',
sepolia: '0x39B7522Ee1A5B13aE5580C40114239D4cE0e7D29',
};
let dao;
let account;
let validationPools;
let reputation;
const fetchReputation = async () => {
reputation = await dao.balanceOf(account);
console.log(`reputation: ${reputation}`);
};
const fetchValidationPool = async (poolIndex) => {
const pool = await dao.validationPools(poolIndex);
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);
};
const initialize = async () => {
const network = process.argv[2];
if (!network) {
throw new Error('please provide network name as first argument');
}
if (!DAOAddress[network]) {
throw new Error(`network '${network}' is unknown`);
}
dao = await ethers.getContractAt('DAO', DAOAddress);
[account] = await ethers.getSigners();
const address = await account.getAddress();
console.log(`account: ${address}`);
await fetchReputation();
await fetchValidationPools();
};
const poolIsActive = (pool) => {
if (new Date() >= new Date(Number(pool.endTime) * 1000)) return false;
if (pool.resolved) return false;
return true;
};
const stake = async (pool, amount) => {
console.log(`staking ${amount} in favor of pool ${pool.id.toString()}`);
await dao.stake(pool.id, amount, true);
await fetchReputation();
};
const stakeEach = async (pools, amountPerPool) => {
const promises = [];
pools.forEach((pool) => {
promises.push(stake(pool, amountPerPool));
});
await Promise.all(promises);
};
async function main() {
await initialize();
validationPools.forEach((pool) => {
let status;
if (poolIsActive(pool)) status = 'Active';
else if (!pool.resolved) status = 'Ready to Evaluate';
else if (pool.outcome) status = 'Accepted';
else status = 'Rejected';
console.log(`pool ${pool.id.toString()}, status: ${status}`);
});
// Stake half of available reputation on any active validation pools
const activePools = validationPools.filter(poolIsActive);
if (activePools.length && reputation > 0) {
const amountPerPool = reputation / BigInt(2) / BigInt(activePools.length);
await stakeEach(activePools, amountPerPool);
}
// Listen for new validation pools
dao.on('ValidationPoolInitiated', async (poolIndex) => {
console.log(`pool ${poolIndex} started`);
await fetchValidationPool(poolIndex);
await fetchReputation();
// Stake half of available reputation on this validation pool
const amount = reputation / BigInt(2);
await stake(poolIndex, amount, true);
});
dao.on('ValidationPoolResolved', async (poolIndex, votePasses) => {
console.log(`pool ${poolIndex} resolved, status: ${votePasses ? 'accepted' : 'rejected'}`);
fetchValidationPool(poolIndex);
fetchReputation();
});
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

View File

@ -44,9 +44,7 @@ describe('DAO', () => {
const POOL_FEE = 100;
beforeEach(async () => {
const setup = await loadFixture(deploy);
dao = setup.dao;
account1 = setup.account1;
({ dao, account1 } = await loadFixture(deploy));
await dao.addPost(account1);
const init = () => dao.initiateValidationPool(0, POOL_DURATION, { value: POOL_FEE });
await expect(init()).to.emit(dao, 'ValidationPoolInitiated').withArgs(0);

View File

@ -44,11 +44,9 @@ describe('Work1', () => {
let account2;
beforeEach(async () => {
const setup = await loadFixture(deploy);
dao = setup.dao;
work1 = setup.work1;
account1 = setup.account1;
account2 = setup.account2;
({
dao, work1, account1, account2,
} = await loadFixture(deploy));
await expect(dao.stakeAvailability(work1.target, 50, STAKE_DURATION)).to.emit(work1, 'AvailabilityStaked').withArgs(0);
});
@ -204,11 +202,9 @@ describe('Work1', () => {
let account2;
beforeEach(async () => {
const setup = await loadFixture(deploy);
dao = setup.dao;
work1 = setup.work1;
account1 = setup.account1;
account2 = setup.account2;
({
dao, work1, account1, account2,
} = await loadFixture(deploy));
await dao.stakeAvailability(work1.target, 50, STAKE_DURATION);
});