@atomiqlabs/chain-solana 10.0.0-dev.3 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/LICENSE +201 -201
  2. package/dist/index.d.ts +29 -29
  3. package/dist/index.js +45 -45
  4. package/dist/solana/SolanaChainType.d.ts +10 -10
  5. package/dist/solana/SolanaChainType.js +2 -2
  6. package/dist/solana/SolanaChains.d.ts +20 -20
  7. package/dist/solana/SolanaChains.js +25 -25
  8. package/dist/solana/SolanaInitializer.d.ts +18 -18
  9. package/dist/solana/SolanaInitializer.js +63 -63
  10. package/dist/solana/btcrelay/SolanaBtcRelay.d.ts +228 -228
  11. package/dist/solana/btcrelay/SolanaBtcRelay.js +441 -441
  12. package/dist/solana/btcrelay/headers/SolanaBtcHeader.d.ts +29 -29
  13. package/dist/solana/btcrelay/headers/SolanaBtcHeader.js +34 -34
  14. package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.d.ts +46 -46
  15. package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.js +78 -78
  16. package/dist/solana/btcrelay/program/programIdl.json +671 -671
  17. package/dist/solana/chain/SolanaAction.d.ts +26 -26
  18. package/dist/solana/chain/SolanaAction.js +86 -86
  19. package/dist/solana/chain/SolanaChainInterface.d.ts +58 -58
  20. package/dist/solana/chain/SolanaChainInterface.js +112 -112
  21. package/dist/solana/chain/SolanaModule.d.ts +14 -14
  22. package/dist/solana/chain/SolanaModule.js +13 -13
  23. package/dist/solana/chain/modules/SolanaAddresses.d.ts +8 -8
  24. package/dist/solana/chain/modules/SolanaAddresses.js +22 -22
  25. package/dist/solana/chain/modules/SolanaBlocks.d.ts +28 -28
  26. package/dist/solana/chain/modules/SolanaBlocks.js +72 -72
  27. package/dist/solana/chain/modules/SolanaEvents.d.ts +25 -25
  28. package/dist/solana/chain/modules/SolanaEvents.js +58 -58
  29. package/dist/solana/chain/modules/SolanaFees.d.ts +121 -121
  30. package/dist/solana/chain/modules/SolanaFees.js +379 -379
  31. package/dist/solana/chain/modules/SolanaSignatures.d.ts +23 -23
  32. package/dist/solana/chain/modules/SolanaSignatures.js +39 -39
  33. package/dist/solana/chain/modules/SolanaSlots.d.ts +31 -31
  34. package/dist/solana/chain/modules/SolanaSlots.js +68 -68
  35. package/dist/solana/chain/modules/SolanaTokens.d.ts +136 -136
  36. package/dist/solana/chain/modules/SolanaTokens.js +248 -248
  37. package/dist/solana/chain/modules/SolanaTransactions.d.ts +124 -124
  38. package/dist/solana/chain/modules/SolanaTransactions.js +332 -332
  39. package/dist/solana/events/SolanaChainEvents.d.ts +88 -88
  40. package/dist/solana/events/SolanaChainEvents.js +256 -256
  41. package/dist/solana/events/SolanaChainEventsBrowser.d.ts +85 -85
  42. package/dist/solana/events/SolanaChainEventsBrowser.js +194 -194
  43. package/dist/solana/program/SolanaProgramBase.d.ts +40 -40
  44. package/dist/solana/program/SolanaProgramBase.js +43 -43
  45. package/dist/solana/program/SolanaProgramModule.d.ts +8 -8
  46. package/dist/solana/program/SolanaProgramModule.js +11 -11
  47. package/dist/solana/program/modules/SolanaProgramEvents.d.ts +59 -59
  48. package/dist/solana/program/modules/SolanaProgramEvents.js +103 -103
  49. package/dist/solana/swaps/SolanaSwapData.d.ts +59 -59
  50. package/dist/solana/swaps/SolanaSwapData.js +267 -267
  51. package/dist/solana/swaps/SolanaSwapModule.d.ts +10 -10
  52. package/dist/solana/swaps/SolanaSwapModule.js +11 -11
  53. package/dist/solana/swaps/SolanaSwapProgram.d.ts +202 -202
  54. package/dist/solana/swaps/SolanaSwapProgram.js +470 -463
  55. package/dist/solana/swaps/SwapTypeEnum.d.ts +11 -11
  56. package/dist/solana/swaps/SwapTypeEnum.js +42 -42
  57. package/dist/solana/swaps/modules/SolanaDataAccount.d.ts +94 -94
  58. package/dist/solana/swaps/modules/SolanaDataAccount.js +231 -231
  59. package/dist/solana/swaps/modules/SolanaLpVault.d.ts +71 -71
  60. package/dist/solana/swaps/modules/SolanaLpVault.js +173 -173
  61. package/dist/solana/swaps/modules/SwapClaim.d.ts +129 -129
  62. package/dist/solana/swaps/modules/SwapClaim.js +291 -291
  63. package/dist/solana/swaps/modules/SwapInit.d.ts +217 -217
  64. package/dist/solana/swaps/modules/SwapInit.js +519 -519
  65. package/dist/solana/swaps/modules/SwapRefund.d.ts +82 -82
  66. package/dist/solana/swaps/modules/SwapRefund.js +252 -252
  67. package/dist/solana/swaps/programIdl.json +945 -945
  68. package/dist/solana/swaps/programTypes.d.ts +943 -943
  69. package/dist/solana/swaps/programTypes.js +945 -945
  70. package/dist/solana/wallet/SolanaKeypairWallet.d.ts +9 -9
  71. package/dist/solana/wallet/SolanaKeypairWallet.js +33 -33
  72. package/dist/solana/wallet/SolanaSigner.d.ts +10 -10
  73. package/dist/solana/wallet/SolanaSigner.js +16 -16
  74. package/dist/utils/Utils.d.ts +53 -53
  75. package/dist/utils/Utils.js +170 -170
  76. package/package.json +41 -41
  77. package/src/index.ts +36 -36
  78. package/src/solana/SolanaChainType.ts +25 -25
  79. package/src/solana/SolanaChains.ts +23 -23
  80. package/src/solana/SolanaInitializer.ts +102 -102
  81. package/src/solana/btcrelay/SolanaBtcRelay.ts +589 -588
  82. package/src/solana/btcrelay/headers/SolanaBtcHeader.ts +57 -57
  83. package/src/solana/btcrelay/headers/SolanaBtcStoredHeader.ts +102 -102
  84. package/src/solana/btcrelay/program/programIdl.json +670 -670
  85. package/src/solana/chain/SolanaAction.ts +108 -108
  86. package/src/solana/chain/SolanaChainInterface.ts +174 -174
  87. package/src/solana/chain/SolanaModule.ts +20 -20
  88. package/src/solana/chain/modules/SolanaAddresses.ts +20 -20
  89. package/src/solana/chain/modules/SolanaBlocks.ts +78 -78
  90. package/src/solana/chain/modules/SolanaEvents.ts +56 -56
  91. package/src/solana/chain/modules/SolanaFees.ts +450 -450
  92. package/src/solana/chain/modules/SolanaSignatures.ts +39 -39
  93. package/src/solana/chain/modules/SolanaSlots.ts +82 -82
  94. package/src/solana/chain/modules/SolanaTokens.ts +307 -307
  95. package/src/solana/chain/modules/SolanaTransactions.ts +370 -370
  96. package/src/solana/events/SolanaChainEvents.ts +299 -299
  97. package/src/solana/events/SolanaChainEventsBrowser.ts +256 -256
  98. package/src/solana/program/SolanaProgramBase.ts +79 -79
  99. package/src/solana/program/SolanaProgramModule.ts +15 -15
  100. package/src/solana/program/modules/SolanaProgramEvents.ts +140 -140
  101. package/src/solana/swaps/SolanaSwapData.ts +379 -379
  102. package/src/solana/swaps/SolanaSwapModule.ts +16 -16
  103. package/src/solana/swaps/SolanaSwapProgram.ts +697 -692
  104. package/src/solana/swaps/SwapTypeEnum.ts +29 -29
  105. package/src/solana/swaps/modules/SolanaDataAccount.ts +307 -307
  106. package/src/solana/swaps/modules/SolanaLpVault.ts +215 -215
  107. package/src/solana/swaps/modules/SwapClaim.ts +389 -389
  108. package/src/solana/swaps/modules/SwapInit.ts +663 -663
  109. package/src/solana/swaps/modules/SwapRefund.ts +312 -312
  110. package/src/solana/swaps/programIdl.json +944 -944
  111. package/src/solana/swaps/programTypes.ts +1885 -1885
  112. package/src/solana/wallet/SolanaKeypairWallet.ts +36 -36
  113. package/src/solana/wallet/SolanaSigner.ts +23 -23
  114. package/src/utils/Utils.ts +180 -180
@@ -1,379 +1,379 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SolanaFees = void 0;
4
- const web3_js_1 = require("@solana/web3.js");
5
- const Utils_1 = require("../../../utils/Utils");
6
- const MAX_FEE_AGE = 5000;
7
- class SolanaFees {
8
- constructor(connection, maxFeeMicroLamports = 250000, numSamples = 8, period = 150, useHeliusApi = "auto", heliusFeeLevel = "veryHigh", getStaticFee, bribeData) {
9
- this.heliusApiSupported = true;
10
- this.logger = (0, Utils_1.getLogger)("SolanaFees: ");
11
- this.blockFeeCache = null;
12
- this.connection = connection;
13
- this.maxFeeMicroLamports = BigInt(maxFeeMicroLamports);
14
- this.numSamples = numSamples;
15
- this.period = period;
16
- this.useHeliusApi = useHeliusApi;
17
- this.heliusFeeLevel = heliusFeeLevel;
18
- this.bribeData = bribeData;
19
- this.getStaticFee = getStaticFee;
20
- }
21
- /**
22
- * Returns solana block with transactionDetails="signatures"
23
- *
24
- * @param slot
25
- * @private
26
- */
27
- async getBlockWithSignature(slot) {
28
- const response = await this.connection._rpcRequest("getBlock", [
29
- slot,
30
- {
31
- encoding: "json",
32
- transactionDetails: "signatures",
33
- commitment: "confirmed",
34
- rewards: true
35
- }
36
- ]);
37
- if (response.error != null) {
38
- if (response.error.code === -32004 || response.error.code === -32007 || response.error.code === -32009 || response.error.code === -32014) {
39
- return null;
40
- }
41
- throw new Error(response.error.message);
42
- }
43
- return response.result;
44
- }
45
- /**
46
- * Returns fee estimate from Helius API - only works with Helius RPC, return null for all other RPC providers
47
- *
48
- * @param mutableAccounts
49
- * @private
50
- */
51
- async getPriorityFeeEstimate(mutableAccounts) {
52
- //Try to use getPriorityFeeEstimate api of Helius
53
- const response = await this.connection._rpcRequest("getPriorityFeeEstimate", [
54
- {
55
- "accountKeys": mutableAccounts.map(e => e.toBase58()),
56
- "options": {
57
- "includeAllPriorityFeeLevels": true
58
- }
59
- }
60
- ]).catch(e => {
61
- //Catching not supported errors
62
- if (e.message != null && (e.message.includes("-32601") || e.message.includes("-32600"))) {
63
- return {
64
- error: {
65
- code: -32601,
66
- message: e.message
67
- }
68
- };
69
- }
70
- throw e;
71
- });
72
- if (response.error != null) {
73
- //Catching not supported errors
74
- if (response.error.code !== -32601 && response.error.code !== -32600)
75
- throw new Error(response.error.message);
76
- return null;
77
- }
78
- return response.result.priorityFeeLevels;
79
- }
80
- /**
81
- * Sends the transaction over Jito
82
- *
83
- * @param tx
84
- * @param options
85
- * @private
86
- * @returns {Promise<string>} transaction signature
87
- */
88
- async sendJitoTx(tx, options) {
89
- if (this.bribeData?.endpoint == null)
90
- throw new Error("Jito endpoint not specified!");
91
- if (options == null)
92
- options = {};
93
- const request = await fetch(this.bribeData.endpoint, {
94
- method: "POST",
95
- body: JSON.stringify({
96
- jsonrpc: "2.0",
97
- id: 1,
98
- method: "sendTransaction",
99
- params: [tx.toString("base64"), {
100
- ...options,
101
- encoding: "base64"
102
- }],
103
- }),
104
- headers: {
105
- "Content-Type": "application/json"
106
- }
107
- });
108
- if (request.ok) {
109
- const parsedResponse = await request.json();
110
- return parsedResponse.result;
111
- }
112
- throw new Error(await request.text());
113
- }
114
- /**
115
- * Checks whether the transaction should be sent over Jito, returns the fee paid to Jito in case the transaction
116
- * should be sent over Jito, returns null if the transaction shouldn't be sent over Jito
117
- *
118
- * @param parsedTx
119
- * @private
120
- */
121
- getJitoTxFee(parsedTx) {
122
- const lastIx = parsedTx.instructions[parsedTx.instructions.length - 1];
123
- if (!lastIx.programId.equals(web3_js_1.SystemProgram.programId))
124
- return null;
125
- if (web3_js_1.SystemInstruction.decodeInstructionType(lastIx) !== "Transfer")
126
- return null;
127
- const decodedIxData = web3_js_1.SystemInstruction.decodeTransfer(lastIx);
128
- if (decodedIxData.toPubkey.toBase58() !== this.bribeData?.address)
129
- return null;
130
- return decodedIxData.lamports;
131
- }
132
- /**
133
- * Gets the mean microLamports/CU fee rate for the block at a specific slot
134
- *
135
- * @param slot
136
- * @private
137
- */
138
- async getBlockMeanFeeRate(slot) {
139
- const block = await this.getBlockWithSignature(slot);
140
- if (block == null)
141
- return null;
142
- const blockComission = block.rewards.find(e => e.rewardType === "Fee");
143
- const totalBlockFees = BigInt(blockComission.lamports) * 2n;
144
- //Subtract per-signature fees to get pure compute fees
145
- const totalTransactionBaseFees = BigInt(block.signatures.length) * 5000n;
146
- const computeFees = totalBlockFees - totalTransactionBaseFees;
147
- //Total compute fees in micro lamports
148
- const computeFeesMicroLamports = computeFees * 1000000n;
149
- //micro lamports per CU considering block was full (48M compute units)
150
- const perCUMicroLamports = computeFeesMicroLamports / 48000000n;
151
- this.logger.debug("getBlockMeanFeeRate(): slot: " + slot + " total reward: " + totalBlockFees.toString(10) +
152
- " total transactions: " + block.signatures.length + " computed fee: " + perCUMicroLamports);
153
- return perCUMicroLamports;
154
- }
155
- /**
156
- * Manually gets global fee rate by sampling random blocks over the last period
157
- *
158
- * @private
159
- * @returns {Promise<BN>} sampled mean microLamports/CU fee over the last period
160
- */
161
- async _getGlobalFeeRate() {
162
- let slot = await this.connection.getSlot();
163
- const slots = [];
164
- for (let i = 0; i < this.period; i++) {
165
- slots.push(slot - i);
166
- }
167
- const promises = [];
168
- for (let i = 0; i < this.numSamples; i++) {
169
- promises.push((async () => {
170
- let feeRate = null;
171
- while (feeRate == null) {
172
- if (slots.length === 0)
173
- throw new Error("Ran out of slots to check!");
174
- const index = Math.floor(Math.random() * slots.length);
175
- const slotNumber = slots[index];
176
- slots.splice(index, 1);
177
- feeRate = await this.getBlockMeanFeeRate(slotNumber);
178
- }
179
- return feeRate;
180
- })());
181
- }
182
- const meanFees = await Promise.all(promises);
183
- let min = null;
184
- meanFees.forEach(e => min == null || min > e ? min = e : 0);
185
- if (min != null)
186
- this.logger.debug("_getGlobalFeeRate(): slot: " + slot + " global fee minimum: " + min.toString(10));
187
- return min;
188
- }
189
- /**
190
- * Gets the combined microLamports/CU fee rate (from localized & global fee market)
191
- *
192
- * @param mutableAccounts
193
- * @private
194
- */
195
- async _getFeeRate(mutableAccounts) {
196
- if (this.useHeliusApi === "yes" || (this.useHeliusApi === "auto" && this.heliusApiSupported)) {
197
- //Try to use getPriorityFeeEstimate api of Helius
198
- const fees = await this.getPriorityFeeEstimate(mutableAccounts);
199
- if (fees != null) {
200
- let calculatedFee = BigInt(fees[this.heliusFeeLevel]);
201
- if (calculatedFee < 8000n)
202
- calculatedFee = 8000n;
203
- if (calculatedFee > this.maxFeeMicroLamports)
204
- calculatedFee = this.maxFeeMicroLamports;
205
- return calculatedFee;
206
- }
207
- this.logger.warn("_getFeeRate(): tried fetching fees from Helius API, not supported," +
208
- " falling back to client-side fee estimation");
209
- this.heliusApiSupported = false;
210
- }
211
- const [globalFeeRate, localFeeRate] = await Promise.all([
212
- this.getGlobalFeeRate(),
213
- this.connection.getRecentPrioritizationFees({
214
- lockedWritableAccounts: mutableAccounts
215
- }).then(resp => {
216
- let lamports = 0;
217
- for (let i = 20; i >= 0; i--) {
218
- const data = resp[resp.length - i - 1];
219
- if (data != null)
220
- lamports = Math.min(lamports, data.prioritizationFee);
221
- }
222
- return BigInt(lamports);
223
- })
224
- ]);
225
- let fee = globalFeeRate;
226
- if (fee < localFeeRate)
227
- fee = localFeeRate;
228
- if (fee < 8000n)
229
- fee = 8000n;
230
- if (fee > this.maxFeeMicroLamports)
231
- fee = this.maxFeeMicroLamports;
232
- return fee;
233
- }
234
- /**
235
- * Gets global fee rate, with caching
236
- *
237
- * @returns {Promise<BN>} global fee rate microLamports/CU
238
- */
239
- getGlobalFeeRate() {
240
- if (this.blockFeeCache == null || Date.now() - this.blockFeeCache.timestamp > MAX_FEE_AGE) {
241
- let obj = {
242
- timestamp: Date.now(),
243
- feeRate: null
244
- };
245
- obj.feeRate = this._getGlobalFeeRate().catch(e => {
246
- if (this.blockFeeCache === obj)
247
- this.blockFeeCache = null;
248
- throw e;
249
- });
250
- this.blockFeeCache = obj;
251
- }
252
- return this.blockFeeCache.feeRate;
253
- }
254
- /**
255
- * Gets the combined microLamports/CU fee rate (from localized & global fee market), cached & adjusted as for
256
- * when bribe and/or static fee should be included, format: <uLamports/CU>;<static fee lamport>[;<bribe address>]
257
- *
258
- * @param mutableAccounts
259
- * @private
260
- */
261
- async getFeeRate(mutableAccounts) {
262
- let feeMicroLamportPerCU = await this._getFeeRate(mutableAccounts);
263
- if (this.bribeData?.getBribeFee != null)
264
- feeMicroLamportPerCU = this.bribeData.getBribeFee(feeMicroLamportPerCU);
265
- let fee = feeMicroLamportPerCU.toString(10);
266
- if (this.getStaticFee != null) {
267
- fee += ";" + this.getStaticFee(feeMicroLamportPerCU);
268
- }
269
- else {
270
- fee += ";0";
271
- }
272
- if (this.bribeData?.address) {
273
- fee += ";" + this.bribeData.address;
274
- }
275
- this.logger.debug("getFeeRate(): calculated fee: " + fee);
276
- return fee;
277
- }
278
- /**
279
- * Calculates the total priority fee paid for a given compute budget at a given fee rate
280
- *
281
- * @param computeUnits
282
- * @param feeRate
283
- * @param includeStaticFee whether the include the static/base part of the fee rate
284
- */
285
- getPriorityFee(computeUnits, feeRate, includeStaticFee = true) {
286
- if (feeRate == null)
287
- return 0n;
288
- const hashArr = feeRate.split("#");
289
- if (hashArr.length > 1) {
290
- feeRate = hashArr[0];
291
- }
292
- const arr = feeRate.split(";");
293
- const cuPrice = BigInt(arr[0]);
294
- const staticFee = includeStaticFee ? BigInt(arr[1]) : 0n;
295
- return staticFee + (cuPrice * BigInt(computeUnits) / 1000000n);
296
- }
297
- /**
298
- * Applies fee rate to a transaction at the beginning of the transaction (has to be called after
299
- * feePayer is set for the tx), specifically adds the setComputeUnitLimit & setComputeUnitPrice instruction
300
- *
301
- * @param tx
302
- * @param computeBudget
303
- * @param feeRate
304
- */
305
- applyFeeRateBegin(tx, computeBudget, feeRate) {
306
- if (feeRate == null)
307
- return false;
308
- const hashArr = feeRate.split("#");
309
- if (hashArr.length > 1) {
310
- feeRate = hashArr[0];
311
- }
312
- if (computeBudget != null && computeBudget > 0)
313
- tx.add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({
314
- units: computeBudget,
315
- }));
316
- //Check if bribe is included
317
- const arr = feeRate.split(";");
318
- if (arr.length > 2) {
319
- }
320
- else {
321
- let fee = BigInt(arr[0]);
322
- if (arr.length > 1) {
323
- const staticFee = BigInt(arr[1]);
324
- const cuBigInt = BigInt(computeBudget || (200000 * Utils_1.SolanaTxUtils.getNonComputeBudgetIxs(tx)));
325
- const staticFeePerCU = staticFee * BigInt(1000000) / cuBigInt;
326
- fee += staticFeePerCU;
327
- }
328
- tx.add(web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
329
- microLamports: fee
330
- }));
331
- }
332
- }
333
- /**
334
- * Applies fee rate to the end of the transaction (has to be called after feePayer is set for the tx),
335
- * specifically adds the bribe SystemProgram.transfer instruction
336
- *
337
- * @param tx
338
- * @param computeBudget
339
- * @param feeRate
340
- */
341
- applyFeeRateEnd(tx, computeBudget, feeRate) {
342
- if (feeRate == null)
343
- return false;
344
- const hashArr = feeRate.split("#");
345
- if (hashArr.length > 1) {
346
- feeRate = hashArr[0];
347
- }
348
- //Check if bribe is included
349
- const arr = feeRate.split(";");
350
- if (arr.length > 2) {
351
- const cuBigInt = BigInt(computeBudget || (200000 * (Utils_1.SolanaTxUtils.getNonComputeBudgetIxs(tx) + 1)));
352
- const cuPrice = BigInt(arr[0]);
353
- const staticFee = BigInt(arr[1]);
354
- const bribeAddress = new web3_js_1.PublicKey(arr[2]);
355
- tx.add(web3_js_1.SystemProgram.transfer({
356
- fromPubkey: tx.feePayer,
357
- toPubkey: bribeAddress,
358
- lamports: staticFee + (cuBigInt * cuPrice / BigInt(1000000))
359
- }));
360
- return;
361
- }
362
- }
363
- /**
364
- * Checks if the transaction should be submitted over Jito and if yes submits it
365
- *
366
- * @param tx
367
- * @param options
368
- * @returns {Promise<string | null>} null if the transaction was not sent over Jito, tx signature when tx was sent over Jito
369
- */
370
- submitTx(tx, options) {
371
- const parsedTx = web3_js_1.Transaction.from(tx);
372
- const jitoFee = this.getJitoTxFee(parsedTx);
373
- if (jitoFee == null)
374
- return null;
375
- this.logger.info("submitTx(): sending tx over Jito, signature: " + parsedTx.signature + " fee: " + jitoFee.toString(10));
376
- return this.sendJitoTx(tx, options);
377
- }
378
- }
379
- exports.SolanaFees = SolanaFees;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SolanaFees = void 0;
4
+ const web3_js_1 = require("@solana/web3.js");
5
+ const Utils_1 = require("../../../utils/Utils");
6
+ const MAX_FEE_AGE = 5000;
7
+ class SolanaFees {
8
+ constructor(connection, maxFeeMicroLamports = 250000, numSamples = 8, period = 150, useHeliusApi = "auto", heliusFeeLevel = "veryHigh", getStaticFee, bribeData) {
9
+ this.heliusApiSupported = true;
10
+ this.logger = (0, Utils_1.getLogger)("SolanaFees: ");
11
+ this.blockFeeCache = null;
12
+ this.connection = connection;
13
+ this.maxFeeMicroLamports = BigInt(maxFeeMicroLamports);
14
+ this.numSamples = numSamples;
15
+ this.period = period;
16
+ this.useHeliusApi = useHeliusApi;
17
+ this.heliusFeeLevel = heliusFeeLevel;
18
+ this.bribeData = bribeData;
19
+ this.getStaticFee = getStaticFee;
20
+ }
21
+ /**
22
+ * Returns solana block with transactionDetails="signatures"
23
+ *
24
+ * @param slot
25
+ * @private
26
+ */
27
+ async getBlockWithSignature(slot) {
28
+ const response = await this.connection._rpcRequest("getBlock", [
29
+ slot,
30
+ {
31
+ encoding: "json",
32
+ transactionDetails: "signatures",
33
+ commitment: "confirmed",
34
+ rewards: true
35
+ }
36
+ ]);
37
+ if (response.error != null) {
38
+ if (response.error.code === -32004 || response.error.code === -32007 || response.error.code === -32009 || response.error.code === -32014) {
39
+ return null;
40
+ }
41
+ throw new Error(response.error.message);
42
+ }
43
+ return response.result;
44
+ }
45
+ /**
46
+ * Returns fee estimate from Helius API - only works with Helius RPC, return null for all other RPC providers
47
+ *
48
+ * @param mutableAccounts
49
+ * @private
50
+ */
51
+ async getPriorityFeeEstimate(mutableAccounts) {
52
+ //Try to use getPriorityFeeEstimate api of Helius
53
+ const response = await this.connection._rpcRequest("getPriorityFeeEstimate", [
54
+ {
55
+ "accountKeys": mutableAccounts.map(e => e.toBase58()),
56
+ "options": {
57
+ "includeAllPriorityFeeLevels": true
58
+ }
59
+ }
60
+ ]).catch(e => {
61
+ //Catching not supported errors
62
+ if (e.message != null && (e.message.includes("-32601") || e.message.includes("-32600"))) {
63
+ return {
64
+ error: {
65
+ code: -32601,
66
+ message: e.message
67
+ }
68
+ };
69
+ }
70
+ throw e;
71
+ });
72
+ if (response.error != null) {
73
+ //Catching not supported errors
74
+ if (response.error.code !== -32601 && response.error.code !== -32600)
75
+ throw new Error(response.error.message);
76
+ return null;
77
+ }
78
+ return response.result.priorityFeeLevels;
79
+ }
80
+ /**
81
+ * Sends the transaction over Jito
82
+ *
83
+ * @param tx
84
+ * @param options
85
+ * @private
86
+ * @returns {Promise<string>} transaction signature
87
+ */
88
+ async sendJitoTx(tx, options) {
89
+ if (this.bribeData?.endpoint == null)
90
+ throw new Error("Jito endpoint not specified!");
91
+ if (options == null)
92
+ options = {};
93
+ const request = await fetch(this.bribeData.endpoint, {
94
+ method: "POST",
95
+ body: JSON.stringify({
96
+ jsonrpc: "2.0",
97
+ id: 1,
98
+ method: "sendTransaction",
99
+ params: [tx.toString("base64"), {
100
+ ...options,
101
+ encoding: "base64"
102
+ }],
103
+ }),
104
+ headers: {
105
+ "Content-Type": "application/json"
106
+ }
107
+ });
108
+ if (request.ok) {
109
+ const parsedResponse = await request.json();
110
+ return parsedResponse.result;
111
+ }
112
+ throw new Error(await request.text());
113
+ }
114
+ /**
115
+ * Checks whether the transaction should be sent over Jito, returns the fee paid to Jito in case the transaction
116
+ * should be sent over Jito, returns null if the transaction shouldn't be sent over Jito
117
+ *
118
+ * @param parsedTx
119
+ * @private
120
+ */
121
+ getJitoTxFee(parsedTx) {
122
+ const lastIx = parsedTx.instructions[parsedTx.instructions.length - 1];
123
+ if (!lastIx.programId.equals(web3_js_1.SystemProgram.programId))
124
+ return null;
125
+ if (web3_js_1.SystemInstruction.decodeInstructionType(lastIx) !== "Transfer")
126
+ return null;
127
+ const decodedIxData = web3_js_1.SystemInstruction.decodeTransfer(lastIx);
128
+ if (decodedIxData.toPubkey.toBase58() !== this.bribeData?.address)
129
+ return null;
130
+ return decodedIxData.lamports;
131
+ }
132
+ /**
133
+ * Gets the mean microLamports/CU fee rate for the block at a specific slot
134
+ *
135
+ * @param slot
136
+ * @private
137
+ */
138
+ async getBlockMeanFeeRate(slot) {
139
+ const block = await this.getBlockWithSignature(slot);
140
+ if (block == null)
141
+ return null;
142
+ const blockComission = block.rewards.find(e => e.rewardType === "Fee");
143
+ const totalBlockFees = BigInt(blockComission.lamports) * 2n;
144
+ //Subtract per-signature fees to get pure compute fees
145
+ const totalTransactionBaseFees = BigInt(block.signatures.length) * 5000n;
146
+ const computeFees = totalBlockFees - totalTransactionBaseFees;
147
+ //Total compute fees in micro lamports
148
+ const computeFeesMicroLamports = computeFees * 1000000n;
149
+ //micro lamports per CU considering block was full (48M compute units)
150
+ const perCUMicroLamports = computeFeesMicroLamports / 48000000n;
151
+ this.logger.debug("getBlockMeanFeeRate(): slot: " + slot + " total reward: " + totalBlockFees.toString(10) +
152
+ " total transactions: " + block.signatures.length + " computed fee: " + perCUMicroLamports);
153
+ return perCUMicroLamports;
154
+ }
155
+ /**
156
+ * Manually gets global fee rate by sampling random blocks over the last period
157
+ *
158
+ * @private
159
+ * @returns {Promise<BN>} sampled mean microLamports/CU fee over the last period
160
+ */
161
+ async _getGlobalFeeRate() {
162
+ let slot = await this.connection.getSlot();
163
+ const slots = [];
164
+ for (let i = 0; i < this.period; i++) {
165
+ slots.push(slot - i);
166
+ }
167
+ const promises = [];
168
+ for (let i = 0; i < this.numSamples; i++) {
169
+ promises.push((async () => {
170
+ let feeRate = null;
171
+ while (feeRate == null) {
172
+ if (slots.length === 0)
173
+ throw new Error("Ran out of slots to check!");
174
+ const index = Math.floor(Math.random() * slots.length);
175
+ const slotNumber = slots[index];
176
+ slots.splice(index, 1);
177
+ feeRate = await this.getBlockMeanFeeRate(slotNumber);
178
+ }
179
+ return feeRate;
180
+ })());
181
+ }
182
+ const meanFees = await Promise.all(promises);
183
+ let min = null;
184
+ meanFees.forEach(e => min == null || min > e ? min = e : 0);
185
+ if (min != null)
186
+ this.logger.debug("_getGlobalFeeRate(): slot: " + slot + " global fee minimum: " + min.toString(10));
187
+ return min;
188
+ }
189
+ /**
190
+ * Gets the combined microLamports/CU fee rate (from localized & global fee market)
191
+ *
192
+ * @param mutableAccounts
193
+ * @private
194
+ */
195
+ async _getFeeRate(mutableAccounts) {
196
+ if (this.useHeliusApi === "yes" || (this.useHeliusApi === "auto" && this.heliusApiSupported)) {
197
+ //Try to use getPriorityFeeEstimate api of Helius
198
+ const fees = await this.getPriorityFeeEstimate(mutableAccounts);
199
+ if (fees != null) {
200
+ let calculatedFee = BigInt(fees[this.heliusFeeLevel]);
201
+ if (calculatedFee < 8000n)
202
+ calculatedFee = 8000n;
203
+ if (calculatedFee > this.maxFeeMicroLamports)
204
+ calculatedFee = this.maxFeeMicroLamports;
205
+ return calculatedFee;
206
+ }
207
+ this.logger.warn("_getFeeRate(): tried fetching fees from Helius API, not supported," +
208
+ " falling back to client-side fee estimation");
209
+ this.heliusApiSupported = false;
210
+ }
211
+ const [globalFeeRate, localFeeRate] = await Promise.all([
212
+ this.getGlobalFeeRate(),
213
+ this.connection.getRecentPrioritizationFees({
214
+ lockedWritableAccounts: mutableAccounts
215
+ }).then(resp => {
216
+ let lamports = 0;
217
+ for (let i = 20; i >= 0; i--) {
218
+ const data = resp[resp.length - i - 1];
219
+ if (data != null)
220
+ lamports = Math.min(lamports, data.prioritizationFee);
221
+ }
222
+ return BigInt(lamports);
223
+ })
224
+ ]);
225
+ let fee = globalFeeRate;
226
+ if (fee < localFeeRate)
227
+ fee = localFeeRate;
228
+ if (fee < 8000n)
229
+ fee = 8000n;
230
+ if (fee > this.maxFeeMicroLamports)
231
+ fee = this.maxFeeMicroLamports;
232
+ return fee;
233
+ }
234
+ /**
235
+ * Gets global fee rate, with caching
236
+ *
237
+ * @returns {Promise<BN>} global fee rate microLamports/CU
238
+ */
239
+ getGlobalFeeRate() {
240
+ if (this.blockFeeCache == null || Date.now() - this.blockFeeCache.timestamp > MAX_FEE_AGE) {
241
+ let obj = {
242
+ timestamp: Date.now(),
243
+ feeRate: null
244
+ };
245
+ obj.feeRate = this._getGlobalFeeRate().catch(e => {
246
+ if (this.blockFeeCache === obj)
247
+ this.blockFeeCache = null;
248
+ throw e;
249
+ });
250
+ this.blockFeeCache = obj;
251
+ }
252
+ return this.blockFeeCache.feeRate;
253
+ }
254
+ /**
255
+ * Gets the combined microLamports/CU fee rate (from localized & global fee market), cached & adjusted as for
256
+ * when bribe and/or static fee should be included, format: <uLamports/CU>;<static fee lamport>[;<bribe address>]
257
+ *
258
+ * @param mutableAccounts
259
+ * @private
260
+ */
261
+ async getFeeRate(mutableAccounts) {
262
+ let feeMicroLamportPerCU = await this._getFeeRate(mutableAccounts);
263
+ if (this.bribeData?.getBribeFee != null)
264
+ feeMicroLamportPerCU = this.bribeData.getBribeFee(feeMicroLamportPerCU);
265
+ let fee = feeMicroLamportPerCU.toString(10);
266
+ if (this.getStaticFee != null) {
267
+ fee += ";" + this.getStaticFee(feeMicroLamportPerCU);
268
+ }
269
+ else {
270
+ fee += ";0";
271
+ }
272
+ if (this.bribeData?.address) {
273
+ fee += ";" + this.bribeData.address;
274
+ }
275
+ this.logger.debug("getFeeRate(): calculated fee: " + fee);
276
+ return fee;
277
+ }
278
+ /**
279
+ * Calculates the total priority fee paid for a given compute budget at a given fee rate
280
+ *
281
+ * @param computeUnits
282
+ * @param feeRate
283
+ * @param includeStaticFee whether the include the static/base part of the fee rate
284
+ */
285
+ getPriorityFee(computeUnits, feeRate, includeStaticFee = true) {
286
+ if (feeRate == null)
287
+ return 0n;
288
+ const hashArr = feeRate.split("#");
289
+ if (hashArr.length > 1) {
290
+ feeRate = hashArr[0];
291
+ }
292
+ const arr = feeRate.split(";");
293
+ const cuPrice = BigInt(arr[0]);
294
+ const staticFee = includeStaticFee ? BigInt(arr[1]) : 0n;
295
+ return staticFee + (cuPrice * BigInt(computeUnits) / 1000000n);
296
+ }
297
+ /**
298
+ * Applies fee rate to a transaction at the beginning of the transaction (has to be called after
299
+ * feePayer is set for the tx), specifically adds the setComputeUnitLimit & setComputeUnitPrice instruction
300
+ *
301
+ * @param tx
302
+ * @param computeBudget
303
+ * @param feeRate
304
+ */
305
+ applyFeeRateBegin(tx, computeBudget, feeRate) {
306
+ if (feeRate == null)
307
+ return false;
308
+ const hashArr = feeRate.split("#");
309
+ if (hashArr.length > 1) {
310
+ feeRate = hashArr[0];
311
+ }
312
+ if (computeBudget != null && computeBudget > 0)
313
+ tx.add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({
314
+ units: computeBudget,
315
+ }));
316
+ //Check if bribe is included
317
+ const arr = feeRate.split(";");
318
+ if (arr.length > 2) {
319
+ }
320
+ else {
321
+ let fee = BigInt(arr[0]);
322
+ if (arr.length > 1) {
323
+ const staticFee = BigInt(arr[1]);
324
+ const cuBigInt = BigInt(computeBudget || (200000 * Utils_1.SolanaTxUtils.getNonComputeBudgetIxs(tx)));
325
+ const staticFeePerCU = staticFee * BigInt(1000000) / cuBigInt;
326
+ fee += staticFeePerCU;
327
+ }
328
+ tx.add(web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
329
+ microLamports: fee
330
+ }));
331
+ }
332
+ }
333
+ /**
334
+ * Applies fee rate to the end of the transaction (has to be called after feePayer is set for the tx),
335
+ * specifically adds the bribe SystemProgram.transfer instruction
336
+ *
337
+ * @param tx
338
+ * @param computeBudget
339
+ * @param feeRate
340
+ */
341
+ applyFeeRateEnd(tx, computeBudget, feeRate) {
342
+ if (feeRate == null)
343
+ return false;
344
+ const hashArr = feeRate.split("#");
345
+ if (hashArr.length > 1) {
346
+ feeRate = hashArr[0];
347
+ }
348
+ //Check if bribe is included
349
+ const arr = feeRate.split(";");
350
+ if (arr.length > 2) {
351
+ const cuBigInt = BigInt(computeBudget || (200000 * (Utils_1.SolanaTxUtils.getNonComputeBudgetIxs(tx) + 1)));
352
+ const cuPrice = BigInt(arr[0]);
353
+ const staticFee = BigInt(arr[1]);
354
+ const bribeAddress = new web3_js_1.PublicKey(arr[2]);
355
+ tx.add(web3_js_1.SystemProgram.transfer({
356
+ fromPubkey: tx.feePayer,
357
+ toPubkey: bribeAddress,
358
+ lamports: staticFee + (cuBigInt * cuPrice / BigInt(1000000))
359
+ }));
360
+ return;
361
+ }
362
+ }
363
+ /**
364
+ * Checks if the transaction should be submitted over Jito and if yes submits it
365
+ *
366
+ * @param tx
367
+ * @param options
368
+ * @returns {Promise<string | null>} null if the transaction was not sent over Jito, tx signature when tx was sent over Jito
369
+ */
370
+ submitTx(tx, options) {
371
+ const parsedTx = web3_js_1.Transaction.from(tx);
372
+ const jitoFee = this.getJitoTxFee(parsedTx);
373
+ if (jitoFee == null)
374
+ return null;
375
+ this.logger.info("submitTx(): sending tx over Jito, signature: " + parsedTx.signature + " fee: " + jitoFee.toString(10));
376
+ return this.sendJitoTx(tx, options);
377
+ }
378
+ }
379
+ exports.SolanaFees = SolanaFees;