@everstake/wallet-sdk-solana
Version:
Solana - Everstake Wallet SDK
903 lines (892 loc) • 32.1 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
FILTER_DATA_SIZE: () => FILTER_DATA_SIZE,
FILTER_OFFSET: () => FILTER_OFFSET,
ParseStakeAccountError: () => ParseStakeAccountError,
SOL_CHAIN: () => SOL_CHAIN,
SOL_DEVNET_VALIDATOR_ADDRESS: () => SOL_DEVNET_VALIDATOR_ADDRESS,
SOL_MAINNET_VALIDATOR_ADDRESS: () => SOL_MAINNET_VALIDATOR_ADDRESS,
SOL_MIN_AMOUNT: () => SOL_MIN_AMOUNT,
SolNetwork: () => SolNetwork,
Solana: () => Solana,
StakeAccount: () => StakeAccount2,
StakeAccountType: () => StakeAccountType,
StakeState: () => StakeState
});
module.exports = __toCommonJS(index_exports);
// src/solana.ts
var import_web33 = require("@solana/web3.js");
// ../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",
GET_EPOCH_INFO_ERROR: "An error occurred while fetching epoch information"
};
// src/constants/index.ts
var import_web3 = require("@solana/web3.js");
var import_superstruct = require("superstruct");
var SOL_CHAIN = "solana";
var SOL_MIN_AMOUNT = 1e7;
var SOL_MAINNET_VALIDATOR_ADDRESS = new import_web3.PublicKey(
"9QU2QSxhb24FUX3Tu2FpczXjpK3VYrvRudywSZaM29mF"
);
var SOL_DEVNET_VALIDATOR_ADDRESS = new import_web3.PublicKey(
"GkqYQysEGmuL6V2AJoNnWZUz2ZBGWhzQXsJiXm2CLKAN"
);
var FILTER_DATA_SIZE = 200;
var FILTER_OFFSET = 44;
var SolNetwork = /* @__PURE__ */ ((SolNetwork2) => {
SolNetwork2["Mainnet"] = "mainnet-beta";
SolNetwork2["Devnet"] = "devnet";
return SolNetwork2;
})(SolNetwork || {});
var StakeState = {
inactive: "inactive",
activating: "activating",
active: "active",
deactivating: "deactivating",
deactivated: "deactivated"
};
var StakeAccountType = (0, import_superstruct.enums)([
"uninitialized",
"initialized",
"delegated",
"rewardsPool"
]);
// src/solana.ts
var import_bignumber3 = __toESM(require("bignumber.js"));
// src/types/stakeAccount.ts
var import_web32 = require("@solana/web3.js");
var import_superstruct2 = require("superstruct");
var import_bignumber = __toESM(require("bignumber.js"));
var BigNumFromString = (0, import_superstruct2.coerce)(
(0, import_superstruct2.instance)(import_bignumber.default),
(0, import_superstruct2.string)(),
(value) => {
if (typeof value === "string") return new import_bignumber.default(value, 10);
throw new Error("invalid big num");
}
);
var PublicKeyFromString = (0, import_superstruct2.coerce)(
(0, import_superstruct2.instance)(import_web32.PublicKey),
(0, import_superstruct2.string)(),
(value) => new import_web32.PublicKey(value)
);
var StakeMeta = (0, import_superstruct2.type)({
rentExemptReserve: BigNumFromString,
authorized: (0, import_superstruct2.type)({
staker: PublicKeyFromString,
withdrawer: PublicKeyFromString
}),
lockup: (0, import_superstruct2.type)({
unixTimestamp: (0, import_superstruct2.number)(),
epoch: (0, import_superstruct2.number)(),
custodian: PublicKeyFromString
})
});
var StakeAccountInfo = (0, import_superstruct2.type)({
meta: StakeMeta,
stake: (0, import_superstruct2.nullable)(
(0, import_superstruct2.type)({
delegation: (0, import_superstruct2.type)({
voter: PublicKeyFromString,
stake: BigNumFromString,
activationEpoch: BigNumFromString,
deactivationEpoch: BigNumFromString,
warmupCooldownRate: (0, import_superstruct2.number)()
}),
creditsObserved: (0, import_superstruct2.number)()
})
)
});
var StakeAccount = (0, import_superstruct2.type)({
type: StakeAccountType,
info: StakeAccountInfo
});
// src/stakeAccount.ts
var import_superstruct3 = require("superstruct");
var import_bignumber2 = __toESM(require("bignumber.js"));
var ParseStakeAccountError = class extends Error {
};
var StakeAccount2 = class {
account;
constructor({
executable,
owner,
lamports,
data,
rentEpoch
}) {
if (!("parsed" in data)) {
throw new ParseStakeAccountError(
"Raw AccountInfo<Buffer>, data not parsed"
);
}
try {
const parsedData = (0, import_superstruct3.create)(data.parsed, StakeAccount);
this.account = {
executable,
owner,
lamports,
data: parsedData,
rentEpoch
};
} catch (e) {
throw new ParseStakeAccountError(e.message);
}
}
/**
* Check if lockup is in force
* @param currEpoch current epoch.
* @param currUnixTimestamp current unix timetamp.
* @returns a bool type result.
*/
isLockupInForce(currEpoch, currUnixTimestamp) {
return this.account.data.info.meta.lockup.unixTimestamp > currUnixTimestamp || this.account.data.info.meta.lockup.epoch > currEpoch;
}
/**
* Determins the current state of a stake account given the current epoch
* @param currentEpoch
* @returns `stakeAccount`'s stake state`string`
*/
stakeAccountState(currentEpoch) {
const {
type: type2,
info: { stake }
} = this.account.data;
if (type2 !== "delegated" || stake === null) {
return StakeState.inactive;
}
const currentEpochBN = new import_bignumber2.default(currentEpoch);
const activationEpoch = new import_bignumber2.default(stake.delegation.activationEpoch);
const deactivationEpoch = new import_bignumber2.default(stake.delegation.deactivationEpoch);
if (activationEpoch.gt(currentEpochBN)) {
return StakeState.inactive;
}
if (activationEpoch.eq(currentEpochBN)) {
if (deactivationEpoch.eq(activationEpoch)) return StakeState.inactive;
return StakeState.activating;
}
if (deactivationEpoch.gt(currentEpochBN)) return StakeState.active;
if (deactivationEpoch.eq(currentEpochBN)) return StakeState.deactivating;
return StakeState.deactivated;
}
};
// src/solana.ts
var Solana = class extends Blockchain {
connection;
validator;
ERROR_MESSAGES = ERROR_MESSAGES;
ORIGINAL_ERROR_MESSAGES = {};
constructor(network = "mainnet-beta" /* Mainnet */, rpc = null) {
super();
if (rpc && !this.isValidURL(rpc)) {
throw this.throwError("INVALID_RPC_ERROR");
}
rpc = rpc || (0, import_web33.clusterApiUrl)(network);
try {
this.connection = new import_web33.Connection(rpc, "confirmed");
} catch (error) {
throw this.handleError("CONNECTION_ERROR", error);
}
switch (network) {
case "mainnet-beta" /* Mainnet */:
this.validator = SOL_MAINNET_VALIDATOR_ADDRESS;
break;
case "devnet" /* Devnet */:
this.validator = SOL_DEVNET_VALIDATOR_ADDRESS;
break;
default:
throw this.throwError("UNSUPPORTED_NETWORK_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(address, lamports, source, lockup = import_web33.Lockup.default) {
if (lamports < SOL_MIN_AMOUNT) {
this.throwError("MIN_AMOUNT_ERROR", SOL_MIN_AMOUNT.toString());
}
try {
const publicKey = new import_web33.PublicKey(address);
const minimumRent = await this.connection.getMinimumBalanceForRentExemption(
import_web33.StakeProgram.space
);
lockup = lockup || import_web33.Lockup.default;
const [createStakeAccountTx, stakeAccountPublicKey, externalSigners] = source === null ? await this.createAccountTx(
publicKey,
lamports + minimumRent,
lockup
) : await this.createAccountWithSeedTx(
publicKey,
lamports + minimumRent,
source,
lockup
);
const versionedTX = await this.prepareTransaction(
createStakeAccountTx.instructions,
publicKey,
externalSigners
);
return {
result: {
createStakeAccountVerTx: versionedTX,
stakeAccount: stakeAccountPublicKey
}
};
} catch (error) {
throw this.handleError("CREATE_ACCOUNT_ERROR", error);
}
}
/**
* Prepares a transaction with the given instructions and payer.
*
* @param instructions - An array of TransactionInstruction objects.
* @param payer - The public key of the payer.
* @param externalSigners - an array of external signers.
* @returns A promise that resolves to a VersionedTransaction object.
*/
async prepareTransaction(instructions, payer, externalSigners) {
const blockhash = await this.getBlockhash();
const messageV0 = new import_web33.TransactionMessage({
payerKey: payer,
recentBlockhash: blockhash,
instructions
}).compileToV0Message();
const tx = new import_web33.VersionedTransaction(messageV0);
if (externalSigners.length > 0) {
tx.sign(externalSigners);
}
return tx;
}
/**
* Retrieves the latest blockhash.
*
* @returns A promise that resolves to a string representing the blockhash.
*/
async getBlockhash() {
const res = await this.connection.getLatestBlockhash({
commitment: "max"
});
return res.blockhash;
}
/**
* 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(address, lamports, stakeAccount) {
if (lamports < SOL_MIN_AMOUNT) {
this.throwError("MIN_AMOUNT_ERROR", SOL_MIN_AMOUNT.toString());
}
try {
const publicKey = new import_web33.PublicKey(address);
const stakeAccountPublicKey = new import_web33.PublicKey(stakeAccount);
const delegateTx = new import_web33.Transaction().add(
import_web33.StakeProgram.delegate({
stakePubkey: stakeAccountPublicKey,
authorizedPubkey: publicKey,
votePubkey: this.validator
})
);
const delegateVerTx = await this.prepareTransaction(
delegateTx.instructions,
publicKey,
[]
);
return { result: delegateVerTx };
} 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(address, stakeAccountPublicKey) {
try {
const publicKey = new import_web33.PublicKey(address);
const stakeAccount = new import_web33.PublicKey(stakeAccountPublicKey);
const deactivateTx = new import_web33.Transaction().add(
import_web33.StakeProgram.deactivate({
stakePubkey: stakeAccount,
authorizedPubkey: publicKey
})
);
const deactivateVerTx = await this.prepareTransaction(
deactivateTx.instructions,
publicKey,
[]
);
return { result: deactivateVerTx };
} 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(address, stakeAccountPublicKey, stakeBalance) {
try {
const publicKey = new import_web33.PublicKey(address);
const stakeAccount = new import_web33.PublicKey(stakeAccountPublicKey);
const withdrawTx = new import_web33.Transaction().add(
import_web33.StakeProgram.withdraw({
stakePubkey: stakeAccount,
authorizedPubkey: publicKey,
toPubkey: publicKey,
lamports: stakeBalance
})
);
const withdrawVerTx = await this.prepareTransaction(
withdrawTx.instructions,
publicKey,
[]
);
return { result: withdrawVerTx };
} 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(address) {
try {
const filters = [
{ dataSize: FILTER_DATA_SIZE },
{ memcmp: { offset: FILTER_OFFSET, bytes: address } }
];
const accounts = await this.connection.getParsedProgramAccounts(
import_web33.StakeProgram.programId,
{ filters }
);
return { result: accounts };
} 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, lockup = import_web33.Lockup.default) {
try {
const senderPublicKey = new import_web33.PublicKey(sender);
const minimumRent = await this.connection.getMinimumBalanceForRentExemption(
import_web33.StakeProgram.space
);
lockup = lockup || import_web33.Lockup.default;
const [createStakeAccountTx, stakeAccountPublicKey, externalSigners] = source === null ? await this.createAccountTx(
senderPublicKey,
lamports + minimumRent,
lockup
) : await this.createAccountWithSeedTx(
senderPublicKey,
lamports + minimumRent,
source,
lockup
);
const stakeTx = new import_web33.Transaction().add(
import_web33.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 50 }),
createStakeAccountTx,
import_web33.StakeProgram.delegate({
stakePubkey: stakeAccountPublicKey,
authorizedPubkey: senderPublicKey,
votePubkey: this.validator
})
);
const stakeVerTx = await this.prepareTransaction(
stakeTx.instructions,
senderPublicKey,
externalSigners
);
return { result: stakeVerTx };
} 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(address, lamports, lockup) {
const blockhash = await this.getBlockhash();
const stakeAccount = import_web33.Keypair.generate();
const createStakeAccountTx = import_web33.StakeProgram.createAccount({
authorized: new import_web33.Authorized(address, address),
fromPubkey: address,
lamports,
stakePubkey: stakeAccount.publicKey,
lockup
});
createStakeAccountTx.recentBlockhash = blockhash;
createStakeAccountTx.sign(stakeAccount);
return [createStakeAccountTx, stakeAccount.publicKey, [stakeAccount]];
}
/**
* 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, lockup) {
const seed = this.formatSource(source);
const stakeAccountPubkey = await import_web33.PublicKey.createWithSeed(
authorityPublicKey,
seed,
import_web33.StakeProgram.programId
);
const createStakeAccountTx = new import_web33.Transaction().add(
import_web33.StakeProgram.createAccountWithSeed({
authorized: new import_web33.Authorized(authorityPublicKey, authorityPublicKey),
fromPubkey: authorityPublicKey,
basePubkey: authorityPublicKey,
stakePubkey: stakeAccountPubkey,
lockup,
seed,
lamports
})
);
return [createStakeAccountTx, stakeAccountPubkey, []];
}
/** unstake - unstake
* @param {string} sender - account blockchain address (staker)
* @param {number} lamports - lamport amount
* @param {string} source - stake source
* @returns {Promise<object>} Promise object with Versioned Tx
*/
async unstake(sender, lamports, source) {
try {
const delegations = await this.getDelegations(sender);
const stakeAccounts = delegations.result.map((delegationAcc) => {
return {
pubkey: delegationAcc.pubkey,
account: new StakeAccount2(delegationAcc.account)
};
});
const epochInfo = await this.connection.getEpochInfo();
const tm = this.timestampInSec();
let totalActiveStake = new import_bignumber3.default(0);
const activeStakeAccounts = stakeAccounts.filter((acc) => {
const isActive = !(acc.account.isLockupInForce(epochInfo.epoch, tm) || acc.account.stakeAccountState(epochInfo.epoch) !== StakeState.active);
if (isActive && acc.account.account.data.info.stake) {
totalActiveStake = totalActiveStake.plus(
acc.account.account.data.info.stake.delegation.stake
);
}
return isActive;
});
let lamportsBN = new import_bignumber3.default(lamports);
if (totalActiveStake.lt(lamportsBN))
throw this.throwError("NOT_ENOUGH_ACTIVE_STAKE_ERROR");
activeStakeAccounts.sort((a, b) => {
const stakeA = a.account.account.data.info.stake?.delegation.stake;
const stakeB = b.account.account.data.info.stake?.delegation.stake;
if (!stakeA || !stakeB) return 0;
return stakeA.minus(stakeB).toNumber();
});
const accountsToDeactivate = [];
const accountsToSplit = [];
let i = 0;
while (lamportsBN.gt(new import_bignumber3.default(0)) && i < activeStakeAccounts.length) {
const acc = activeStakeAccounts[i];
if (acc === void 0 || acc.account.account.data.info.stake === null) {
i++;
continue;
}
const stakeAmount = acc.account.account.data.info.stake.delegation.stake;
const isBelowThreshold = stakeAmount.lte(lamportsBN) || stakeAmount.minus(lamportsBN).lt(SOL_MIN_AMOUNT);
if (isBelowThreshold) {
accountsToDeactivate.push(acc);
lamportsBN = lamportsBN.minus(stakeAmount);
i++;
continue;
}
accountsToSplit.push({ account: acc, lamports: lamportsBN.toNumber() });
break;
}
const senderPublicKey = new import_web33.PublicKey(sender);
let instructions = [
import_web33.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 50 })
];
const minimumRent = await this.connection.getMinimumBalanceForRentExemption(
import_web33.StakeProgram.space
);
for (const acc of accountsToSplit) {
const [tx, newStakeAccountPubkey] = await this.split(
senderPublicKey,
acc.lamports,
acc.account.pubkey,
source,
minimumRent
);
const deactivateTx = import_web33.StakeProgram.deactivate({
stakePubkey: newStakeAccountPubkey,
authorizedPubkey: senderPublicKey
});
instructions.push(...tx.instructions, ...deactivateTx.instructions);
}
for (const acc of accountsToDeactivate) {
const deactivateTx = import_web33.StakeProgram.deactivate({
stakePubkey: acc.pubkey,
authorizedPubkey: senderPublicKey
});
instructions.push(...deactivateTx.instructions);
}
instructions = instructions.map((instruction) => {
return new import_web33.TransactionInstruction(instruction);
});
const versionedTX = await this.prepareTransaction(
instructions,
senderPublicKey,
[]
);
return { result: versionedTX };
} 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 import_web33.PublicKey.createWithSeed(
authorityPublicKey,
seed,
import_web33.StakeProgram.programId
);
const splitStakeAccountTx = new import_web33.Transaction().add(
import_web33.StakeProgram.splitWithSeed(
{
stakePubkey: oldStakeAccountPubkey,
authorizedPubkey: authorityPublicKey,
splitStakePubkey: newStakeAccountPubkey,
basePubkey: authorityPublicKey,
seed,
lamports
},
rentExemptReserve
)
);
return [splitStakeAccountTx, 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) {
try {
const delegations = await this.getDelegations(sender);
const stakeAccounts = delegations.result.map((delegationAcc) => {
return {
pubkey: delegationAcc.pubkey,
account: new StakeAccount2(delegationAcc.account)
};
});
const epochInfo = await this.connection.getEpochInfo();
const tm = this.timestampInSec();
let totalClaimableStake = new import_bignumber3.default(0);
const deactivatedStakeAccounts = stakeAccounts.filter((acc) => {
const { data } = acc.account.account;
const { info } = data;
const isDeactivated = !acc.account.isLockupInForce(epochInfo.epoch, tm) && acc.account.stakeAccountState(epochInfo.epoch) === StakeState.deactivated;
if (info.stake && isDeactivated) {
totalClaimableStake = totalClaimableStake.plus(
info.stake.delegation.stake
);
}
return isDeactivated;
});
if (deactivatedStakeAccounts.length === 0)
throw this.throwError("NOTHING_TO_CLAIM_ERROR");
const senderPublicKey = new import_web33.PublicKey(sender);
let instructions = [
import_web33.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 50 })
];
for (const acc of deactivatedStakeAccounts) {
const withdrawTx = import_web33.StakeProgram.withdraw({
stakePubkey: acc.pubkey,
authorizedPubkey: senderPublicKey,
toPubkey: senderPublicKey,
lamports: acc.account.account.lamports
});
instructions.push(...withdrawTx.instructions);
}
instructions = instructions.map((instruction) => {
return new import_web33.TransactionInstruction(instruction);
});
const versionedTX = await this.prepareTransaction(
instructions,
senderPublicKey,
[]
);
return { result: versionedTX };
} catch (error) {
throw this.handleError("CLAIM_ERROR", error);
}
}
async getEpochInfo() {
try {
const epochInfo = await this.connection.getEpochInfo();
return { result: epochInfo };
} catch (error) {
throw this.handleError("GET_EPOCH_INFO_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.
*
*/
async merge(authorityPublicKey, stakeAccount1, stakeAccount2) {
const mergeStakeAccountTx = import_web33.StakeProgram.merge({
stakePubkey: stakeAccount1,
sourceStakePubKey: stakeAccount2,
authorizedPubkey: authorityPublicKey
});
return [mergeStakeAccountTx];
}
/**
* 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;
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
FILTER_DATA_SIZE,
FILTER_OFFSET,
ParseStakeAccountError,
SOL_CHAIN,
SOL_DEVNET_VALIDATOR_ADDRESS,
SOL_MAINNET_VALIDATOR_ADDRESS,
SOL_MIN_AMOUNT,
SolNetwork,
Solana,
StakeAccount,
StakeAccountType,
StakeState
});