调用solana(锚点)程序。没有错误,但提示未按要求在帐户之间转移

发布于 2025-01-14 10:37:37 字数 1497 浏览 1 评论 0原文

当使用凹凸+种子时,我的代码运行良好。所有测试都通过了。 但请参阅下面使用 Anchor 创建的众筹活动的代码。 问题是我必须在我的捐赠函数中使用“调用”,以便首先将资金从捐赠者(provider.wallet.publicKey)转移到捐赠者的PDA,然后从捐赠者的PDA转移到另一个PDA使用我的执行程序以便完成捐赠。

我的意思是没有更好的方法使用 Anchor 来做到这一点吗? Anchor 不支持在从授权机构(签名者)向其 PDA 进行 rpc 调用时传输 lamport,我的意思是在开始时,当在 #[derive(Accounts)] 中使用 init 属性时?

 #[derive(Accounts)]
    #[instruction(donator_program_account_bump : u8)]
    pub struct InitializeDonation<'info> {
        #[account(init, 
            seeds = [b"shutUpAndDance!".as_ref(), authority.key().as_ref()], 
            bump = donator_program_account_bump, 
            payer = authority, 
            space = 50
            amount = ? )]
            pub donator_program_account : Account<'info, Donation>,

            #[account(mut)]
            pub authority : Signer<'info>,
            pub system_program : Program<'info, System>,
            
    }

像“金额”这样的东西不能在指令中传递?或者其他方式?

我已经阅读了锚文档,但我找不到这样的信息。 现在有趣的部分是:lamport 没有按照请求进行传输,这意味着存在逻辑错误(如果不是验证错误的话)下面是来自终端的消息:

Got ProgramDerivedWritingAccountAddress: bump: 254, pubkey: 4xgaJXeBXVxLwY9uvELmS3yhBgLVGBbXs3wJmjYSihXB
Got ProgramDerivedDonatorProgramAccountAddress: bump: 255, pubkey: 5JqnV2bhUE8MPWs8jJBavc57G86ruZRzzhfKGKNbgt3V
Balance of CampaignWritingAccount before Donation :  63530880
Balance of Donator before Donation :  499999975882047550
Balance of DonatorProgramAccount before Donation :  1238880

              

My code runs fine when using bump + seeds. All tests are passing.
But Please see below the code of a Crowdfunding campaign, created using Anchor.
The problem is that I have to use 'invoke' inside my donate function in order to transfer funds from the donator(provider.wallet.publicKey) to the donator's PDA first and then from the donator PDA to another PDA using my executing program so that the donation can be complete.

I mean isn't there a better way to do this using Anchor? Doesn't Anchor support transferring of lamports at the time of making an rpc call from authority(signer) to its PDA, I mean at the start, when using the init attribute inside #[derive(Accounts)]?

 #[derive(Accounts)]
    #[instruction(donator_program_account_bump : u8)]
    pub struct InitializeDonation<'info> {
        #[account(init, 
            seeds = [b"shutUpAndDance!".as_ref(), authority.key().as_ref()], 
            bump = donator_program_account_bump, 
            payer = authority, 
            space = 50
            amount = ? )]
            pub donator_program_account : Account<'info, Donation>,

            #[account(mut)]
            pub authority : Signer<'info>,
            pub system_program : Program<'info, System>,
            
    }

Something like 'amount' cannot be passed somewhere in instruction? Or some other way?

I have read through the anchor docs, and nowhere I can find such info.
Now the Funny part: The lamports are not getting transferred as requested, which means there is a logic error, if not validation error. Below is the message from the Terminal:

Got ProgramDerivedWritingAccountAddress: bump: 254, pubkey: 4xgaJXeBXVxLwY9uvELmS3yhBgLVGBbXs3wJmjYSihXB
Got ProgramDerivedDonatorProgramAccountAddress: bump: 255, pubkey: 5JqnV2bhUE8MPWs8jJBavc57G86ruZRzzhfKGKNbgt3V
Balance of CampaignWritingAccount before Donation :  63530880
Balance of Donator before Donation :  499999975882047550
Balance of DonatorProgramAccount before Donation :  1238880
???? Your Donation transaction signature is :  5WtEEXAUjXMeULwbQ1zKy4NS89wtoDXDafK9NGpYhpG4ysPQeFaiBghD9qwSyzkNtMTpfV1TbZtX6qbNGW3BgFBQ
Balance of CampaignWritingAccount after Donation :  63530880
Balance of Donator post Donation :  499999975882042560
Balance of DonatorProgramAccount post Donation :  1238880
    ✔ Can Make a Donation (585ms)
Got ProgramDerivedWritingAccountAddress: bump: 254, pubkey: 4xgaJXeBXVxLwY9uvELmS3yhBgLVGBbXs3wJmjYSihXB
Balance of Campaign before Withdrawal:  1164769760
Your Withdrawal transaction signature TZBhJ8Z51DyENL343gY5EizV6rjbARdkjKavdKjHjDP1kB8Yio8vc44ZFFT99qvHngr24neSmFqZugUTTuDsSud
Balance of Campaign after Withdrawal:  1164769760
    ✔ Can Make a Withdrawal (1063ms)


  5 passing (4s)

Check out the full code below :

lib.rs


use anchor_lang::prelude::*;
use anchor_lang::solana_program::program::{invoke /* , invoke_signed */ };
use anchor_lang::solana_program::system_instruction;
use anchor_lang::solana_program::program_error::ProgramError;
use anchor_lang::solana_program::pubkey::Pubkey;

declare_id!("Br3pwYVUCP8iafhtoqRSFYjZ4QsreqaZffVT6GtaoiUR");


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

    // `seeds` and `bump` tell us that our `writing_account` is a PDA that can be derived from their respective values

    pub fn initialize_campaign(ctx: Context<InitializeCampaign>, writing_account_bump: u8) -> ProgramResult {
        let writing_account = &mut ctx.accounts.writing_account;
        let authority = &mut ctx.accounts.authority;
         
        writing_account.bump = writing_account_bump;
        writing_account.count = 0;
        writing_account.authority = *authority.key; 
        writing_account.campaign_details = Vec::new();
        writing_account.withdraw_request = Vec::new();
        writing_account.donation_received = Vec::new();

        
        Ok(())
    }

    pub fn initialize_donation(ctx: Context<InitializeDonation>, donator_program_account_bump : u8) -> ProgramResult {
        let donator_program_account = &mut ctx.accounts.donator_program_account;
        let authority = &mut ctx.accounts.authority;

        donator_program_account.bump = donator_program_account_bump;
        donator_program_account.authority = *authority.key; 

        Ok(())

    }

    pub fn create_campaign
    (
        ctx : Context<CreateCampaign>, 
        name : String, 
        description : String, 
        image_link: String,
        writing_account_bump : u8
        
        
    ) 
        -> ProgramResult {
            
        let writing_account = &mut ctx.accounts.writing_account;
        let authority = &mut ctx.accounts.authority;
        
             
        let (pda, bump ) = Pubkey::find_program_address(
        &[b"upOnlyCrypto!", &*authority.key().as_ref()], &self::ID
        );

        if pda != writing_account.key()  {                         // Confirm if passed in PDA address is the same
            return Err(ProgramError::Custom(1))
        };

        if bump != writing_account_bump {
            return Err(ProgramError::Custom(2))
        };

        if name.len()  > 30 || description.len() > 50 {
            return Err(ErrorCode::NameOrDescriptionTooLong.into())
        }


        let campaign_data = CampaignDetails {
            admin : *authority.key,
            name : name.to_string(),
            description : description.to_string(),
            image_link : image_link.to_string(),
               
        };

        writing_account.count += 1;
        writing_account.campaign_details.push(campaign_data);

        
        Ok(())
    }



    
    pub fn withdraw
    (
        ctx : Context<Withdraw>, 
        amount : u64,
        writing_account_bump : u8
        

    ) -> ProgramResult {
       
        let writing_account = &mut ctx.accounts.writing_account;
        let authority = &mut ctx.accounts.authority;

        let (pda, bump ) = Pubkey::find_program_address(
            &[b"upOnlyCrypto!", &*authority.key().as_ref()], &self::ID
            );
    
            if pda != writing_account.key()  {                         // Confirm if passed in PDA address is the same
                return Err(ProgramError::Custom(1))
            };
    
            if bump != writing_account_bump {
                return Err(ProgramError::Custom(2))
            };
      
         **writing_account.to_account_info().try_borrow_mut_lamports()? -= amount;
         **authority.to_account_info().try_borrow_mut_lamports()? += amount;

        let withdraw_data = WithdrawRequest {
            amount_withdrawn : amount,
            admin : *authority.to_account_info().key,
        };

        writing_account.withdraw_request.push(withdraw_data);
         
        Ok(())

    }

    


    pub fn donate
    (
        ctx : Context<Donate>, 
        amount : u64, 
        donator_program_account_bump : u8


    ) -> ProgramResult {
 
    let writing_account = &mut ctx.accounts.writing_account;
    let donator_program_account = &mut ctx.accounts.donator_program_account;
    let authority = &mut ctx.accounts.authority;

    let (pda, bump ) = Pubkey::find_program_address(
    &[b"shutUpAndDance!", &*authority.key().as_ref()], &self::ID
    );

    if pda != donator_program_account.key()  {                         // Confirm if passed in PDA address is the same
        return Err(ProgramError::Custom(1))
    };

    if bump != donator_program_account_bump {
        return Err(ProgramError::Custom(2))
    };

   let transfer_ix = system_instruction::transfer(
        &authority.to_account_info().key(),
        &donator_program_account.to_account_info().key(),
        amount,
   );

   invoke(
       &transfer_ix, 
       &[
           authority.to_account_info(),
           donator_program_account.to_account_info(),
        ],
    )?;

     
    **writing_account.to_account_info().try_borrow_mut_lamports()? += **donator_program_account.to_account_info().lamports.borrow();
    **donator_program_account.to_account_info().try_borrow_mut_lamports()? = 0;
    

    let donation = DonationMade {
        amount_donated : amount,
    };

    writing_account.donation_received.push(donation);
   
    Ok(())

    }


    #[derive(Accounts)]
    #[instruction(writing_account_bump : u8)]
    pub struct InitializeCampaign<'info> {
        #[account(init, 
            seeds = [b"upOnlyCrypto!".as_ref(), authority.key().as_ref()], 
            bump = writing_account_bump, 
            payer = authority, 
            space = 9000)]
            pub writing_account : Account<'info, CampaignState>,

            #[account(mut)]
            pub authority : Signer<'info>,
            pub system_program : Program<'info, System>,
            
    }
    

    #[derive(Accounts)]
    #[instruction(donator_program_account_bump : u8)]
    pub struct InitializeDonation<'info> {
        #[account(init, 
            seeds = [b"shutUpAndDance!".as_ref(), authority.key().as_ref()], 
            bump = donator_program_account_bump, 
            payer = authority, 
            space = 50)]
            pub donator_program_account : Account<'info, Donation>,

            #[account(mut)]
            pub authority : Signer<'info>,
            pub system_program : Program<'info, System>,
            
    }

    
    #[derive(Accounts)] 
    pub struct CreateCampaign<'info> {
       #[account(mut, has_one = authority)]
        pub writing_account  : Account<'info, CampaignState>,
        #[account(mut)]
        pub authority : Signer<'info>,      
             
    }

   
    #[derive(Accounts)] 
    pub struct Withdraw<'info> {
        #[account(mut, has_one = authority)]
        pub writing_account : Account<'info, CampaignState>,
        #[account(mut)]
        pub authority : Signer<'info>,  
          
    }

   
    #[derive(Accounts)]
    pub struct Donate<'info> {
        #[account(mut, has_one = authority)]
        pub donator_program_account : Account<'info, Donation>,
        #[account(mut)]
        pub writing_account : Account<'info, CampaignState>,
        #[account(mut)]
        pub authority : Signer<'info>,
        pub system_program : Program<'info, System>,
    }


    #[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)]
    pub struct CampaignDetails  {
        pub admin: Pubkey,
        pub name: String,
        pub description: String,
        pub image_link: String,       
             
    }

    #[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)]
    pub struct WithdrawRequest {
        pub amount_withdrawn : u64,
        pub admin : Pubkey,        
    }

    #[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)]
    pub struct DonationMade {
        pub amount_donated: u64,
           
    }


    #[account]
    pub struct CampaignState {    
       pub campaign_details : Vec<CampaignDetails>,
       pub bump : u8,
       pub count : u8,
       pub authority: Pubkey,  
       pub withdraw_request : Vec<WithdrawRequest>, 
       pub donation_received : Vec<DonationMade>,        
    }

    #[account]
    pub struct Donation {
        
        pub bump : u8,
        pub authority : Pubkey,
    }
   
    #[error]
    pub enum ErrorCode {    
       
        #[msg("Name cannot be more than 30 charecters and Description cannot be more than 50 charecters")]
            NameOrDescriptionTooLong,
    }



}


Also, check tests/solanacrowdfundingproject.js below :

const assert = require('assert');
const anchor = require('@project-serum/anchor');
const { PublicKey, Connection } = require("@solana/web3.js");
const cluster = "http://localhost:8899";
//const cluster = "https://api.devnet.solana.com";
const connection = new Connection(cluster, "confirmed");
const { SystemProgram /*, Keypair, SYSVAR_RENT_PUBKEY*/ } = anchor.web3;
const { Buffer } = require('buffer');



// Specify provider environment. 
const provider = anchor.Provider.env();
//Set provider.
anchor.setProvider(provider);
//Specify the workspace 
const program = anchor.workspace.Solanacrowdfundingproject;
//const programID = await connection.programID(program);
const programID = new PublicKey("Br3pwYVUCP8iafhtoqRSFYjZ4QsreqaZffVT6GtaoiUR");




describe('Solanacrowdfundingproject', () => {

  console.log("???? Starting tests...");
  try {
    it('gets the Campaign Writing Account initialized', async () => {
      const { writingAccount, bump } = await getProgramDerivedCampaignWritingAccountAddress();
      

      let tx = await program.rpc.initializeCampaign(new anchor.BN(bump),  {
        accounts: {
          writingAccount: writingAccount,
          authority: provider.wallet.publicKey,
          systemProgram: SystemProgram.programId,

        },

      });
      //Console.log the Transaction signature of the Initialization procedure. 
      console.log("Campaign Writing Account Initialization signature : ", tx);

      //Console logs
      const account = await program.account.campaignState.fetch(writingAccount);
      console.log("???? Created A New Campaign Writing Account : ", account);
      console.log("???? Writing Account's Campaign count is :", account.count);

      //Asserts
      assert.equal(account.authority.toBase58(), provider.wallet.publicKey.toBase58());
      //assert.ok(account.count.eq(new anchor.BN(0)));
      //console.log('???? Account Authority pubKey : ', account.authority.toBase58());
      
    });
  } catch (error) {
    console.log(error);
  }


  try {
    it('gets the Donator Account initialized', async () => {
      const { donatorProgramAccount, bump } = await getProgramDerivedDonatorProgramAccountAddress();
      
      let tx = await program.rpc.initializeDonation(new anchor.BN(bump),  {
        accounts: {
          authority: provider.wallet.publicKey,
          donatorProgramAccount : donatorProgramAccount,
          systemProgram: SystemProgram.programId,

        },

      });
      //Console.log the Transaction signature of the Initialization procedure. 
      console.log("Donation Account Initialization signature : ", tx);

      //Console.log the accounts created:
      const account = await program.account.donation.fetch(donatorProgramAccount);
      console.log("???? Created a New Donator Program Account : ", account);
      
      //Asserts
      assert.equal(account.authority.toBase58(), provider.wallet.publicKey.toBase58());
      
    });
  } catch (error) {
    console.log(error);
  }


  try {
    it('Creates a campaign', async () => {

      const { writingAccount, bump } = await getProgramDerivedCampaignWritingAccountAddress();

      //Lets invocate the createCampaign function using provider.wallet.publicKey
      let tx = await program.rpc.createCampaign("Suveett", "Blockchain Speaker", "Enter a fancy giflink for Campaign", new anchor.BN(bump),
        {
          accounts: {
            writingAccount: writingAccount,
            authority: provider.wallet.publicKey,

          },
          
        });
      
      //Console.log the Transaction signature of the Initialization procedure. 
      console.log("Your CreateCampaign transaction signature", tx);
      //Console Logs
      let account = await program.account.campaignState.fetch(writingAccount);
      console.log("Writing Account after Campaign Creation :", account);
      //console.log("This Writing account's address is : ", account.key().toBase58());
      //console.log("This writing Account's owner is the Executing Program : ", account.owner().toBase58());
      console.log("This Writing account's admin is : ", account.campaignDetails[0].admin.toBase58());
      console.log("This Writing account's Campaign Details contains `name` :", account.campaignDetails[0].name);
      console.log("This Writing account's Campaign Details contains `description` :", account.campaignDetails[0].description);
      //Asserts
      //assert.ok(account.count.eq(new anchor.BN(1)));

    });

  } catch (error) {
    console.log(error);
  }



 
  try {
    it('Can Make a Donation', async () => {

      const { writingAccount } = await getProgramDerivedCampaignWritingAccountAddress();
      const { donatorProgramAccount, bump } = await getProgramDerivedDonatorProgramAccountAddress();


      let balanceOfCampaignAccountPreDonation = await connection.getBalance(writingAccount);
      console.log("Balance of CampaignWritingAccount before Donation : ", balanceOfCampaignAccountPreDonation);

      let balanceOfDonatorPreDonation = await connection.getBalance(provider.wallet.publicKey);
      console.log("Balance of Donator before Donation : ", balanceOfDonatorPreDonation);

      let balanceOfDonatorProgramAccountPreDonation = await connection.getBalance(donatorProgramAccount);
      console.log("Balance of DonatorProgramAccount before Donation : ", balanceOfDonatorProgramAccountPreDonation);
     
        let donateTx = await program.rpc.donate(new anchor.BN(100000000), new anchor.BN(bump),
        {
          accounts: {
            writingAccount: writingAccount,
            donatorProgramAccount: donatorProgramAccount,
            authority: provider.wallet.publicKey,
            systemProgram: SystemProgram.programId,   

          },
         
        });

      //Console Logs
      //Console.log the Transaction signature of the Donation procedure. 
      
      console.log("???? Your Donation transaction signature is : ", donateTx);

      let balanceOfCampaignAccountPostDonation = await connection.getBalance(writingAccount);
      console.log("Balance of CampaignWritingAccount after Donation : ", balanceOfCampaignAccountPostDonation);

      let balanceOfDonatorPostDonation = await connection.getBalance(provider.wallet.publicKey);
      console.log("Balance of Donator post Donation : ", balanceOfDonatorPostDonation);

      let balanceOfDonatorProgramAccountPostDonation = await connection.getBalance(donatorProgramAccount);
      console.log("Balance of DonatorProgramAccount post Donation : ", balanceOfDonatorProgramAccountPostDonation);


    });
  } catch (error) {
    console.log(error);
  }



  try {
    it('Can Make a Withdrawal', async () => {
      const { writingAccount , bump } = await getProgramDerivedCampaignWritingAccountAddress();
      const signature = await connection.requestAirdrop(writingAccount, 1000000000);
      await connection.confirmTransaction(signature);
      let balanceOfCampaignAccountPreWithdrawal = await connection.getBalance(writingAccount);
      console.log("Balance of Campaign before Withdrawal: ", balanceOfCampaignAccountPreWithdrawal);

      let withdrawTx = await program.rpc.withdraw(new anchor.BN(500000000), new anchor.BN(bump),
        {
          accounts: {
            writingAccount: writingAccount,
            authority: provider.wallet.publicKey,

          }

        });
      //Console Logs
      //Console.log the Transaction signature of the Withdrawal procedure.
      console.log("Your Withdrawal transaction signature", withdrawTx);
      let balanceOfCampaignAccountPostWithdrawal = await connection.getBalance(writingAccount);
      console.log("Balance of Campaign after Withdrawal: ", balanceOfCampaignAccountPostWithdrawal);


    });
  } catch (error) {
    console.log(error);
  }


});



async function getProgramDerivedCampaignWritingAccountAddress() {
  const [writingAccount, bump] = await PublicKey.findProgramAddress(
    [Buffer.from('upOnlyCrypto!'), provider.wallet.publicKey.toBuffer()],
    programID
  );

  console.log(`Got ProgramDerivedWritingAccountAddress: bump: ${bump}, pubkey: ${writingAccount.toBase58()}`);
  return { writingAccount, bump };

};


async function getProgramDerivedDonatorProgramAccountAddress() {
  const [donatorProgramAccount, bump] = await PublicKey.findProgramAddress(
    [Buffer.from(anchor.utils.bytes.utf8.encode('shutUpAndDance!')) , provider.wallet.publicKey.toBuffer() ],
    programID
  );

  console.log(`Got ProgramDerivedDonatorProgramAccountAddress: bump: ${bump}, pubkey: ${donatorProgramAccount.toBase58()}`);
  return { donatorProgramAccount, bump };

};

Kindly please also suggest a better way to write this code (in case any)??

Thanks and Regards

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

乖乖兔^ω^ 2025-01-21 10:37:37

您可能应该等到您发送的交易得到确认。你可以做一些像

let txSignature = await program.rpc.initializeDonation(new anchor.BN(bump),  {
        accounts: {
          authority: provider.wallet.publicKey,
          donatorProgramAccount : donatorProgramAccount,
          systemProgram: SystemProgram.programId,

        },

      });
connection.confirm(txSignature, "confirmed");

我相信它是“确认”的事情,可能是一个非常相似的关键字。

还要确保所有 TokenAccounts(以及任何接收和发送 SOL 的帐户)都是可变的。

You should probably wait until the transactions you sent are confirmed. You could do something like

let txSignature = await program.rpc.initializeDonation(new anchor.BN(bump),  {
        accounts: {
          authority: provider.wallet.publicKey,
          donatorProgramAccount : donatorProgramAccount,
          systemProgram: SystemProgram.programId,

        },

      });
connection.confirm(txSignature, "confirmed");

I believe it was "confirm", might be a very similar keyword.

Also make sure that all TokenAccounts (and any accounts that receive and send SOL) are mutable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文