Skip to content

Commit 83438b6

Browse files
authored
Whitelist tests (#42)
* Whitelist tests * format
1 parent b871df3 commit 83438b6

File tree

5 files changed

+312
-9
lines changed

5 files changed

+312
-9
lines changed

clients/js/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"@tensor-foundation/mpl-token-metadata": "^0.4.0",
5151
"@tensor-foundation/test-helpers": "^0.4.0",
5252
"@types/node": "^20.14.12",
53+
"@types/uuid": "^10.0.0",
5354
"@typescript-eslint/eslint-plugin": "^6.0.0",
5455
"@typescript-eslint/parser": "^6.0.0",
5556
"ava": "^6.1.2",
@@ -61,7 +62,7 @@
6162
"typedoc": "^0.25.12",
6263
"typedoc-plugin-missing-exports": "^2.2.0",
6364
"typescript": "^5.4.2",
64-
"uuid": "^9.0.1",
65+
"uuid": "^10.0.0",
6566
"ws": "^8.14.0"
6667
},
6768
"ava": {

clients/js/pnpm-lock.yaml

Lines changed: 16 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

clients/js/test/_common.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
export const MAX_PROOF_LENGTH = 28;
3232

3333
export const CURRENT_WHITELIST_VERSION = 1;
34+
export const SLOT_DELAY = 100;
3435

3536
export const WHITELIST_V2_BASE_SIZE = 139;
3637
export const WHITELIST_V2_CONDITION_SIZE = 33;

clients/js/test/mintProof.test.ts

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,22 @@ import {
1919
MintProofV2,
2020
Mode,
2121
TENSOR_WHITELIST_ERROR__FAILED_MERKLE_PROOF_VERIFICATION,
22+
TENSOR_WHITELIST_ERROR__INVALID_AUTHORITY,
2223
TENSOR_WHITELIST_ERROR__NOT_MERKLE_ROOT,
24+
fetchMaybeMintProofV2,
2325
fetchMintProofV2,
2426
findMintProofV2Pda,
27+
getCloseMintProofV2Instruction,
2528
getInitUpdateMintProofV2InstructionAsync,
2629
intoAddress,
2730
} from '../src';
2831
import {
2932
MAX_PROOF_LENGTH,
33+
SLOT_DELAY,
34+
TRANSACTION_FEE,
3035
createMintProofThrows,
3136
createWhitelist,
37+
expectCustomError,
3238
updateWhitelist,
3339
upsertMintProof,
3440
} from './_common';
@@ -546,3 +552,190 @@ test('invalid whitelist fails', async (t) => {
546552
code: ANCHOR_ERROR__ACCOUNT_NOT_INITIALIZED,
547553
});
548554
});
555+
556+
test('cannot close the mint proof account before slot delay if not original signer', async (t) => {
557+
const client = createDefaultSolanaClient();
558+
559+
const updateAuthority = await generateKeyPairSignerWithSol(client);
560+
const mintProofSigner = await generateKeyPairSignerWithSol(client);
561+
const notMintProofSigner = await generateKeyPairSignerWithSol(client);
562+
563+
const { mint } = await createDefaultNft({
564+
client,
565+
payer: mintProofSigner,
566+
authority: mintProofSigner,
567+
owner: mintProofSigner.address,
568+
});
569+
570+
const {
571+
root,
572+
proofs: [p],
573+
} = await generateTreeOfSize(10, [mint]);
574+
575+
const conditions: Condition[] = [
576+
{ mode: Mode.MerkleTree, value: intoAddress(root) },
577+
];
578+
579+
const { whitelist } = await createWhitelist({
580+
client,
581+
updateAuthority,
582+
conditions,
583+
});
584+
585+
const [mintProof] = await findMintProofV2Pda({ mint, whitelist });
586+
587+
const createMintProofIx = await getInitUpdateMintProofV2InstructionAsync({
588+
payer: mintProofSigner,
589+
mint,
590+
mintProof,
591+
whitelist,
592+
proof: p.proof,
593+
});
594+
595+
await pipe(
596+
await createDefaultTransaction(client, mintProofSigner),
597+
(tx) => appendTransactionMessageInstruction(createMintProofIx, tx),
598+
(tx) => signAndSendTransaction(client, tx)
599+
);
600+
601+
const creationSlot = (await fetchMintProofV2(client.rpc, mintProof)).data
602+
.creationSlot;
603+
604+
// If not original signer => Can't close it with own address as payer...
605+
const closeMintProofIxWrongAddress = getCloseMintProofV2Instruction({
606+
payer: mintProofSigner.address,
607+
mintProof,
608+
signer: notMintProofSigner,
609+
});
610+
611+
const promise = pipe(
612+
await createDefaultTransaction(client, notMintProofSigner),
613+
(tx) =>
614+
appendTransactionMessageInstruction(closeMintProofIxWrongAddress, tx),
615+
(tx) => signAndSendTransaction(client, tx)
616+
);
617+
618+
await expectCustomError(
619+
t,
620+
promise,
621+
TENSOR_WHITELIST_ERROR__INVALID_AUTHORITY
622+
);
623+
624+
// Mint proof is still open and unchanged
625+
let mintProofAcc = await fetchMintProofV2(client.rpc, mintProof);
626+
t.like(mintProofAcc.data, {
627+
proof: p.proof,
628+
creationSlot,
629+
proofLen: p.proof.length,
630+
payer: mintProofSigner.address,
631+
});
632+
633+
// ... and also not with the correct address as payer (DOS attack vector)
634+
const closeMintProofIxNotAllowed = getCloseMintProofV2Instruction({
635+
payer: mintProofSigner.address,
636+
mintProof,
637+
signer: notMintProofSigner,
638+
});
639+
640+
const promiseV2 = pipe(
641+
await createDefaultTransaction(client, mintProofSigner),
642+
(tx) => appendTransactionMessageInstruction(closeMintProofIxNotAllowed, tx),
643+
(tx) => signAndSendTransaction(client, tx)
644+
);
645+
646+
await expectCustomError(
647+
t,
648+
promiseV2,
649+
TENSOR_WHITELIST_ERROR__INVALID_AUTHORITY
650+
);
651+
652+
// Mint proof is still open and unchanged
653+
mintProofAcc = await fetchMintProofV2(client.rpc, mintProof);
654+
t.like(mintProofAcc.data, {
655+
proof: p.proof,
656+
creationSlot,
657+
proofLen: p.proof.length,
658+
payer: mintProofSigner.address,
659+
});
660+
661+
// Assert that the test ran WITHIN expected slot delay
662+
const currentSlot = await client.rpc.getSlot().send();
663+
t.assert(currentSlot - creationSlot < SLOT_DELAY);
664+
});
665+
666+
test('mint proof can be closed by original signer before slot delay - receives rent back', async (t) => {
667+
const client = createDefaultSolanaClient();
668+
669+
const mintProofSigner = await generateKeyPairSignerWithSol(client);
670+
const updateAuthority = await generateKeyPairSignerWithSol(client);
671+
const { mint } = await createDefaultNft({
672+
client,
673+
payer: mintProofSigner,
674+
authority: mintProofSigner,
675+
owner: mintProofSigner.address,
676+
});
677+
678+
const {
679+
root,
680+
proofs: [p],
681+
} = await generateTreeOfSize(10, [mint]);
682+
683+
const conditions: Condition[] = [
684+
{ mode: Mode.MerkleTree, value: intoAddress(root) },
685+
];
686+
687+
const { whitelist } = await createWhitelist({
688+
client,
689+
updateAuthority,
690+
conditions,
691+
});
692+
693+
const [mintProof] = await findMintProofV2Pda({ mint, whitelist });
694+
695+
const beforeFunds = (
696+
await client.rpc.getBalance(mintProofSigner.address).send()
697+
).value;
698+
699+
const createMintProofIx = await getInitUpdateMintProofV2InstructionAsync({
700+
payer: mintProofSigner,
701+
mint,
702+
mintProof,
703+
whitelist,
704+
proof: p.proof,
705+
});
706+
707+
await pipe(
708+
await createDefaultTransaction(client, mintProofSigner),
709+
(tx) => appendTransactionMessageInstruction(createMintProofIx, tx),
710+
(tx) => signAndSendTransaction(client, tx)
711+
);
712+
const creationSlot = (await fetchMintProofV2(client.rpc, mintProof)).data
713+
.creationSlot;
714+
715+
const closeMintProofIxOriginalSigner = getCloseMintProofV2Instruction({
716+
payer: mintProofSigner.address,
717+
mintProof,
718+
signer: mintProofSigner,
719+
});
720+
721+
await pipe(
722+
await createDefaultTransaction(client, mintProofSigner),
723+
(tx) =>
724+
appendTransactionMessageInstruction(closeMintProofIxOriginalSigner, tx),
725+
(tx) => signAndSendTransaction(client, tx)
726+
);
727+
728+
const afterFunds = (
729+
await client.rpc.getBalance(mintProofSigner.address).send()
730+
).value;
731+
732+
// Mint proof is closed now...
733+
t.assert(
734+
(await fetchMaybeMintProofV2(client.rpc, mintProof)).exists === false
735+
);
736+
// ... the original signer received rent back ...
737+
t.assert(afterFunds === beforeFunds - TRANSACTION_FEE * 2n); // Creation + Closing tx fees subtracted
738+
// ... within the slot delay
739+
const currentSlot = await client.rpc.getSlot().send();
740+
t.assert(currentSlot - creationSlot < SLOT_DELAY);
741+
});

0 commit comments

Comments
 (0)