Skip to content

Commit

Permalink
Add priority fees to cron jobs (#529)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChewingGlass authored Dec 27, 2023
1 parent 72b8da5 commit 47e142e
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 51 deletions.
27 changes: 9 additions & 18 deletions packages/crons/src/close-governance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import {
proposalKey,
} from "@helium/organization-sdk";
import { init as initProposal } from "@helium/proposal-sdk";
import { bulkSendTransactions } from "@helium/spl-utils";
import {
bulkSendTransactions,
batchParallelInstructionsWithPriorityFee,
} from "@helium/spl-utils";
import { init as initState } from "@helium/state-controller-sdk";
import { init as initVsr, positionKey } from "@helium/voter-stake-registry-sdk";
import { SystemProgram, Transaction } from "@solana/web3.js";
Expand Down Expand Up @@ -78,15 +81,10 @@ import pLimit from "p-limit";
closedProposals.add(proposal.pubkey.toBase58());
}
}
const txs = chunks(resolveIxs, 10).map((ixs) => {
const tx = new Transaction({
feePayer: provider.wallet.publicKey,
});
tx.add(...ixs);
return tx;
});

await bulkSendTransactions(provider, txs);
await batchParallelInstructionsWithPriorityFee(
provider,
resolveIxs
);
}

const markers = (await vsrProgram.account.voteMarkerV0.all()).filter(
Expand All @@ -110,14 +108,7 @@ import pLimit from "p-limit";
})
)
);
const txns = chunks(relinquishIxns, 10).map((ixs) => {
const tx = new Transaction({
feePayer: provider.wallet.publicKey,
});
tx.add(...ixs);
return tx;
});
await bulkSendTransactions(provider, txns);
await batchParallelInstructionsWithPriorityFee(provider, relinquishIxns);

process.exit(0);
} catch (err) {
Expand Down
14 changes: 8 additions & 6 deletions packages/crons/src/end-epoch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
createMintInstructions,
sendAndConfirmWithRetry,
sendInstructions,
sendInstructionsWithPriorityFee,
} from "@helium/spl-utils";
import { getAccount } from "@solana/spl-token";
import { ComputeBudgetProgram as CBP, Keypair } from "@solana/web3.js";
Expand Down Expand Up @@ -90,13 +91,14 @@ const MAX_CLAIM_AMOUNT = new BN("207020547945205");

if (!subDaoEpochInfo?.utilityScore) {
try {
await sendInstructions(provider, [
CBP.setComputeUnitLimit({ units: 1000000 }),
await sendInstructionsWithPriorityFee(provider, [
await heliumSubDaosProgram.methods
.calculateUtilityScoreV0({ epoch })
.accounts({ subDao: subDao.publicKey })
.instruction(),
]);
], {
computeUnitLimit: 1000000
});
} catch (err: any) {
const strErr = JSON.stringify(err);

Expand Down Expand Up @@ -130,7 +132,7 @@ const MAX_CLAIM_AMOUNT = new BN("207020547945205");

if (!subDaoEpochInfo?.rewardsIssuedAt) {
try {
await sendInstructions(provider, [
await sendInstructionsWithPriorityFee(provider, [
await heliumSubDaosProgram.methods
.issueRewardsV0({ epoch })
.accounts({ subDao: subDao.publicKey })
Expand All @@ -147,7 +149,7 @@ const MAX_CLAIM_AMOUNT = new BN("207020547945205");

if (!daoEpochInfo?.doneIssuingHstPool) {
try {
await sendInstructions(provider, [
await sendInstructionsWithPriorityFee(provider, [
await heliumSubDaosProgram.methods
.issueHstPoolV0({ epoch })
.accounts({ dao })
Expand Down Expand Up @@ -181,7 +183,7 @@ const MAX_CLAIM_AMOUNT = new BN("207020547945205");
).owner;

try {
await sendInstructions(provider, [
await sendInstructionsWithPriorityFee(provider, [
await hydraProgram.methods
.distributeV0()
.accounts({
Expand Down
52 changes: 25 additions & 27 deletions packages/helium-admin-cli/src/end-epoch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { BN } from "bn.js";
import b58 from "bs58";
import os from "os";
import yargs from "yargs/yargs";
import { sendInstructionsWithPriorityFee } from "@helium/spl-utils";

export async function run(args: any = process.argv) {
const yarg = yargs(args).options({
Expand Down Expand Up @@ -61,17 +62,18 @@ export async function run(args: any = process.argv) {
if (!daoEpochInfo?.doneCalculatingScores) {
for (const subDao of subdaos) {
try {
await heliumSubDaosProgram.methods
.calculateUtilityScoreV0({
epoch,
})
.preInstructions([
ComputeBudgetProgram.setComputeUnitLimit({ units: 500000 }),
])
.accounts({
subDao: subDao.publicKey,
})
.rpc({ skipPreflight: true });
await sendInstructionsWithPriorityFee(
provider,
[
await heliumSubDaosProgram.methods
.calculateUtilityScoreV0({ epoch })
.accounts({ subDao: subDao.publicKey })
.instruction(),
],
{
computeUnitLimit: 1000000,
}
);
} catch (e: any) {
console.log(
`Failed to calculate utility score for ${subDao.account.dntMint.toBase58()}: ${
Expand All @@ -84,14 +86,12 @@ export async function run(args: any = process.argv) {
if (!daoEpochInfo?.doneIssuingRewards) {
for (const subDao of subdaos) {
try {
await heliumSubDaosProgram.methods
.issueRewardsV0({
epoch,
})
.accounts({
subDao: subDao.publicKey,
})
.rpc({ skipPreflight: true });
await sendInstructionsWithPriorityFee(provider, [
await heliumSubDaosProgram.methods
.issueRewardsV0({ epoch })
.accounts({ subDao: subDao.publicKey })
.instruction(),
]);
} catch (e: any) {
console.log(
`Failed to issue rewards for ${subDao.account.dntMint.toBase58()}: ${
Expand All @@ -105,14 +105,12 @@ export async function run(args: any = process.argv) {
}
try {
if (!daoEpochInfo?.doneIssuingHstPool) {
await heliumSubDaosProgram.methods
.issueHstPoolV0({
epoch,
})
.accounts({
dao,
})
.rpc({ skipPreflight: true });
await sendInstructionsWithPriorityFee(provider, [
await heliumSubDaosProgram.methods
.issueHstPoolV0({ epoch })
.accounts({ dao })
.instruction(),
]);
}
} catch (e: any) {
console.log(`Failed to issue hst pool: ${e.message}`);
Expand Down
1 change: 1 addition & 0 deletions packages/spl-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export {
getAssetBatch,
getAssetProofBatch,
} from './mplAssetAPI';
export { estimatePrioritizationFee } from "./priorityFees";

export { proofArgsAndAccounts } from './proofArgsAndAccounts';
export type { ProofArgsAndAccountsArgs } from './proofArgsAndAccounts';
72 changes: 72 additions & 0 deletions packages/spl-utils/src/priorityFees.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {
Connection,
PublicKey,
RecentPrioritizationFees,
TransactionInstruction,
} from "@solana/web3.js";

const MAX_RECENT_PRIORITY_FEE_ACCOUNTS = 128;

// Borrowed with love from https://github.com/blockworks-foundation/mango-v4/blob/57a9835aa8f636b6d231ba2c4008bfe89cbf08ba/ts/client/src/client.ts#L4552
/**
* Returns an estimate of a prioritization fee for a set of instructions.
*
* The estimate is based on the median fees of writable accounts that will be involved in the transaction.
*
* @param ixs - the instructions that make up the transaction
* @returns prioritizationFeeEstimate -- in microLamports
*/
export async function estimatePrioritizationFee(
connection: Connection,
ixs: TransactionInstruction[]
): Promise<number> {
const writableAccounts = ixs
.map((x) => x.keys.filter((a) => a.isWritable).map((k) => k.pubkey))
.flat();
const uniqueWritableAccounts = [
...new Set(writableAccounts.map((x) => x.toBase58())),
]
.map((a) => new PublicKey(a))
.slice(0, MAX_RECENT_PRIORITY_FEE_ACCOUNTS);

const priorityFees = await connection.getRecentPrioritizationFees({
lockedWritableAccounts: uniqueWritableAccounts,
});

if (priorityFees.length < 1) {
return 1;
}

// get max priority fee per slot (and sort by slot from old to new)
const groupedBySlot = priorityFees.reduce((acc, fee) => {
const key = fee.slot;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(fee);
return acc;
}, {} as Record<string, RecentPrioritizationFees[]>);

const maxFeeBySlot = Object.keys(groupedBySlot).reduce((acc, slot) => {
acc[slot] = groupedBySlot[slot].reduce((max, fee) => {
return fee.prioritizationFee > max.prioritizationFee ? fee : max;
});
return acc;
}, {} as Record<string, RecentPrioritizationFees>);
const maximumFees = Object.values(maxFeeBySlot).sort(
(a: RecentPrioritizationFees, b: RecentPrioritizationFees) =>
a.slot - b.slot
) as RecentPrioritizationFees[];

// get median of last 20 fees
const recentFees = maximumFees.slice(Math.max(maximumFees.length - 20, 0));
const mid = Math.floor(recentFees.length / 2);
const medianFee =
recentFees.length % 2 !== 0
? recentFees[mid].prioritizationFee
: (recentFees[mid - 1].prioritizationFee +
recentFees[mid].prioritizationFee) /
2;

return Math.max(1, Math.ceil(medianFee));
}
Loading

0 comments on commit 47e142e

Please sign in to comment.