Related
I have the contract
use anchor_lang::prelude::*;
declare_id!("kek");
#[constant]
pub const MEDIA_TAG: &[u8] = b"MEDIA_STATE";
#[account]
#[derive(Default)]
pub struct MediaState {
pub authority: Pubkey,
pub name: String,
}
#[program]
pub mod publisher {
use super::*;
pub fn create_media(ctx: Context<CreateMedia>, name: String) -> Result<()> {
msg!("Media name: {}", name);
let media_state = &mut ctx.accounts.media_state;
media_state.authority = ctx.accounts.authority.key();
media_state.name = name;
msg!("Media created");
Ok(())
}
}
#[derive(Accounts)]
#[instruction()]
pub struct CreateMedia<'info> {
#[account(mut)]
pub authority: Signer<'info>,
#[account(
init,
seeds = [MEDIA_TAG, authority.key().as_ref()],
bump,
payer = authority,
space = 8 + std::mem::size_of::<MediaState>(),
)]
pub media_state: Box<Account<'info, MediaState>>,
pub system_program: Program<'info, System>,
}
and I`d like to get the MediaState/CreateMedia discriminator in my javascript code
const publisher = new web3.PublicKey(
"kek"
);
const connection = new web3.Connection(web3.clusterApiUrl("devnet"));
const accounts = await connection.getProgramAccounts(publisher, {
filters: [{ memcmp: { offset: 0, bytes: ?????????? } }],
});
I don`t want use anchor in my js code bc i need only the getProgramAccounts
Thanks
filters: [{ memcmp: { offset: 0, bytes: "FtoqZ3bt1he" } }] - it works. But I don`t know how I can get "FtoqZ3bt1he".
I stole it from SolanaPlayground (network devtools)
const name = 'MediaState';
const discriminator = Buffer.from(sha256.digest("account:" + name)).slice(
0,
8
);
const publisher = new web3.PublicKey(
"kek"
);
const connection = new web3.Connection(web3.clusterApiUrl("devnet"));
const accounts = await connection.getProgramAccounts(publisher, {
filters: [{ memcmp: { offset: 0, bytes: base58.encode(discriminator) } }],
});
I am implementing a smart contract with ink!
I have defined my own errors in the smart contract like the example below.
I don't know how to get the error information in my frontend app using polkadot.js when this error occurs.
Can anyone tell me who knows?
smart contract sample:
-- snip --
#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub enum OwnErrors {
/// The Token Does Not Exists.
OwnErrorIsOccured,
}
-- snip --
#[ink(message)]
pub fn own_error_test(&mut self, account_id:AccountId, token_type:u8) -> OwnResult<()> {
if self.value == false {
return Err(OwnErrors::OwnErrorIsOccured);
}
self.token_list_for_id.insert(&self.next_id, &TokenInfo{token_address:account_id,token_type:TokenType::GovernanceToken});
self.next_id = self.next_id + 1;
Ok(())
}
-- snip --
frontend sample:
-- snip --
const own_error_test = async () => {
const { web3FromSource } = await import("#polkadot/extension-dapp");
const contract = new ContractPromise(api, abi, contractAddress);
const performingAccount = accounts[0];
const injector = await web3FromSource(performingAccount.meta.source);
const flip = await contract.tx.ownErrorTest(
{ value: 0, gasLimit: gasLimit },
actingAddress,
0
);
if (injector !== undefined) {
flip.signAndSend(actingAddress, { signer: injector.signer }, (result) => {
if (result.status.isInBlock) {
setResult("in a block");
} else if (result.status.isFinalized) {
setResult("finalized");
}
console.log("###result: ",result);
});
}
};
-- snip --
I got the answer on Astar Network's Discord.
https://substrate.stackexchange.com/questions/4247/how-to-get-output-when-calling-a-contract-method-and-signing-it/4261#4261
pub enum OwnErrors {
/// The Token Does Not Exists.
OwnErrorIsOccured,
}
if ( output?.toHuman()?.Err == "OwnErrorIsOccured"){
alert("I can handle errors");
return;
}
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
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.
I have basically converted a Crowd Funding Campaign written in Native Rust (Solana CLI) into an Anchor Version.
So my tests are running fine except the donation test.
Please if some Solana super shadowy dev can help me figure this out, I shall be grateful. I have spent almost a week debugging, but unable to figure out. Obviously somewhere my logic or syntax is wrong, but I am unable to understand.
Honestly, I feel my logic is right, because I have read the code so many times. But surely, something is wrong......
lib.rs
use anchor_lang::solana_program::system_program;
use anchor_lang::solana_program::program::{invoke /* , invoke_signed */};
use anchor_lang::solana_program::system_instruction;
use anchor_lang::solana_program::program_error::ProgramError;
declare_id!("HwHzMftiyTEYy6w8K6E3AipuF3vgswS1d7c6T6hUeRxf");
#[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(ctx: Context<Initialize>, 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();
Ok(())
}
pub fn create_campaign
(
ctx : Context<CreateCampaign>,
name : String,
description : String,
image_link: String,
)
-> ProgramResult {
let writing_account = &mut ctx.accounts.writing_account;
let authority = &mut ctx.accounts.authority;
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(),
amount_donated : 0,
};
writing_account.count += 1;
writing_account.campaign_details.push(campaign_data);
Ok(())
}
pub fn withdraw(ctx : Context<Withdraw>, amount : u64) -> ProgramResult {
let writing_account = &mut ctx.accounts.writing_account;
let authority = &mut ctx.accounts.authority;
let withdraw_data = WithdrawRequest {
amount_withdrawn : amount,
admin : *authority.to_account_info().key,
};
writing_account.withdraw_request.push(withdraw_data);
**writing_account.to_account_info().try_borrow_mut_lamports()? -= amount;
**authority.to_account_info().try_borrow_mut_lamports()? += amount;
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;
donator_program_account.amount_donated = amount;
donator_program_account.bump = donator_program_account_bump;
let transfer_ix = system_instruction::transfer(
&authority.to_account_info().key(),
&donator_program_account.to_account_info().key(),
amount,
);
#[warn(unused_must_use)]
invoke(
&transfer_ix,
&[
authority.to_account_info(),
donator_program_account.to_account_info(),
],
)?;
let mut campaign_data = CampaignDetails::try_from_slice(*writing_account.to_account_info().try_borrow_mut_data()?)
.expect("Error deserializing data");
campaign_data.amount_donated += **donator_program_account.to_account_info().lamports.borrow();
**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;
*donator_program_account.to_account_info().try_borrow_mut_data()? = &mut [];
Ok(())
}
#[derive(Accounts)]
#[instruction(writing_account_bump : u8)]
pub struct Initialize<'info> {
#[account(init,
seeds = [b"please_____".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>,
#[account(address = system_program::ID)]
pub system_program : AccountInfo<'info>,
}
#[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)]
#[instruction(donator_program_account_bump : u8)]
pub struct Donate<'info> {
#[account(mut)]
pub writing_account : Account<'info, CampaignState>,
#[account(mut)]
pub authority : Signer<'info>,
#[account(init,
seeds = [b"donate____".as_ref(),
authority.key().as_ref()],
bump = donator_program_account_bump,
payer = authority,
space = 100)]
pub donator_program_account : Account<'info, DonatorProgramAccount>,
#[account(address = system_program::ID)]
pub system_program : AccountInfo<'info>,
}
#[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)]
pub struct CampaignDetails {
pub admin: Pubkey,
pub name: String,
pub description: String,
pub image_link: String,
pub amount_donated: u64,
}
#[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)]
pub struct WithdrawRequest {
pub amount_withdrawn : u64,
pub admin : Pubkey,
}
#[account]
pub struct CampaignState {
pub campaign_details : Vec<CampaignDetails>,
pub bump : u8,
pub count : u8,
pub authority: Pubkey,
pub withdraw_request : Vec<WithdrawRequest>,
}
#[account]
pub struct DonatorProgramAccount {
pub amount_donated : u64,
bump : u8,
}
#[error]
pub enum ErrorCode {
#[msg("Name cannot be more than 30 characters and Description cannot be more than 50 characters")]
NameOrDescriptionTooLong,
}
}
also my tests as below
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("HwHzMftiyTEYy6w8K6E3AipuF3vgswS1d7c6T6hUeRxf");
// const otherUser (i.e Donator)
const donator = Keypair.generate();
describe('Solanacrowdfundingproject', () => {
console.log("🚀 Starting tests...");
try {
it('gets initialized', async () => {
const { writingAccount, bump } = await getProgramDerivedCampaignWritingAccountAddress();
let tx = await program.rpc.initialize(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("Initialization transaction signature : ", tx);
//Asserts and console.logs
const account = await program.account.campaignState.fetch(writingAccount);
console.log("👀 Created A Writing Account : ", account);
assert.equal(account.authority.toBase58(), provider.wallet.publicKey.toBase58());
console.log("👀 Account's count is :", account.count);
});
} catch (error) {
console.log(error);
}
try {
it('Creates a campaign', async () => {
const { writingAccount } = 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",
{
accounts: {
writingAccount: writingAccount,
authority: provider.wallet.publicKey,
},
});
//Asserts and Console Logs
//Console.log the Transaction signature of the Initialization procedure.
console.log("Your CreateCampaign transaction signature", tx);
let account = await program.account.campaignState.fetch(writingAccount);
console.log("Writing Account after Campaign Creation :", account);
console.log("This Writing account's address is : ", writingAccount.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);
assert.equal(account.authority.toBase58(), provider.wallet.publicKey.toBase58());
});
} catch (error) {
console.log(error);
}
try {
it('Can Make a Donation', async () => {
const signature = await connection.requestAirdrop(donator.publicKey, 1000000000);
await connection.confirmTransaction(signature);
console.log("Airdrop confirmed :", await connection.getBalance(donator.publicKey));
const { writingAccount } = await getProgramDerivedCampaignWritingAccountAddress();
const { donatorProgramAccount, bump } = await getProgramDerivedDonatorProgramAccountAddress();
let balanceOfCampaignCreatorPreDonation = await connection.getBalance(writingAccount);
console.log("Balance of Campaign before Donation : ", balanceOfCampaignCreatorPreDonation);
let donateTx = await program.rpc.donate(new anchor.BN(1000000), new anchor.BN(bump),
{
accounts: {
writingAccount: writingAccount,
authority: donator.publicKey,
donatorProgramAccount: donatorProgramAccount,
systemProgram: SystemProgram.programId,
},
signers: [donator],
});
//Asserts and Console Logs
//Console.log the Transaction signature of the Donation procedure.
let account = await program.account.donatorProgramAccount.fetch(donatorProgramAccount);
console.log("👀 Created a New Donator Program Account : ", account);
console.log("👀 Your Donation transaction signature is : ", donateTx);
let balanceOfCampaignCreatorPostDonation = await connection.getBalance(writingAccount);
console.log("Balance of Campaign after Donation : ", balanceOfCampaignCreatorPostDonation);
});
} catch (error) {
console.log(error);
}
try {
it('Can Make a Withdrawal', async () => {
const { writingAccount } = await getProgramDerivedCampaignWritingAccountAddress();
let withdrawTx = await program.rpc.withdraw(new anchor.BN(50000),
{
accounts: {
writingAccount: writingAccount,
authority: provider.wallet.publicKey,
}
});
//Asserts and Console Logs
//Console.log the Transaction signature of the Withdrawal procedure.
console.log("Your Withdrawal transaction signature", withdrawTx);
let balanceOfCampaignCreator = await connection.getBalance(writingAccount);
console.log("Balance of Campaign after Withdrawal: ", balanceOfCampaignCreator);
});
} catch (error) {
console.log(error);
}
});
async function getProgramDerivedCampaignWritingAccountAddress() {
const [writingAccount, bump] = await PublicKey.findProgramAddress(
[Buffer.from('please_____'), 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('donate____'), donator.publicKey.toBuffer()],
programID
);
console.log(`Got ProgramDerivedDonatorProgramAccountAddress: bump: ${bump}, pubkey: ${donatorProgramAccount.toBase58()}`);
return { donatorProgramAccount, bump };
};
Can someone go through the code and help me?
Also, the line Code below (where I am getting the error):
https://github.com/Suveett/solana-crowd-funding-master-using-Anchor/blob/ad87fb2226c800fce1faabd355eb88b812441e0c/tests/solanacrowdfundingproject.js#L91
Try to add mut in your declaration:
#[account(init, mut,seeds = [b"please_____".as_ref(), authority.key().as_ref()],bump = writing_account_bump, payer = authority,space = 9000)]
I am trying to perform inner join on two vectors in rust but not sure how to achieve this.
In short, I am looking for employees with their department name.
If anybody is familiar with C# LINQ here, then I am trying to achieve something like below in rust.
Considering same model as specified below in model.rs
from e in employees
join d in department
on e.dept_id equals d.dept_id
select new
{
DeptName= d.dept_name,
Name = e.name
};
Here is my rust language code:
models.rs
use bigdecimal::BigDecimal;
use chrono::{NaiveDateTime, Utc};
pub struct Department {
pub dept_id: i32,
pub dept_name: Option<String>,
pub created_on: Option<NaiveDateTime>,
pub created_by: Option<String>,
pub modified_on: Option<NaiveDateTime>,
pub modified_by: Option<String>,
pub is_active: Option<bool>,
}
impl Department {
pub fn get_departments() -> Vec<Department> {
vec![
Department {
dept_id: 101,
dept_name: Option::from("Software".to_string()),
created_on: Option::from(Utc::now().naive_utc()),
created_by: Option::from("Jadon".to_string()),
modified_on: None,
modified_by: None,
is_active: Option::from(true),
},
Department {
dept_id: 102,
dept_name: Option::from("Hr".to_string()),
created_on: Option::from(Utc::now().naive_utc()),
created_by: Option::from("Jadon".to_string()),
modified_on: None,
modified_by: None,
is_active: Option::from(true),
},
Department {
dept_id: 103,
dept_name: Option::from("Hr".to_string()),
created_on: Option::from(Utc::now().naive_utc()),
created_by: Option::from("Jadon".to_string()),
modified_on: None,
modified_by: None,
is_active: Option::from(true),
},
]
}
}
pub struct Employee {
pub employee_id: i32,
pub name: Option<String>,
pub age: Option<i32>,
pub address: Option<String>,
pub email: Option<String>,
pub dept_id: i32,
pub salary: Option<BigDecimal>,
pub created_on: Option<NaiveDateTime>,
pub created_by: Option<String>,
pub modified_on: Option<NaiveDateTime>,
pub modified_by: Option<String>,
pub is_active: Option<bool>,
}
impl Employee {
pub fn get_employees() -> Vec<Employee> {
vec![
Employee {
employee_id: 1001,
name: Option::from("Marten Babel".to_string()),
age: Option::from(25),
address: Option::from("Netherland".to_string()),
email: Option::from("somemail#gmail.com".to_string()),
dept_id: 101,
salary: Option::from(BigDecimal::from(50000.00)),
created_on: Option::from(Utc::now().naive_utc()),
created_by: Option::from("Tom".to_string()),
modified_on: None,
modified_by: None,
is_active: Option::from(true),
},
Employee {
employee_id: 1002,
name: Option::from("Jack Sancho".to_string()),
age: Option::from(22),
address: Option::from("England".to_string()),
email: Option::from("jacksemail#gmail.com".to_string()),
dept_id: 102,
salary: Option::from(BigDecimal::from(80000.00)),
created_on: Option::from(Utc::now().naive_utc()),
created_by: Option::from("Tom".to_string()),
modified_on: None,
modified_by: None,
is_active: Option::from(true),
},
Employee {
employee_id: 1003,
name: Option::from("Phil Foden".to_string()),
age: Option::from(29),
address: Option::from("England".to_string()),
email: Option::from("philsemail#gmail.com".to_string()),
dept_id: 101,
salary: Option::from(BigDecimal::from(80000.00)),
created_on: Option::from(Utc::now().naive_utc()),
created_by: Option::from("Tom".to_string()),
modified_on: None,
modified_by: None,
is_active: Option::from(true),
},
]
}
}
main.rs
fn main() {
let department: Vec<Department> = Department::get_departments();
for dept in department {
println!(
"Dept Id: {} and Dept Name: {}",
dept.dept_id,
dept.dept_name.unwrap_or_default()
);
}
let employee: Vec<Employee> = Employee::get_employees();
for emp in employee {
println!(
"Name is: {} and age is : {}",
emp.name.unwrap_or_default(),
emp.age.unwrap_or_default()
)
}
}
Here "dept_id" will be acting as a foreign key. The code in main.rs is working fine. I am getting departments and employees but how can I perform join on these two vectors.
Note: As of now I am not using diesel orm. Only looking to perform join on these vectors.
Is it possible in rust ?
Thanks
If you just want to get pairs of &str for the names of the departments and the employees, you can use an iterator chain like so:
let department: Vec<Department> = Department::get_departments();
let employee: Vec<Employee> = Employee::get_employees();
let dept_employee_names = department.iter().flat_map(|d| {
let dept_id = d.dept_id;
let dept_name = &d.dept_name;
employee
.iter()
.filter(move |e| e.dept_id == dept_id)
.map(move |e| {
(
dept_name.as_deref().unwrap_or_default(),
e.name.as_deref().unwrap_or_default(),
)
})
});
for (dept, emp) in dept_employee_names {
println!("dept = {}, emp = {}", dept, emp);
}
Note that this has to search through all of the employees for each department, but that is really a limitation of using vectors. You can probably make this more efficient by changing the data structures.