diff --git a/packages/crons/src/end-epoch.ts b/packages/crons/src/end-epoch.ts index 3c82f4892..4aed80d2e 100644 --- a/packages/crons/src/end-epoch.ts +++ b/packages/crons/src/end-epoch.ts @@ -270,72 +270,78 @@ async function getSolanaUnixTimestamp(connection: Connection): Promise { const hemProgram = await initHem(provider); const lazyProgram = await initLazy(provider); const rewardsOracleProgram = await initRewards(provider); - const [lazyDistributor] = lazyDistributorKey(iotMint); - const [keyToAsset] = keyToAssetKey(dao, IOT_OPERATIONS_FUND, "utf8"); - const assetId = (await hemProgram.account.keyToAssetV0.fetch(keyToAsset)) - .asset; - - const [recipient] = recipientKey(lazyDistributor, assetId); - if (!(await provider.connection.getAccountInfo(recipient))) { - const method = lazyProgram.methods.initializeRecipientV0().accounts({ - lazyDistributor, - mint: assetId, - }); - - await sendInstructionsWithPriorityFee(provider, [await method.instruction()], { - basePriorityFee: BASE_PRIORITY_FEE - }); - } + for (const token of [IOT_MINT, HNT_MINT]) { + const [lazyDistributor] = lazyDistributorKey(token); + const [keyToAsset] = keyToAssetKey(dao, IOT_OPERATIONS_FUND, "utf8"); + const assetId = (await hemProgram.account.keyToAssetV0.fetch(keyToAsset)) + .asset; + + const [recipient] = recipientKey(lazyDistributor, assetId); + if (!(await provider.connection.getAccountInfo(recipient))) { + const method = lazyProgram.methods.initializeRecipientV0().accounts({ + lazyDistributor, + mint: assetId, + }); + + await sendInstructionsWithPriorityFee( + provider, + [await method.instruction()], + { + basePriorityFee: BASE_PRIORITY_FEE, + } + ); + } - const rewards = await client.getCurrentRewards( - lazyProgram, - lazyDistributor, - assetId - ); + const rewards = await client.getCurrentRewards( + lazyProgram, + lazyDistributor, + assetId + ); - const pending = await client.getPendingRewards( - lazyProgram, - lazyDistributor, - daoKey(HNT_MINT)[0], - [IOT_OPERATIONS_FUND], - "utf8" - ); + const pending = await client.getPendingRewards( + lazyProgram, + lazyDistributor, + daoKey(HNT_MINT)[0], + [IOT_OPERATIONS_FUND], + "utf8" + ); - // Avoid claiming too much and tripping the breaker - if (new BN(pending[IOT_OPERATIONS_FUND]).gt(MAX_CLAIM_AMOUNT)) { - rewards[0].currentRewards = new BN(rewards[0].currentRewards) - .sub(new BN(pending[IOT_OPERATIONS_FUND])) - .add(MAX_CLAIM_AMOUNT) - .toString(); - } + // Avoid claiming too much and tripping the breaker + if (new BN(pending[IOT_OPERATIONS_FUND]).gt(MAX_CLAIM_AMOUNT)) { + rewards[0].currentRewards = new BN(rewards[0].currentRewards) + .sub(new BN(pending[IOT_OPERATIONS_FUND])) + .add(MAX_CLAIM_AMOUNT) + .toString(); + } - const tx = await client.formTransaction({ - program: lazyProgram, - rewardsOracleProgram: rewardsOracleProgram, - provider, - rewards, - asset: assetId, - lazyDistributor, - }); + const tx = await client.formTransaction({ + program: lazyProgram, + rewardsOracleProgram: rewardsOracleProgram, + provider, + rewards, + asset: assetId, + lazyDistributor, + }); - const signed = await provider.wallet.signTransaction(tx); + const signed = await provider.wallet.signTransaction(tx); - try { - await sendAndConfirmWithRetry( - provider.connection, - Buffer.from(signed.serialize()), - { skipPreflight: true }, - "confirmed" - ); - } catch (err: any) { - errors.push(`Failed to distribute iot op funds: ${err}`); + try { + await sendAndConfirmWithRetry( + provider.connection, + Buffer.from(signed.serialize()), + { skipPreflight: true }, + "confirmed" + ); + } catch (err: any) { + errors.push(`Failed to distribute iot op funds: ${err}`); + } } // Only do this if that feature has been deployed if (hemProgram.methods.issueNotEmittedEntityV0) { console.log("Issuing no_emit"); const noEmitProgram = await initBurn(provider); - const tokens = [MOBILE_MINT, IOT_MINT]; + const tokens = [MOBILE_MINT, IOT_MINT, HNT_MINT]; for (const token of tokens) { const [lazyDistributor] = lazyDistributorKey(token); const notEmittedEntityKta = keyToAssetKey(dao, NOT_EMITTED, "utf-8")[0]; diff --git a/packages/helium-admin-cli/emissions/hst.json b/packages/helium-admin-cli/emissions/hst.json index d21b1f5b0..d754cf152 100644 --- a/packages/helium-admin-cli/emissions/hst.json +++ b/packages/helium-admin-cli/emissions/hst.json @@ -1,14 +1,10 @@ [ - { - "startTime": "2023-04-18T00:00:00Z", - "percent": 32 - }, - { - "startTime": "2023-08-01T00:00:00Z", - "percent": 31 - }, { "startTime": "2024-08-01T00:00:01Z", "percent": 30 + }, + { + "startTime": "2025-08-01T00:00:01Z", + "percent": 0 } ] diff --git a/packages/helium-admin-cli/src/create-subdao.ts b/packages/helium-admin-cli/src/create-subdao.ts index debcc5fb6..159e0b104 100644 --- a/packages/helium-admin-cli/src/create-subdao.ts +++ b/packages/helium-admin-cli/src/create-subdao.ts @@ -523,6 +523,7 @@ export async function run(args: any = process.argv) { registrar: null, delegatorRewardsPercent: null, activeDeviceAuthority: null, + rewardsEscrow: null }) .accounts({ subDao, diff --git a/packages/helium-admin-cli/src/initialize-vetoken-tracking.ts b/packages/helium-admin-cli/src/initialize-vetoken-tracking.ts index 71284073b..93072021d 100644 --- a/packages/helium-admin-cli/src/initialize-vetoken-tracking.ts +++ b/packages/helium-admin-cli/src/initialize-vetoken-tracking.ts @@ -142,6 +142,7 @@ export async function run(args: any = process.argv) { registrar: null, delegatorRewardsPercent: null, activeDeviceAuthority: null, + rewardsEscrow: null, }) .accounts({ subDao, diff --git a/packages/helium-admin-cli/src/migrate-to-hip-138.ts b/packages/helium-admin-cli/src/migrate-to-hip-138.ts new file mode 100644 index 000000000..b5cac9291 --- /dev/null +++ b/packages/helium-admin-cli/src/migrate-to-hip-138.ts @@ -0,0 +1,196 @@ +import * as anchor from "@coral-xyz/anchor"; +import { init as initLazy } from "@helium/lazy-distributor-sdk"; +import { + daoKey, + init as initHsd, + subDaoKey, +} from "@helium/helium-sub-daos-sdk"; +import { HNT_MINT, IOT_MINT, MOBILE_MINT } from "@helium/spl-utils"; +import { PublicKey, TransactionInstruction } from "@solana/web3.js"; +import Squads from "@sqds/sdk"; +import os from "os"; +import yargs from "yargs/yargs"; +import { loadKeypair, parseEmissionsSchedule, sendInstructionsOrSquads } from "./utils"; +import { lazyDistributorKey } from "@helium/lazy-distributor-sdk"; +import { ThresholdType } from "@helium/circuit-breaker-sdk"; +import { oracleSignerKey } from "@helium/rewards-oracle-sdk"; +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; + +export async function run(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", + }, + iotMint: { + type: "string", + describe: "IOT mint of the subdao to migrate", + default: IOT_MINT.toBase58(), + }, + hntMint: { + type: "string", + describe: "HNT mint of the subdao to migrate", + default: HNT_MINT.toBase58(), + }, + mobileMint: { + type: "string", + describe: "Mobile mint of the subdao to migrate", + default: MOBILE_MINT.toBase58(), + }, + rewardsOracleUrl: { + alias: "ro", + type: "string", + describe: "The rewards oracle URL", + required: true, + }, + oracleKey: { + type: "string", + describe: "Pubkey of the oracle", + required: true, + }, + emissionSchedulePath: { + required: true, + describe: "Path to file that contains the hnt emissions schedule", + type: "string", + }, + hstEmissionsSchedulePath: { + required: true, + describe: "Path to file that contains the new HST emissions schedule", + type: "string", + }, + executeTransaction: { + type: "boolean", + }, + multisig: { + type: "string", + describe: + "Address of the squads multisig to be authority. If not provided, your wallet will be the authority", + }, + authorityIndex: { + type: "number", + describe: "Authority index for squads. Defaults to 1", + default: 1, + }, + }); + 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 wallet = new anchor.Wallet(loadKeypair(argv.wallet)); + const lazyDistProgram = await initLazy(provider); + const hsdProgram = await initHsd(provider); + + const instructions: TransactionInstruction[] = []; + + const iotMint = new PublicKey(argv.iotMint); + const mobileMint = new PublicKey(argv.mobileMint); + const hntMint = new PublicKey(argv.hntMint); + const iotSubDao = subDaoKey(iotMint)[0]; + const mobileSubDao = subDaoKey(mobileMint)[0]; + const dao = daoKey(hntMint)[0]; + const daoAcc = await hsdProgram.account.daoV0.fetch(dao); + const authority = daoAcc.authority; + const oracleKey = new PublicKey(argv.oracleKey!); + const emissionSchedule = await parseEmissionsSchedule( + argv.emissionSchedulePath + ); + + const ld = lazyDistributorKey(hntMint)[0]; + const rewardsEscrow = getAssociatedTokenAddressSync(hntMint, ld, true); + const ldAcc = await lazyDistProgram.account.lazyDistributorV0.fetchNullable(ld); + if (ldAcc) { + console.warn("Lazy distributor already exists, skipping."); + } else { + instructions.push( + await lazyDistProgram.methods + .initializeLazyDistributorV0({ + authority: daoAcc.authority, + oracles: [ + { + oracle: oracleKey, + url: argv.rewardsOracleUrl, + }, + ], + // 5 x epoch rewards in a 24 hour period + windowConfig: { + windowSizeSeconds: new anchor.BN(24 * 60 * 60), + thresholdType: ThresholdType.Absolute as never, + threshold: new anchor.BN(emissionSchedule[0].emissionsPerEpoch).mul( + new anchor.BN(5) + ), + }, + approver: oracleSignerKey()[0], + }) + .accounts({ + payer: authority, + rewardsMint: hntMint, + }) + .instruction() + ); + } + + for (const subDao of [iotSubDao, mobileSubDao]) { + instructions.push( + await hsdProgram.methods + .updateSubDaoV0({ + vetokenTracker: null, + votingRewardsPercent: null, + authority: null, + emissionSchedule: null, + dcBurnAuthority: null, + onboardingDcFee: null, + onboardingDataOnlyDcFee: null, + registrar: null, + delegatorRewardsPercent: null, + activeDeviceAuthority: null, + rewardsEscrow, + }) + .accounts({ + payer: authority, + authority, + subDao, + }) + .instruction() + ); + } + + instructions.push( + await hsdProgram.methods + .updateDaoV0({ + authority: null, + emissionSchedule: null, + hstEmissionSchedule: await parseEmissionsSchedule( + argv.hstEmissionsSchedulePath! + ), + netEmissionsCap: null, + hstPool: null, + }) + .accounts({ + dao, + authority: daoAcc.authority, + payer: daoAcc.authority, + }) + .instruction() + ); + + const squads = Squads.endpoint(process.env.ANCHOR_PROVIDER_URL, wallet, { + commitmentOrConfig: "finalized", + }); + + await sendInstructionsOrSquads({ + provider, + instructions, + executeTransaction: argv.executeTransaction, + squads, + multisig: argv.multisig ? new PublicKey(argv.multisig) : undefined, + authorityIndex: argv.authorityIndex, + signers: [], + }); +} diff --git a/packages/helium-admin-cli/src/update-subdao.ts b/packages/helium-admin-cli/src/update-subdao.ts index 1f21c382a..639ad4c78 100644 --- a/packages/helium-admin-cli/src/update-subdao.ts +++ b/packages/helium-admin-cli/src/update-subdao.ts @@ -205,6 +205,7 @@ export async function run(args: any = process.argv) { instructions.push( await program.methods .updateSubDaoV0({ + rewardsEscrow: null, vetokenTracker: null, votingRewardsPercent: null, authority: argv.newAuthority ? new PublicKey(argv.newAuthority) : null, diff --git a/packages/monitor-service/src/index.ts b/packages/monitor-service/src/index.ts index b6807c415..e85b6a516 100644 --- a/packages/monitor-service/src/index.ts +++ b/packages/monitor-service/src/index.ts @@ -110,7 +110,6 @@ async function run() { const mobileMint = mobile.dntMint; const mobileTreasury = mobile.treasury; const mobileRewardsEscrow = mobile.rewardsEscrow; - await Recipient.sync(); await monitorVehnt(); @@ -127,8 +126,11 @@ async function run() { await monitorTokenBalance(mobileTreasury, "mobile_treasury"); await setTotalRewards(IOT_MINT); await setTotalRewards(MOBILE_MINT); + await setTotalRewards(HNT_MINT); + const resetMobileTotal = debounce(() => setTotalRewards(MOBILE_MINT)); const resetIotTotal = debounce(() => setTotalRewards(IOT_MINT)); + const resetHntTotal = debounce(() => setTotalRewards(HNT_MINT)); await monitorTokenBalance( iotRewardsEscrow, "iot_rewards_escrow", @@ -145,6 +147,14 @@ async function run() { resetMobileTotal(); } ); + await monitorTokenBalance( + getAssociatedTokenAddressSync(hntMint, lazyDistributorKey(hntMint)[0], true), + "hnt_rewards_escrow", + false, + async () => { + resetHntTotal(); + } + ); await monitorTokenBalance( getAssociatedTokenAddressSync(dao.dcMint, iot.activeDeviceAuthority), "iot_active_device_oracle_dc" diff --git a/scripts/init-idls.sh b/scripts/init-idls.sh index 236980427..e6966140c 100755 --- a/scripts/init-idls.sh +++ b/scripts/init-idls.sh @@ -14,7 +14,7 @@ for program in $program_list; do cluster="${1:-localnet}" if [ -n "$id" ]; then - anchor_command="anchor idl init ${id} --filepath ${filepath} --provider.cluster ${cluster} --provider.wallet $HOME/.config/solana/id.json" + anchor_command="anchor28 idl init ${id} --filepath ${filepath} --provider.cluster ${cluster} --provider.wallet $HOME/.config/solana/id.json" echo "Running command: $anchor_command" # Run the anchor idl init command in the background and store the PID diff --git a/scripts/upgrade-idls.sh b/scripts/upgrade-idls.sh index 5d21ccd02..62f48c6d0 100755 --- a/scripts/upgrade-idls.sh +++ b/scripts/upgrade-idls.sh @@ -14,7 +14,7 @@ for program in $program_list; do cluster="${1:-localnet}" if [ -n "$id" ]; then - anchor_command="anchor idl upgrade ${id} --filepath ${filepath} --provider.cluster ${cluster} --provider.wallet $HOME/.config/solana/id.json" + anchor_command="anchor28 idl upgrade ${id} --filepath ${filepath} --provider.cluster ${cluster} --provider.wallet $HOME/.config/solana/id.json" echo "Running command: $anchor_command" # Run the anchor idl init command in the background and store the PID diff --git a/tests/helium-entity-manager.ts b/tests/helium-entity-manager.ts index f5da17a01..f7071c654 100644 --- a/tests/helium-entity-manager.ts +++ b/tests/helium-entity-manager.ts @@ -894,6 +894,7 @@ describe("helium-entity-manager", () => { await hsdProgram.methods .updateSubDaoV0({ + rewardsEscrow: null, vetokenTracker: null, votingRewardsPercent: null, authority: null, diff --git a/tests/helium-sub-daos.ts b/tests/helium-sub-daos.ts index 8b334e278..cfc391556 100644 --- a/tests/helium-sub-daos.ts +++ b/tests/helium-sub-daos.ts @@ -281,6 +281,7 @@ describe("helium-sub-daos", () => { vetokenTracker = tracker!; await program.methods .updateSubDaoV0({ + rewardsEscrow: null, vetokenTracker, votingRewardsPercent: delegatorRewardsPercent(2), authority: null, @@ -323,6 +324,7 @@ describe("helium-sub-daos", () => { const newAuth = Keypair.generate().publicKey; await program.methods .updateSubDaoV0({ + rewardsEscrow: null, vetokenTracker: null, votingRewardsPercent: null, authority: newAuth,