If the campaign doesn't reach the goal and users attempt to get refunds, they will get no tokens back because their ctx.accounts.contribution.amount
will be zero.
Loss of funds for users.
async function airdropSol(publicKey: PublicKey, amount: number) {
const airdropTx = await anchor
.getProvider()
.connection.requestAirdrop(publicKey, amount);
await confirmTransaction(airdropTx);
}
async function confirmTransaction(tx: string) {
const latestBlockHash = await anchor
.getProvider()
.connection.getLatestBlockhash();
await anchor.getProvider().connection.confirmTransaction({
blockhash: latestBlockHash.blockhash,
lastValidBlockHeight: latestBlockHash.lastValidBlockHeight,
signature: tx,
});
}
it.only("eronous refunds due to wrong contributions", async () => {
const testFundName = "RefundBugFund-" + Date.now();
const testDescription = "Test fund for refund bug demonstration";
const testGoal = new anchor.BN(10_000_000_000);
const depositAmount = new anchor.BN(4_000_000_000);
const deadline = new anchor.BN(Math.floor(Date.now() / 1000) + 10);
const [testFundPDA] = await PublicKey.findProgramAddress(
[Buffer.from(testFundName), creator.publicKey.toBuffer()],
program.programId
);
await program.methods
.fundCreate(testFundName, testDescription, testGoal)
.accounts({
fund: testFundPDA,
creator: creator.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
await program.methods
.setDeadline(deadline)
.accounts({
fund: testFundPDA,
creator: creator.publicKey,
})
.rpc();
const [creatorContributionPDA] = await PublicKey.findProgramAddress(
[testFundPDA.toBuffer(), creator.publicKey.toBuffer()],
program.programId
);
await program.methods
.contribute(depositAmount)
.accounts({
fund: testFundPDA,
contributor: creator.publicKey,
contribution: creatorContributionPDA,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
await airdropSol(otherUser.publicKey, 10_000_000_000);
const [otherContributionPDA] = await PublicKey.findProgramAddress(
[testFundPDA.toBuffer(), otherUser.publicKey.toBuffer()],
program.programId
);
await program.methods
.contribute(depositAmount)
.accounts({
fund: testFundPDA,
contributor: otherUser.publicKey,
contribution: otherContributionPDA,
systemProgram: anchor.web3.SystemProgram.programId,
})
.signers([otherUser])
.rpc();
const fundAccount = await program.account.fund.fetch(testFundPDA);
console.log("Fund goal:", fundAccount.goal.toString());
console.log("Fund amount raised:", fundAccount.amountRaised.toString());
expect(fundAccount.amountRaised.eq(new anchor.BN(8_000_000_000))).to.be
.true;
expect(fundAccount.amountRaised.lt(testGoal)).to.be.true;
console.log("Waiting for deadline to pass...");
await new Promise((resolve) => setTimeout(resolve, 15000));
const contributorBalanceBefore = await provider.connection.getBalance(
otherUser.publicKey
);
await program.methods
.refund()
.accounts({
fund: testFundPDA,
contribution: otherContributionPDA,
contributor: otherUser.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.signers([otherUser])
.rpc();
const contributorBalanceAfter = await provider.connection.getBalance(
otherUser.publicKey
);
console.log("Contributor balance before refund:", contributorBalanceBefore);
console.log("Contributor balance after refund:", contributorBalanceAfter);
});
The test clearly shows that the balance of the user doesn't change before and after a refund.