From feea304ed28403c7c1af06aeea75a8a51520181f Mon Sep 17 00:00:00 2001 From: Ondra Chaloupka Date: Mon, 5 Aug 2024 11:21:42 +0200 Subject: [PATCH] [cli] show-bond to parse stake accounts on input --- .../__tests__/test-validator/show.spec.ts | 126 +++++++++++++++--- .../validator-bonds-cli/src/commands/utils.ts | 33 ++++- 2 files changed, 136 insertions(+), 23 deletions(-) diff --git a/packages/validator-bonds-cli/__tests__/test-validator/show.spec.ts b/packages/validator-bonds-cli/__tests__/test-validator/show.spec.ts index 99ac3df8..3c1feb2e 100644 --- a/packages/validator-bonds-cli/__tests__/test-validator/show.spec.ts +++ b/packages/validator-bonds-cli/__tests__/test-validator/show.spec.ts @@ -591,7 +591,28 @@ describe('Show command using CLI', () => { ) const withdrawRequestAmount = withdrawRequestData.requestedAmount.toNumber() - const epoch = (await provider.connection.getEpochInfo()).epoch + const expectedDataWithdrawRequest = YAML.stringify({ + ...expectedData, + amountActive: `${ + (sumLamports - withdrawRequestAmount) / LAMPORTS_PER_SOL + }.000000000 SOLs`, + amountToWithdraw: `${ + withdrawRequestAmount / LAMPORTS_PER_SOL + }.000000000 SOLs`, + withdrawRequest: { + publicKey: withdrawRequestAccount.toBase58(), + account: { + voteAccount: withdrawRequestData.voteAccount.toBase58(), + bond: bondAccount.toBase58(), + epoch: (await provider.connection.getEpochInfo()).epoch, + requestedAmount: `${ + withdrawRequestAmount / LAMPORTS_PER_SOL + }.000000000 SOLs`, + withdrawnAmount: '0.000000000 SOL', + }, + }, + }) + await ( expect([ 'pnpm', @@ -614,27 +635,88 @@ describe('Show command using CLI', () => { code: 0, signal: '', // stderr: '', - stdout: YAML.stringify({ - ...expectedData, - amountActive: `${ - (sumLamports - withdrawRequestAmount) / LAMPORTS_PER_SOL - }.000000000 SOLs`, - amountToWithdraw: `${ - withdrawRequestAmount / LAMPORTS_PER_SOL - }.000000000 SOLs`, - withdrawRequest: { - publicKey: withdrawRequestAccount.toBase58(), - account: { - voteAccount: withdrawRequestData.voteAccount.toBase58(), - bond: bondAccount.toBase58(), - epoch, - requestedAmount: `${ - withdrawRequestAmount / LAMPORTS_PER_SOL - }.000000000 SOLs`, - withdrawnAmount: '0.000000000 SOL', - }, - }, - }), + stdout: expectedDataWithdrawRequest, + }) + + // check show-bond to work with vote account, withdraw request addresses and stake account + console.log('CLI program id', program.programId.toBase58()) + await ( + expect([ + 'pnpm', + [ + '--silent', + 'cli', + '-u', + provider.connection.rpcEndpoint, + '--program-id', + program.programId.toBase58(), + 'show-bond', + voteAccount.toBase58(), + '--config', + configAccount.toBase58(), + '--with-funding', + '-f', + 'yaml', + ], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ]) as any + ).toHaveMatchingSpawnOutput({ + code: 0, + signal: '', + // stderr: '', + stdout: expectedDataWithdrawRequest, + }) + await ( + expect([ + 'pnpm', + [ + '--silent', + 'cli', + '-u', + provider.connection.rpcEndpoint, + '--program-id', + program.programId.toBase58(), + 'show-bond', + withdrawRequestAccount.toBase58(), + '--with-funding', + '-f', + 'yaml', + ], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ]) as any + ).toHaveMatchingSpawnOutput({ + code: 0, + signal: '', + // stderr: '', + stdout: expectedDataWithdrawRequest, + }) + await ( + expect([ + 'pnpm', + [ + '--silent', + 'cli', + '-u', + provider.connection.rpcEndpoint, + '--program-id', + program.programId.toBase58(), + 'show-bond', + lastStakeAccount!.toBase58(), + '--config', + configAccount.toBase58(), + '--with-funding', + '-f', + 'yaml', + ], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ]) as any + ).toHaveMatchingSpawnOutput({ + code: 0, + signal: '', + // stderr: '', + stdout: new RegExp( + `${lastStakeAccount!.toBase58()} is a STAKE ACCOUNT(.|\\n)*publicKey: ${bondAccount.toBase58()}` + ), }) const { instruction: ixCancel } = await cancelWithdrawRequestInstruction({ diff --git a/packages/validator-bonds-cli/src/commands/utils.ts b/packages/validator-bonds-cli/src/commands/utils.ts index 52996fd8..544cbdbc 100644 --- a/packages/validator-bonds-cli/src/commands/utils.ts +++ b/packages/validator-bonds-cli/src/commands/utils.ts @@ -2,6 +2,7 @@ import { CliCommandError } from '@marinade.finance/cli-common' import { Bond, bondAddress, + deserializeStakeState, Errors, MARINADE_CONFIG_ADDRESS, ValidatorBondsProgram, @@ -20,6 +21,7 @@ import { LAMPORTS_PER_SOL, PublicKey, SendTransactionError, + StakeProgram, } from '@solana/web3.js' import { Logger } from 'pino' import { setProgramIdByOwner } from '../context' @@ -52,7 +54,7 @@ export async function getBondFromAddress({ address = address.publicKey } - const voteAccountAddress = await isVoteAccount({ + let voteAccountAddress = await isVoteAccount({ address, accountInfo, logger, @@ -83,6 +85,35 @@ export async function getBondFromAddress({ } } + // Let's check if provided account is a stake account, if so using delegated vote account + if (accountInfo.owner.equals(StakeProgram.programId)) { + let isStakeAccountError = false + try { + const stakeAccountData = deserializeStakeState(accountInfo.data) + voteAccountAddress = + stakeAccountData.Stake?.stake.delegation.voterPubkey || null + if (voteAccountAddress !== null) { + logger.info( + `Address ${address.toBase58()} is a STAKE ACCOUNT delegated to vote account ` + + `${voteAccountAddress.toBase58()}. Using the vote account to show bond data.` + ) + } else { + isStakeAccountError = true + } + } catch (e) { + isStakeAccountError = true + } + if (isStakeAccountError) { + throw new CliCommandError({ + valueName: '[stake account address]', + value: address.toBase58(), + msg: + 'Provided address is a stake account but it is not delegated or cannot be deserialized. ' + + 'Please provide a bond account or vote account to fetch bond data.', + }) + } + } + // If the address is a vote account, derive the bond account address from it if (voteAccountAddress !== null) { if (config === undefined) {