UNPKG

@everstake/wallet-sdk-solana

Version:
889 lines (883 loc) 31.1 kB
// src/solana.ts import { createAddressWithSeed, createDefaultRpcTransport, createSolanaRpcFromTransport, mainnet, devnet, address as address2, createNoopSigner, pipe, createTransactionMessage, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstruction, generateKeyPair, createSignerFromKeyPair, partiallySignTransactionMessageWithSigners, parseBase64RpcAccount, prependTransactionMessageInstruction } from "@solana/kit"; import { getCreateAccountWithSeedInstruction, getCreateAccountInstruction, getTransferSolInstruction, getAllocateWithSeedInstruction } from "@solana-program/system"; import { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction } from "@solana-program/compute-budget"; // ../utils/constants/errors.ts var COMMON_ERROR_MESSAGES = { UNKNOWN_ERROR: "An unknown error occurred", TOKEN_ERROR: "Please create or use correct token" }; // ../utils/index.ts var WalletSDKError = class extends Error { constructor(message, code, originalError) { super(message); this.code = code; this.originalError = originalError; Object.setPrototypeOf(this, new.target.prototype); } }; var Blockchain = class { /** * Handles errors that occur within the Ethereum class. * * @param {keyof typeof ERROR_MESSAGES} code - The error code associated with the error. * @param {Error | WalletSDKError | unknown} originalError - The original error that was thrown. * * If the original error is an instance of WalletSDKError, it is thrown as is. * If the original error is an instance of the built-in Error class, a new WalletSDKError is thrown with the original error as the cause. * If the original error is not an instance of WalletSDKError or Error, a new WalletSDKError is thrown with a generic message and code. */ handleError(code, originalError) { const message = this.ERROR_MESSAGES[code]; if (originalError instanceof WalletSDKError || !message || !code) { throw originalError; } if (originalError instanceof Error) { const newMessage = Object.entries(this.ORIGINAL_ERROR_MESSAGES).find( ([originalMessage]) => originalError.message.includes(originalMessage) )?.[1]; const errorMessage = newMessage || this.ERROR_MESSAGES[code] || COMMON_ERROR_MESSAGES["UNKNOWN_ERROR"]; throw new WalletSDKError(errorMessage, String(code), originalError); } throw new WalletSDKError( COMMON_ERROR_MESSAGES["UNKNOWN_ERROR"], "UNKNOWN_ERROR" ); } /** * Throws a WalletSDKError with a specified error code and message. * * @param {keyof typeof ERROR_MESSAGES} code - The error code associated with the error. * @param {...string[]} values - The values to be inserted into the error message. * * The method retrieves the error message template associated with the provided code from the ERROR_MESSAGES object. * It then replaces placeholders in the message template with provided values and throws a WalletSDKError with the final message and the provided code. */ throwError(code, ...values) { let message = this.ERROR_MESSAGES[code]; values.forEach((value, index) => { message = message?.replace(`{${index}}`, value); }); if (!message) { throw new WalletSDKError( COMMON_ERROR_MESSAGES["UNKNOWN_ERROR"], "UNKNOWN_ERROR" ); } throw new WalletSDKError(message, String(code)); } /** * Check if the URL is valid * * @param {string} url - URL * @returns a bool type result. * */ isValidURL(url) { let urlClass; try { urlClass = new URL(url); } catch (_) { return false; } return urlClass.protocol === "http:" || urlClass.protocol === "https:"; } }; // src/constants/errors.ts var ERROR_MESSAGES = { CONNECTION_ERROR: "An error occurred while connecting to the network", MIN_AMOUNT_ERROR: "Min Amount {0}", CREATE_ACCOUNT_ERROR: "An error occurred while creating the account", DELEGATE_ERROR: "An error occurred while delegating the stake", DEACTIVATE_ERROR: "An error occurred while deactivating the stake", WITHDRAW_ERROR: "An error occurred while withdrawing the stake", GET_DELEGATIONS_ERROR: "An error occurred while fetching the delegations", STAKE_ERROR: "An error occurred while staking", INVALID_RPC_ERROR: "Invalid RPC URL", UNSUPPORTED_NETWORK_ERROR: "Unsupported Network", CLAIM_ERROR: "An error occurred while claim SOL", UNSTAKE_ERROR: "An error occurred while unstaking the stake", NOTHING_TO_CLAIM_ERROR: "Nothing to claim while claiming", NOT_ENOUGH_ACTIVE_STAKE_ERROR: "Active stake less than requested" }; // src/constants/index.ts import { address } from "@solana/kit"; var CHAIN = "solana"; var MIN_AMOUNT = 1e7; var MAINNET_VALIDATOR_ADDRESS = address( "9QU2QSxhb24FUX3Tu2FpczXjpK3VYrvRudywSZaM29mF" ); var DEVNET_VALIDATOR_ADDRESS = address( "GkqYQysEGmuL6V2AJoNnWZUz2ZBGWhzQXsJiXm2CLKAN" ); var FILTER_DATA_SIZE = 200n; var FILTER_OFFSET = 44n; var Network = /* @__PURE__ */ ((Network2) => { Network2["Mainnet"] = "mainnet-beta"; Network2["Devnet"] = "devnet"; return Network2; })(Network || {}); var StakeState = { Inactive: "inactive", Activating: "activating", Active: "active", Deactivating: "deactivating", Deactivated: "deactivated" }; var STAKE_ACCOUNT_V2_SIZE = 200; var ADDRESS_DEFAULT = address("11111111111111111111111111111111"); var STAKE_HISTORY_ACCOUNT = "SysvarStakeHistory1111111111111111111111111"; var STAKE_CONFIG_ACCOUNT = "StakeConfig11111111111111111111111111111111"; var MAX_CLAIM_ACCOUNTS = 16; var MAX_DEACTIVATE_ACCOUNTS = 22; var MAX_DEACTIVATE_ACCOUNTS_WITH_SPLIT = 16; // src/solana.ts import { getWithdrawInstruction, getDelegateStakeInstruction, getDeactivateInstruction, getInitializeInstruction, getSplitInstruction, STAKE_PROGRAM_ADDRESS, decodeStakeStateAccount } from "@solana-program/stake"; var Solana = class extends Blockchain { connection; validator; ERROR_MESSAGES = ERROR_MESSAGES; ORIGINAL_ERROR_MESSAGES = {}; constructor(network = "mainnet-beta" /* Mainnet */, rpcConfig = {}) { super(); if (rpcConfig.rpc && !this.isValidURL(rpcConfig.rpc)) { throw this.throwError("INVALID_RPC_ERROR"); } switch (network) { case "mainnet-beta" /* Mainnet */: rpcConfig.rpc = rpcConfig.rpc || mainnet("https://api.mainnet-beta.solana.com"); this.validator = MAINNET_VALIDATOR_ADDRESS; break; case "devnet" /* Devnet */: rpcConfig.rpc = rpcConfig.rpc || devnet("https://api.devnet.solana.com"); this.validator = DEVNET_VALIDATOR_ADDRESS; break; default: throw this.throwError("UNSUPPORTED_NETWORK_ERROR"); } try { const transport = createDefaultRpcTransport({ url: rpcConfig.rpc, headers: { "User-Agent": rpcConfig.userAgent || "" } }); this.connection = createSolanaRpcFromTransport(transport); } catch (error) { throw this.handleError("CONNECTION_ERROR", error); } } /** * Creates a new stake account. * * @param address - The public key of the account as PublicKey. * @param lamports - The amount to stake in lamports. * @param source - stake source * @param lockup - stake account lockup * * @throws Throws an error if the lamports is less than the minimum amount. * @throws Throws an error if there's an issue creating the stake account. * * @returns Returns a promise that resolves with the versioned transaction of the stake account creation and the public key of the stake account. * */ async createAccount(sender, amountInLamports, source, params) { if (amountInLamports < MIN_AMOUNT) { this.throwError("MIN_AMOUNT_ERROR", MIN_AMOUNT.toString()); } try { const minimumRent = await this.connection.getMinimumBalanceForRentExemption( //TODO get from account when it's would be available BigInt(STAKE_ACCOUNT_V2_SIZE) ).send(); const [ createAccountInstruction, initializeInstruction, stakeAccountPubkey ] = source === null ? ( // TODO fix create account sign await this.createAccountTx( address2(sender), BigInt(amountInLamports) + minimumRent // lockup, ) ) : await this.createAccountWithSeedTx( address2(sender), BigInt(amountInLamports) + minimumRent, source // lockup, ); let transactionMessage = await this.baseTx(sender, params); transactionMessage = appendTransactionMessageInstruction( createAccountInstruction, transactionMessage ); transactionMessage = appendTransactionMessageInstruction( initializeInstruction, transactionMessage ); const signedTransactionMessage = source === null ? await partiallySignTransactionMessageWithSigners(transactionMessage) : transactionMessage; return { result: { transaction: signedTransactionMessage, stakeAccount: stakeAccountPubkey } }; } catch (error) { throw this.handleError("CREATE_ACCOUNT_ERROR", error); } } /** * Delegates a specified amount from a stake account to a validator. * * @param address - The public key of the account. * @param lamports - The amount in lamports to be delegated. * @param stakeAccount - The public key of the stake account. * * @throws Throws an error if the amount is less than the minimum amount, or if there's an issue during the delegation process. * * @returns Returns a promise that resolves with the delegation transaction. * */ async delegate(sender, lamports, stakeAccount, params) { if (lamports < MIN_AMOUNT) { this.throwError("MIN_AMOUNT_ERROR", MIN_AMOUNT.toString()); } try { const delegateInstruction = getDelegateStakeInstruction({ stake: address2(stakeAccount), vote: this.validator, stakeHistory: STAKE_HISTORY_ACCOUNT, unused: STAKE_CONFIG_ACCOUNT, stakeAuthority: createNoopSigner(address2(sender)) }); let transactionMessage = await this.baseTx(sender, params); transactionMessage = appendTransactionMessageInstruction( delegateInstruction, transactionMessage ); return { result: transactionMessage }; } catch (error) { throw this.handleError("DELEGATE_ERROR", error); } } /** * Deactivates a stake account. * * @param address - The public key of the account. * @param stakeAccountPublicKey - The public key of the stake account. * @throws Throws an error if there's an issue during the deactivation process. * @returns Returns a promise that resolves with the deactivation transaction. * */ async deactivate(sender, stakeAccountPublicKey, params) { try { const deactivateInstruction = getDeactivateInstruction({ stake: address2(stakeAccountPublicKey), stakeAuthority: createNoopSigner(address2(sender)) }); let transactionMessage = await this.baseTx(sender, params); transactionMessage = appendTransactionMessageInstruction( deactivateInstruction, transactionMessage ); return { result: transactionMessage }; } catch (error) { throw this.handleError("DEACTIVATE_ERROR", error); } } /** * Withdraws a specified amount from a stake account. * * @param address - The public key of the account. * @param stakeAccountPublicKey - The public key of the stake account. * @param stakeBalance - The amount in lamports to be withdrawn from the stake account. * * @throws Throws an error if there's an issue during the withdrawal process. * * @returns Returns a promise that resolves with the withdrawal transaction. * */ async withdraw(sender, stakeAccountPublicKey, stakeBalance, params) { try { const withdrawInstruction = getWithdrawInstruction({ stake: stakeAccountPublicKey, recipient: sender, stakeHistory: STAKE_HISTORY_ACCOUNT, withdrawAuthority: createNoopSigner(address2(sender)), args: stakeBalance }); let transactionMessage = await this.baseTx(sender, params); transactionMessage = appendTransactionMessageInstruction( withdrawInstruction, transactionMessage ); return { result: transactionMessage }; } catch (error) { throw this.handleError("WITHDRAW_ERROR", error); } } /** * Fetches the delegations of a given account. * * @param address - The public key of the account. * * @throws Throws an error if there's an issue fetching the delegations. * * @returns Returns a promise that resolves with the delegations of the account. * */ async getDelegations(address3) { try { const accounts = await this.connection.getProgramAccounts(STAKE_PROGRAM_ADDRESS, { encoding: "base64", filters: [ { dataSize: FILTER_DATA_SIZE // Token account size }, { memcmp: { bytes: address3, encoding: "base58", offset: FILTER_OFFSET } } ] }).send(); const acs = accounts.map((account) => { const acc = parseBase64RpcAccount(account.pubkey, account.account); return decodeStakeStateAccount(acc); }); return { result: acs }; } catch (error) { throw this.handleError("GET_DELEGATIONS_ERROR", error); } } /** * Stakes a certain amount of lamports. * * @param sender - The public key of the sender. * @param lamports - The number of lamports to stake. * @param source - stake source * @param lockup - stake account lockup * @returns A promise that resolves to a VersionedTransaction object. */ async stake(sender, lamports, source, params) { try { const minimumRent = await this.connection.getMinimumBalanceForRentExemption( //TODO get from account when would be added BigInt(STAKE_ACCOUNT_V2_SIZE) ).send(); const [ createStakeAccountInstruction, initializeStakeAccountInstruction, stakeAccountPublicKey ] = source === null ? ( // TODO fix create account sign await this.createAccountTx( address2(sender), BigInt(lamports) + minimumRent // lockup, ) ) : await this.createAccountWithSeedTx( address2(sender), BigInt(lamports) + minimumRent, source // lockup, ); const delegateInstruction = getDelegateStakeInstruction({ stake: stakeAccountPublicKey, vote: this.validator, stakeHistory: STAKE_HISTORY_ACCOUNT, unused: STAKE_CONFIG_ACCOUNT, stakeAuthority: createNoopSigner(address2(sender)) }); let transactionMessage = await this.baseTx(sender, params); transactionMessage = appendTransactionMessageInstruction( createStakeAccountInstruction, transactionMessage ); transactionMessage = appendTransactionMessageInstruction( initializeStakeAccountInstruction, transactionMessage ); transactionMessage = appendTransactionMessageInstruction( delegateInstruction, transactionMessage ); const signedTransactionMessage = source === null ? await partiallySignTransactionMessageWithSigners(transactionMessage) : transactionMessage; return { result: { stakeTx: signedTransactionMessage, stakeAccount: stakeAccountPublicKey } }; } catch (error) { throw this.handleError("STAKE_ERROR", error); } } /** * Create account Tx, public key and array of keypair. * * @param address - The public key of the account. * @param lamports - The number of lamports to stake. * @param lockup - The stake account lockup * * @throws Throws an error if there's an issue creating an account. * * @returns Returns a promise that resolves with the Transaction, PublicKey and array of Keypair. * */ async createAccountTx(authorityPublicKey, lamports) { const stakeAccountKeyPair = await generateKeyPair(); const signer = await createSignerFromKeyPair(stakeAccountKeyPair); const createAccountInstruction = getCreateAccountInstruction({ payer: createNoopSigner(authorityPublicKey), newAccount: signer, lamports, // TODO get from package space: STAKE_ACCOUNT_V2_SIZE, programAddress: STAKE_PROGRAM_ADDRESS }); const initializeInstruction = getInitializeInstruction( /** Uninitialized stake account */ { stake: signer.address, arg0: { staker: authorityPublicKey, withdrawer: authorityPublicKey }, arg1: { //TODO use default unixTimestamp: 0, epoch: 0, custodian: ADDRESS_DEFAULT } } ); return [createAccountInstruction, initializeInstruction, signer.address]; } /** * Create account Tx, public key and array of keypair using seed. * * @param authorityPublicKey - The public key of the account. * @param lamports - The number of lamports to stake. * @param source - The stake source * @param lockup - The stake account lockup * * @throws Throws an error if there's an issue creating an account. * * @returns Returns a promise that resolves with the Transaction, PublicKey and array of Keypair. * */ async createAccountWithSeedTx(authorityPublicKey, lamports, source) { const seed = this.formatSource(source || ""); const stakeAccountPubkey = await createAddressWithSeed({ baseAddress: authorityPublicKey, programAddress: STAKE_PROGRAM_ADDRESS, seed }); const createAccountInstruction = getCreateAccountWithSeedInstruction({ payer: createNoopSigner(authorityPublicKey), newAccount: stakeAccountPubkey, baseAccount: createNoopSigner(authorityPublicKey), base: address2(authorityPublicKey), seed, amount: lamports, // TODO get from package space: STAKE_ACCOUNT_V2_SIZE, programAddress: STAKE_PROGRAM_ADDRESS }); const initializeInstruction = getInitializeInstruction( /** Uninitialized stake account */ { stake: stakeAccountPubkey, arg0: { staker: authorityPublicKey, withdrawer: authorityPublicKey }, arg1: { //TODO implement Lockup unixTimestamp: 0, epoch: 0, custodian: ADDRESS_DEFAULT } } ); return [ createAccountInstruction, initializeInstruction, stakeAccountPubkey ]; } /** unstake - unstake * @param {string} sender - account blockchain address (staker) * @param {bigint} lamports - lamport amount * @param {string} source - stake source * @returns {Promise<object>} Promise object with Versioned Tx */ async unstake(sender, lamports, source, params) { try { const stakeAccounts = (await this.getDelegations(sender)).result; const epoch = params?.epoch || (await this.connection.getEpochInfo().send()).epoch; const tm = this.timestampInSec(); let unstakeAmount = lamports; let totalActiveStake = 0n; const activeStakeAccounts = stakeAccounts.filter((acc) => { if (acc.data.state.__kind !== "Stake") { return false; } const isActive = !(isLockupInForce(acc.data, epoch, BigInt(tm)) || stakeAccountState(acc.data, epoch) !== StakeState.Active); if (isActive) { totalActiveStake = totalActiveStake + acc.data.state.fields[1].delegation.stake; } return isActive; }); if (totalActiveStake < lamports) throw this.throwError("NOT_ENOUGH_ACTIVE_STAKE_ERROR"); activeStakeAccounts.sort((a, b) => { const stakeA = isStake(a.data.state) ? a.data.state.fields[1].delegation.stake : 0n; const stakeB = isStake(b.data.state) ? b.data.state.fields[1].delegation.stake : 0n; if (activeStakeAccounts.length < MAX_DEACTIVATE_ACCOUNTS_WITH_SPLIT) { return Number(stakeA - stakeB); } return Number(stakeB - stakeA); }); const accountsToDeactivate = []; const accountsToSplit = []; let i = 0; while (lamports > 0n && i < activeStakeAccounts.length) { const acc = activeStakeAccounts[i]; if (acc === void 0 || !isStake(acc.data.state)) { i++; continue; } const stakeAmount = acc.data.state.fields[1].delegation.stake; const isBelowThreshold = stakeAmount <= lamports || stakeAmount - lamports < MIN_AMOUNT; if (isBelowThreshold) { accountsToDeactivate.push(acc); lamports = lamports - stakeAmount; i++; if (accountsToDeactivate.length === MAX_DEACTIVATE_ACCOUNTS) { unstakeAmount -= lamports; break; } continue; } if (accountsToDeactivate.length > MAX_DEACTIVATE_ACCOUNTS_WITH_SPLIT) { unstakeAmount -= lamports; break; } accountsToSplit.push([acc, lamports]); break; } const senderPublicKey = address2(sender); let transactionMessage = await this.baseTx(sender, params); const minimumRent = accountsToSplit.length > 0 ? await this.connection.getMinimumBalanceForRentExemption( //TODO get from account when it's would be available BigInt(STAKE_ACCOUNT_V2_SIZE) ).send() : 0n; for (const acc of accountsToSplit) { const [splitInstructions, newStakeAccountPubkey] = await this.split( senderPublicKey, acc[1], acc[0].address, source, // Need additional value for rent minimumRent ); splitInstructions.forEach( (splitInstruction) => transactionMessage = appendTransactionMessageInstruction( splitInstruction, transactionMessage ) ); const deactivateInstruction = getDeactivateInstruction({ stake: newStakeAccountPubkey, stakeAuthority: createNoopSigner(address2(sender)) }); transactionMessage = appendTransactionMessageInstruction( deactivateInstruction, transactionMessage ); } accountsToDeactivate.forEach((acc) => { const deactivateInstruction = getDeactivateInstruction({ stake: acc.address, stakeAuthority: createNoopSigner(address2(sender)) }); transactionMessage = appendTransactionMessageInstruction( deactivateInstruction, transactionMessage ); }); if (transactionMessage.instructions.length === 0) { this.handleError("UNSTAKE_ERROR", "zero instructions"); } return { result: { unstakeTx: transactionMessage, unstakeAmount } }; } catch (error) { throw this.handleError("UNSTAKE_ERROR", error); } } /** * Split existing account to create a new one * * @param authorityPublicKey - The public key of the account. * @param lamports - The number of lamports to stake. * @param oldStakeAccountPubkey -The public key of the old account. * @param source - The stake source * * @throws Throws an error if there's an issue splitting an account. * * @returns Returns a promise that resolves with the Transaction, PublicKey and array of Keypair. * */ async split(authorityPublicKey, lamports, oldStakeAccountPubkey, source, rentExemptReserve) { const seed = this.formatSource(source); const newStakeAccountPubkey = await createAddressWithSeed({ baseAddress: authorityPublicKey, programAddress: STAKE_PROGRAM_ADDRESS, seed }); const instructions = []; const allocateWithSeedInstruction = getAllocateWithSeedInstruction({ newAccount: newStakeAccountPubkey, baseAccount: createNoopSigner(address2(authorityPublicKey)), base: authorityPublicKey, seed, //TODO get from library if possible space: STAKE_ACCOUNT_V2_SIZE, programAddress: STAKE_PROGRAM_ADDRESS }); instructions.push(allocateWithSeedInstruction); if (rentExemptReserve && rentExemptReserve > 0) { const rentTransferInstruction = getTransferSolInstruction({ source: createNoopSigner(authorityPublicKey), destination: newStakeAccountPubkey, amount: rentExemptReserve }); instructions.push(rentTransferInstruction); } const splitInstruction = getSplitInstruction({ stake: oldStakeAccountPubkey, splitStake: newStakeAccountPubkey, stakeAuthority: createNoopSigner(authorityPublicKey), args: lamports }); instructions.push(splitInstruction); return [instructions, newStakeAccountPubkey]; } /** * Claim makes withdrawal from all sender's deactivated accounts. * * @param sender - The sender solana address. * * @throws Throws an error if there's an issue while claiming a stake. * * @returns Returns a promise that resolves with a Versioned Transaction. * */ async claim(sender, params) { try { const delegations = await this.getDelegations(sender); const epoch = params?.epoch || (await this.connection.getEpochInfo().send()).epoch; const tm = this.timestampInSec(); const deactivatedStakeAccounts = delegations.result.filter((acc) => { return !isLockupInForce(acc.data, epoch, BigInt(tm)) && stakeAccountState(acc.data, epoch) === StakeState.Deactivated; }); if (deactivatedStakeAccounts.length === 0) throw this.throwError("NOTHING_TO_CLAIM_ERROR"); let transactionMessage = await this.baseTx(sender, params); let totalClaimableStake = 0n; let accountsForClaim = 0; for (const acc of deactivatedStakeAccounts) { const withdrawInstruction = getWithdrawInstruction({ stake: acc.address, recipient: address2(sender), stakeHistory: STAKE_HISTORY_ACCOUNT, withdrawAuthority: createNoopSigner(address2(sender)), args: acc.lamports }); transactionMessage = appendTransactionMessageInstruction( withdrawInstruction, transactionMessage ); totalClaimableStake += acc.lamports; accountsForClaim++; if (accountsForClaim === MAX_CLAIM_ACCOUNTS) { break; } } return { result: { claimTx: transactionMessage, totalClaimAmount: totalClaimableStake } }; } catch (error) { throw this.handleError("CLAIM_ERROR", error); } } /** * Merge two accounts into a new one * * @param authorityPublicKey - The public key of the account. * @param stakeAccount1 - The public key of the first account. * @param stakeAccount2 - The public key of the second account. * * @throws Throws an error if there's an issue while merging an account. * * @returns Returns a promise that resolves with the Transaction, PublicKey and array of Keypair. * */ // private async merge( // authorityPublicKey: PublicKey, // stakeAccount1: PublicKey, // stakeAccount2: PublicKey, // ) { // const mergeStakeAccountTx = StakeProgram.merge({ // stakePubkey: stakeAccount1, // sourceStakePubKey: stakeAccount2, // authorizedPubkey: authorityPublicKey, // }); // return [mergeStakeAccountTx]; // } async baseTx(sender, params) { const finalLatestBlockhash = params?.finalLatestBlockhash || (await this.connection.getLatestBlockhash().send()).value; let transactionMessage = pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayer(address2(sender), tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(finalLatestBlockhash, tx) ); if (params?.computeUnitLimit !== void 0 && params?.computeUnitLimit > 0) { const unitLimitInstruction = getSetComputeUnitLimitInstruction({ /** Transaction compute unit limit used for prioritization fees. */ units: params?.computeUnitLimit }); transactionMessage = prependTransactionMessageInstruction( unitLimitInstruction, transactionMessage ); } if (params?.\u0441omputeUnitPrice !== void 0 && params?.\u0441omputeUnitPrice > 0) { const unitPriceInstruction = getSetComputeUnitPriceInstruction({ /** Transaction compute unit price used for prioritization fees. */ microLamports: params?.\u0441omputeUnitPrice }); transactionMessage = prependTransactionMessageInstruction( unitPriceInstruction, transactionMessage ); } return transactionMessage; } /** * Generate a unique source for crating an account. * * @param source - source ID. * * @returns Returns a unique source for an account. * */ formatSource(source) { const timestamp = (/* @__PURE__ */ new Date()).getTime(); source = `everstake ${source}:${timestamp}`; return source; } /** * Generate timestamp in seconds. * * @returns Returns a timestamp in seconds. * */ timestampInSec() { return Date.now() / 1e3 | 0; } }; function stakeAccountState(account, currentEpoch) { if (account.state.__kind !== "Stake") { return StakeState.Inactive; } const activationEpoch = account.state.fields[1].delegation.activationEpoch; const deactivationEpoch = account.state.fields[1].delegation.deactivationEpoch; if (activationEpoch > currentEpoch) { return StakeState.Inactive; } if (activationEpoch === currentEpoch) { if (deactivationEpoch === activationEpoch) return StakeState.Inactive; return StakeState.Activating; } if (deactivationEpoch > currentEpoch) return StakeState.Active; if (deactivationEpoch === currentEpoch) return StakeState.Deactivating; return StakeState.Deactivated; } function isLockupInForce(account, currEpoch, currUnixTimestamp) { if (account.state.__kind !== "Stake" && account.state.__kind !== "Initialized") { return false; } const { unixTimestamp, epoch } = account.state.fields[0].lockup; return unixTimestamp > currUnixTimestamp || epoch > currEpoch; } function isStake(state) { return state.__kind === "Stake"; } export { ADDRESS_DEFAULT, Blockchain, CHAIN, DEVNET_VALIDATOR_ADDRESS, FILTER_DATA_SIZE, FILTER_OFFSET, MAINNET_VALIDATOR_ADDRESS, MAX_CLAIM_ACCOUNTS, MAX_DEACTIVATE_ACCOUNTS, MAX_DEACTIVATE_ACCOUNTS_WITH_SPLIT, MIN_AMOUNT, Network, STAKE_ACCOUNT_V2_SIZE, STAKE_CONFIG_ACCOUNT, STAKE_HISTORY_ACCOUNT, Solana, StakeState, WalletSDKError, isLockupInForce, isStake, stakeAccountState };