I understand that in Solana, an account has an holder
, which is the private key to the public key representing the account. An account also has anowner
which is the program
that is allowed to modify the data
and lamport
fields of the account.
I understand what a transaction is created to modify an account, such a transaction must be signed by the holder
, that is, the private key corresponding to the public key of the account to be modified, and this transaction can only be processed successfully if sent to the programid
that corresponds to the owner
of the account.
Please if the above assumption is wrong, do correct me.
What I now do not understand is how this example code works.
On line 36 you have the following code:
let createIx = SystemProgram.createAccount({
fromPubkey: feePayer.publicKey,
newAccountPubkey: counterKey,
/** Amount of lamports to transfer to the created account */
lamports: await connection.getMinimumBalanceForRentExemption(8),
/** Amount of space in bytes to allocate to the created account */
space: 8,
/** Public key of the program to assign as the owner of the created account */
programId: programId,
});
I interpret this to mean, an instruction to create an account, where the public key is counterKey
which was created previous in the code, and the owner will be programId
, which was also created earlier in the code.
Previously the code also creates a fee payer, which it does by first creating a new key pair, funds it with some SOL
and then add the key pair to an array of signers
const feePayer = new Keypair();
console.log("Requesting Airdrop of 1 SOL...");
await connection.requestAirdrop(feePayer.publicKey, 2e9);
console.log("Airdrop received");
const counter = new Keypair();
let counterKey = counter.publicKey;
let tx = new Transaction();
let signers = [feePayer];
It adds this feePayer
to an array of signers
.
Then on line 46 it also adds the key pair for the account that is to be created to the signers
array. Basically this:
signers.push(counter);
tx.add(createIx);
It also adds the instruction to create an account to tx
which represents the transactions.
So at this point we have a signers
array that contains both the feePayer
and counter
and a createIx
transaction.
So first question is at this point.
First Question:
Who signs the createIx
transaction? The feePayer
or the counter
Further down in the code on line 54 you have the following:
const incrIx = new TransactionInstruction({
keys: [
{
pubkey: counterKey,
isSigner: false,
isWritable: true,
},
],
programId: programId,
data: idx,
});
tx.add(incrIx);
let txid = await sendAndConfirmTransaction(connection, tx, signers, {
skipPreflight: true,
preflightCommitment: "confirmed",
commitment: "confirmed",
});
Which is a transaction instruction to increment the counter
account. Now my second question:
Second Question:
Why does it set isSigner
to false when passing in counterKey
? That is:
{
pubkey: counterKey,
isSigner: false, // <- why is this false?
isWritable: true,
}
should isSigner
not be true? since we are going to be updating the counter
account? should this instruction not be signed by the private key corresponding to the counter
account?
Now for the third question:
Third Question:
I attempted to answer the first and second question above, by reasoning maybe the answer is in the order things were added to signers
array and tx
object. Basically:
position: | 1st | 2nd |
signers: | feePayer | counter |
tx: | createIx | incrIx |
Meaning, feePayer
signs the createIx
transaction, while counter
signs the incrIx
transactions. If that is the case, then why have:
keys: [
{
pubkey: counterKey,
isSigner: false,
isWritable: true,
},
]
When creating TransactionInstruction
?
Also in what scenario will isSigner
ever be set to true, if it is the order of things in the signers
array that determine which key signs.