solana-py icon indicating copy to clipboard operation
solana-py copied to clipboard

Multiple Transactions Converts First Account to Signer

Open benedictbrady opened this issue 2 years ago • 3 comments

If you add a second instruction to a transaction that has no signing accounts it will convert the first account to is_signer=True. An example below. I am curious if anyone knows where this behavior is emerging from so I can push a patch


instruction = TransactionInstruction(
    keys=[AccountMeta(SYSVAR_CLOCK_PUBKEY, is_signer=False, is_writable=False)],
    program_id=SYSVAR_CLOCK_PUBKEY,
    data=b''
)
transaction.add(instruction)
transaction.add(instruction)

print(transaction.instructions)

Output

(TransactionInstruction(keys=[AccountMeta(pubkey=SysvarC1ock11111111111111111111111111111111, is_signer=True, is_writable=False)], program_id=SysvarC1ock11111111111111111111111111111111, data=b''),
 TransactionInstruction(keys=[AccountMeta(pubkey=SysvarC1ock11111111111111111111111111111111, is_signer=True, is_writable=False)], program_id=SysvarC1ock11111111111111111111111111111111, data=b''))

benedictbrady avatar Jul 14 '22 03:07 benedictbrady

What version are you using?

michaelhly avatar Jul 14 '22 05:07 michaelhly

What version are you using?

0.25.0

benedictbrady avatar Jul 14 '22 14:07 benedictbrady

what happens if you .serialize() and complete the transaction? does it fail?

crypt0miester avatar Jul 21 '22 11:07 crypt0miester

I think this comes from the .instructions setter using the self.fee_payer property which assumes that the first account is the fee payer. After adding the first instruction, self.fee_payer is the pubkey of the first account in the first instruction. This fee_payer then gets passed to Message.new_with_blockhash which causes it to be considered a signer.

I'm not sure what we should do here. Using the first account as the fee payer comes from the Rust SDK. Here's what the Message docs say about the fee payer:

Some constructors accept an optional payer, the account responsible for paying the cost of executing a transaction. In most cases, callers should specify the payer explicitly in these constructors. In some cases though, the caller is not required to specify the payer, but is still allowed to: in the Message structure, the first account is always the fee-payer, so if the caller has knowledge that the first account of the constructed transaction's Message is both a signer and the expected fee-payer, then redundantly specifying the fee-payer is not strictly required.

kevinheavey avatar Aug 22 '22 16:08 kevinheavey

I think the solution here is to require fee_payer when initializing a Transaction instead of making it optional. This is a breaking change though. Thoughts?

kevinheavey avatar Sep 14 '22 17:09 kevinheavey

This would be totally fine with me but I'm not sure how much pain it would cause others. This behavior makes it hard for me to build complicated transactions with multiple instructions

benedictbrady avatar Sep 15 '22 05:09 benedictbrady

Jumping here. On my side that would be a breaking change, forcing fee payer at init.

Glad to explore possible paths with y'all!

aleixisp avatar Sep 15 '22 05:09 aleixisp

This would be totally fine with me but I'm not sure how much pain it would cause others. This behavior makes it hard for me to build complicated transactions with multiple instructions

Might be already tried, did you tried to manually swap the book value within the transaction? Setting the prop. Fee_payer to None?

Just dumping first thoughts to see a possible fixes here.

aleixisp avatar Sep 15 '22 05:09 aleixisp

@benedictbrady for now if you pass the fee_payer arg when initializing Transaction it should work. Also if you're building complicated Transactions it's probably simpler to just use Solders directly as there's one less layer of indirection

kevinheavey avatar Sep 15 '22 07:09 kevinheavey

@benedictbrady for now if you pass the fee_payer arg when initializing Transaction it should work. Also if you're building complicated Transactions it's probably simpler to just use Solders directly as there's one less layer of indirection

perfect, thanks @kevinheavey

benedictbrady avatar Sep 16 '22 02:09 benedictbrady