Merge pull request #662 from blockworks-foundation/lou/estimate-priority-fees

Add estimatePrioritizationFee to the ts client
This commit is contained in:
Lou-Kamades 2023-08-16 15:42:55 -05:00 committed by GitHub
commit 2460afce85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 147 additions and 6 deletions

View File

@ -62,7 +62,7 @@
"@project-serum/serum": "0.13.65",
"@pythnetwork/client": "~2.14.0",
"@solana/spl-token": "0.3.7",
"@solana/web3.js": "^1.73.2",
"@solana/web3.js": "^1.78.2",
"@switchboard-xyz/sbv2-lite": "^0.1.6",
"big.js": "^6.1.1",
"binance-api-node": "^0.12.0",

View File

@ -26,10 +26,14 @@ import {
SystemProgram,
TransactionInstruction,
TransactionSignature,
RecentPrioritizationFees,
} from '@solana/web3.js';
import bs58 from 'bs58';
import chunk from 'lodash/chunk';
import cloneDeep from 'lodash/cloneDeep';
import groupBy from 'lodash/groupBy';
import mapValues from 'lodash/mapValues';
import maxBy from 'lodash/maxBy';
import uniq from 'lodash/uniq';
import { Bank, MintInfo, TokenIndex } from './accounts/bank';
import { Group } from './accounts/group';
@ -68,7 +72,12 @@ import {
buildIxGate,
TokenRegisterParams,
} from './clientIxParamBuilder';
import { MANGO_V4_ID, OPENBOOK_PROGRAM_ID, RUST_U64_MAX } from './constants';
import {
MANGO_V4_ID,
MAX_RECENT_PRIORITY_FEE_ACCOUNTS,
OPENBOOK_PROGRAM_ID,
RUST_U64_MAX,
} from './constants';
import { Id } from './ids';
import { IDL, MangoV4 } from './mango_v4';
import { I80F48 } from './numbers/I80F48';
@ -97,6 +106,7 @@ export type MangoClientOptions = {
idsSource?: IdsSource;
postSendTxCallback?: ({ txid }: { txid: string }) => void;
prioritizationFee?: number;
estimateFee?: boolean;
txConfirmationCommitment?: Commitment;
openbookFeesToDao?: boolean;
};
@ -105,6 +115,7 @@ export class MangoClient {
private idsSource: IdsSource;
private postSendTxCallback?: ({ txid }) => void;
private prioritizationFee: number;
private estimateFee: boolean;
private txConfirmationCommitment: Commitment;
private openbookFeesToDao: boolean;
@ -116,6 +127,7 @@ export class MangoClient {
) {
this.idsSource = opts?.idsSource || 'get-program-accounts';
this.prioritizationFee = opts?.prioritizationFee || 0;
this.estimateFee = opts?.estimateFee || false;
this.postSendTxCallback = opts?.postSendTxCallback;
this.openbookFeesToDao = opts?.openbookFeesToDao ?? true;
this.txConfirmationCommitment =
@ -140,13 +152,21 @@ export class MangoClient {
ixs: TransactionInstruction[],
opts: any = {},
): Promise<string> {
let prioritizationFee: number;
if (opts.prioritizationFee) {
prioritizationFee = opts.prioritizationFee;
} else if (this.estimateFee) {
prioritizationFee = await this.estimatePrioritizationFee(ixs);
} else {
prioritizationFee = this.prioritizationFee;
}
return await sendTransaction(
this.program.provider as AnchorProvider,
ixs,
opts.alts ?? [],
{
postSendTxCallback: this.postSendTxCallback,
prioritizationFee: this.prioritizationFee,
prioritizationFee,
txConfirmationCommitment: this.txConfirmationCommitment,
...opts,
},
@ -4193,4 +4213,54 @@ export class MangoClient {
transactionInstructions,
);
}
/**
* 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
*/
public async estimatePrioritizationFee(
ixs: TransactionInstruction[],
): Promise<number> {
const writableAccounts = ixs
.map((x) => x.keys.filter((a) => a.isWritable).map((k) => k.pubkey))
.flat();
const uniqueWritableAccounts = uniq(
writableAccounts.map((x) => x.toBase58()),
)
.map((a) => new PublicKey(a))
.slice(0, MAX_RECENT_PRIORITY_FEE_ACCOUNTS);
const priorityFees = await this.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 maxFeeBySlot = mapValues(groupBy(priorityFees, 'slot'), (items) =>
maxBy(items, 'prioritizationFee'),
);
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));
}
}

View File

@ -25,3 +25,4 @@ export const MANGO_V4_ID = {
export const USDC_MINT = new PublicKey(
'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
);
export const MAX_RECENT_PRIORITY_FEE_ACCOUNTS = 128;

View File

@ -37,6 +37,13 @@
dependencies:
regenerator-runtime "^0.13.11"
"@babel/runtime@^7.22.6":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.10.tgz#ae3e9631fd947cb7e3610d3e9d8fef5f76696682"
integrity sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==
dependencies:
regenerator-runtime "^0.14.0"
"@colors/colors@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
@ -171,11 +178,23 @@
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@noble/curves@^1.0.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d"
integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==
dependencies:
"@noble/hashes" "1.3.1"
"@noble/ed25519@^1.7.0":
version "1.7.3"
resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.3.tgz#57e1677bf6885354b466c38e2b620c62f45a7123"
integrity sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==
"@noble/hashes@1.3.1", "@noble/hashes@^1.3.0":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9"
integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==
"@noble/hashes@^1.1.2":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12"
@ -413,7 +432,7 @@
rpc-websockets "^7.5.0"
superstruct "^0.14.2"
"@solana/web3.js@^1.68.0", "@solana/web3.js@^1.73.2":
"@solana/web3.js@^1.68.0":
version "1.73.2"
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.73.2.tgz#4b30cd402b35733dae3a7d0b638be26a7742b395"
integrity sha512-9WACF8W4Nstj7xiDw3Oom22QmrhBh0VyZyZ7JvvG3gOxLWLlX3hvm5nPVJOGcCE/9fFavBbCUb5A6CIuvMGdoA==
@ -435,6 +454,27 @@
rpc-websockets "^7.5.0"
superstruct "^0.14.2"
"@solana/web3.js@^1.78.2":
version "1.78.3"
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.78.3.tgz#1214f17f51612e403c4dc7844c7a9694938bfbab"
integrity sha512-qhpnyIlrj/4Czw1dBFZK6KgZBk5FwuJhvMl0C7m94jhl90yDC8b6w4svKwPjhB+OOrdQAzHyRp0+ocEs/Liw7w==
dependencies:
"@babel/runtime" "^7.22.6"
"@noble/curves" "^1.0.0"
"@noble/hashes" "^1.3.0"
"@solana/buffer-layout" "^4.0.0"
agentkeepalive "^4.3.0"
bigint-buffer "^1.1.5"
bn.js "^5.2.1"
borsh "^0.7.0"
bs58 "^4.0.1"
buffer "6.0.3"
fast-stable-stringify "^1.0.0"
jayson "^4.1.0"
node-fetch "^2.6.12"
rpc-websockets "^7.5.1"
superstruct "^0.14.2"
"@switchboard-xyz/sbv2-lite@^0.1.6":
version "0.1.6"
resolved "https://registry.yarnpkg.com/@switchboard-xyz/sbv2-lite/-/sbv2-lite-0.1.6.tgz#dc3fbb5b3b028dbd3c688b991bcc48a670131ddb"
@ -672,6 +712,13 @@ agentkeepalive@^4.2.1:
depd "^1.1.2"
humanize-ms "^1.2.1"
agentkeepalive@^4.3.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923"
integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==
dependencies:
humanize-ms "^1.2.1"
ajv@^6.10.0, ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
@ -858,7 +905,7 @@ bn.js@^4.11.9:
resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz"
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0:
bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0, bn.js@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70"
integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==
@ -1754,6 +1801,24 @@ jayson@^3.4.4:
uuid "^8.3.2"
ws "^7.4.5"
jayson@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/jayson/-/jayson-4.1.0.tgz#60dc946a85197317f2b1439d672a8b0a99cea2f9"
integrity sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==
dependencies:
"@types/connect" "^3.4.33"
"@types/node" "^12.12.54"
"@types/ws" "^7.4.4"
JSONStream "^1.3.5"
commander "^2.20.3"
delay "^5.0.0"
es6-promisify "^5.0.0"
eyes "^0.1.8"
isomorphic-ws "^4.0.1"
json-stringify-safe "^5.0.1"
uuid "^8.3.2"
ws "^7.4.5"
js-sha256@^0.9.0:
version "0.9.0"
resolved "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz"
@ -2071,7 +2136,7 @@ node-addon-api@^2.0.0:
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz"
integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==
node-fetch@2, node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7, "node-fetch@npm:@blockworks-foundation/node-fetch@2.6.11":
node-fetch@2, node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7, "node-fetch@npm:@blockworks-foundation/node-fetch@2.6.11":
version "2.6.11"
resolved "https://registry.yarnpkg.com/@blockworks-foundation/node-fetch/-/node-fetch-2.6.11.tgz#fb536ef0e6a960e7b7993f3c1d3b3bba9bdfbc56"
integrity sha512-HeDTxpIypSR4qCoqgUXGr8YL4OG1z7BbV4VhQ9iQs+pt2wV3MtqO+sQk2vXK3WDKu5C6BsbGmWE22BmIrcuOOw==
@ -2241,6 +2306,11 @@ regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.4:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
regenerator-runtime@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
regexpp@^3.1.0, regexpp@^3.2.0:
version "3.2.0"
resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz"