Could not create program address with signer seeds when creating a token account - solana

I have a PDA and I'm trying to create a token account for it, using Solana Spl Associated Token Account(https://spl.solana.com/associated-token-account):
let (token_account_key, token_account_key_bump_seed) = Pubkey::find_program_address(&[&stake_info_bytes, &token_program_bytes, &mint_address_bytes], spl_associated_token_program.key);
Now I'm trying to create the account:
let account_rent = rent.minimum_balance(Account::LEN);
let authority_signature_seeds = [&stake_info_bytes[..32], &token_program_bytes[..32], &mint_address_bytes[..32], &[token_account_key_bump_seed]];
let signers = &[&authority_signature_seeds[..]];
let create_ix = create_account(
feepayer.key,
token_account.key,
account_rent,
Account::LEN as u64,
spl_associated_token_program.key
);
invoke_signed(&create_ix, &[
spl_associated_token_program.clone(),
feepayer.clone(),
token_account.clone()
], signers);
But I'm getting this error:
> Program returned error: Could not create program address with signer seeds: Provided seeds do not result in a valid address

With PDAs, your program can only "sign" for PDAs generated using its program id. In this case, your program is using the associated token account program as the program id, when it should be using itself. So this should become:
let (token_account_key, token_account_key_bump_seed) = Pubkey::find_program_address(&[&stake_info_bytes, &token_program_bytes, &mint_address_bytes], my_program_id);
Using your program's id as my_program_id, and then you would actually use this as:
let create_ix = create_account(
feepayer.key,
token_account.key,
account_rent,
Account::LEN as u64,
spl_token_program.key
);
invoke_signed(&create_ix, &[
feepayer.clone(),
token_account.clone()
], signers);
Note: I'm assuming that you're trying to create a token account, which must be owned by the token program.
Otherwise, it would be possible to fake "signatures" for another program, which would be a huge security risk. For example, associated token accounts must be signed and created by the associated token account program.

Related

How can I use the `close_account` function properly in order to close an Associated Token Account?

I am trying to close an Associated Token Account (ATA) from inside a program. The ATA belongs to the program. I found a function called close_account, but I haven't figured out how to use it properly. I'm using Anchor.
The desired flow of my program is:
Send a token from the program ATA to the user ATA (I've done this successfully)
Close the program ATA that was used for the token
This is what the implementation of close_account looks like:
pub fn close_account<'a, 'b, 'c, 'info>(
ctx: CpiContext<'a, 'b, 'c, 'info, CloseAccount<'info>>,
) -> Result<()> {
let ix = spl_token::instruction::close_account(
&spl_token::ID,
ctx.accounts.account.key,
ctx.accounts.destination.key,
ctx.accounts.authority.key,
&[], // TODO: support multisig
)?;
solana_program::program::invoke_signed(
&ix,
&[
ctx.accounts.account.clone(),
ctx.accounts.destination.clone(),
ctx.accounts.authority.clone(),
],
ctx.signer_seeds,
)
.map_err(Into::into)
}
The CloseAccount struct looks as follows:
#[derive(Accounts)]
pub struct CloseAccount<'info> {
pub account: AccountInfo<'info>,
pub destination: AccountInfo<'info>,
pub authority: AccountInfo<'info>,
}
(I assume) the account is the ATA and the authority is my program - but what is the destination in this context? Why would closing an account need a destination and which account should I use as the destination account?
what is the destination in this context? Why would closing an account need a destination and which account should I use as the destination account?
Since token accounts are like any other account in Solana, they must have enough SOL to cover the minimum balance requirement. When closing an account, you must send that SOL somewhere else, hence the "destination". The best option is to use a wallet / SOL account that is only used for SOL and nothing else.
More info at https://spl.solana.com/token#closing-accounts

method: "hardhat_impersonateAccount" - What happens when you call this method with an address that doesn't exist?

async function impersonateAccount(acctAddress) {
await hre.network.provider.request({
method: "hardhat_impersonateAccount",
params: [acctAddress],
});
return await ethers.getSigner(acctAddress);
}
When forking the blockchain locally on Hardhat, the function above allows developers to impersonate the address passed as argument to it.
So you can create transactions as if you're the owner of the account.
What happens when forking the mainnet, and you pass an address that does not exist on the mainnet as an argument?
Would it throw an error?
Does it create the account for you locally and give you access?
It will create the account locally with a balance of 0 ETH.
I tried this with the Ropsten address 0xFD391b604E9456c0Ec4aC13Cc881FbAF68868eB2, which currently has 210 testnet ETH and does not exist on the mainnet.
With your code example it will return a valid signer, and if you check the balance of the signer's address it will have 0 ETH.

How to check instruction in Solana on-chain program?

I am developing game, which guesses number and get reward if they success.
This is summary of my program.
First, user send amount of sol and his guessing number.
Second, Program get random number and store user's sol to vault.
Third, Program make random number, if user is right, gives him reward.
Here, how can I check if the user sent correct amount of sol in program?
This is test code for calling program.
const result = await program.rpc.play(
new anchor.BN(40),
new anchor.BN(0),
new anchor.BN(20000000),
_nonce, {
accounts: {
vault: vaultPDA,
user: provider.wallet.publicKey, // User wallet
storage: storageAccount.publicKey,
systemProgram: systemProgram
},
instructions: [
SystemProgram.transfer({
fromPubkey: provider.wallet.publicKey,
toPubkey: vaultPDA,`enter code here`
lamports: 20000000`enter code here`
})
],
signers: [storageAccount]`enter code here`
}
)
The best solution would be to directly transfer the lamports inside of your program using a cross-program invocation, like this program: Cross-program invocation with unauthorized signer or writable account
Otherwise, from within your program, you can check the lamports on the AccountInfo passed, and make sure it's the proper number, similar to this example: https://solanacookbook.com/references/programs.html#transferring-lamports
The difference there is that you don't need to move the lamports.

My Token.createMintToInstruction is throwing "Error processing instruction 0: invalid account data for instruction"

I'm trying to mint some tokens on the frontend like this:
let transaction = new Transaction();
let mintToInstruction = Token.createMintToInstruction(
splToken.TOKEN_PROGRAM_ID,
myTokenMint.publicKey,
userAccount.publicKey,
airdropAdmin.publicKey,
[],
sendAmount.toNumber()
)
transaction.add(mintToInstruction);
let conn: Connection = ctx.connection;
const tx1 = await conn.sendTransaction(
transaction,
[airdropAdmin]
);
But I get an obscure error:
Error processing Instruction 0: invalid account data for instruction
What's happening?
One of the accounts you're passing in is not the account the Token Program expected.
Either:
The userAccount is incorrect. This must be a Token Account, did you use the user's System Account instead?
The myMintAccount is incorrect. Is this a real token mint?
Consider logging those public keys and putting them into the explorer. Does the userAccount say "Token Account" at the top? Does the myMintAccount say "Token Mint"?
The invalid account data for instruction typically happens when a program can't run unpack on the data inside the account you're passing in.
So either the Account::unpack is failing, or the the Mint::unpack is failing.

Using Stored Twitter access_tokens with Twitterizer

I am using C3 & the latest twitterizer api. I have managed to get the user to authenticate & authorize my twitter application after which I persist only the access_token, access_token_secret and access_token_verifier.
The problem I have now is that when the user returns ( at a later stage, cookies removed / expired ), they identify themselves using our own credentials system, and then I attempt to see if their twitter credentials are still valid. I do this by calling the following method
OAuthTokens t = new OAuthTokens();
t.ConsumerKey = "XXX"; // my applications key
t.ConsumerSecret = "XXX";// my applications secret
t.AccessToken = "XXX";// the users token from the DB
t.AccessTokenSecret = "XXX";//the users secret from the DB
TwitterResponse<TwitterUser> resp = TwitterAccount.VerifyCredentials(tokens);
This is the error I get : "error":"Could not authenticate with OAuth.","request":"/1/account/verify_credentials.json"
I know my tokens are valid because if I call this method :
TwitterResponse<TwitterUser> showUserResponse = TwitterUser.Show(tokens, CORRECT_SCREEN_NAME_HERE);
with my screen name passed in and the same OAuth tokens, it returns correctly.
Any Ideas?
C# -> v4.0.30319
Twitterizer -> 2.4.0.2028
In your code, you're defining tokens as t, but when you call VerifyCredentials you're passing it tokens. Is that just an error in your sample code?

Resources