I'm writing an Anchor program which has an instruction that initializes a PDA account using some seeds. I want to write a test that verifies that my implementation ensures uniqueness: I can't call my create instruction with the same arguments twice. I'm mostly using it as a motivating example for using the PDA, but I think it's a valid test case regardless.
This is the test I've written:
it("Does not allow creating the same pixel twice", async () => {
const x = 20
const y = 20
const [pixelPublicKey] = web3.PublicKey.findProgramAddressSync(
[Buffer.from("pixel"), Buffer.from([x, y])],
program.programId,
)
// Create the pixel: this should pass
await program.methods
.createPixel(x, y, 0, 0, 255)
.accounts({
pixel: pixelPublicKey,
user: anchorProvider.wallet.publicKey,
systemProgram: web3.SystemProgram.programId,
})
.rpc()
// Create the same pixel: this should fail
await program.methods
.createPixel(x, y, 0, 0, 255)
.accounts({
pixel: pixelPublicKey,
user: anchorProvider.wallet.publicKey,
systemProgram: web3.SystemProgram.programId,
})
.rpc()
.then(
() => Promise.reject(new Error('Expected to error!')),
(e: any) => {
console.log(e)
assert.ok(e instanceof AnchorError)
// TODO: improve assertion using anchor logs
}
)
})
This test should pass, with an AnchorError
being logged that says the address is already in use.
Instead it fails, with the actual error thrown by that second transaction looking like this:
SendTransactionError: failed to send transaction: Transaction simulation failed: This transaction has already been processed
It seems that something is considering the two transactions to be the same transaction, and refusing to send the second one.
One workaround is to send the transaction as another user, so it's meaningfully different. But is there a better/more standard workaround for this problem? I'd expect it to be possible to send the same transaction as the same user twice, and for the program to be responsible for deciding what should happen in that case.