NewBeeSolCoder
NewBeeSolCoder

Reputation: 21

How I can I solve "TypeError: Cannot read properties of undefined (reading 'size')" while using anchor?

I am trying to write a basic smart contract on Solana where anyone can start a crowdfunding campaign. I am working with the front end at the moment. I have managed to check for a Phantom wallet, connect the wallet, and am now stuck at creating the crowdfunding campaign with the TypeError above. This is the full error when I click the :

App.js:84 Error creating campaign account: TypeError: Cannot read properties of undefined (reading 'size')
    at new AccountClient (account.ts:121:1)
    at account.ts:28:1
    at Array.reduce (<anonymous>)
    at AccountFactory.build (account.ts:27:1)
    at NamespaceFactory.build (index.ts:55:1)
    at new Program (index.ts:300:1)
    at createCampaign (App.js:64:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)
    at invokeGuardedCallback (react-dom.development.js:4277:1)

I used Anchor as a basis and kept going from there. The lib.rs code is below:

use anchor_lang::prelude::*;
use anchor_lang::solana_program::entrypoint::ProgramResult;

declare_id!("***Contract address was posted here***");

#[program]
pub mod crowdfunding {
    use super::*;

    pub fn create(ctx: Context<Create>, name:String, description: String) -> ProgramResult {
        let campaign = &mut ctx.accounts.campaign;
        campaign.name = name;
        campaign.description = description;
        campaign.amount_donated = 0;
        campaign.admin = *ctx.accounts.user.key;
        Ok(())
    }

    pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> ProgramResult {
        let campaign = &mut ctx.accounts.campaign;
        let user = &mut ctx.accounts.user;
        if campaign.admin != *user.key {
            return Err(ProgramError::IncorrectProgramId);
        }
        let rent_balance = Rent::get()?.minimum_balance(campaign.to_account_info().data_len());
        if **campaign.to_account_info().lamports.borrow() - rent_balance < amount {
            return Err(ProgramError::InsufficientFunds);
        }
        **campaign.to_account_info().try_borrow_mut_lamports()? -= amount;
        **user.to_account_info().try_borrow_mut_lamports()?  += amount;
        Ok(())
    }

    pub fn donate(ctx: Context<Donate>, amount: u64) -> ProgramResult {
        let ix = anchor_lang::solana_program::system_instruction::transfer(
            &ctx.accounts.user.key(),
            &ctx.accounts.campaign.key(),
            amount
        );
        anchor_lang::solana_program::program::invoke(
            &ix,
            &[
                ctx.accounts.user.to_account_info(),
                ctx.accounts.campaign.to_account_info()
            ]
        );
        (&mut ctx.accounts.campaign).amount_donated += amount;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Create<'info> {
    #[account(init, payer=user, space=9000, seeds=[b"CAMPAIGN_DEMO".as_ref(), user.key().as_ref()], bump)]
    pub campaign: Account<'info, Campaign>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>
}

#[derive(Accounts)]
pub struct Withdraw<'info> {
    #[account(mut)]
    pub campaign: Account<'info, Campaign>,
    #[account(mut)]
    pub user: Signer<'info>
}

#[derive(Accounts)]
pub struct Donate<'info> {
    #[account(mut)]
    pub campaign: Account<'info, Campaign>,
    #[account(mut)]
    pub user: Signer<'info>,
    pub system_program: Program<'info, System>
}

#[account]
pub struct Campaign {
    pub admin: Pubkey,
    pub name: String,
    pub description: String,
    pub amount_donated: u64
}

This is the App.js file for the front end:

import './App.css';
import idl from "./idl.json";
import { Connection, PublicKey, clusterApiUrl} from "@solana/web3.js";
import { Program, AnchorProvider, web3, utils, BN} from '@coral-xyz/anchor';
import { useEffect, useState } from "react";
import { Buffer} from "buffer"
window.Buffer = Buffer

const programID = new PublicKey(idl.address);
const network = clusterApiUrl("devnet");
const opts = {
  preflightCommitment: "processed", 
};
const {SystemProgram} = web3;

const App = () => {
  const[walletAddress, setWalletAddress] = useState(null);
  const getProvider = () => {
    const connection = new Connection(network, opts.preflightCommitment);
    const provider = new AnchorProvider(
      connection, 
      window.solana, 
      opts.preflightCommitment
    );
    return provider
  }
  const checkIfWalletIsConnected = async() => {
    try {
      const {solana} = window;
      if (solana) {
        if (solana.isPhantom) {
          console.log("Phantom wallet found!");
          const response = await solana.connect({
            onlyIfTrusted: true,
          });
          console.log(
            "Connected with public key:", 
            response.publicKey.toString()
          );
          setWalletAddress(response.publicKey.toString());
        }
      } else {
        alert("Solana object not found! Get a Phantom wallet.");
      }
    } catch(error) {
      console.error(error);
    }
  };
  const connectWallet = async () => {
    const {solana} = window;
    if (solana) {
      const response = await solana.connect();
      console.log(
        'Connected with public key:',
        response.publicKey.toString()
      );
      setWalletAddress(response.publicKey.toString());
    }
  };

  const createCampaign = async() => {
    try {
      const provider = getProvider()
      const program = new Program(idl, programID, provider)
      const [campaign] = PublicKey.findProgramAddressSync(
        [
        utils.bytes.utf8.encode("CAMPAIGN_DEMO"),
        provider.wallet.publicKey.toBuffer(),
        ],
        program.programId
      );
      program.methods.create('campaign name', 'campaign description',{
        accounts: {
          campaign,
          user: provider.wallet.publicKey,
          systemProgram: SystemProgram.programId,
        },
      });
      console.log(
        'Created a new campaign w/ address:', 
        campaign.toString()
      );
    } catch(error) {
      console.error("Error creating campaign account:", error)
    }
  }

  const renderNotConnectedContainer = () => (
    <button onClick={connectWallet}>Connect to Wallet</button>
  );
  const renderConnectedContainer = () => (
    <button onClick={createCampaign}>Create a campaign...</button>
  );
  useEffect(() => {
    const onLoad = async() => {
      await checkIfWalletIsConnected();
    };
    window.addEventListener('load', onLoad);
    return () => window.removeEventListener('load', onLoad);
  }, []);

  return (
  <div className="App">
    {!walletAddress && renderNotConnectedContainer()}
    {walletAddress && renderConnectedContainer()}
  </div>
  );
};

export default App;

I tried going through the account.ts files from anchor, but am still not finding a way to define the size. In my mind it has already been defined. What am I missing?

Upvotes: 2

Views: 382

Answers (1)

eclat_soft
eclat_soft

Reputation: 808

I was able to resolve it by modifying the initialization from:

    const program = new Program(idl, programID, provider); //you will find this line inside 

TO:

const program = new Program(idl, provider);

FOR MORE ANSWERS AS TO WHY: The error occurs because the Program constructor in Anchor expects only two arguments when using the Program class from @project-serum/anchor. The correct function signature is:

new Program(idl: Idl, provider: Provider, programId?: PublicKey)

So, when calling:

const program = new Program(idl, programID, provider);

It mistakenly treats programID as the provider, which leads to the error

Solution: Change the initialization to:

    const program = new Program(idl, provider);

Upvotes: 0

Related Questions