diff --git a/packages/client/src/blaze.ts b/packages/client/src/blaze.ts index df341874..54d31cb0 100644 --- a/packages/client/src/blaze.ts +++ b/packages/client/src/blaze.ts @@ -7,13 +7,10 @@ import { type Transaction, } from "@solana/web3.js"; import type BN from "bn.js"; -import { - findBSolTokenAccountAuthority, - findGSolMintAuthority, - type SunriseStakeConfig, -} from "./util"; +import { findBSolTokenAccountAuthority, findGSolMintAuthority } from "./util"; import { STAKE_POOL_PROGRAM_ID } from "./constants"; import { type AnchorProvider, type Program, utils } from "@coral-xyz/anchor"; +import { type SunriseStakeConfig } from "./types/Config"; import { type SunriseStake } from "./types/sunrise_stake"; import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { type BlazeState } from "./types/Solblaze"; diff --git a/packages/client/src/constants.ts b/packages/client/src/constants.ts index 5f503572..764a9893 100644 --- a/packages/client/src/constants.ts +++ b/packages/client/src/constants.ts @@ -16,11 +16,21 @@ export const IMPACT_NFT_PROGRAM_ID = new PublicKey( "SUNFT6ErsQvMcDzMcGyndq2P31wYCFs6G6WEcoyGkGc" ); +export const MARINADE_PROGRAM_ID = new PublicKey( + "MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD" +); + interface BlazeConfig { pool: PublicKey; bsolMint: PublicKey; } +interface MarinadeConfig { + stateAddress: PublicKey; + msolMintAddress: PublicKey; + lpMintAddress: PublicKey; +} + export interface EnvironmentConfig { state: PublicKey; holdingAccount: PublicKey; @@ -30,6 +40,7 @@ export interface EnvironmentConfig { impactNFT: { state: PublicKey | undefined; // if undefined, impact nft is disabled }; + marinade: MarinadeConfig; } export const Environment: Record< WalletAdapterNetwork | "localnet", @@ -51,6 +62,17 @@ export const Environment: Record< impactNFT: { state: new PublicKey("6RzCneyeEqnjiWxrzqfBwHDEpTrbcSkBFFUrtMZnNjpc"), }, + marinade: { + stateAddress: new PublicKey( + "8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC" + ), + msolMintAddress: new PublicKey( + "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So" + ), + lpMintAddress: new PublicKey( + "LPmSozJJ8Jh69ut2WP3XmVohTjL4ipR18yiCzxrUmVj" + ), + }, }, // TODO placeholders testnet: { @@ -67,6 +89,17 @@ export const Environment: Record< impactNFT: { state: PublicKey.default, // TODO }, + marinade: { + stateAddress: new PublicKey( + "8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC" + ), + msolMintAddress: new PublicKey( + "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So" + ), + lpMintAddress: new PublicKey( + "LPmSozJJ8Jh69ut2WP3XmVohTjL4ipR18yiCzxrUmVj" + ), + }, }, devnet: { state: new PublicKey("Jpp29FzyV7rXdVRWFaiE9tBcVCaEMvj16gk87rC3S4z"), @@ -84,6 +117,17 @@ export const Environment: Record< impactNFT: { state: new PublicKey("6iyfwPbbLeYAoUcQkECCPwftFuw3j5VEcXF7xcQeAdX6"), }, + marinade: { + stateAddress: new PublicKey( + "8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC" + ), + msolMintAddress: new PublicKey( + "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So" + ), + lpMintAddress: new PublicKey( + "LPmSozJJ8Jh69ut2WP3XmVohTjL4ipR18yiCzxrUmVj" + ), + }, }, localnet: { state: new PublicKey("28SkW4iD7UJc9zkxcq6yNb1MFX2hxqdJjxjZs67Jwr2b"), @@ -101,6 +145,17 @@ export const Environment: Record< impactNFT: { state: PublicKey.default, // TODO }, + marinade: { + stateAddress: new PublicKey( + "8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC" + ), + msolMintAddress: new PublicKey( + "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So" + ), + lpMintAddress: new PublicKey( + "LPmSozJJ8Jh69ut2WP3XmVohTjL4ipR18yiCzxrUmVj" + ), + }, }, }; diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 56c77b02..8cd6596b 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -10,6 +10,7 @@ import { SYSVAR_CLOCK_PUBKEY, Transaction, type TransactionInstruction, + type EpochInfo, } from "@solana/web3.js"; import { confirm, @@ -24,7 +25,6 @@ import { PROGRAM_ID, proportionalBN, setUpAnchor, - type SunriseStakeConfig, ZERO, ZERO_BALANCE, toSol, @@ -38,6 +38,10 @@ import { type MarinadeState, } from "@sunrisestake/marinade-ts-sdk"; import BN from "bn.js"; +import { + type SunriseStakeConfig, + type SunriseTokenConfig, +} from "./types/Config"; import { type Balance, type Details, @@ -62,6 +66,7 @@ import { NETWORK_FEE, SOLBLAZE_ENABLED, STAKE_POOL_PROGRAM_ID, + MARINADE_PROGRAM_ID, } from "./constants"; import { deposit, @@ -99,6 +104,7 @@ export { toSol, findImpactNFTMintAuthority, ZERO_BALANCE } from "./util"; export class SunriseStakeClient { readonly program: Program; config: SunriseStakeConfig | undefined; + tokenConfig: SunriseTokenConfig | undefined; // TODO make private once all functions are moved in here marinade: Marinade | undefined; @@ -108,15 +114,7 @@ export class SunriseStakeClient { readonly staker: PublicKey; stakerGSolTokenAccount: PublicKey | undefined; - msolTokenAccountAuthority: PublicKey | undefined; - msolTokenAccount: PublicKey | undefined; - - bsolTokenAccountAuthority: PublicKey | undefined; - bsolTokenAccount: PublicKey | undefined; - blazeState: BlazeState | undefined; - - liqPoolTokenAccount: PublicKey | undefined; lockClient: LockClient | undefined; readonly env: EnvironmentConfig; @@ -148,10 +146,14 @@ export class SunriseStakeClient { this.env.state ); - const stakePoolInfo = await getStakePoolAccount( - this.provider.connection, - this.env.blaze.pool - ); + this.stakerGSolTokenAccount = PublicKey.findProgramAddressSync( + [ + this.staker.toBuffer(), + TOKEN_PROGRAM_ID.toBuffer(), + sunriseStakeState.gsolMint.toBuffer(), + ], + ASSOCIATED_TOKEN_PROGRAM_ID + )[0]; this.config = { gsolMint: sunriseStakeState.gsolMint, @@ -164,54 +166,87 @@ export class SunriseStakeClient { impactNFTStateAddress: this.env.impactNFT.state, options: this.options, }; + this.tokenConfig = await this.getTokenConfig(this.config); - this.stakerGSolTokenAccount = PublicKey.findProgramAddressSync( - [ - this.staker.toBuffer(), - TOKEN_PROGRAM_ID.toBuffer(), - sunriseStakeState.gsolMint.toBuffer(), - ], - ASSOCIATED_TOKEN_PROGRAM_ID - )[0]; - const [gsolMintAuthority] = findGSolMintAuthority(this.config); - this.msolTokenAccountAuthority = findMSolTokenAccountAuthority( - this.config - )[0]; - this.bsolTokenAccountAuthority = findBSolTokenAccountAuthority( - this.config - )[0]; + [this.marinade, this.marinadeState] = await this.makeMarinade(); + + this.blazeState = await this.makeBlaze(); + + this.lockClient = await LockClient.build( + this.config, + this.program, + this.staker + ); + } + + private async getTokenConfig( + config: SunriseStakeConfig + ): Promise { + const gsolMintAuthority = findGSolMintAuthority(config); + + const msolTokenAccountAuthority = findMSolTokenAccountAuthority(config); + const msolTokenAccount = await utils.token.associatedAddress({ + mint: this.env.marinade.msolMintAddress, + owner: msolTokenAccountAuthority[0], + }); + + const liqPoolTokenAccount = await utils.token.associatedAddress({ + mint: this.env.marinade.lpMintAddress, + owner: msolTokenAccountAuthority[0], + }); + + const bsolTokenAccountAuthority = findBSolTokenAccountAuthority(config); + const bsolTokenAccount = await utils.token.associatedAddress({ + mint: this.env.blaze.bsolMint, + owner: bsolTokenAccountAuthority[0], + }); + + return { + gsolMintAuthority, + msolTokenAccount, + msolTokenAccountAuthority, + liqPoolTokenAccount, + bsolTokenAccount, + bsolTokenAccountAuthority, + }; + } + + private async makeMarinade(): Promise<[Marinade, MarinadeState]> { + if (!this.config || !this.tokenConfig) throw new Error("Init not called"); const marinadeConfig = new MarinadeConfig({ connection: this.provider.connection, publicKey: this.provider.publicKey, proxyStateAddress: this.env.state, - proxySolMintAuthority: gsolMintAuthority, + proxySolMintAuthority: this.tokenConfig.gsolMintAuthority[0], proxySolMintAddress: this.config.gsolMint, - msolTokenAccountAuthority: this.msolTokenAccountAuthority, + msolTokenAccountAuthority: this.tokenConfig.msolTokenAccountAuthority[0], proxyTreasury: this.config.treasury, }); - this.marinade = new Marinade(marinadeConfig); - this.marinadeState = await this.marinade.getMarinadeState(); - this.msolTokenAccount = await utils.token.associatedAddress({ - mint: this.marinadeState.mSolMintAddress, - owner: this.msolTokenAccountAuthority, - }); - this.liqPoolTokenAccount = await utils.token.associatedAddress({ - mint: this.marinadeState.lpMint.address, - owner: this.msolTokenAccountAuthority, - }); + + const marinade = new Marinade(marinadeConfig); + const marinadeState = await marinade.getMarinadeState(); + + return [marinade, marinadeState]; + } + + // TODO + private async makeBlaze(): Promise { + const stakePoolInfo = await getStakePoolAccount( + this.provider.connection, + this.env.blaze.pool + ); const [withdrawAuthority] = PublicKey.findProgramAddressSync( [this.env.blaze.pool.toBuffer(), Buffer.from("withdraw")], STAKE_POOL_PROGRAM_ID ); - const [depositAuthority] = PublicKey.findProgramAddressSync( [this.env.blaze.pool.toBuffer(), Buffer.from("deposit")], STAKE_POOL_PROGRAM_ID ); - this.blazeState = { + return { pool: this.env.blaze.pool, bsolMint: stakePoolInfo.poolMint, validatorList: stakePoolInfo.validatorList, @@ -221,17 +256,6 @@ export class SunriseStakeClient { withdrawAuthority, depositAuthority, }; - - this.bsolTokenAccount = await utils.token.associatedAddress({ - mint: stakePoolInfo.poolMint, - owner: this.bsolTokenAccountAuthority, - }); - - this.lockClient = await LockClient.build( - this.config, - this.program, - this.staker - ); } public async sendAndConfirmTransaction( @@ -473,7 +497,6 @@ export class SunriseStakeClient { !this.marinadeState || !this.marinade || !this.config || - !this.msolTokenAccount || !this.stakerGSolTokenAccount || !this.blazeState ) @@ -504,8 +527,7 @@ export class SunriseStakeClient { !this.marinadeState || !this.marinade || !this.config || - !this.msolTokenAccount || - !this.bsolTokenAccount || + !this.tokenConfig || !this.stakerGSolTokenAccount || !this.blazeState ) @@ -573,13 +595,13 @@ export class SunriseStakeClient { liqPoolSolLegPda: await this.marinadeState.solLeg(), liqPoolMsolLeg: this.marinadeState.mSolLeg, liqPoolMsolLegAuthority: await this.marinadeState.mSolLegAuthority(), - liqPoolTokenAccount: this.liqPoolTokenAccount, + liqPoolTokenAccount: this.tokenConfig.liqPoolTokenAccount, reservePda: await this.marinadeState.reserveAddress(), treasuryMsolAccount: this.marinadeState.treasuryMsolAccount, - getMsolFrom: this.msolTokenAccount, - getMsolFromAuthority: this.msolTokenAccountAuthority, - getBsolFrom: this.bsolTokenAccount, - getBsolFromAuthority: this.bsolTokenAccountAuthority, + getMsolFrom: this.tokenConfig.msolTokenAccount, + getMsolFromAuthority: this.tokenConfig.msolTokenAccountAuthority[0], + getBsolFrom: this.tokenConfig.bsolTokenAccount, + getBsolFromAuthority: this.tokenConfig.bsolTokenAccountAuthority[0], epochReportAccount: epochReportAccountAddress, systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, @@ -602,7 +624,6 @@ export class SunriseStakeClient { !this.marinadeState || !this.marinade || !this.config || - !this.msolTokenAccount || !this.stakerGSolTokenAccount ) throw new Error("init not called"); @@ -686,12 +707,15 @@ export class SunriseStakeClient { !this.marinadeState || !this.marinade || !this.config || - !this.msolTokenAccount + !this.tokenConfig ) throw new Error("init not called"); const { transaction, newTicketAccount, proxyTicketAccount } = - await this.marinade.orderUnstake(lamports, this.msolTokenAccount); + await this.marinade.orderUnstake( + lamports, + this.tokenConfig.msolTokenAccount + ); Boolean(this.config?.options.verbose) && logKeys(transaction); @@ -744,7 +768,7 @@ export class SunriseStakeClient { public async claimUnstakeTicket( ticketAccount: TicketAccount ): Promise { - if (!this.marinade || !this.marinadeState) + if (!this.marinade || !this.marinadeState || !this.tokenConfig) throw new Error("init not called"); const reservePda = await this.marinadeState.reserveAddress(); @@ -760,7 +784,7 @@ export class SunriseStakeClient { reservePda, marinadeTicketAccount: ticketAccount.marinadeTicketAccount, sunriseTicketAccount: ticketAccount.address, - msolAuthority: this.msolTokenAccountAuthority, + msolAuthority: this.tokenConfig.msolTokenAccountAuthority[0], transferSolTo: this.staker, systemProgram: SystemProgram.programId, clock: SYSVAR_CLOCK_PUBKEY, @@ -778,12 +802,7 @@ export class SunriseStakeClient { } public async withdrawFromBlaze(amount: BN): Promise { - if ( - !this.blazeState || - !this.config || - !this.stakerGSolTokenAccount || - !this.bsolTokenAccount - ) + if (!this.blazeState || !this.config || !this.stakerGSolTokenAccount) throw new Error("init not called"); const withdrawIx = await blazeWithdrawSol( @@ -803,12 +822,7 @@ export class SunriseStakeClient { newStakeAccount: PublicKey, amount: BN ): Promise { - if ( - !this.blazeState || - !this.config || - !this.stakerGSolTokenAccount || - !this.bsolTokenAccount - ) + if (!this.blazeState || !this.config || !this.stakerGSolTokenAccount) throw new Error("init not called"); const withdrawStakeIx = await blazeWithdrawStake( @@ -832,9 +846,7 @@ export class SunriseStakeClient { !this.blazeState || !this.marinade || !this.config || - !this.msolTokenAccount || - !this.msolTokenAccountAuthority || - !this.liqPoolTokenAccount + !this.tokenConfig ) { throw new Error("init not called"); } @@ -857,12 +869,12 @@ export class SunriseStakeClient { liqPoolMint: this.marinadeState.lpMint.address, liqPoolSolLegPda, liqPoolMsolLeg: this.marinadeState.mSolLeg, - liqPoolTokenAccount: this.liqPoolTokenAccount, + liqPoolTokenAccount: this.tokenConfig.liqPoolTokenAccount, treasuryMsolAccount: this.marinadeState.treasuryMsolAccount, - getMsolFrom: this.msolTokenAccount, - getMsolFromAuthority: this.msolTokenAccountAuthority, - getBsolFrom: this.bsolTokenAccount, - getBsolFromAuthority: this.bsolTokenAccountAuthority, + getMsolFrom: this.tokenConfig.msolTokenAccount, + getMsolFromAuthority: this.tokenConfig.msolTokenAccountAuthority[0], + getBsolFrom: this.tokenConfig.bsolTokenAccount, + getBsolFromAuthority: this.tokenConfig.bsolTokenAccountAuthority[0], epochReportAccount: epochReportAccountAddress, treasury: this.config.treasury, systemProgram: SystemProgram.programId, @@ -893,9 +905,7 @@ export class SunriseStakeClient { !this.blazeState || !this.marinade || !this.config || - !this.msolTokenAccount || - !this.msolTokenAccountAuthority || - !this.liqPoolTokenAccount + !this.tokenConfig ) { throw new Error("init not called"); } @@ -920,12 +930,12 @@ export class SunriseStakeClient { liqPoolMint: this.marinadeState.lpMint.address, liqPoolSolLegPda, liqPoolMsolLeg: this.marinadeState.mSolLeg, - liqPoolTokenAccount: this.liqPoolTokenAccount, + liqPoolTokenAccount: this.tokenConfig.liqPoolTokenAccount, treasuryMsolAccount: this.marinadeState.treasuryMsolAccount, - getMsolFrom: this.msolTokenAccount, - getMsolFromAuthority: this.msolTokenAccountAuthority, - getBsolFrom: this.bsolTokenAccount, - getBsolFromAuthority: this.bsolTokenAccountAuthority, + getMsolFrom: this.tokenConfig.msolTokenAccount, + getMsolFromAuthority: this.tokenConfig.msolTokenAccountAuthority[0], + getBsolFrom: this.tokenConfig.bsolTokenAccount, + getBsolFromAuthority: this.tokenConfig.bsolTokenAccountAuthority[0], epochReportAccount, treasury: this.config.treasury, systemProgram: SystemProgram.programId, @@ -1048,64 +1058,67 @@ export class SunriseStakeClient { }; } - public async details(): Promise
{ - if ( - !this.marinadeState || - !this.stakerGSolTokenAccount || - !this.msolTokenAccount || - !this.config - ) - throw new Error("init not called"); - - const currentEpochPromise = this.provider.connection.getEpochInfo(); + private async getEpochDetails(): Promise<{ + currentEpoch: EpochInfo; + epochReport: EpochReportAccount; + }> { + if (!this.config) throw new Error("Init not called"); - const lpMintInfoPromise = this.marinadeState.lpMint.mintInfo(); - const lpMsolBalancePromise = - this.provider.connection.getTokenAccountBalance( - this.marinadeState.mSolLeg - ); + const currentEpoch = await this.provider.connection.getEpochInfo(); + const { account: epochReport } = await getEpochReportAccount( + this.config, + this.program + ); - const solLeg = await this.marinadeState.solLeg(); - const solLegBalancePromise = this.provider.connection.getBalance(solLeg); + return { + currentEpoch, + epochReport: epochReport ?? EMPTY_EPOCH_REPORT, + }; + } - const balancesPromise = this.balance(); + private async getMpDetails(): Promise { + // TODO: Return undefined. Don't error + if (!this.marinadeState) throw new Error("Marinade not initialized"); - const lockAccountPromise = await this.getLockAccount(); + const balance = await this.balance(); - const impactNFTPromise = await getImpactNFT( - this.config, - this.staker, - this.provider + const solValueOfMSol = this.computeLamportsFromMSol( + new BN(balance.msolBalance.amount), + this.marinadeState ); - const [ - currentEpoch, - lpMintInfo, - lpSolLegBalance, - lpMsolBalance, - balances, - lockAccountDetails, - impactNFT, - ] = await Promise.all([ - currentEpochPromise, - lpMintInfoPromise, - solLegBalancePromise, - lpMsolBalancePromise, - balancesPromise, - lockAccountPromise, - impactNFTPromise, - ]); + return { + msolPrice: this.marinadeState.mSolPrice, + msolValue: solValueOfMSol, + stakeDelta: this.marinadeState.stakeDelta().toNumber(), + }; + } - const availableLiqPoolSolLegBalance = new BN(lpSolLegBalance).sub( - this.marinadeState.state.rentExemptForTokenAcc + private async getLpDetails(balance: Balance): Promise { + // TODO: Return undefined. Don't error + if (!this.marinadeState) throw new Error("Marinade not initialized"); + + const lpMintInfo = await this.marinadeState.lpMint.mintInfo(); + + const lpMsolBalance = await this.provider.connection.getTokenAccountBalance( + this.marinadeState.mSolLeg ); + const lpMsolShare = proportionalBN( - new BN(balances.liqPoolBalance.amount), + new BN(balance.liqPoolBalance.amount), new BN(lpMsolBalance.value.amount), new BN(lpMintInfo.supply.toString()) ); + + const solLeg = await this.marinadeState.solLeg(); + const lpSolLegBalance = await this.provider.connection.getBalance(solLeg); + + const availableLiqPoolSolLegBalance = new BN(lpSolLegBalance).sub( + this.marinadeState.state.rentExemptForTokenAcc + ); + const lpSolShare = proportionalBN( - new BN(balances.liqPoolBalance.amount), + new BN(balance.liqPoolBalance.amount), availableLiqPoolSolLegBalance, new BN(lpMintInfo.supply.toString()) ); @@ -1116,18 +1129,7 @@ export class SunriseStakeClient { const lpSolValue = lpSolShare.add(solValueOlpMSolShare); - const solValueOfMSol = this.computeLamportsFromMSol( - new BN(balances.msolBalance.amount), - this.marinadeState - ); - - const mpDetails = { - msolPrice: this.marinadeState.mSolPrice, - msolValue: solValueOfMSol, - stakeDelta: this.marinadeState.stakeDelta().toNumber(), - }; - - const lpDetails = { + return { mintAddress: this.marinadeState.lpMint.address.toBase58(), supply: lpMintInfo.supply, mintAuthority: lpMintInfo.mintAuthority?.toBase58(), @@ -1137,22 +1139,27 @@ export class SunriseStakeClient { lpSolValue, // total SOL value of the LP tokens held by the sunrise instance msolLeg: this.marinadeState.mSolLeg.toBase58(), }; + } - const { account: epochReport } = await getEpochReportAccount( - this.config, - this.program - ); + private async getBpDetails(): Promise { + if (!this.config || !this.tokenConfig) throw new Error("Init not called"); const stakePoolInfo = await getStakePoolAccount( this.provider.connection, this.env.blaze.pool ); + + const bsolLamportsBalance = + await this.provider.connection.getTokenAccountBalance( + this.tokenConfig.bsolTokenAccount + ); + const [bsolPrice, bsolValue] = this.computeLamportsFromBSol( - new BN(balances.bsolBalance.amount), + new BN(bsolLamportsBalance.value.amount), stakePoolInfo ); - const bpDetails = { + return { pool: this.env.blaze.pool.toString(), bsolPrice, bsolValue, @@ -1161,6 +1168,10 @@ export class SunriseStakeClient { denominator: stakePoolInfo.solWithdrawalFee.denominator, }, }; + } + + private async getLockDetails(): Promise { + const lockAccountDetails = await this.getLockAccount(); const lockDetails: Details["lockDetails"] = lockAccountDetails.lockAccount && @@ -1179,6 +1190,18 @@ export class SunriseStakeClient { } : undefined; + return lockDetails; + } + + private async getImpactNFTDetails(): Promise { + if (!this.config) throw new Error("Init not called"); + + const impactNFT = await getImpactNFT( + this.config, + this.staker, + this.provider + ); + const nftSummary = this.config.impactNFTStateAddress ? { stateAddress: this.config.impactNFTStateAddress, @@ -1191,23 +1214,38 @@ export class SunriseStakeClient { ? nftSummary : undefined; + return impactNFTDetails; + } + + public async details(): Promise
{ + if (!this.config || !this.tokenConfig || !this.stakerGSolTokenAccount) + throw new Error("Init not called"); + + const epochDetails = await this.getEpochDetails(); + const balances = await this.balance(); + const mpDetails = await this.getMpDetails(); + const lpDetails = await this.getLpDetails(balances); + const bpDetails = await this.getBpDetails(); + const lockDetails = await this.getLockDetails(); + const impactNFTDetails = await this.getImpactNFTDetails(); + const detailsWithoutYield: Omit = { staker: this.staker.toBase58(), + currentEpoch: epochDetails.currentEpoch, + epochReport: epochDetails.epochReport, balances, - currentEpoch, - epochReport: epochReport ?? EMPTY_EPOCH_REPORT, stakerGSolTokenAccount: this.stakerGSolTokenAccount.toBase58(), sunriseStakeConfig: { gsolMint: this.config.gsolMint.toBase58(), programId: this.config.programId.toBase58(), stateAddress: this.config.stateAddress.toBase58(), treasury: this.config.treasury.toBase58(), - msolTokenAccount: this.msolTokenAccount.toBase58(), - msolTokenAccountAuthority: this.msolTokenAccountAuthority?.toBase58(), + msolTokenAccount: this.tokenConfig.msolTokenAccount.toBase58(), + msolTokenAccountAuthority: + this.tokenConfig.msolTokenAccountAuthority[0].toBase58(), }, - marinadeFinanceProgramId: - this.marinadeState.marinadeFinanceProgramId.toBase58(), - marinadeStateAddress: this.marinadeState.marinadeStateAddress.toBase58(), + marinadeFinanceProgramId: MARINADE_PROGRAM_ID.toBase58(), + marinadeStateAddress: this.env.marinade.stateAddress.toBase58(), mpDetails, lpDetails, bpDetails, @@ -1282,29 +1320,13 @@ export class SunriseStakeClient { const marinadeState = await new Marinade(marinadeConfig).getMarinadeState(); - const [, gsolMintAuthorityBump] = findGSolMintAuthority(config); + const tokenAccounts = await this.getTokenConfig(config); const [msolAuthority, msolAuthorityBump] = - findMSolTokenAccountAuthority(config); - const msolAssociatedTokenAccountAddress = - await utils.token.associatedAddress({ - mint: marinadeState.mSolMintAddress, - owner: msolAuthority, - }); - // use the same token authority PDA for the msol token account - // and the liquidity pool token account for convenience - const liqPoolAssociatedTokenAccountAddress = - await utils.token.associatedAddress({ - mint: marinadeState.lpMint.address, - owner: msolAuthority, - }); - + tokenAccounts.msolTokenAccountAuthority; const [bsolAuthority, bsolAuthorityBump] = - findBSolTokenAccountAuthority(config); - const bsolTokenAccountAddress = await utils.token.associatedAddress({ - mint: this.env.blaze.bsolMint, - owner: bsolAuthority, - }); + tokenAccounts.bsolTokenAccountAuthority; + const gsolMintAuthorityBump = tokenAccounts.gsolMintAuthority[1]; type Accounts = Parameters< ReturnType["accounts"] @@ -1317,11 +1339,11 @@ export class SunriseStakeClient { msolMint: marinadeState.mSolMintAddress, bsolMint: this.env.blaze.bsolMint, msolTokenAccountAuthority: msolAuthority, - msolTokenAccount: msolAssociatedTokenAccountAddress, + msolTokenAccount: tokenAccounts.msolTokenAccount, liqPoolMint: marinadeState.lpMint.address, - liqPoolTokenAccount: liqPoolAssociatedTokenAccountAddress, + liqPoolTokenAccount: tokenAccounts.liqPoolTokenAccount, bsolTokenAccountAuthority: bsolAuthority, - bsolTokenAccount: bsolTokenAccountAddress, + bsolTokenAccount: tokenAccounts.bsolTokenAccount, systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -1350,8 +1372,7 @@ export class SunriseStakeClient { epochReport, bpDetails, }: Omit): BN { - if (!this.marinadeState || !this.msolTokenAccount) - throw new Error("init not called"); + if (!this.marinadeState) throw new Error("init not called"); // deposited in Stake Pool const solValueOfMSol = mpDetails.msolValue; @@ -1448,8 +1469,14 @@ export class SunriseStakeClient { } public async balance(): Promise { - if (!this.marinadeState || !this.stakerGSolTokenAccount || !this.config) + if ( + !this.config || + !this.tokenConfig || + !this.marinadeState || + !this.stakerGSolTokenAccount + ) throw new Error("init not called"); + const gsolBalancePromise = this.provider.connection .getTokenAccountBalance(this.stakerGSolTokenAccount) .catch((e) => { @@ -1464,49 +1491,21 @@ export class SunriseStakeClient { this.config.gsolMint ); - const msolTokenAccountAuthority = findMSolTokenAccountAuthority( - this.config - )[0]; - const msolAssociatedTokenAccountAddress = - await utils.token.associatedAddress({ - mint: this.marinadeState.mSolMintAddress, - owner: msolTokenAccountAuthority, - }); const msolLamportsBalancePromise = this.provider.connection.getTokenAccountBalance( - msolAssociatedTokenAccountAddress + this.tokenConfig.msolTokenAccount ); - - const bsolTokenAccountAuthority = findBSolTokenAccountAuthority( - this.config - )[0]; - const bsolAssociatedTokenAccountAddress = - await utils.token.associatedAddress({ - mint: this.env.blaze.bsolMint, - owner: bsolTokenAccountAuthority, - }); const bsolLamportsBalancePromise = this.provider.connection.getTokenAccountBalance( - bsolAssociatedTokenAccountAddress + this.tokenConfig.bsolTokenAccount ); - - // use the same token authority PDA for the msol token account - // and the liquidity pool token account for convenience - const liqPoolAssociatedTokenAccountAddress = - await utils.token.associatedAddress({ - mint: this.marinadeState.lpMint.address, - owner: msolTokenAccountAuthority, - }); - const liqPoolBalancePromise = this.provider.connection.getTokenAccountBalance( - liqPoolAssociatedTokenAccountAddress + this.tokenConfig.liqPoolTokenAccount ); - const treasuryBalancePromise = this.provider.connection.getBalance( this.config.treasury ); - const holdingAccountBalancePromise = this.provider.connection.getBalance( this.env.holdingAccount ); diff --git a/packages/client/src/lock.ts b/packages/client/src/lock.ts index f92055d2..9aca3d27 100644 --- a/packages/client/src/lock.ts +++ b/packages/client/src/lock.ts @@ -5,7 +5,6 @@ import { findLockAccount, findLockTokenAccount, getTokenAccountNullable, - type SunriseStakeConfig, } from "./util"; import { ComputeBudgetProgram, @@ -15,6 +14,7 @@ import { type Transaction, type TransactionInstruction, } from "@solana/web3.js"; +import { type SunriseStakeConfig } from "./types/Config"; import { type LockAccount } from "./types/LockAccount"; import * as anchor from "@coral-xyz/anchor"; import { diff --git a/packages/client/src/marinade.ts b/packages/client/src/marinade.ts index 6078f2af..57a40712 100644 --- a/packages/client/src/marinade.ts +++ b/packages/client/src/marinade.ts @@ -15,7 +15,6 @@ import { findMSolTokenAccountAuthority, findOrderUnstakeTicketAccount, getValidatorIndex, - type SunriseStakeConfig, } from "./util"; import { type Marinade, @@ -23,6 +22,7 @@ import { MarinadeUtils, } from "@sunrisestake/marinade-ts-sdk"; import { type Program, utils } from "@coral-xyz/anchor"; +import { type SunriseStakeConfig } from "./types/Config"; import { type BlazeState } from "./types/Solblaze"; import { type SunriseStake } from "./types/sunrise_stake"; import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; diff --git a/packages/client/src/types/Config.ts b/packages/client/src/types/Config.ts new file mode 100644 index 00000000..db99df2e --- /dev/null +++ b/packages/client/src/types/Config.ts @@ -0,0 +1,23 @@ +import { type PublicKey } from "@solana/web3.js"; +import { type Options } from "../util"; + +export interface SunriseStakeConfig { + stateAddress: PublicKey; + gsolMint: PublicKey; + treasury: PublicKey; + updateAuthority: PublicKey; + programId: PublicKey; + liqPoolProportion: number; + liqPoolMinProportion: number; + options: Options; + impactNFTStateAddress: PublicKey | undefined; // a state can exist without an impact nft state +} + +export interface SunriseTokenConfig { + gsolMintAuthority: [PublicKey, number]; + msolTokenAccount: PublicKey; + msolTokenAccountAuthority: [PublicKey, number]; + bsolTokenAccount: PublicKey; + bsolTokenAccountAuthority: [PublicKey, number]; + liqPoolTokenAccount: PublicKey; +} diff --git a/packages/client/src/util.ts b/packages/client/src/util.ts index f50ce626..98b1018f 100644 --- a/packages/client/src/util.ts +++ b/packages/client/src/util.ts @@ -14,6 +14,7 @@ import { Provider, type Wallet, } from "@sunrisestake/marinade-ts-sdk"; +import { type SunriseStakeConfig } from "./types/Config"; import { type Details } from "./types/Details"; import { type EnvironmentConfig, MAX_NUM_PRECISION } from "./constants"; import { @@ -51,20 +52,6 @@ export const enum ProgramDerivedAddressSeed { IMPACT_NFT_MINT_ACCOUNT = "impact_nft_mint_account", } -export interface SunriseStakeConfig { - stateAddress: PublicKey; - gsolMint: PublicKey; - treasury: PublicKey; - updateAuthority: PublicKey; - programId: PublicKey; - liqPoolProportion: number; - - liqPoolMinProportion: number; - - options: Options; - impactNFTStateAddress: PublicKey | undefined; // a state can exist without an impact nft state -} - // Return the type of an element in an array // type A = ArrayElement; // string // type B = ArrayElement; // string diff --git a/packages/tests/sunrise-stake.ts b/packages/tests/sunrise-stake.ts index dfbd758b..30d1f211 100644 --- a/packages/tests/sunrise-stake.ts +++ b/packages/tests/sunrise-stake.ts @@ -676,7 +676,7 @@ describe("sunrise-stake", () => { const liqPoolBalance = await client.provider.connection.getTokenAccountBalance( - client.liqPoolTokenAccount! + client.tokenConfig!.liqPoolTokenAccount ); await client.sendAndConfirmTransaction( diff --git a/packages/tests/util.ts b/packages/tests/util.ts index d62ef9c3..c6bff9e9 100644 --- a/packages/tests/util.ts +++ b/packages/tests/util.ts @@ -98,7 +98,7 @@ export const expectMSolTokenBalance = async ( tolerance = 0 // Allow for a tolerance as the msol calculation is inaccurate. The test uses the price which has limited precision ) => { const msolBalance = await client.provider.connection.getTokenAccountBalance( - client.msolTokenAccount! + client.tokenConfig!.msolTokenAccount ); log("mSOL balance", msolBalance.value.amount); expectAmount(new BN(msolBalance.value.amount), expectedAmount, tolerance); @@ -110,7 +110,7 @@ export const expectBSolTokenBalance = async ( tolerance = 0 ) => { const bsolBalance = await client.provider.connection.getTokenAccountBalance( - client.bsolTokenAccount! + client.tokenConfig!.bsolTokenAccount ); expectAmount(new BN(bsolBalance.value.amount), expectedAmount, tolerance); }; @@ -122,7 +122,7 @@ export const expectLiqPoolTokenBalance = async ( ) => { const liqPoolBalance = await client.provider.connection.getTokenAccountBalance( - client.liqPoolTokenAccount! + client.tokenConfig!.liqPoolTokenAccount ); log("LiqPool balance", liqPoolBalance.value.amount); log("Expected amount", expectedAmount);