Skip to content

Commit

Permalink
feat: Implement account recovery test in staking application
Browse files Browse the repository at this point in the history
A new test case has been added to the staking application which asserts the functionality of recovering staking accounts. This includes the implementation of 'buildRecoverAccountInstruction' in 'StakeConnection' which is integral to this recovery process. This test case bolsters the reliability and resilience of the staking system.
  • Loading branch information
keyvankhademi committed Mar 22, 2024
1 parent 1a8a1db commit 3c8c92d
Showing 1 changed file with 182 additions and 0 deletions.
182 changes: 182 additions & 0 deletions staking/tests/recouver_account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { AnchorProvider, Program } from "@coral-xyz/anchor";
import {
Keypair,
PublicKey,
Transaction,
TransactionInstruction,
} from "@solana/web3.js";
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
Token,
TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
import {
ANCHOR_CONFIG_PATH,
CustomAbortController,
getDummyAgreementHash,
getPortNumber,
readAnchorConfig,
requestPythAirdrop,
startValidator,
} from "./utils/before";
import { createMint, getTargetAccount, StakeTarget } from "./utils/utils";
import BN from "bn.js";
import assert from "assert";
import path from "path";
import { PYTH_DECIMALS, PythBalance, StakeConnection } from "../app";
import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";

// When DEBUG is turned on, we turn preflight transaction checking off
// That way failed transactions show up in the explorer, which makes them
// easier to debug.
const DEBUG = true;
const portNumber = getPortNumber(path.basename(__filename));

describe("config", async () => {
const pythMintAccount = new Keypair();
const pythMintAuthority = new Keypair();

const pdaAuthorityKeypair = new Keypair();
const config = readAnchorConfig(ANCHOR_CONFIG_PATH);
const pdaAuthority = pdaAuthorityKeypair.publicKey;
const governanceProgram = new PublicKey(config.programs.localnet.governance);

let program: Program<any>;
let provider: AnchorProvider;
let controller: CustomAbortController;

let votingProductMetadataAccount: PublicKey;

const votingProduct: StakeTarget = { voting: {} };

after(async () => {
controller.abort();
});

before(async () => {
// Can't we use standard setup here? I tried and it gave me errors I couldn't resolve

({ controller, program } = await startValidator(portNumber, config));

provider = program.provider as AnchorProvider;

await createMint(
provider,
pythMintAccount,
pythMintAuthority.publicKey,
null,
PYTH_DECIMALS,
TOKEN_PROGRAM_ID
);

votingProductMetadataAccount = await getTargetAccount(
votingProduct,
program.programId
);

await program.methods
.initConfig({
governanceAuthority: provider.wallet.publicKey,
pythTokenMint: pythMintAccount.publicKey,
unlockingDuration: 2,
epochDuration: new BN(3600),
freeze: false,
pdaAuthority: pdaAuthority,
governanceProgram: governanceProgram,
pythTokenListTime: null,
agreementHash: getDummyAgreementHash(),
mockClockTime: new BN(10),
})
.rpc({
skipPreflight: DEBUG,
});

// Why do you I get this error without this line
// ypeError: Cannot read properties of null (reading 'data')
await program.methods
.createTarget(votingProduct)
.accounts({
targetAccount: votingProductMetadataAccount,
governanceSigner: provider.wallet.publicKey,
})
.rpc();

await requestPythAirdrop(
provider.wallet.publicKey,
pythMintAccount.publicKey,
pythMintAuthority,
PythBalance.fromString("100"),
provider.connection
);

// Why do we need to advance the clock in this test? It gives me error without it
await program.methods.advanceClock(new BN(5)).rpc({ skipPreflight: DEBUG });
});

it("creates recover account instruction", async () => {
const governanceConnection = await StakeConnection.createStakeConnection(
program.provider.connection,
provider.wallet as NodeWallet,
program.programId
);

const newOwner = new Keypair();

const newOwnerAta = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
pythMintAccount.publicKey,
newOwner.publicKey,
true
);

const createAtaIx = Token.createAssociatedTokenAccountInstruction(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
pythMintAccount.publicKey,
newOwnerAta,
newOwner.publicKey,
pythMintAuthority.publicKey
);

const instructions: TransactionInstruction[] = [createAtaIx];

const badStakeAccountAddress = await governanceConnection.withCreateAccount(
instructions,
newOwnerAta,
{
fullyVested: {},
}
);

const transaction: Transaction = new Transaction();
transaction.instructions.push(...instructions);

await governanceConnection.program.provider.sendAndConfirm(transaction, [
pythMintAuthority,
]);

// The fix

const recoverAccountInstruction =
await governanceConnection.buildRecoverAccountInstruction(
badStakeAccountAddress,
provider.wallet.publicKey
);
const recoverAccountTransaction = new Transaction();
recoverAccountTransaction.instructions.push(recoverAccountInstruction);

await governanceConnection.program.provider.sendAndConfirm(
recoverAccountTransaction
);

const fixedAccount = await governanceConnection.loadStakeAccount(
badStakeAccountAddress
);

assert.equal(
fixedAccount.stakeAccountPositionsJs.owner.toString(),
newOwner.publicKey.toString()
);
});
});

0 comments on commit 3c8c92d

Please sign in to comment.