Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: whitelist and unwhitelist SPL tokens #41

Merged
merged 13 commits into from
Oct 8, 2024
Merged

Conversation

brewmaster012
Copy link
Contributor

@brewmaster012 brewmaster012 commented Oct 8, 2024

closes #42

Add whitelisting function to SPL tokens to be deposited into the program PDA.
Right now the authority are authorized to whitelist/de-whitelist SPL token.
The integration tests are also refactored somewhat to increase code reuse.

Only deposit_spl_token*() instructions need to provide evidence of whitelisting.
withdraw_spl_token is not encumbered by whitelisting for the following reasons:

  1. when the SPL token has never been whitelisted, there is no way to deposit into the program therefore
    no way to withdraw; there is no need for witheraw_spl_token to respect whitelisting;
  2. when the SPL token was whitelisted and later de-whitelisted, the desired behavior would be to
    pause deposit, and allow withdraw otherwise the deposited SPL tokens will be stuck.

Whitelisting is implemented as a special PDA for each mint account (SPL token id).

#[derive(Accounts)]
pub struct AddToWhitelist<'info> {
    #[account(
        init,
        space=8,
        payer=authority,
        seeds=[
            b"whitelist",
            whitelist_candidate.key().as_ref()
        ],
        bump
    )]
    pub whitelist_entry: Account<'info, WhitelistEntry>,
    pub whitelist_candidate: Account<'info, Mint>,

    #[account(mut, seeds = [b"meta"], bump, has_one = authority)]
    pub pda: Account<'info, Pda>,
    #[account(mut)]
    pub authority: Signer<'info>,

    pub system_program: Program<'info, System>,
}

Whitelisting creates a PDA derived from the mint_account of the whitelisted SPL token.
When a user deposits the whitelisted token, she must provide evidence, i.e. provide the PDA whitelist entry
in the accounts:

#[derive(Accounts)]
pub struct DepositSplToken<'info> {
    #[account(mut)]
    pub signer: Signer<'info>,

    #[account(seeds = [b"meta"], bump)]
    pub pda: Account<'info, Pda>,

    #[account(seeds=[b"whitelist", mint_account.key().as_ref()], bump)]
    pub whitelist_entry: Account<'info, WhitelistEntry>, // attach whitelist entry to show the mint_account is whitelisted

    pub mint_account: Account<'info, Mint>,

    pub token_program: Program<'info, Token>,

    #[account(mut)]
    pub from: Account<'info, TokenAccount>, // this must be owned by signer; normally the ATA of signer
    #[account(mut)]
    pub to: Account<'info, TokenAccount>, // this must be ATA of PDA
}

This way, the existence of the PDA whitelist_entry is the evidence of the mint_account being whitelisted.

De-whitelisting closes the PDA whitelist_entry so that deposit spl token will error with AccountNotInitialized.

query whitelisted status

To query whether a SPL token is whitelisted by the program off-chain, derive the whitelist_entry PDA account
from seeds [b"whitelist", mint_account.key().as_ref()] and access the PDA account. The existence of this
PDA account means whitelisted; otherwise not whitelisted.

To query whether a SPL token is whitelisted by the program on-chain, add evidence whitelist_entry

 #[account(seeds=[b"whitelist", mint_account.key().as_ref()], bump)]
    pub whitelist_entry: Account<'info, WhitelistEntry>, // attach whitelist entry to show the mint_account is whitelisted

    pub mint_account: Account<'info, Mint>,

to the accounts of the instruction. Anchor constraints ensures that whitelist_entry must exists (and initialized)
which means the referenced mint_account must be whitelisted.

testing

both positive and negative tests are in the integration test suite.

$ anchor test

@codecov-commenter
Copy link

codecov-commenter commented Oct 8, 2024

Codecov Report

Attention: Patch coverage is 33.33333% with 6 lines in your changes missing coverage. Please review.

Project coverage is 9.16%. Comparing base (74c9d81) to head (75ac812).

Files with missing lines Patch % Lines
programs/protocol-contracts-solana/src/lib.rs 33.33% 6 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##            main     #41      +/-   ##
========================================
+ Coverage   8.30%   9.16%   +0.85%     
========================================
  Files          1       1              
  Lines        253     262       +9     
========================================
+ Hits          21      24       +3     
- Misses       232     238       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@brewmaster012 brewmaster012 marked this pull request as ready for review October 8, 2024 03:40
Comment on lines 91 to 98
// whitelisting SPL tokens
pub fn whitelist_spl_mint(_ctx: Context<AddToWhitelist>) -> Result<()> {
Ok(())
}

pub fn de_whitelist_spl_mint(_ctx: Context<DeleteFromWhitelist>) -> Result<()> {
Ok(())
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unclear what these are for

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think all logic for this is in the accounts definition, when you whitelist new one is created (with init), when you unwhitelist its closed (with close)

@@ -414,6 +428,51 @@ pub struct UpdatePaused<'info> {
pub signer: Signer<'info>,
}

#[derive(Accounts)]
pub struct AddToWhitelist<'info> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use consistent terminology with Solidity contracts:

  • whitelist
  • unwhitelist

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to whitelist/unwhitelist across the board
e905e67

@@ -19,6 +19,56 @@ const keyPair = ec.keyFromPrivate('5b81cdf52ba0766983acf8dd0072904733d92afe4dd34

const usdcDecimals = 6;

async function mintSPLToken(conn: anchor.web3.Connection, wallet: anchor.web3.Keypair, mint: anchor.web3.Keypair) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should remove all console.log in the test, logging in tests should be minimal by default, should printing passing tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disabled console.log in the test
e905e67

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if its ok to overwrite console.log like this, i think the point was to just remove all console.logs calls as they are just useful while tests are being written or during debugging

if there is console.log that is absolutely needed, it is probably better to replace it with assertion

#[account(seeds=[b"whitelist", mint_account.key().as_ref()], bump)]
pub whitelist_entry: Account<'info, WhitelistEntry>, // attach whitelist entry to show the mint_account is whitelisted

pub mint_account: Account<'info, Mint>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does mint mean?

There should be no minting involved since we lock/unlock SPL tokens?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

according to description Whitelisting is implemented as a special PDA for each mint account (SPL token id). is needed to create pda for mint account and existence of that pda marks that token is whitelisted

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does mint mean?

There should be no minting involved since we lock/unlock SPL tokens?

mint (short for mint account) is the Solana equivalent for ERC20 contract address.
This is Solana jargon.

@@ -423,6 +482,9 @@ pub struct Pda {
deposit_paused: bool,
}

#[account]
pub struct WhitelistEntry {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious if is there a pros/cons for whitelisting in 2 following ways:

  1. init/close account
  2. store flag within pda marking if its whitelisted or not

is it because having long lived pda in solana is not efficient if not needed because of rent, or something else?

Copy link
Contributor Author

@brewmaster012 brewmaster012 Oct 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For whitelisting we really need is a mapping like Ethereum address=>bool

But Solana does not have mapping as a data structure like Ethereum mapping.
You'll need to roll your own. Say you want to create a mapping data account
and then store a hashmap or list in the PDA to implement the mapping.
There are two issues:

  1. size of the data structure and therefore the account size could dynamically change;
  2. hard to get deterministic constant access time (key-value lookup)

The PDA for each key-value pair is the idiomatic way to do Ethereum mapping on Solana.
Think of part of the seed as the key (mint account address), and value is the PDA account.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good explanation

Base automatically changed from enhance-pda-validation to main October 8, 2024 16:06
#[account(mut, seeds = [b"meta"], bump, has_one = authority)]
pub pda: Account<'info, Pda>,
#[account(mut)]
pub authority: Signer<'info>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It means the signer (the authority) of the Whitelist instruction must be one of the fields in pda struct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes,
the b"meta" PDA contains the authority.

Copy link
Collaborator

@ws4charlie ws4charlie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good

@brewmaster012 brewmaster012 changed the title add: whitelist and de-whitelist SPL tokens feat: whitelist and de-whitelist SPL tokens Oct 8, 2024
@brewmaster012 brewmaster012 changed the title feat: whitelist and de-whitelist SPL tokens feat: whitelist and unwhitelist SPL tokens Oct 8, 2024
README.md Outdated
@@ -1,6 +1,7 @@

**Note**: Mainnet-beta, testnet, devnet gateway program address:

Mainnet-beta, testnet, devnet gateway program address:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like changes in this file are from merge with main, probably should be reverted

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted

@@ -19,6 +19,56 @@ const keyPair = ec.keyFromPrivate('5b81cdf52ba0766983acf8dd0072904733d92afe4dd34

const usdcDecimals = 6;

async function mintSPLToken(conn: anchor.web3.Connection, wallet: anchor.web3.Keypair, mint: anchor.web3.Keypair) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if its ok to overwrite console.log like this, i think the point was to just remove all console.logs calls as they are just useful while tests are being written or during debugging

if there is console.log that is absolutely needed, it is probably better to replace it with assertion

@brewmaster012
Copy link
Contributor Author

removed the console logs in the next PR. Don't want to create merging issue later so maybe let's put the console logs aside for this one.

@brewmaster012 brewmaster012 merged commit 705831d into main Oct 8, 2024
14 checks passed
@brewmaster012 brewmaster012 deleted the whitelist-spl branch October 8, 2024 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

add: whitelist SPL token to avoid erroneous user deposits
5 participants