This Solana program (smart contract) provides basic whitelisting functionality using PDAs. It was written using Anchor.
The program is deployed on Solana's Devnet at 7Q5v4ftKm5Xk88NMf5kNCkvLmmBPcBfkynJA9x244i5Q
. It is not yet deployed on Mainnet.
IDL files and TypeScript type files can be found in target/
.
- Install Anchor.
- Navigate to the project root and run
anchor build
. This compiles the project and generates a keypair for the program based on your personal wallet. - The following steps only need to be performed on the first build (to replace the public key with one corresponding to your personal wallet).
- Run
anchor keys list
to get the new public key for the program. - Replace all occurences of the old public key in the project (
7Q5v4ftK
...) with the new public key. - Re-compile with
anchor build
(to account for the replacement of the public key in the source code).
- Run
- Run
anchor deploy
to deploy the compiled program on the blockchain. - Run the tests with
anchor run test
.
init_whitelist(name: String) -> Result<()>
Arguments:
name: String
The identifying name of this whitelist. Since whitelists are tied to the signer's wallet, the name need be unique to other whitelists initialized by the signer.
Accounts:
whitelist
The whitelist to be initialized. The address should be a PDA derived from this whitelisting program address and seeded with the signer's public key and the whitelist name (as bytes). Uses the canonical bump.authority
The signer of this transaction. This account will pay for the whitelist and new whitelist entries. Only this account is able to add to the whitelist.
delete_whitelist(name: String) -> Result<()>
Note that deleting a whitelist does not delete the whitelist entries associated with it. Use remove_from_whitelist
to delete whitelist entries.
Arguments:
name: String
The name of the whitelist to delete.
Accounts:
whitelist
The whitelist to delete. This account will be closed and remaining lamports will be sent toauthority
.authority
The signer of this transaction. This account must be the one that created the whitelist.
add_to_whitelist(account_to_whitelist: Pubkey) -> Result<()>
Arguments:
account_to_whitelist: Pubkey
The address of the account to add to the whitelist.
Accounts:
entry
The account to store the new whitelist entry. The address should be a PDA derived from this whitelisting program address and seeded withaccount_to_add
and the public key ofwhitelist
. Uses the canonical bump.whitelist
The whitelist to add to, initialized withinit_whitelist
.authority
must be the one who initialized this whitelist.authority
The signer of this transaction. This must correspond to the account that initialized the whitelist.
delete_from_whitelist(account_to_delete: Pubkey) -> Result<()>
Arguments:
account_to_delete: Pubkey
The address of the account to remove from the whitelist.
Accounts:
entry
The whitelist entry (initialized withadd_to_whitelist
) corresponding withaccount_to_delete
.whitelist
The whitelist to remove from (initialized withinit_whitelist
).authority
must be the one who initialized this whitelist.authority
The signer of this transaction. This must correspond to the account that initialized the whitelist.
check_whitelisted(account_to_check: Pubkey) -> Result<()>
Throws an AnchorError
if the user is not whitelisted. Unlike other endpoints, this endpoint can be called even by those who did not create the whitelist.
Arguments:
account_to_check
The public key of the account whose whitelisting status will be checked.
Accounts:
entry
The whitelisting entry ofaccount_to_check
(initialized withadd_to_whitelist
).whitelist
The whitelist that will be checked.
First, create a PDA for an account that will hold a named whitelist. The PDA should be derived from the address of this whitelisting program and seeded with your wallet public key and the whitelist name (in bytes), in that order.
The whitelist name is used as an identifier to differentiate between the whitelists already associated with your wallet. As such, the name should be different from any of your existing whitelists created with this program.
import * as anchor from "@project-serum/anchor";
const whitelistName = "Whitelist Name";
const [whitelist, whitelistBump] = await anchor.web3.PublicKey.findProgramAddress(
[
wallet.publicKey.toBytes(),
anchor.utils.bytes.utf8.encode(whitelistName)
],
whitelistProgram.programId // the address of this whitelisting program
);
Then, initialize the whitelist with a call to init_whitelist
:
const systemProgram = anchor.web3.SystemProgram;
await whitelistProgram.methods
.initWhitelist(whitelistName)
.accounts({
whitelist,
authority: wallet.publicKey,
systemProgram: systemProgram.programId,
})
.rpc();
The funds used to create the account are taken from authority
.
First, create a PDA for the account that will hold the whitelist entry for one address. The PDA should be derived from the address of this whitelisting program and seeded with the address of the whitelist (see Creating a whitelist) and the account to whitelist.
Note that the authority of the transaction sending the instruction add_to_whitelist
must be the same as the one who created the whitelist.
const accountToWhitelist: Pubkey = "SomePublicKey";
const [whitelistEntry, entryBump] = await anchor.web3.PublicKey.findProgramAddress(
[
accountToWhitelist.toBytes(),
whitelist.toBytes(),
],
program.programId // ID of this whitelisting program
);
Then, add the account to the whitelist with a call to add_to_whitelist
:
await program.methods
.addToWhitelist(accountToWhitelist.publicKey)
.accounts({
entry: whitelistEntry,
whitelist: whitelist,
authority: wallet.publicKey,
systemProgram: systemProgram.programId,
})
.rpc();
Use the check_whitelisted
endpoint: check_whitelisted
throws if the user is not whitelisted.
try {
await program.methods
.checkWhitelisted(accountToWhitelist.publicKey)
.accounts({
entry: whitelistEntry,
whitelist: whitelist,
})
.rpc();
// user is whitelisted
} catch (err: unknown) {
// user is not whitelisted
}
Use the delete_from_whitelist
endpoint. Only the creator of the whitelist can remove from the whitelist. The remaining lamports stored in the whitelist entry account are returned to the authority (creator of the whitelist).
await program.methods
.deleteFromWhitelist(accountToWhitelist.publicKey)
.accounts({
entry: whitelistEntry,
whitelist: whitelist,
authority: wallet.publicKey,
})
.rpc()
Use the delete_whitelist
endpoint. Only the creator of the whitelist can delete the whitelist. The remaining lamports stored in the whitelist entry account are returned to the authority (creator of the whitelist).
This endpoint does NOT remove corresponding whitelist entries; they have to be removed separately with remove_from_whitelist
.
await program.methods
.deleteWhitelist(whitelistName)
.accounts({
whitelist,
authority: wallet.publicKey,
})
.rpc();