michaelhly / solana-py

Solana Python SDK

Home Page:https://michaelhly.github.io/solana-py

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

I found a Design Issue when using solana and anchor...

zilinissleepin opened this issue · comments

intro

I am working for using my own deployed program to swap in DEXs such as orca using anchorpy and solana-py.

But I have found that when I create a instruction with my program, there will be an Error: InvalidParamsMessage { message: "invalid transaction: Transaction failed to sanitize accounts offsets correctly" }

this is my client code:

async def update_owner(new_owner: Pubkey, owner: Keypair):
    acc = await AccessControl.fetch(provider.connection, pda[0])
    print(f"The owner will change from {acc.only_owner} to {new_owner}")
    
    ix = instructions.update_owner({
        "new_owner": new_owner,
    }, {
        "access_control": pda[0],
        "only_owner": owner.pubkey()
    })
    
    recent_blockhash = await provider.connection.get_latest_blockhash(commitment="finalized")
    
    tx = Transaction(recent_blockhash=recent_blockhash.value.blockhash).add(ix)
    
    tx.sign(owner)
    
    print(await provider.simulate(tx))

pda is a PDA address that stores some Access Control Infos.

only_owner is a read-only account that must be signed.

instructions.update_owner is a piece of auto-generated code by anchorpy.

class UpdateOwnerAccounts(typing.TypedDict):
    access_control: Pubkey
    only_owner: Pubkey

def update_owner(
    args: UpdateOwnerArgs,
    accounts: UpdateOwnerAccounts,
    program_id: Pubkey = PROGRAM_ID,
    remaining_accounts: typing.Optional[typing.List[AccountMeta]] = None,
) -> Instruction:
    keys: list[AccountMeta] = [
        AccountMeta(
            pubkey=accounts["access_control"], is_signer=False, is_writable=True
        ),
        AccountMeta(pubkey=accounts["only_owner"], is_signer=True, is_writable=False),
    ]
    if remaining_accounts is not None:
        keys += remaining_accounts
    identifier = b"\xa4\xbc|\xfe\x84\x1a\xc6\xb2"
    encoded_args = layout.build(
        {
            "new_owner": args["new_owner"],
        }
    )
    data = identifier + encoded_args
    return Instruction(program_id, data, keys)

Therefore, I will include 3 accounts: access_control pda, only_owner account and a transaction payer. But I set the payer the same as the only_owner account. So there will be 2 accounts.

I found that when I run this client code, I will get an error InvalidParamsMessage { message: "invalid transaction: Transaction failed to sanitize accounts offsets correctly" }.

I dive deep and find that the MessageHeader builds incorrectly in solders pacakage. the MessageHeader is header: MessageHeader { num_required_signatures: 1, num_readonly_signed_accounts: 1, num_readonly_unsigned_accounts: 1 }

num_readonly_signed_accounts is 0 not 1, because the only_owner account will be the transaction payer and become writable. However, it seems that the solder and solana SDK does not effectively address this issue.

I have found that the root reason is at tx = Transaction(recent_blockhash=recent_blockhash.value.blockhash).add(ix), Transaction init must clarify the fee_payer, otherwise, the fee_payer will be set to None and cause the Sanitize Error.

But I have clarified the payer already when init the provider.

This is maybe a SDK Design Issue?