solana-py
solana-py copied to clipboard
Multiple Transactions Converts First Account to Signer
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''))
What version are you using?
What version are you using?
0.25.0
what happens if you .serialize()
and complete the transaction?
does it fail?
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 theMessage
structure, the first account is always the fee-payer, so if the caller has knowledge that the first account of the constructed transaction'sMessage
is both a signer and the expected fee-payer, then redundantly specifying the fee-payer is not strictly required.
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?
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
Jumping here. On my side that would be a breaking change, forcing fee payer at init.
Glad to explore possible paths with y'all!
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.
@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
@benedictbrady for now if you pass the
fee_payer
arg when initializingTransaction
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