coral-xyz / anchor

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

2006 Seed Constraint Error When Generating Dynamic Seed Client-Side and Passing to Program #2865

Closed solanacryptodev closed 6 months ago

solanacryptodev commented 6 months ago

Hey all, I'm having a pretty serious problem generating dynamic game_id seeds in my client-side NextJS/TS app and passing that seed down to the program so that I can generate multiple game account instances. I've tried a multitude of fixes, but I keep getting a 2006 - Seed Constraint Error. The instruction works fine when I just pass down the constant SEED and owner key, it only wigs out when I try to pass down the dynamic seed.

Rust instruction

#[derive(Accounts)]
#[instruction(game_id: GameIdState)]
pub struct CreateGame<'info> {
    #[account(
    init,
    payer = owner,
    space = 8 + GameData::MAX_SIZE,
    seeds = [
        GameData::SEED.as_bytes(),
        game_id.current_game_id.clone().as_bytes(),
        owner.key().as_ref()
    ],
    bump
    )]
    pub game: Account<'info, GameData>,
    #[account(
    init,
    seeds = [
        GameData::SEED.as_bytes(),
        game_id.current_game_id.clone().as_bytes(),
        owner.key().as_ref()
    ],
    bump,
    payer = owner,
    space = 8 + WordVault::MAX_SIZE
    )]
    pub word_vault: Account<'info, WordVault>,
    #[account(
    init,
    payer = owner,
    seeds = [
        GameData::SEED.as_bytes(),
        game_id.current_game_id.clone().as_bytes(),
        owner.key().as_ref()
    ],
    space = 8 + GameTreasury::MAX_SIZE,
    bump,
    )]
    pub game_treasury: Account<'info, GameTreasury>,
    #[account(mut)]
    pub global_game_data: Account<'info, GlobalGameData>,
    #[account(mut)]
    pub owner: Signer<'info>,
    pub system_program: Program<'info, System>,
}

Client Side Hook

const initializeGame = async (wallet: WalletContextState, connection: Connection, gameID: string): Promise<void> => {
        if (!wallet) return;

        const setGameId: GameIdState = {
            currentGameId: gameID
        }

        let [gameAcctPDA] = web3.PublicKey.findProgramAddressSync(
            [
                Buffer.from( SEED ),
                wallet.publicKey!.toBuffer(),
            ],
            PROGRAM_ID
        );
        console.log('gameAcct: ', gameAcctPDA.toString());

        let [wordVaultAcctPDA] = web3.PublicKey.findProgramAddressSync(
            [
                Buffer.from(SEED),
                wallet.publicKey!.toBuffer(),
            ],
            PROGRAM_ID
        );
        console.log('wordVaultAcct: ', wordVaultAcctPDA.toString())

        let [gameTreasuryPDA] = web3.PublicKey.findProgramAddressSync(
            [
                Buffer.from(SEED),
                wallet.publicKey!.toBuffer(),
            ],
            PROGRAM_ID
        );
        console.log('gameTreasuryPDA: ', gameTreasuryPDA.toString())

        const gameAccounts: CreateGameInstructionAccounts = {
            game: gameAcctPDA,
            wordVault: wordVaultAcctPDA,
            gameTreasury: gameTreasuryPDA,
            globalGameData: DEVNET_GLOBAL_GAME_DATA_ACCOUNT,
            owner: wallet.publicKey!
        }

        const gameCreationArgs: CreateGameInstructionArgs = {
            gameId: setGameId,

            name: 'TestVerse',
            minDeposit: 0.1,
        }

        const transactionInstruction = createCreateGameInstruction(gameAccounts, gameCreationArgs);

That's pretty much it. I've added a Buffer.from(gameID) to each of the findProgramAddressSync calls as well, same error. This is just the most recent failed build lol

acheroncrypto commented 6 months ago

You're using:

seeds = [
    GameData::SEED.as_bytes(),
    game_id.current_game_id.clone().as_bytes(),
    owner.key().as_ref()
]

but you're not accounting for game_id.current_game_id.clone().as_bytes() client side:

let [gameAcctPDA] = web3.PublicKey.findProgramAddressSync(
    [
        Buffer.from(SEED),
        wallet.publicKey!.toBuffer(),
    ],
    PROGRAM_ID
);

To solve, also serialize the gameID based on it's type.

You're also using the exact same seeds for multiple accounts, which means those accounts will have the same public key.

Closing, as this is not a bug with Anchor.