From aa76e85fbac06e9f67b1f98a6e4993d43c06e988 Mon Sep 17 00:00:00 2001 From: Kozer4 Date: Thu, 7 Mar 2024 11:06:41 +0200 Subject: [PATCH] feat(BuildingTx): Solana add txFeeParams?: TxFeeParams --- .../bridge/solana/sol-build-send-tx.ts | 25 ++-- .../bridge/solana/sol-build-swap-tx.ts | 6 +- .../solana/sol-build-claim-rewards-tx.ts | 11 +- .../solana/sol-build-deposit-tx.ts | 11 +- .../solana/sol-build-withdraw-tx.ts | 10 +- .../data/nock/get-user-balance-info-rec.json | 61 --------- .../services/liquidity-pool/evm/index.test.ts | 19 +-- src/models/index.ts | 3 +- src/services/bridge/evm/index.ts | 2 +- src/services/bridge/models/bridge.model.ts | 4 +- src/services/bridge/sol/index.ts | 39 ++---- src/services/bridge/srb/index.ts | 2 +- src/services/bridge/trx/index.ts | 2 +- .../liquidity-pool/models/pool.model.ts | 5 +- src/services/liquidity-pool/sol/index.ts | 18 +-- src/services/models/index.ts | 32 +++-- src/services/models/sol/index.ts | 20 +++ src/services/utils/sol/compute-budget.ts | 113 ++++++++++++++++ src/utils/sol/index.ts | 122 +----------------- 19 files changed, 226 insertions(+), 279 deletions(-) delete mode 100644 src/__tests__/services/liquidity-pool/evm/data/nock/get-user-balance-info-rec.json create mode 100644 src/services/utils/sol/compute-budget.ts diff --git a/examples/src/examples/bridge/solana/sol-build-send-tx.ts b/examples/src/examples/bridge/solana/sol-build-send-tx.ts index fd2b8135..957fb438 100644 --- a/examples/src/examples/bridge/solana/sol-build-send-tx.ts +++ b/examples/src/examples/bridge/solana/sol-build-send-tx.ts @@ -1,8 +1,15 @@ -import { AllbridgeCoreSdk, ChainSymbol, Messenger, nodeUrlsDefault } from "@allbridge/bridge-core-sdk"; +import { + AllbridgeCoreSdk, + ChainSymbol, + Messenger, + nodeUrlsDefault, + RawBridgeSolanaTransaction, + SolanaAutoTxFee, +} from "@allbridge/bridge-core-sdk"; import * as dotenv from "dotenv"; import { getEnvVar } from "../../../utils/env"; import { ensure } from "../../../utils/utils"; -import solanaWeb3, { VersionedTransaction, Keypair } from "@solana/web3.js"; +import solanaWeb3 from "@solana/web3.js"; import bs58 from "bs58"; dotenv.config({ path: ".env" }); @@ -23,20 +30,20 @@ const exampleViaWormhole = async () => { const destinationTokenInfoPol = ensure(destinationChainPol.tokens.find((tokenInfo) => tokenInfo.symbol === "USDC")); // initiate transfer using Messenger.WORMHOLE - const { transaction, wormholeMessageSigner } = (await sdk.bridge.rawTxBuilder.send({ + const transaction = (await sdk.bridge.rawTxBuilder.send({ amount: "0.2", fromAccountAddress: fromAddress, toAccountAddress: toAddressPol, sourceToken: sourceTokenInfo, destinationToken: destinationTokenInfoPol, messenger: Messenger.WORMHOLE, - })) as { transaction: VersionedTransaction; wormholeMessageSigner: Keypair }; - - await sdk.utils.sol.addPriorityFeeToTx(transaction, "100000"); - // await sdk.utils.sol.addPriorityFeePerUnitToTx(transaction); + txFeeParams: { + solana: SolanaAutoTxFee, + }, + })) as RawBridgeSolanaTransaction; const keypair = solanaWeb3.Keypair.fromSecretKey(bs58.decode(privateKey)); - transaction.sign([wormholeMessageSigner, keypair]); + transaction.sign([keypair]); const connection = new solanaWeb3.Connection(nodeUrlsDefault.solanaRpcUrl, "confirmed"); const txid = await connection.sendTransaction(transaction); @@ -62,7 +69,7 @@ const exampleViaAllbridge = async () => { sourceToken: sourceTokenInfo, destinationToken: destinationTokenInfoPol, messenger: Messenger.ALLBRIDGE, - })) as VersionedTransaction; + })) as RawBridgeSolanaTransaction; const keypair = solanaWeb3.Keypair.fromSecretKey(bs58.decode(privateKey)); transaction.sign([keypair]); diff --git a/examples/src/examples/bridge/solana/sol-build-swap-tx.ts b/examples/src/examples/bridge/solana/sol-build-swap-tx.ts index a3b0fa17..f8fa9d56 100644 --- a/examples/src/examples/bridge/solana/sol-build-swap-tx.ts +++ b/examples/src/examples/bridge/solana/sol-build-swap-tx.ts @@ -1,8 +1,8 @@ -import { AllbridgeCoreSdk, ChainSymbol, nodeUrlsDefault } from "@allbridge/bridge-core-sdk"; +import { AllbridgeCoreSdk, ChainSymbol, nodeUrlsDefault, RawSolanaTransaction } from "@allbridge/bridge-core-sdk"; import * as dotenv from "dotenv"; import { getEnvVar } from "../../../utils/env"; import { ensure } from "../../../utils/utils"; -import solanaWeb3, { VersionedTransaction } from "@solana/web3.js"; +import solanaWeb3 from "@solana/web3.js"; import bs58 from "bs58"; dotenv.config({ path: ".env" }); @@ -30,7 +30,7 @@ const example = async () => { sourceToken: sourceTokenInfo, destinationToken: destinationTokenInfo, minimumReceiveAmount: await sdk.getAmountToBeReceived(amount, sourceTokenInfo, destinationTokenInfo), - })) as VersionedTransaction; + })) as RawSolanaTransaction; const keypair = solanaWeb3.Keypair.fromSecretKey(bs58.decode(privateKey)); transaction.sign([keypair]); diff --git a/examples/src/examples/liquidity-pool/solana/sol-build-claim-rewards-tx.ts b/examples/src/examples/liquidity-pool/solana/sol-build-claim-rewards-tx.ts index 497da20d..cf663da4 100644 --- a/examples/src/examples/liquidity-pool/solana/sol-build-claim-rewards-tx.ts +++ b/examples/src/examples/liquidity-pool/solana/sol-build-claim-rewards-tx.ts @@ -1,9 +1,10 @@ import * as dotenv from "dotenv"; import { getEnvVar } from "../../../utils/env"; -import { AllbridgeCoreSdk, nodeUrlsDefault, RawTransaction } from "@allbridge/bridge-core-sdk"; +import { AllbridgeCoreSdk, nodeUrlsDefault, RawPoolSolanaTransaction } from "@allbridge/bridge-core-sdk"; import { ensure } from "../../../utils/utils"; import solanaWeb3, { sendAndConfirmTransaction, Transaction } from "@solana/web3.js"; import bs58 from "bs58"; + dotenv.config({ path: ".env" }); const main = async () => { @@ -16,20 +17,20 @@ const main = async () => { const tokenInfo = ensure((await sdk.tokens()).find((tokenInfo) => tokenInfo.tokenAddress === tokenAddress)); // create claim rewards raw transaction - const transaction = await sdk.pool.rawTxBuilder.claimRewards({ + const transaction = (await sdk.pool.rawTxBuilder.claimRewards({ accountAddress: accountAddress, token: tokenInfo, - }); + })) as RawPoolSolanaTransaction; const tx = await sendRawTransaction(transaction, privateKey, nodeUrlsDefault.solanaRpcUrl); console.log("Token claim rewards:", tx); }; -async function sendRawTransaction(transaction: RawTransaction, privateKey: string, solanaRpcUrl: string) { +async function sendRawTransaction(transaction: Transaction, privateKey: string, solanaRpcUrl: string) { const keypair = solanaWeb3.Keypair.fromSecretKey(bs58.decode(privateKey)); const connection = new solanaWeb3.Connection(solanaRpcUrl, "confirmed"); - return await sendAndConfirmTransaction(connection, transaction as Transaction, [keypair]); + return await sendAndConfirmTransaction(connection, transaction, [keypair]); } main() diff --git a/examples/src/examples/liquidity-pool/solana/sol-build-deposit-tx.ts b/examples/src/examples/liquidity-pool/solana/sol-build-deposit-tx.ts index d672067d..e9b079c6 100644 --- a/examples/src/examples/liquidity-pool/solana/sol-build-deposit-tx.ts +++ b/examples/src/examples/liquidity-pool/solana/sol-build-deposit-tx.ts @@ -1,9 +1,10 @@ import * as dotenv from "dotenv"; import { getEnvVar } from "../../../utils/env"; -import { AllbridgeCoreSdk, nodeUrlsDefault, RawTransaction } from "@allbridge/bridge-core-sdk"; +import { AllbridgeCoreSdk, nodeUrlsDefault, RawPoolSolanaTransaction } from "@allbridge/bridge-core-sdk"; import { ensure } from "../../../utils/utils"; import solanaWeb3, { sendAndConfirmTransaction, Transaction } from "@solana/web3.js"; import bs58 from "bs58"; + dotenv.config({ path: ".env" }); const main = async () => { @@ -17,21 +18,21 @@ const main = async () => { const oneToken = "1"; // create deposit raw transaction - const transaction = await sdk.pool.rawTxBuilder.deposit({ + const transaction = (await sdk.pool.rawTxBuilder.deposit({ amount: oneToken, accountAddress: accountAddress, token: tokenInfo, - }); + })) as RawPoolSolanaTransaction; const tx = await sendRawTransaction(transaction, privateKey, nodeUrlsDefault.solanaRpcUrl); console.log("Token deposit:", tx); }; -async function sendRawTransaction(transaction: RawTransaction, privateKey: string, solanaRpcUrl: string) { +async function sendRawTransaction(transaction: Transaction, privateKey: string, solanaRpcUrl: string) { const keypair = solanaWeb3.Keypair.fromSecretKey(bs58.decode(privateKey)); const connection = new solanaWeb3.Connection(solanaRpcUrl, "confirmed"); - return await sendAndConfirmTransaction(connection, transaction as Transaction, [keypair]); + return await sendAndConfirmTransaction(connection, transaction, [keypair]); } main() diff --git a/examples/src/examples/liquidity-pool/solana/sol-build-withdraw-tx.ts b/examples/src/examples/liquidity-pool/solana/sol-build-withdraw-tx.ts index 2fcc65e4..77d8b8a7 100644 --- a/examples/src/examples/liquidity-pool/solana/sol-build-withdraw-tx.ts +++ b/examples/src/examples/liquidity-pool/solana/sol-build-withdraw-tx.ts @@ -1,6 +1,6 @@ import * as dotenv from "dotenv"; import { getEnvVar } from "../../../utils/env"; -import { AllbridgeCoreSdk, nodeUrlsDefault, RawTransaction } from "@allbridge/bridge-core-sdk"; +import { AllbridgeCoreSdk, nodeUrlsDefault, RawPoolSolanaTransaction } from "@allbridge/bridge-core-sdk"; import { ensure } from "../../../utils/utils"; import solanaWeb3, { sendAndConfirmTransaction, Transaction } from "@solana/web3.js"; import bs58 from "bs58"; @@ -17,21 +17,21 @@ const main = async () => { const halfToken = "0.5"; // create withdraw raw transaction - const transaction = await sdk.pool.rawTxBuilder.withdraw({ + const transaction = (await sdk.pool.rawTxBuilder.withdraw({ amount: halfToken, accountAddress: accountAddress, token: tokenInfo, - }); + })) as RawPoolSolanaTransaction; const tx = await sendRawTransaction(transaction, privateKey, nodeUrlsDefault.solanaRpcUrl); console.log("Token withdraw:", tx); }; -async function sendRawTransaction(transaction: RawTransaction, privateKey: string, solanaRpcUrl: string) { +async function sendRawTransaction(transaction: Transaction, privateKey: string, solanaRpcUrl: string) { const keypair = solanaWeb3.Keypair.fromSecretKey(bs58.decode(privateKey)); const connection = new solanaWeb3.Connection(solanaRpcUrl, "confirmed"); - return await sendAndConfirmTransaction(connection, transaction as Transaction, [keypair]); + return await sendAndConfirmTransaction(connection, transaction, [keypair]); } main() diff --git a/src/__tests__/services/liquidity-pool/evm/data/nock/get-user-balance-info-rec.json b/src/__tests__/services/liquidity-pool/evm/data/nock/get-user-balance-info-rec.json deleted file mode 100644 index 609460e6..00000000 --- a/src/__tests__/services/liquidity-pool/evm/data/nock/get-user-balance-info-rec.json +++ /dev/null @@ -1,61 +0,0 @@ -[ - { - "scope": "https://goerli.infura.io:443", - "method": "POST", - "path": "/", - "body": [ - { - "jsonrpc": "2.0", - "id": 8551125359729656, - "method": "eth_call", - "params": [ - { - "data": "0x4bf6f9e7000000000000000000000000b3a88d47eeda762610c4d86ea6c8562288d53dfa", - "to": "0x57fb363e8e96b086cc16e0f35b369a14b1ac1ac2" - }, - "latest" - ] - }, - { - "jsonrpc": "2.0", - "id": 8551125359729657, - "method": "eth_call", - "params": [ - { - "data": "0x70a08231000000000000000000000000b3a88d47eeda762610c4d86ea6c8562288d53dfa", - "to": "0x57fb363e8e96b086cc16e0f35b369a14b1ac1ac2" - }, - "latest" - ] - } - ], - "status": 200, - "response": [ - { - "jsonrpc": "2.0", - "id": 8551125359729656, - "result": "0x000000000000000000000000000000000000000000000000000e3a80f4aa9219" - }, - { - "jsonrpc": "2.0", - "id": 8551125359729657, - "result": "0x0000000000000000000000000000000000000000000000000000000000002702" - } - ], - "rawHeaders": [ - "Date", - "Thu, 13 Jul 2023 07:34:18 GMT", - "Content-Type", - "application/json", - "Content-Length", - "237", - "Connection", - "keep-alive", - "Vary", - "Origin", - "Vary", - "Accept-Encoding" - ], - "responseIsBinary": false - } -] \ No newline at end of file diff --git a/src/__tests__/services/liquidity-pool/evm/index.test.ts b/src/__tests__/services/liquidity-pool/evm/index.test.ts index d2eb2369..902cc30b 100644 --- a/src/__tests__/services/liquidity-pool/evm/index.test.ts +++ b/src/__tests__/services/liquidity-pool/evm/index.test.ts @@ -1,5 +1,4 @@ -import * as fs from "fs"; -import nock, { abortPendingRequests, cleanAll, disableNetConnect, load } from "nock"; +import { abortPendingRequests, cleanAll, disableNetConnect } from "nock"; import Web3 from "web3"; import { ChainSymbol } from "../../../../chains"; import { AllbridgeCoreClient } from "../../../../client/core-api"; @@ -88,19 +87,3 @@ describe("EvmPool", () => { }); }); }); - -function nockRequests(recName: string) { - const nocks = load(`./src/__tests__/services/liquidity-pool/evm/data/nock/${recName}-rec.json`); - nocks.forEach(function (nock) { - nock.filteringRequestBody((b) => { - try { - const body = JSON.parse(b); - body[0].id = 8551125359729656; - body[1].id = 8551125359729657; - return JSON.stringify(body); - } catch (e) { - return b; - } - }); - }); -} diff --git a/src/models/index.ts b/src/models/index.ts index d8ebe1e9..927d5b33 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -10,7 +10,6 @@ export { } from "../services/bridge/models/bridge.model"; export { BridgeService } from "../services/bridge/index"; export { LiquidityPoolService } from "../services/liquidity-pool/index"; -export { TransactionResponse } from "../services/models/index"; export { Messenger, TransferStatusResponse, @@ -38,7 +37,7 @@ export { CheckAllowanceParams as LiquidityPoolsCheckAllowanceParams, GetAllowanceParams as LiquidityPoolsGetAllowanceParams, } from "../services/liquidity-pool/models/pool.model"; -export { Provider, RawTransaction } from "../services/models/index"; +export * from "../services/models/index"; export { SwapAndBridgeCalculationData, SwapFromVUsdCalcResult, diff --git a/src/services/bridge/evm/index.ts b/src/services/bridge/evm/index.ts index 97e39f82..592aaa46 100644 --- a/src/services/bridge/evm/index.ts +++ b/src/services/bridge/evm/index.ts @@ -1,4 +1,4 @@ -import Big from "big.js"; +import { Big } from "big.js"; import BN from "bn.js"; import Web3 from "web3"; import { TransactionConfig } from "web3-core"; diff --git a/src/services/bridge/models/bridge.model.ts b/src/services/bridge/models/bridge.model.ts index d65ae74b..6ff04554 100644 --- a/src/services/bridge/models/bridge.model.ts +++ b/src/services/bridge/models/bridge.model.ts @@ -1,7 +1,7 @@ import { Big } from "big.js"; import { ChainSymbol } from "../../../chains"; import { Messenger } from "../../../client/core-api/core-api.model"; -import { AmountFormat, FeePaymentMethod } from "../../../models"; +import { AmountFormat, FeePaymentMethod, TxFeeParams } from "../../../models"; import { TokenWithChainDetails } from "../../../tokens-info"; export interface ApproveParams { @@ -72,6 +72,8 @@ export interface BaseSendParams { * {@link TokenWithChainDetails |The token info object} on the destination chain. */ destinationToken: TokenWithChainDetails; + + txFeeParams?: TxFeeParams; } /** diff --git a/src/services/bridge/sol/index.ts b/src/services/bridge/sol/index.ts index ecaa6de9..1c3ecaa4 100644 --- a/src/services/bridge/sol/index.ts +++ b/src/services/bridge/sol/index.ts @@ -11,7 +11,7 @@ import { TransactionMessageArgs, VersionedTransaction, } from "@solana/web3.js"; -import Big from "big.js"; +import { Big } from "big.js"; import { ChainDecimalsByType, ChainType } from "../../../chains"; import { AllbridgeCoreClient } from "../../../client/core-api"; import { Messenger } from "../../../client/core-api/core-api.model"; @@ -23,7 +23,7 @@ import { SdkError, SdkRootError, } from "../../../exceptions"; -import { FeePaymentMethod, SwapParams } from "../../../models"; +import { FeePaymentMethod, SwapParams, TxFeeParams } from "../../../models"; import { convertIntAmountToFloat } from "../../../utils/calculation"; import { RawTransaction, TransactionResponse } from "../../models"; import { SwapAndBridgeSolData } from "../../models/sol"; @@ -41,6 +41,7 @@ import { getPriceAccount, getSendMessageAccount, } from "../../utils/sol/accounts"; +import { addUnitLimitAndUnitPriceToTx, addUnitLimitAndUnitPriceToVersionedTx } from "../../utils/sol/compute-budget"; import { SendParams, TxSendParams, TxSwapParams } from "../models"; import { ChainBridgeService } from "../models/bridge"; import { getNonce, prepareTxSendParams, prepareTxSwapParams } from "../utils"; @@ -51,8 +52,6 @@ export interface SolanaBridgeParams { solanaLookUpTable: string; } -const COMPUTE_UNIT_LIMIT = 1000000; - export class SolanaBridgeService extends ChainBridgeService { chainType: ChainType.SOLANA = ChainType.SOLANA; jupiterService: JupiterService; @@ -64,18 +63,19 @@ export class SolanaBridgeService extends ChainBridgeService { async buildRawTransactionSwap(params: SwapParams): Promise { const txSwapParams = prepareTxSwapParams(this.chainType, params); - const transaction = await this.buildSwapTransaction( + return await this.buildSwapTransaction( txSwapParams, params.sourceToken.poolAddress, - params.destinationToken.poolAddress + params.destinationToken.poolAddress, + params.txFeeParams ); - return { transaction }; } private async buildSwapTransaction( params: TxSwapParams, poolAddress: string, - toPoolAddress: string + toPoolAddress: string, + txFeeParams?: TxFeeParams ): Promise { const { fromAccountAddress, @@ -111,11 +111,7 @@ export class SolanaBridgeService extends ChainBridgeService { const receivePool = new PublicKey(receivePoolAddress); const receiveUserToken = await getAssociatedAccount(receiverAccount, receiveMint); - const preInstructions: TransactionInstruction[] = [ - web3.ComputeBudgetProgram.setComputeUnitLimit({ - units: COMPUTE_UNIT_LIMIT, - }), - ]; + const preInstructions: TransactionInstruction[] = []; try { await getTokenAccountData(receiveUserToken, provider); @@ -153,6 +149,7 @@ export class SolanaBridgeService extends ChainBridgeService { const connection = provider.connection; transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; transaction.feePayer = userAccount; + await addUnitLimitAndUnitPriceToTx(transaction, txFeeParams, this.solanaRpcUrl); return await this.convertToVersionedTransaction(transaction, connection); } @@ -227,10 +224,12 @@ export class SolanaBridgeService extends ChainBridgeService { swapAndBridgeTx = await this.jupiterService.amendJupiterWithSdkTx(jupTx, swapAndBridgeTx); } + await addUnitLimitAndUnitPriceToVersionedTx(swapAndBridgeTx, params.txFeeParams, this.solanaRpcUrl); + if (wormMessageSigner) { swapAndBridgeTx.sign([wormMessageSigner]); } - return { transaction: swapAndBridgeTx, wormholeMessageSigner: wormMessageSigner }; + return swapAndBridgeTx; } private addPoolAddress(params: SendParams, txSendParams: TxSendParams): SolTxSendParams { @@ -432,11 +431,6 @@ export class SolanaBridgeService extends ChainBridgeService { sentMessageAccount, otherBridgeToken: otherBridgeTokenAccount, }) - .preInstructions([ - web3.ComputeBudgetProgram.setComputeUnitLimit({ - units: COMPUTE_UNIT_LIMIT, - }), - ]) .postInstructions(instructions) .transaction(); const connection = this.buildAnchorProvider(userAccount.toString()).connection; @@ -560,12 +554,7 @@ export class SolanaBridgeService extends ChainBridgeService { receiveToken, }) .accounts(accounts) - .preInstructions([ - web3.ComputeBudgetProgram.setComputeUnitLimit({ - units: COMPUTE_UNIT_LIMIT, - }), - feeInstruction, - ]) + .preInstructions([feeInstruction]) .postInstructions(instructions) .signers([messageAccount]) .transaction(); diff --git a/src/services/bridge/srb/index.ts b/src/services/bridge/srb/index.ts index d164b3f4..5c7076ab 100644 --- a/src/services/bridge/srb/index.ts +++ b/src/services/bridge/srb/index.ts @@ -1,4 +1,4 @@ -import Big from "big.js"; +import { Big } from "big.js"; import { Address } from "stellar-sdk"; import { ChainSymbol, ChainType } from "../../../chains"; import { AllbridgeCoreClient } from "../../../client/core-api"; diff --git a/src/services/bridge/trx/index.ts b/src/services/bridge/trx/index.ts index 1aa07d88..225173a1 100644 --- a/src/services/bridge/trx/index.ts +++ b/src/services/bridge/trx/index.ts @@ -1,4 +1,4 @@ -import Big from "big.js"; +import { Big } from "big.js"; // @ts-expect-error import tron import TronWeb from "tronweb"; import { ChainType } from "../../../chains"; diff --git a/src/services/liquidity-pool/models/pool.model.ts b/src/services/liquidity-pool/models/pool.model.ts index dd57f314..f9bfe109 100644 --- a/src/services/liquidity-pool/models/pool.model.ts +++ b/src/services/liquidity-pool/models/pool.model.ts @@ -1,5 +1,5 @@ import { Big } from "big.js"; -import { FeePaymentMethod } from "../../../models"; +import { FeePaymentMethod, TxFeeParams } from "../../../models"; import { PoolInfo, TokenWithChainDetails } from "../../../tokens-info"; import { convertIntAmountToFloat, getEarned } from "../../../utils/calculation"; import { SYSTEM_PRECISION } from "../../../utils/calculation/constants"; @@ -47,6 +47,8 @@ export interface LiquidityPoolsParams { * {@link TokenWithChainDetails |The token info object} of operation token. */ token: TokenWithChainDetails; + + txFeeParams?: TxFeeParams; } export interface LiquidityPoolsParamsWithAmount extends LiquidityPoolsParams { @@ -63,6 +65,7 @@ export interface UserBalanceInfoDTO { export interface UserBalanceInfo extends UserBalanceInfoDTO { userLiquidity: string; + earned(poolInfo: PoolInfo, decimals?: number): string; } diff --git a/src/services/liquidity-pool/sol/index.ts b/src/services/liquidity-pool/sol/index.ts index b638109b..f1b5add4 100644 --- a/src/services/liquidity-pool/sol/index.ts +++ b/src/services/liquidity-pool/sol/index.ts @@ -1,4 +1,4 @@ -import { AnchorProvider, BN, Program, Provider, Spl, web3 } from "@project-serum/anchor"; +import { AnchorProvider, BN, Program, Provider, Spl } from "@project-serum/anchor"; import { Connection, PublicKey, TransactionInstruction } from "@solana/web3.js"; import { ChainType } from "../../../chains"; import { AllbridgeCoreClient } from "../../../client/core-api"; @@ -14,6 +14,7 @@ import { getConfigAccount, getUserDepositAccount, } from "../../utils/sol/accounts"; +import { addUnitLimitAndUnitPriceToTx } from "../../utils/sol/compute-budget"; import { LiquidityPoolsParams, LiquidityPoolsParamsWithAmount, UserBalance, UserBalanceInfo } from "../models"; import { ChainPoolService } from "../models/pool"; @@ -92,7 +93,8 @@ export class SolanaPoolService extends ChainPoolService { await this.buildAnchorProvider(params.accountAddress).connection.getLatestBlockhash() ).blockhash; tx.feePayer = new PublicKey(params.accountAddress); - return { transaction: tx }; + await addUnitLimitAndUnitPriceToTx(tx, params.txFeeParams, this.solanaRpcUrl); + return tx; } async buildRawTransactionWithdraw(params: LiquidityPoolsParamsWithAmount): Promise { @@ -107,7 +109,8 @@ export class SolanaPoolService extends ChainPoolService { await this.buildAnchorProvider(params.accountAddress).connection.getLatestBlockhash() ).blockhash; tx.feePayer = new PublicKey(params.accountAddress); - return { transaction: tx }; + await addUnitLimitAndUnitPriceToTx(tx, params.txFeeParams, this.solanaRpcUrl); + return tx; } async buildRawTransactionClaimRewards(params: LiquidityPoolsParams): Promise { @@ -118,7 +121,8 @@ export class SolanaPoolService extends ChainPoolService { await this.buildAnchorProvider(params.accountAddress).connection.getLatestBlockhash() ).blockhash; tx.feePayer = new PublicKey(params.accountAddress); - return { transaction: tx }; + await addUnitLimitAndUnitPriceToTx(tx, params.txFeeParams, this.solanaRpcUrl); + return tx; } private async prepareDataForTransaction(params: LiquidityPoolsParams) { @@ -170,11 +174,7 @@ export class SolanaPoolService extends ChainPoolService { const bridgeTokenAccount = await getBridgeTokenAccount(tokenMintAccount, bridge.programId); const userDepositAccount = await getUserDepositAccount(user, tokenMintAccount, bridge.programId); - const preInstructions: TransactionInstruction[] = [ - web3.ComputeBudgetProgram.setComputeUnitLimit({ - units: 1000000, - }), - ]; + const preInstructions: TransactionInstruction[] = []; try { await getTokenAccountData(userToken, provider); diff --git a/src/services/models/index.ts b/src/services/models/index.ts index ea6f3e4e..77d421c5 100644 --- a/src/services/models/index.ts +++ b/src/services/models/index.ts @@ -1,8 +1,18 @@ -import { Keypair, VersionedTransaction } from "@solana/web3.js"; +import { Transaction, VersionedTransaction } from "@solana/web3.js"; import type { TronWeb } from "tronweb-typings"; import type Web3 from "web3"; import { TransactionConfig } from "web3-core"; +import { SolanaTxFee } from "./sol"; + +export { SolanaTxFee, PricePerUnitInMicroLamports, ExtraFeeInLamports, SolanaAutoTxFee } from "./sol"; + +/** + * Blockchain fee added to tx + */ +export interface TxFeeParams { + solana?: SolanaTxFee; +} /** * The provider is type that combines connection implementations for different chains.
@@ -10,17 +20,17 @@ import { TransactionConfig } from "web3-core"; */ export type Provider = Web3 | TronWeb; -/** - * EVM TransactionConfig - * Tron Object - * Soroban string - * Solana {transaction: VersionedTransaction; wormholeMessageSigner?: Keypair} - */ export type RawTransaction = - | Object - | TransactionConfig - | string - | { transaction: VersionedTransaction; wormholeMessageSigner?: Keypair }; + | RawTronTransaction + | RawEvmTransaction + | RawSorobanTransaction + | RawBridgeSolanaTransaction + | RawPoolSolanaTransaction; +export type RawEvmTransaction = TransactionConfig; +export type RawTronTransaction = Object; +export type RawSorobanTransaction = string; +export type RawPoolSolanaTransaction = Transaction; +export type RawBridgeSolanaTransaction = VersionedTransaction; export interface SmartContractMethodParameter { type: string; diff --git a/src/services/models/sol/index.ts b/src/services/models/sol/index.ts index 83e553f4..a6dd6fea 100644 --- a/src/services/models/sol/index.ts +++ b/src/services/models/sol/index.ts @@ -2,6 +2,26 @@ import { BN, Program } from "@project-serum/anchor"; import { PublicKey, TransactionInstruction } from "@solana/web3.js"; import { Bridge as BridgeType } from "./types/bridge"; +export type SolanaTxFee = PricePerUnitInMicroLamports | ExtraFeeInLamports | typeof SolanaAutoTxFee; +/** + * Priority Fee will be calculated based on {@link Connection#getRecentPrioritizationFees} + */ +export const SolanaAutoTxFee = "AUTO" as const; + +/** + * Add Priority Fee as price per unit in micro-lamports + */ +export interface PricePerUnitInMicroLamports { + pricePerUnitInMicroLamports: string; +} + +/** + * Total Priority Fee impact will be as extraFeeInLamports param + */ +export interface ExtraFeeInLamports { + extraFeeInLamports: string; +} + export interface SwapAndBridgeSolData { bridge: Program; amount: BN; diff --git a/src/services/utils/sol/compute-budget.ts b/src/services/utils/sol/compute-budget.ts new file mode 100644 index 00000000..b88a6297 --- /dev/null +++ b/src/services/utils/sol/compute-budget.ts @@ -0,0 +1,113 @@ +import { + ComputeBudgetInstruction, + ComputeBudgetProgram, + Connection, + Transaction, + TransactionInstruction, + TransactionMessage, + VersionedTransaction, +} from "@solana/web3.js"; +import { Big } from "big.js"; +import { toPowBase10 } from "../../../utils/calculation"; +import { fetchAddressLookupTableAccountsFromTx } from "../../../utils/sol/utils"; +import { SolanaAutoTxFee, TxFeeParams } from "../../models"; + +export async function addUnitLimitAndUnitPriceToTx( + transaction: Transaction, + txFeeParams: TxFeeParams | undefined, + solanaRpcUrl: string +) { + const connection = new Connection(solanaRpcUrl, "confirmed"); + /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ + const simUnitsConsumed = (await connection.simulateTransaction(transaction)).value.unitsConsumed!; + await addUnitLimitAndUnitPriceToInstructions(transaction.instructions, simUnitsConsumed, txFeeParams, connection); +} + +export async function addUnitLimitAndUnitPriceToVersionedTx( + transaction: VersionedTransaction, + txFeeParams: TxFeeParams | undefined, + solanaRpcUrl: string +) { + const connection = new Connection(solanaRpcUrl, "confirmed"); + const addressLookupTableAccounts = await fetchAddressLookupTableAccountsFromTx(transaction, connection); + const message = TransactionMessage.decompile(transaction.message, { + addressLookupTableAccounts: addressLookupTableAccounts, + }); + + /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ + const simUnitsConsumed = (await connection.simulateTransaction(transaction)).value.unitsConsumed!; + await addUnitLimitAndUnitPriceToInstructions(message.instructions, simUnitsConsumed, txFeeParams, connection); + + transaction.message = message.compileToV0Message(addressLookupTableAccounts); +} + +async function addUnitLimitAndUnitPriceToInstructions( + instructions: TransactionInstruction[], + simUnitsConsumed: number, + txFeeParams: TxFeeParams | undefined, + connection: Connection +) { + const units = updateUnitLimit(simUnitsConsumed, instructions); + if (txFeeParams?.solana) { + const solanaTxFee = txFeeParams.solana; + if (solanaTxFee === SolanaAutoTxFee) { + await updateUnitPrice(instructions, connection); + } else if ("pricePerUnitInMicroLamports" in solanaTxFee) { + await updateUnitPrice(instructions, connection, solanaTxFee.pricePerUnitInMicroLamports); + } else { + const pricePerUnitInMicroLamports = Big(solanaTxFee.extraFeeInLamports).div(units).mul(toPowBase10(6)).toFixed(0); + await updateUnitPrice(instructions, connection, pricePerUnitInMicroLamports); + } + } +} + +function updateUnitLimit(simUnitsConsumed: number, instructions: TransactionInstruction[]): string { + const computeUnitLimitIndex = instructions.findIndex( + (instruction) => + instruction.programId.equals(ComputeBudgetProgram.programId) && + ComputeBudgetInstruction.decodeInstructionType(instruction) === "SetComputeUnitLimit" + ); + const units = Number((simUnitsConsumed * 1.3).toFixed(0)); + const computeUnitLimitInstruction = ComputeBudgetProgram.setComputeUnitLimit({ + units: units, + }); + if (computeUnitLimitIndex >= 0) { + instructions[computeUnitLimitIndex] = computeUnitLimitInstruction; + } else { + instructions.push(computeUnitLimitInstruction); + } + return units.toString(); +} + +async function updateUnitPrice( + instructions: TransactionInstruction[], + connection: Connection, + pricePerUnitInMicroLamports?: string +): Promise { + const computeUnitPriceIndex = instructions.findIndex( + (instruction) => + instruction.programId.equals(ComputeBudgetProgram.programId) && + ComputeBudgetInstruction.decodeInstructionType(instruction) === "SetComputeUnitPrice" + ); + const unitPrice = pricePerUnitInMicroLamports + ? BigInt(pricePerUnitInMicroLamports) + : BigInt(await getAveragePrioritizationFee(connection)); + const computeUnitPriceInstruction = ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: unitPrice, + }); + if (computeUnitPriceIndex >= 0) { + instructions[computeUnitPriceIndex] = computeUnitPriceInstruction; + } else { + instructions.push(computeUnitPriceInstruction); + } + return unitPrice.toString(); +} + +async function getAveragePrioritizationFee(connection: Connection) { + const prioritizationFees = await connection.getRecentPrioritizationFees(); + let sum = 0; + for (const prioritizationFee of prioritizationFees) { + sum += prioritizationFee.prioritizationFee; + } + return (sum / prioritizationFees.length).toFixed(0); +} diff --git a/src/utils/sol/index.ts b/src/utils/sol/index.ts index 5e594bb0..7241b307 100644 --- a/src/utils/sol/index.ts +++ b/src/utils/sol/index.ts @@ -1,15 +1,6 @@ -import { - ComputeBudgetInstruction, - ComputeBudgetProgram, - Connection, - PublicKey, - TransactionMessage, - VersionedTransaction, -} from "@solana/web3.js"; -import { Big } from "big.js"; +import { Connection, PublicKey, TransactionMessage, VersionedTransaction } from "@solana/web3.js"; import { AllbridgeCoreSdkOptions, ChainSymbol, SdkError } from "../../index"; import { NodeRpcUrlsConfig } from "../../services"; -import { toPowBase10 } from "../calculation"; import { fetchAddressLookupTableAccountsFromTx } from "./utils"; /** @@ -22,26 +13,6 @@ export interface SolUtils { * @param memo memo to add (28 char max) */ addMemoToTx(transaction: VersionedTransaction, memo: string): Promise; - - /** - * Add Priority Fee to solana's VersionedTransaction - * - ComputeUnitLimit will be added/updated with actual data from the simulation - * - ComputeUnitPrice will be added/updated for extra fee impact will be as extraFeeInLamports param - * @param transaction - * @param extraFeeInLamports extra fee - * @return extra fee in Lamports - */ - addPriorityFeeToTx(transaction: VersionedTransaction, extraFeeInLamports: string): Promise; - - /** - * Add Priority Fee to solana's VersionedTransaction - * - ComputeUnitLimit will be added/updated with actual data from the simulation - * - ComputeUnitPrice will be added/updated with priorityFeePricePerUnitInMicroLamports param - * @param transaction - * @param pricePerUnitInMicroLamports price per unit in micro-lamports
Optional. Will be calculated throw {@link Connection#getRecentPrioritizationFees} - * @return extra fee in Lamports - */ - addPriorityFeePerUnitToTx(transaction: VersionedTransaction, pricePerUnitInMicroLamports?: string): Promise; } export class DefaultSolUtils implements SolUtils { @@ -63,95 +34,4 @@ export class DefaultSolUtils implements SolUtils { }); transaction.message = message.compileToV0Message(addressLookupTableAccounts); } - - async addPriorityFeePerUnitToTx( - transaction: VersionedTransaction, - pricePerUnitInMicroLamports?: string - ): Promise { - const connection = new Connection(this.nodeRpcUrlsConfig.getNodeRpcUrl(ChainSymbol.SOL), "confirmed"); - const addressLookupTableAccounts = await fetchAddressLookupTableAccountsFromTx(transaction, connection); - const message = TransactionMessage.decompile(transaction.message, { - addressLookupTableAccounts: addressLookupTableAccounts, - }); - - const units = await this.updateUnitLimit(transaction, message, connection); - const unitPrice = await this.updateUnitPrice(message, connection, pricePerUnitInMicroLamports); - - transaction.message = message.compileToV0Message(addressLookupTableAccounts); - return Big(unitPrice).mul(units).div(toPowBase10(6)).toFixed(0); - } - - async addPriorityFeeToTx(transaction: VersionedTransaction, extraFeeInLamports: string): Promise { - const connection = new Connection(this.nodeRpcUrlsConfig.getNodeRpcUrl(ChainSymbol.SOL), "confirmed"); - const addressLookupTableAccounts = await fetchAddressLookupTableAccountsFromTx(transaction, connection); - const message = TransactionMessage.decompile(transaction.message, { - addressLookupTableAccounts: addressLookupTableAccounts, - }); - - const units = await this.updateUnitLimit(transaction, message, connection); - const unitPrice = await this.updateUnitPrice( - message, - connection, - Big(extraFeeInLamports).div(units).mul(toPowBase10(6)).toFixed(0) - ); - - transaction.message = message.compileToV0Message(addressLookupTableAccounts); - return Big(unitPrice).mul(units).div(toPowBase10(6)).toFixed(0); - } - - private async updateUnitLimit( - transaction: VersionedTransaction, - message: TransactionMessage, - connection: Connection - ): Promise { - const computeUnitLimitIndex = message.instructions.findIndex( - (instruction) => - instruction.programId.equals(ComputeBudgetProgram.programId) && - ComputeBudgetInstruction.decodeInstructionType(instruction) === "SetComputeUnitLimit" - ); - const simUnitsConsumed = (await connection.simulateTransaction(transaction)).value.unitsConsumed!; - const units = Number((simUnitsConsumed * 1.3).toFixed(0)); - const computeUnitLimitInstruction = ComputeBudgetProgram.setComputeUnitLimit({ - units: units, - }); - if (computeUnitLimitIndex >= 0) { - message.instructions[computeUnitLimitIndex] = computeUnitLimitInstruction; - } else { - message.instructions.push(computeUnitLimitInstruction); - } - return units.toString(); - } - - private async updateUnitPrice( - message: TransactionMessage, - connection: Connection, - pricePerUnitInMicroLamports?: string - ): Promise { - const computeUnitPriceIndex = message.instructions.findIndex( - (instruction) => - instruction.programId.equals(ComputeBudgetProgram.programId) && - ComputeBudgetInstruction.decodeInstructionType(instruction) === "SetComputeUnitPrice" - ); - const unitPrice = pricePerUnitInMicroLamports - ? BigInt(pricePerUnitInMicroLamports) - : BigInt(await this.getAveragePrioritizationFee(connection)); - const computeUnitPriceInstruction = ComputeBudgetProgram.setComputeUnitPrice({ - microLamports: unitPrice, - }); - if (computeUnitPriceIndex >= 0) { - message.instructions[computeUnitPriceIndex] = computeUnitPriceInstruction; - } else { - message.instructions.push(computeUnitPriceInstruction); - } - return unitPrice.toString(); - } - - private async getAveragePrioritizationFee(connection: Connection) { - const prioritizationFees = await connection.getRecentPrioritizationFees(); - let sum = 0; - for (const prioritizationFee of prioritizationFees) { - sum += prioritizationFee.prioritizationFee; - } - return (sum / prioritizationFees.length).toFixed(0); - } }