diff --git a/src/index.ts b/src/index.ts index 7b98158a..b30b354f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,6 +22,7 @@ import { TransferStatusResponse, } from "./models"; import { AllbridgeCoreSdkService, NodeRpcUrlsConfig } from "./services"; +import { DefaultUtils, Utils } from "./utils"; export * from "./configs"; export * from "./models"; @@ -69,6 +70,7 @@ export class AllbridgeCoreSdk { bridge: BridgeService; pool: LiquidityPoolService; + utils: Utils; private service: AllbridgeCoreSdkService; @@ -89,6 +91,7 @@ export class AllbridgeCoreSdk { this.service = new AllbridgeCoreSdkService(nodeRpcUrlsConfig, params); this.bridge = this.service.bridge; this.pool = this.service.pool; + this.utils = new DefaultUtils(nodeRpcUrlsConfig.getNodeRpcUrl(ChainSymbol.SOL)); this.params = params; } diff --git a/src/models/index.ts b/src/models/index.ts index 64d9cbc8..ef1f1961 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -22,11 +22,6 @@ export { TxCostAmount, MessengerTransferTime, } from "../tokens-info/tokens-info.model"; -export { - CheckAllowanceParams as TokensCheckAllowanceParams, - GetAllowanceParams as TokensGetAllowanceParams, - GetTokenBalanceParams as TokensGetTokenBalanceParams, -} from "../services/token/models/token.model"; export { UserBalanceInfo, UserBalanceInfoDTO, @@ -44,6 +39,7 @@ export { } from "../utils/calculation/swap-and-bridge-fee-calc"; export { SendAmountDetails, AmountImpact } from "../utils/calculation/swap-and-bridge-details"; export { ChainDetailsMap, ChainDetailsWithTokens } from "../tokens-info"; +export { Utils } from "../utils"; export * from "../exceptions"; export enum FeePaymentMethod { diff --git a/src/services/bridge/index.ts b/src/services/bridge/index.ts index 6bf42563..ac22603a 100644 --- a/src/services/bridge/index.ts +++ b/src/services/bridge/index.ts @@ -7,7 +7,7 @@ import { AllbridgeCoreClient } from "../../client/core-api"; import { Messenger } from "../../client/core-api/core-api.model"; import { CCTPDoesNotSupportedError, MethodNotSupportedError } from "../../exceptions"; import { TokenWithChainDetails } from "../../tokens-info"; -import { validateAmountDecimals, validateAmountGtZero } from "../../utils"; +import { validateAmountDecimals, validateAmountGtZero } from "../../utils/utils"; import { Provider, TransactionResponse } from "../models"; import { TokenService } from "../token"; import { EvmBridgeService } from "./evm"; diff --git a/src/services/bridge/raw-bridge-transaction-builder.ts b/src/services/bridge/raw-bridge-transaction-builder.ts index 30640f44..f24c7f07 100644 --- a/src/services/bridge/raw-bridge-transaction-builder.ts +++ b/src/services/bridge/raw-bridge-transaction-builder.ts @@ -1,6 +1,6 @@ import { NodeRpcUrlsConfig } from ".."; import { AllbridgeCoreClient } from "../../client/core-api"; -import { validateAmountDecimals, validateAmountGtZero } from "../../utils"; +import { validateAmountDecimals, validateAmountGtZero } from "../../utils/utils"; import { Provider, RawTransaction } from "../models"; import { TokenService } from "../token"; import { ApproveParams, SendParams, SwapParams } from "./models"; diff --git a/src/services/bridge/sol/jupiter.ts b/src/services/bridge/sol/jupiter.ts index b228031d..6b79aae7 100644 --- a/src/services/bridge/sol/jupiter.ts +++ b/src/services/bridge/sol/jupiter.ts @@ -1,7 +1,8 @@ import { NATIVE_MINT } from "@solana/spl-token"; -import { AddressLookupTableAccount, Connection, TransactionMessage, VersionedTransaction } from "@solana/web3.js"; +import { Connection, TransactionMessage, VersionedTransaction } from "@solana/web3.js"; import axios, { AxiosError } from "axios"; -import { JupiterError, SdkError } from "../../../exceptions"; +import { JupiterError } from "../../../exceptions"; +import { fetchAddressLookupTableAccountsFromTx } from "../../../utils/sol"; export class JupiterService { connection: Connection; @@ -70,36 +71,8 @@ export class JupiterService { sdkTx: VersionedTransaction ): Promise { try { - const addressLookupTableAccounts = await Promise.all( - transaction.message.addressTableLookups.map(async (lookup) => { - return new AddressLookupTableAccount({ - key: lookup.accountKey, - state: AddressLookupTableAccount.deserialize( - await this.connection.getAccountInfo(lookup.accountKey).then((res) => { - if (!res) { - throw new SdkError("Cannot get AccountInfo"); - } - return res.data; - }) - ), - }); - }) - ); - const sdkAddressLookupTableAccounts = await Promise.all( - sdkTx.message.addressTableLookups.map(async (lookup) => { - return new AddressLookupTableAccount({ - key: lookup.accountKey, - state: AddressLookupTableAccount.deserialize( - await this.connection.getAccountInfo(lookup.accountKey).then((res) => { - if (!res) { - throw new SdkError("Cannot get AccountInfo"); - } - return res.data; - }) - ), - }); - }) - ); + const addressLookupTableAccounts = await fetchAddressLookupTableAccountsFromTx(transaction, this.connection); + const sdkAddressLookupTableAccounts = await fetchAddressLookupTableAccountsFromTx(sdkTx, this.connection); const message = TransactionMessage.decompile(transaction.message, { addressLookupTableAccounts: addressLookupTableAccounts, diff --git a/src/services/index.ts b/src/services/index.ts index d404cc4d..4bb22595 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -23,7 +23,6 @@ import { SwapAndBridgeCalculationData, } from "../models"; import { ChainDetailsMap, PoolInfo, PoolKeyObject, TokenWithChainDetails } from "../tokens-info"; -import { getPoolInfoByToken, validateAmountDecimals, validateAmountGtZero } from "../utils"; import { aprInPercents, convertAmountPrecision, @@ -43,6 +42,7 @@ import { swapAndBridgeFeeCalculation, swapAndBridgeFeeCalculationReverse, } from "../utils/calculation/swap-and-bridge-fee-calc"; +import { getPoolInfoByToken, validateAmountDecimals, validateAmountGtZero } from "../utils/utils"; import { BridgeService, DefaultBridgeService } from "./bridge"; import { GetNativeTokenBalanceParams } from "./bridge/models"; import { SolanaBridgeParams } from "./bridge/sol"; diff --git a/src/services/liquidity-pool/evm/index.ts b/src/services/liquidity-pool/evm/index.ts index dc47f612..0f661483 100644 --- a/src/services/liquidity-pool/evm/index.ts +++ b/src/services/liquidity-pool/evm/index.ts @@ -3,8 +3,8 @@ import { AbiItem } from "web3-utils"; import { ChainType } from "../../../chains"; import { AllbridgeCoreClient } from "../../../client/core-api"; import { PoolInfo, TokenWithChainDetails } from "../../../tokens-info"; -import { promiseWithTimeout } from "../../../utils"; import { calculatePoolInfoImbalance } from "../../../utils/calculation"; +import { promiseWithTimeout } from "../../../utils/utils"; import { RawTransaction } from "../../models"; import PoolAbi from "../../models/abi/Pool.json"; import { Pool as PoolContract } from "../../models/abi/types/Pool"; diff --git a/src/services/liquidity-pool/index.ts b/src/services/liquidity-pool/index.ts index 548ab696..d4e6afc3 100644 --- a/src/services/liquidity-pool/index.ts +++ b/src/services/liquidity-pool/index.ts @@ -7,9 +7,9 @@ import { chainProperties, ChainSymbol, ChainType } from "../../chains"; import { AllbridgeCoreClient } from "../../client/core-api"; import { MethodNotSupportedError } from "../../exceptions"; import { PoolInfo, TokenWithChainDetails } from "../../tokens-info"; -import { validateAmountDecimals, validateAmountGtZero } from "../../utils"; import { convertIntAmountToFloat, fromSystemPrecision } from "../../utils/calculation"; import { SYSTEM_PRECISION } from "../../utils/calculation/constants"; +import { validateAmountDecimals, validateAmountGtZero } from "../../utils/utils"; import { Provider, TransactionResponse } from "../models"; import { TokenService } from "../token"; import { depositAmountToVUsd, vUsdToWithdrawalAmount } from "../utils/calculation"; diff --git a/src/services/liquidity-pool/raw-pool-transaction-builder.ts b/src/services/liquidity-pool/raw-pool-transaction-builder.ts index a402215c..5142e3a3 100644 --- a/src/services/liquidity-pool/raw-pool-transaction-builder.ts +++ b/src/services/liquidity-pool/raw-pool-transaction-builder.ts @@ -1,8 +1,8 @@ import { NodeRpcUrlsConfig } from ".."; import { AllbridgeCoreClient } from "../../client/core-api"; -import { validateAmountDecimals, validateAmountGtZero } from "../../utils"; import { convertFloatAmountToInt } from "../../utils/calculation"; import { SYSTEM_PRECISION } from "../../utils/calculation/constants"; +import { validateAmountDecimals, validateAmountGtZero } from "../../utils/utils"; import { Provider, RawTransaction } from "../models"; import { TokenService } from "../token"; import { ApproveParams, LiquidityPoolsParams, LiquidityPoolsParamsWithAmount } from "./models"; diff --git a/src/services/token/index.ts b/src/services/token/index.ts index 9675387f..7e58a1da 100644 --- a/src/services/token/index.ts +++ b/src/services/token/index.ts @@ -5,8 +5,8 @@ import Web3 from "web3"; import { ChainDecimalsByType, chainProperties, ChainSymbol, ChainType } from "../../chains"; import { AllbridgeCoreClient } from "../../client/core-api"; import { AmountFormat, AmountFormatted, MethodNotSupportedError } from "../../models"; -import { validateAmountDecimals, validateAmountGtZero } from "../../utils"; import { convertFloatAmountToInt, convertIntAmountToFloat } from "../../utils/calculation"; +import { validateAmountDecimals, validateAmountGtZero } from "../../utils/utils"; import { GetNativeTokenBalanceParams } from "../bridge/models"; import { NodeRpcUrlsConfig } from "../index"; import { Provider, RawTransaction, TransactionResponse } from "../models"; diff --git a/src/utils/index.ts b/src/utils/index.ts index c13f3695..c2317c92 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,36 +1,36 @@ -import { Big, BigSource } from "big.js"; -import { AllbridgeCoreClientPoolInfoCaching } from "../client/core-api/core-client-pool-info-caching"; -import { ArgumentInvalidDecimalsError, InvalidAmountError, TimeoutError } from "../exceptions"; -import { PoolInfo, TokenWithChainDetails } from "../tokens-info"; +import { Connection, PublicKey, TransactionMessage, VersionedTransaction } from "@solana/web3.js"; +import { SdkError } from "../exceptions"; +import { fetchAddressLookupTableAccountsFromTx } from "./sol"; -export async function getPoolInfoByToken( - api: AllbridgeCoreClientPoolInfoCaching, - sourceChainToken: TokenWithChainDetails -): Promise { - return await api.getPoolInfoByKey({ - chainSymbol: sourceChainToken.chainSymbol, - poolAddress: sourceChainToken.poolAddress, - }); +/** + * Contains usefully methods + */ +export interface Utils { + /** + * Add memo to solana's transaction + * @param transaction transaction to add memo + * @param memo memo to add (32 char max) + */ + addMemoToTx(transaction: VersionedTransaction, memo: string): Promise; } -export function validateAmountGtZero(amount: BigSource) { - if (Big(amount).lte(0)) { - throw new InvalidAmountError("Amount must be greater than zero"); - } -} +export class DefaultUtils implements Utils { + constructor(readonly solanaRpcUrl: string) {} -export function validateAmountDecimals(argName: string, amountFloat: number | string | Big, decimalRequired: number) { - const amount = Big(amountFloat).toFixed(); - if (amount.split(".").length == 2 && amount.split(".")[1].length > decimalRequired) { - throw new ArgumentInvalidDecimalsError(argName, amount.split(".")[1].length, decimalRequired); + async addMemoToTx(transaction: VersionedTransaction, memo: string): Promise { + if (memo.length > 32) { + throw new SdkError("InvalidArgumentException memo cannot be more than 32 characters"); + } + const connection = new Connection(this.solanaRpcUrl, "confirmed"); + const addressLookupTableAccounts = await fetchAddressLookupTableAccountsFromTx(transaction, connection); + const message = TransactionMessage.decompile(transaction.message, { + addressLookupTableAccounts: addressLookupTableAccounts, + }); + message.instructions[message.instructions.length - 1].keys.push({ + pubkey: new PublicKey(Buffer.from(memo)), + isSigner: false, + isWritable: false, + }); + transaction.message = message.compileToV0Message(addressLookupTableAccounts); } } - -export async function promiseWithTimeout(promise: Promise, msg: string, timeoutMs: number): Promise { - return (await Promise.race([ - promise, - new Promise((resolve, reject) => { - setTimeout(() => reject(new TimeoutError(msg)), timeoutMs); - }), - ])) as any as T; -} diff --git a/src/utils/sol/index.ts b/src/utils/sol/index.ts new file mode 100644 index 00000000..36cfcf76 --- /dev/null +++ b/src/utils/sol/index.ts @@ -0,0 +1,22 @@ +import { AddressLookupTableAccount, Connection, VersionedTransaction } from "@solana/web3.js"; + +export async function fetchAddressLookupTableAccountsFromTx( + transaction: VersionedTransaction, + connection: Connection +): Promise { + return await Promise.all( + transaction.message.addressTableLookups.map(async (lookup) => { + return new AddressLookupTableAccount({ + key: lookup.accountKey, + state: AddressLookupTableAccount.deserialize( + await connection.getAccountInfo(lookup.accountKey).then((res) => { + if (!res) { + throw new Error("Cannot get AccountInfo"); + } + return res.data; + }) + ), + }); + }) + ); +} diff --git a/src/utils/utils.ts b/src/utils/utils.ts new file mode 100644 index 00000000..c13f3695 --- /dev/null +++ b/src/utils/utils.ts @@ -0,0 +1,36 @@ +import { Big, BigSource } from "big.js"; +import { AllbridgeCoreClientPoolInfoCaching } from "../client/core-api/core-client-pool-info-caching"; +import { ArgumentInvalidDecimalsError, InvalidAmountError, TimeoutError } from "../exceptions"; +import { PoolInfo, TokenWithChainDetails } from "../tokens-info"; + +export async function getPoolInfoByToken( + api: AllbridgeCoreClientPoolInfoCaching, + sourceChainToken: TokenWithChainDetails +): Promise { + return await api.getPoolInfoByKey({ + chainSymbol: sourceChainToken.chainSymbol, + poolAddress: sourceChainToken.poolAddress, + }); +} + +export function validateAmountGtZero(amount: BigSource) { + if (Big(amount).lte(0)) { + throw new InvalidAmountError("Amount must be greater than zero"); + } +} + +export function validateAmountDecimals(argName: string, amountFloat: number | string | Big, decimalRequired: number) { + const amount = Big(amountFloat).toFixed(); + if (amount.split(".").length == 2 && amount.split(".")[1].length > decimalRequired) { + throw new ArgumentInvalidDecimalsError(argName, amount.split(".")[1].length, decimalRequired); + } +} + +export async function promiseWithTimeout(promise: Promise, msg: string, timeoutMs: number): Promise { + return (await Promise.race([ + promise, + new Promise((resolve, reject) => { + setTimeout(() => reject(new TimeoutError(msg)), timeoutMs); + }), + ])) as any as T; +}