From 55033c718df08f41eb2f77d726d59d916ebbd677 Mon Sep 17 00:00:00 2001 From: Bryan Date: Thu, 31 Aug 2023 11:59:40 -0500 Subject: [PATCH] feat(#373): replace clockwork (#388) * wip * end epoch logic added * combined script * remove unused conditional * Revert prittier changes for better diff --- packages/crons/package.json | 60 +++++++ .../crons/src/end-epoch-and-distribute-hst.ts | 163 ++++++++++++++++++ packages/crons/tsconfig.cjs.json | 7 + packages/crons/tsconfig.esm.json | 8 + packages/crons/tsconfig.json | 50 ++++++ 5 files changed, 288 insertions(+) create mode 100644 packages/crons/package.json create mode 100644 packages/crons/src/end-epoch-and-distribute-hst.ts create mode 100644 packages/crons/tsconfig.cjs.json create mode 100644 packages/crons/tsconfig.esm.json create mode 100644 packages/crons/tsconfig.json diff --git a/packages/crons/package.json b/packages/crons/package.json new file mode 100644 index 000000000..c6904e21f --- /dev/null +++ b/packages/crons/package.json @@ -0,0 +1,60 @@ +{ + "name": "@helium/crons", + "version": "1.0.0", + "description": "scripts to run on a schedule", + "private": true, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/helium/helium-program-library" + }, + "main": "./lib/cjs/index.js", + "module": "./lib/esm/src/index.js", + "types": "./lib/types/src/index.d.ts", + "sideEffects": false, + "files": [ + "lib" + ], + "exports": { + "import": "./lib/esm/src/index.js", + "require": "./lib/cjs/index.js", + "types": "./lib/types/src/index.d.ts" + }, + "scripts": { + "format": "prettier --write \"src/**/*.{ts,tsx}\"", + "precommit": "npx git-format-staged -f 'prettier --ignore-unknown --stdin --stdin-filepath \"{}\"' .", + "clean": "npx shx mkdir -p lib && npx shx rm -rf lib", + "package": "npx shx mkdir -p lib/cjs lib/esm", + "prebuild": "npm run clean && npm run package" + }, + "dependencies": { + "@coral-xyz/anchor": "^0.26.0", + "@helium/fanout-sdk": "^0.2.21", + "@helium/helium-sub-daos-sdk": "^0.2.21", + "@helium/lazy-distributor-sdk": "^0.2.21", + "@helium/mobile-entity-manager-sdk": "^0.2.21", + "@helium/price-oracle-sdk": "^0.2.21", + "@helium/spl-utils": "^0.2.21", + "@helium/treasury-management-sdk": "^0.2.21", + "@solana/spl-token": "^0.3.8", + "@solana/web3.js": "^1.78.4", + "axios": "^1.3.6", + "bn.js": "^5.2.0", + "bs58": "^4.0.1", + "yargs": "^17.7.1" + }, + "devDependencies": { + "@types/bs58": "^4.0.1", + "@types/bn.js": "^5.1.0", + "@types/yargs": "^17.0.24", + "git-format-staged": "^2.1.3", + "ts-loader": "^9.2.3", + "ts-node": "^10.9.1", + "typescript": "^4.8.4", + "yarn": "^1.22.18" + } +} diff --git a/packages/crons/src/end-epoch-and-distribute-hst.ts b/packages/crons/src/end-epoch-and-distribute-hst.ts new file mode 100644 index 000000000..ea4a5f078 --- /dev/null +++ b/packages/crons/src/end-epoch-and-distribute-hst.ts @@ -0,0 +1,163 @@ +import * as anchor from '@coral-xyz/anchor'; +import { fanoutKey, init as initHydra } from '@helium/fanout-sdk'; +import { + EPOCH_LENGTH, + currentEpoch, + daoEpochInfoKey, + daoKey, + init as initDao, + subDaoEpochInfoKey, +} from '@helium/helium-sub-daos-sdk'; +import { HNT_MINT, chunks } from '@helium/spl-utils'; +import { getAccount } from '@solana/spl-token'; +import { ComputeBudgetProgram as CBP, PublicKey } from '@solana/web3.js'; +import BN from 'bn.js'; +import bs58 from 'bs58'; +import os from 'os'; +import yargs from 'yargs/yargs'; + +const FANOUT_NAME = 'HST'; +(async (args: any = process.argv) => { + const yarg = yargs(args).options({ + wallet: { + alias: 'k', + describe: 'Anchor wallet keypair', + default: `${os.homedir()}/.config/solana/id.json`, + }, + url: { + alias: 'u', + default: 'http://127.0.0.1:8899', + describe: 'The solana url', + }, + }); + + const argv = await yarg.argv; + process.env.ANCHOR_WALLET = argv.wallet; + process.env.ANCHOR_PROVIDER_URL = argv.url; + anchor.setProvider(anchor.AnchorProvider.local(argv.url)); + + const provider = anchor.getProvider() as anchor.AnchorProvider; + const heliumSubDaosProgram = await initDao(provider); + const hntMint = HNT_MINT; + const unixNow = new Date().valueOf() / 1000; + const [dao] = daoKey(hntMint); + const subDaos = await heliumSubDaosProgram.account.subDaoV0.all([ + { + memcmp: { + offset: 8, + bytes: bs58.encode(dao.toBuffer()), + }, + }, + ]); + + let targetTs = subDaos.reduce( + (acc, subDao) => BN.min(acc, subDao.account.vehntLastCalculatedTs), + new BN(unixNow) + ); + + while (targetTs.toNumber() < unixNow) { + const epoch = currentEpoch(targetTs); + console.log(epoch.toNumber(), targetTs.toNumber()); + const [daoEpoch] = daoEpochInfoKey(dao, targetTs); + const daoEpochInfo = + await heliumSubDaosProgram.account.daoEpochInfoV0.fetchNullable(daoEpoch); + + if (!daoEpochInfo?.doneCalculatingScores) { + for (const subDao of subDaos) { + const [subDaoEpoch] = subDaoEpochInfoKey(subDao.publicKey, targetTs); + const subDaoEpochInfo = + await heliumSubDaosProgram.account.subDaoEpochInfoV0.fetchNullable( + subDaoEpoch + ); + + if (!subDaoEpochInfo?.doneCalculatingScores) { + try { + await heliumSubDaosProgram.methods + .calculateUtilityScoreV0({ epoch }) + .accounts({ subDao: subDao.publicKey }) + .preInstructions([CBP.setComputeUnitLimit({ units: 350000 })]) + .rpc({ skipPreflight: true }); + } catch (err: any) { + console.log( + `Failed to calculate utility score for ${subDao.account.dntMint.toBase58()}: ${ + err.message + }` + ); + } + } + } + } + + if (!daoEpochInfo?.doneIssuingRewards) { + for (const subDao of subDaos) { + const [subDaoEpoch] = subDaoEpochInfoKey(subDao.publicKey, targetTs); + const subDaoEpochInfo = + await heliumSubDaosProgram.account.subDaoEpochInfoV0.fetchNullable( + subDaoEpoch + ); + + if (!subDaoEpochInfo?.doneIssuingRewards) { + try { + await heliumSubDaosProgram.methods + .issueRewardsV0({ epoch }) + .accounts({ subDao: subDao.publicKey }) + .rpc({ skipPreflight: true }); + } catch (err: any) { + console.log( + `Failed to issue rewards for ${subDao.account.dntMint.toBase58()}: ${ + err.message + }` + ); + } + } + } + } + + if (!daoEpochInfo?.doneIssuingHstPool) { + try { + await heliumSubDaosProgram.methods + .issueHstPoolV0({ epoch }) + .accounts({ dao }) + .rpc({ skipPreflight: true }); + } catch (err: any) { + console.log(`Failed to issue hst pool: ${err.message}`); + } + } + + targetTs = targetTs.add(new BN(EPOCH_LENGTH)); + } + + const hydraProgram = await initHydra(provider); + const [fanoutK] = fanoutKey(FANOUT_NAME); + const members = (await hydraProgram.account.fanoutVoucherV0.all()).filter( + (m) => m.account.fanout.equals(fanoutK) + ); + + await Promise.all( + chunks(members, 100).map(async (chunk) => { + await Promise.all( + chunk.map(async (member) => { + const mint = member.account.mint; + const owners = await provider.connection.getTokenLargestAccounts( + mint + ); + const owner = ( + await getAccount(provider.connection, owners.value[0].address) + ).owner; + + console.log('Distributing for mint', mint.toBase58()); + + await hydraProgram.methods + .distributeV0() + .accounts({ + payer: provider.wallet.publicKey, + fanout: fanoutK, + owner, + mint, + }) + .rpc({ skipPreflight: true }); + }) + ); + }) + ); +})(); diff --git a/packages/crons/tsconfig.cjs.json b/packages/crons/tsconfig.cjs.json new file mode 100644 index 000000000..5445b9909 --- /dev/null +++ b/packages/crons/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.cjs.json", + "include": ["src"], + "compilerOptions": { + "outDir": "lib/cjs" + } +} diff --git a/packages/crons/tsconfig.esm.json b/packages/crons/tsconfig.esm.json new file mode 100644 index 000000000..f2835b2e5 --- /dev/null +++ b/packages/crons/tsconfig.esm.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.esm.json", + "include": ["src"], + "compilerOptions": { + "outDir": "lib/esm", + "declarationDir": "lib/types" + } +} \ No newline at end of file diff --git a/packages/crons/tsconfig.json b/packages/crons/tsconfig.json new file mode 100644 index 000000000..3c77a27bd --- /dev/null +++ b/packages/crons/tsconfig.json @@ -0,0 +1,50 @@ +{ + "extends": "../../tsconfig.root.json", + "references": [ + { + "path": "../spl-utils" + }, + { + "path": "../idls" + }, + { + "path": "../fanout-sdk" + }, + { + "path": "../distributor-oracle" + }, + { + "path": "../data-credits-sdk" + }, + { + "path": "../circuit-breaker-sdk" + }, + { + "path": "../helium-sub-daos-sdk" + }, + { + "path": "../lazy-distributor-sdk" + }, + { + "path": "../helium-entity-manager-sdk" + }, + { + "path": "../mobile-entity-manager-sdk" + }, + { + "path": "../treasury-management-sdk" + }, + { + "path": "../price-oracle-sdk" + }, + { + "path": "../rewards-oracle-sdk" + }, + { + "path": "./tsconfig.cjs.json" + }, + { + "path": "./tsconfig.esm.json" + } + ] +}