coral-xyz / anchor

⚓ Solana Sealevel Framework
https://anchor-lang.com
Apache License 2.0
3.59k stars 1.32k forks source link

Wrapping an account with `Box` causes a compilation error when using the `owner` constraint #3074

Closed ifiokjr closed 2 months ago

ifiokjr commented 2 months ago

Description

The following code fails to compile:

#[derive(Accounts)]
pub struct MultisigCreateTransaction<'info> {
    pub multisig: Box<Account<'info, MultisigData>>,
    #[account(zero, signer, owner = program.key())]
    pub transaction: Box<Account<'info, MultisigTransactionData>>,
    pub proposer: Signer<'info>,
    pub program: Program<'info, Launchpad>,
}

The error produced is as follows.

the trait bound `std::boxed::Box<anchor_lang::prelude::Account<'_, multisig::MultisigTransactionData>>: derive_more::AsRef<anchor_lang::prelude::AccountInfo<'_>>` is not satisfied
the trait `derive_more::AsRef<anchor_lang::prelude::Account<'_, multisig::MultisigTransactionData>>` is implemented for `std::boxed::Box<anchor_lang::prelude::Account<'_, multisig::MultisigTransactionData>>`
for that trait implementation, expected `anchor_lang::prelude::Account<'_, multisig::MultisigTransactionData>`, found `anchor_lang::prelude::AccountInfo<'_>`

The problem is caused by the derive(Accounts) macro which produces the following for both boxed and unboxed values.

let my_owner = AsRef::<AccountInfo>::as_ref(&transaction).owner;
let owner_address = launchpad_program.key();
if my_owner != &owner_address {
    return Err(anchor_lang::error::Error::from(
        anchor_lang::error::ErrorCode::ConstraintOwner,
    )
    .with_account_name("transaction")
    .with_pubkeys((*my_owner, owner_address)));
}

A fix would be to generate different code if the account is wrapped with Box.

Changing the first line of the macro output to the following resolves the issue.

let my_owner = AsRef::<AccountInfo>::as_ref(&*ctx.accounts.transaction).owner;

Current Workaround

For those looking for a temporary workaround, you can use the constraint in the following way to replicate the behaviour.

#[derive(Accounts)]
pub struct MultisigCreateTransaction<'info> {
    pub multisig: Box<Account<'info, MultisigData>>,
    #[account(
        zero,
        signer,
-       owner = program.key()
+       constraint = *transaction.to_account_info().owner = program.key()
    )]
    #[account(zero, signer, constraint = program.key())]
    pub transaction: Box<Account<'info, MultisigTransactionData>>,
    pub proposer: Signer<'info>,
    pub program: Program<'info, Launchpad>,
}
acheroncrypto commented 2 months ago

Fixed the compilation error in https://github.com/coral-xyz/anchor/pull/3087. However, note that using the owner constraint with the Account type is redundant because Account<T> already checks T::owner() == account.owner.