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

Example: program that creates/closes a SPL Token account that it owns #3

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

mvines
Copy link
Owner

@mvines mvines commented Nov 11, 2020

A simple program that supports two instructions:

  • 0 - create a SPL Token account at a constant program-derived address
  • 1 - close the SPL Token account created by "0". This demonstrates the program's authority over the account

The unit test can be run by installing Solana 1.4.6 or greater and running cargo test-bpf

let program_token_signer_seeds: &[&[_]] = &[br"program-token", &[program_token_bump_seed]];

match instruction_data.get(0) {
Some(0) => {
Copy link
Owner Author

Choose a reason for hiding this comment

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

Note that the create instruction is currently unprivileged, meaning that anybody can grief you by creating an arbitrary token at the fixed program address. In the real world, some authority should be checked before creating the token account

src/lib.rs Show resolved Hide resolved
let rent = &Rent::from_account_info(rent_sysvar_info)?;

invoke_signed(
&system_instruction::create_account(
Copy link

@wilbarnes wilbarnes Nov 12, 2020

Choose a reason for hiding this comment

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

i noticed you used system_instruction::create_account() here, is that any different than using pubkey::create_program_address() https://github.com/solana-labs/solana/blob/89b474e192ea2f4b2274ad26cbc47443d5767571/sdk/program/src/pubkey.rs#L135 ?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Yes, very different. Sadly the names are close enough that I can see where confusion can arise!

system_instruction::create_account() - this is transaction instruction implemented by the system program that when executed allocates a new account on the chain based on the given parameters. The address that the account is allocated at is also provided as an input to this instruction

pubkey::create_program_address() - a local function that returns an account address owned by the specified program, assuming the seeds are valid. This is just an address, a 32 byte number.

Copy link

@wilbarnes wilbarnes Nov 12, 2020

Choose a reason for hiding this comment

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

ok, and i presume you chose to use Pubkey::find_program_address() instead of Pubkey::create_program_address(), because as you mentioned above in the initial comments we have a constant address in this program for users to deposit SPL tokens

i'll try it myself, but it seems to me that i can also just call the Pubkey::find_program_address(&[br"program-token2"], program_id); and it'll make a second associated account users can send same tokens to

then, i can use a totally different program_id for a whole different SPL token in this same program and be able to intake 2 SPL tokens: Pubkey::find_program_address(&[br"program-token"], program_id2); right?

Choose a reason for hiding this comment

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

and after creating them, this program can do invoke_signed on them and send them wherever they want

Copy link
Owner Author

Choose a reason for hiding this comment

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

Yep, that's right.

i can use a totally different program_id for a whole different SPL token in this same program and be able to intake 2 SPL tokens: Pubkey::find_program_address(&[br"program-token"], program_id2); right?

Alternatively you use the same program, but adding more seeds to find_program_address:

Pubkey::find_program_address(&[br"program-token", some_other_seed], program_id);

Here's an example to check out: https://github.com/solana-labs/solana-program-library/blob/487ad2d2d7cb0b8a58f02a156a659e6c8a74f354/associated-token-account/program/src/lib.rs#L24-L32

Choose a reason for hiding this comment

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

thank you, this is very helpful

Choose a reason for hiding this comment

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

is program_id in the Pubkey::find_program_address() function supposed to be the SPL token address that i've created (not the spl_token::id()? for example, I've created an SPL token, and i want to create an associated account for that token for my program (same as the one above)

Comment on lines +134 to +136
pc.add_program(
"spl_token",
spl_token::id(),

Choose a reason for hiding this comment

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

i'm still confused by this pc.add_program() function, it seems like it's not even being used. i've browsed around through the repo and i can't really find precisely what spl_token::id() is, though i'm pretty sure it's just the spl_token's account address that we've added to ProgramTest

what is spl_token::id() here? and further, how is that the program itself and also the tests (see line 56) can simply use spl_token::id()?

going back to my confusion of pc.add_program()... on line 145, you've just called let program_id = Pubkey::new_unique(); that returns a unique account pubkey, shouldn't the spl_token program we added to the program test be the program_id we're using in our tests?

Copy link
Owner Author

Choose a reason for hiding this comment

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

spl_token::id() is actually this: https://github.com/solana-labs/solana-program-library/blob/master/token/program/src/lib.rs#L29

going back to my confusion of pc.add_program()... on line 145, you've just called let program_id = Pubkey::new_unique(); that returns a unique account pubkey, shouldn't the spl_token program we added to the program test be the program_id we're using in our tests?

let program_id = Pubkey::new_unique() is the program id of bpf_program_template, the actual program that was written here. spl_token::id() is the program id of the SPL Token program, which is invoked by bpf_program_template

#[tokio::test]
async fn test_transaction() {
async fn test_create_then_close() {
let program_id = Pubkey::new_unique();

Choose a reason for hiding this comment

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

why is a program_id created here, when we have the pc.add_program() call in fn program_test(), which should just create a mock spl_token to use?

Choose a reason for hiding this comment

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

i was just interpreting it incorrectly, Pubkey::new_unique() creates the program_id we then instantiante

Copy link
Owner Author

Choose a reason for hiding this comment

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

This is the program_id for the "bpf-program-template program, passed in via ProgramTest::new(..). It's just a unnamed, unique id here because the actual value doesn't matter for testing purposes -- it just needs to be something. On a real cluster, this would be the value returned by solana deploy` when the program is deployed

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.

2 participants