Having a Solana program mint tokens while a user pays the fee - rust

I'm trying to have a Solana Anchor program mint tokens to a user's token account (so the program or its PDA should be the authority for the mint), while the owner of the token account that's receiving the tokens should pay the fee.
This is what I have right now:
use anchor_lang::prelude::*;
use anchor_spl::token;
use anchor_spl::token::{Token, MintTo};
declare_id!("EcFTDXxknt3vRBi1pVZYN7SjZLcbHjJRAmCmjZ7Js3fd");
//token HKTPz1skkSNAC8TXJiYvPyYtMCQDHtAheCL4FrBArCDJ
//program EcFTDXxknt3vRBi1pVZYN7SjZLcbHjJRAmCmjZ7Js3fd
//account GHQMHrt4j8i6bBaVhpMCLP8uoFfWUrLZsQtWCWqSJKA6
#[program]
pub mod solana_ubi {
use super::*;
pub fn mint_token(ctx: Context<MintToken>,) -> Result<()> {
// Create the MintTo struct for our context
let cpi_accounts = MintTo {
mint: ctx.accounts.mint.to_account_info(),
to: ctx.accounts.token_account.to_account_info(),
authority: ctx.accounts.owner.to_account_info(),
};
//need to make pda. see cookbook: sign a transaction with a pda.
let cpi_program = ctx.accounts.token_program.to_account_info();
// Create the CpiContext we need for the request
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
// Execute anchor's helper function to mint tokens
token::mint_to(cpi_ctx, 10_000_000_000)?;
Ok(())
}
}
#[derive(Accounts)]
pub struct MintToken<'info> {
/// CHECK: This is the token that we want to mint
#[account()]
pub mint: UncheckedAccount<'info>,
pub token_program: Program<'info, Token>,
/// CHECK: This is the token account that we want to mint tokens to
#[account(mut)]
pub token_account: UncheckedAccount<'info>,
/// CHECK: ugh
#[account(signer)]
pub owner: AccountInfo<'info>,
}
this does mint the tokens, if I pass the mint's authority (any account holding SOL) as owner. Again for clarity: I want this program to be the mint authority for the token, but the user receiving the tokens to pay the fee.
how should I change the code to make it work as described above? Please provide a code answer.

Related

Init Token Account error - "Error processing Instruction 0: custom program error: 0x0"

In my program I'm trying to init PDA Token Accounts so that my program can store tokens of specified mints like this:
#[derive(Accounts)]
pub struct StartAuction<'info> {
#[account(init, seeds=[&state.positions_count.to_ne_bytes()], bump, payer = authority, space = 5000)]
pub position: Account<'info, Position>,
#[account(mut, seeds=[b"state"], bump)]
pub state: Account<'info, State>,
pub token: Account<'info, Mint>,
pub token2: Account<'info, Mint>,
#[account(init, payer = authority, token::mint = token, token::authority = position)]
pub vault: Account<'info, TokenAccount>,
// #[account(init, payer = authority, token::mint = token2, token::authority = position)]
// pub token2_vault: Account<'info, TokenAccount>,
#[account(mut)]
pub authority: Signer<'info>,
pub token_program: Program<'info, Token>,
pub rent: Sysvar<'info, Rent>,
pub system_program: Program<'info, System>
}
If the token2_vault account is commented like shown above everything is good. But when I uncomment it (to have two Token Accounts for different mints), I'm getting a confusing error: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x0.
Assuming that this is the NotRentExempt ("Lamport balance below rent-exempt threshold") SPL Error as specified here and given that the authority account definitely has enough lamports, it's completely unclear why I can init one account but not two, with such strange error.
Thanks in advance!
I solved it by adding seeds to the accounts definitions, like this:
#[derive(Accounts)]
pub struct StartAuction<'info> {
#[account(init, seeds=[b"vault1".as_ref()], payer = authority, token::mint = token, token::authority = position)]
pub vault: Account<'info, TokenAccount>,
#[account(init, seeds=[b"vault2".as_ref()], payer = authority, token::mint = token2, token::authority = position)]
pub token2_vault: Account<'info, TokenAccount>
}
I guess without defining seeds those two accounts had the same address which caused the error. Why this generates the 0x0 NotRentExempt error is still not clear.

Need help on building a function that creates a new edition nft from a master edition nft

[image link to my code]
https://i.stack.imgur.com/UWtpj.png
I am currently writing a Solana Metaplex NFT program in Rust/Anchor-Solana, specifically working on writing a logic to create a edition NFT from a master edition NFT.
While invoking mpl_token_metadata::instruction::mint_new_edition_from_master_edition_via_token instruction, I found out that it requires both metadata and metadata_mint field as arguments. Whereas, on the metaplex documentation, it seems the instruction only requires the Master record metadata account.
Question:
Which account key, or value, should I put in to each of those fields(metadata and metadata_mint) and why?
Code:
pub fn create_new_edition_nft(
ctx: Context<CreateNewEdition>,
edition: u64,
) -> Result<()> {
let edition_infos = vec![
ctx.accounts.token_program.to_account_info(),
ctx.accounts.new_metadata.to_account_info(),
ctx.accounts.new_edition.to_account_info(),
ctx.accounts.master_edition.to_account_info(),
ctx.accounts.new_mint.to_account_info(),
ctx.accounts.new_mint_authority.to_account_info(),
ctx.accounts.payer.to_account_info(),
ctx.accounts.token_account_owner.to_account_info(),
ctx.accounts.token_account.to_account_info(),
ctx.accounts.new_metadata_update_authority.to_account_info(),
ctx.accounts.metadata.to_account_info(),
ctx.accounts.system_program.to_account_info(),
ctx.accounts.rent.to_account_info(),
];
msg!("Edition Account Infos Assigned");
invoke(&mint_new_edition_from_master_edition_via_token(
ctx.accounts.token_program.key(),ctx.accounts.new_metadata.key(),ctx.accounts.new_edition.key(), ctx.accounts.master_edition.key(), ctx.accounts.new_mint.key(),ctx.accounts.new_mint_authority.key(), ctx.accounts.payer.key(), ctx.accounts.token_account_owner.key(), ctx.accounts.token_account.key(), ctx.accounts.new_metadata_update_authority.key(), ctx.accounts.metadata.key(), ctx.accounts.metadata.key(), edition
), edition_infos.as_slice())?;
msg!("A New Edition Nft Minted !!!");
Ok(())
}
#[derive(Accounts)]
pub struct CreateNewEdition<'info> {
/// CHECK: This is not dangerous because we don't read or write from this account
#[account(mut)]
pub new_metadata: UncheckedAccount<'info>,
/// CHECK: This is not dangerous because we don't read or write from this account
#[account(mut)]
pub new_edition: UncheckedAccount<'info>,
/// CHECK: This is not dangerous because we don't read or write from this account
#[account(mut)]
pub master_edition: UncheckedAccount<'info>,
/// CHECK: This is not dangerous because we don't read or write from this account
#[account(mut)]
pub new_mint: UncheckedAccount<'info>,
/// CHECK: This is not dangerous because we don't read or write from this account
#[account(mut)]
pub edition_mark_pda: UncheckedAccount<'info>,
#[account(mut)]
pub new_mint_authority: Signer<'info>,
#[account(mut)]
pub payer: AccountInfo<'info>,
// /// CHECK: This is not dangerous because we don't read or write from this account
#[account(mut)]
pub token_account_owner: UncheckedAccount<'info>,
// /// CHECK: This is not dangerous because we don't read or write from this account
#[account(mut)]
pub token_account: UncheckedAccount<'info>,
/// CHECK: This is not dangerous because we don't read or write from this account
#[account(mut)]
pub new_metadata_update_authority: UncheckedAccount<'info>,
/// CHECK: This is not dangerous because we don't read or write from this account
#[account(mut)]
pub metadata: UncheckedAccount<'info>,
// #[account(mut)]
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,
/// CHECK: This is not dangerous because we don't read or write from this account
pub rent: AccountInfo<'info>,
}
Documentation Reference:
Documentation for printing a new edition from a master edition:
https://docs.metaplex.com/programs/token-metadata/instructions#print-a-new-edition-from-a-master-edition
The documentation on metaplex you are talking about is the documentation of the solana instructions but not the rust api. They are different. If you take a look at the rust code, you will see the differences.
The accounts described in the documentation match the accounts provided by the code:
let accounts = vec![
AccountMeta::new(new_metadata, false),
AccountMeta::new(new_edition, false),
AccountMeta::new(master_edition, false),
AccountMeta::new(new_mint, false),
AccountMeta::new(edition_mark_pda, false),
AccountMeta::new_readonly(new_mint_authority, true),
AccountMeta::new(payer, true),
AccountMeta::new_readonly(token_account_owner, true),
AccountMeta::new_readonly(token_account, false),
AccountMeta::new_readonly(new_metadata_update_authority, false),
AccountMeta::new_readonly(metadata, false),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
The variable metadata_mint you are talking about is used to calculate the address of the edition_mark_pda address on chain:
let (edition_mark_pda, _) = Pubkey::find_program_address(
&[
PREFIX.as_bytes(),
program_id.as_ref(),
metadata_mint.as_ref(),
EDITION.as_bytes(),
as_string.as_bytes(),
],
&program_id,
);
When you compare this with the documentation, you will see what should be provided:
Edition pda to mark creation - will be checked for pre-existence. (pda
of ['metadata', program id, master metadata mint id, 'edition',
edition_number]) where edition_number is NOT the edition number you
pass in args but actually edition_number =
floor(edition/EDITION_MARKER_BIT_SIZE).
metadata := Master record metadata account
metadata_mint := master metadata mint id

How can I transfer a token from a contract to a wallet from within the contract

So I am trying to transfer a FT, saved as a file ft.rs, which is generated in the standard way, like so:
impl Contract {
/// Initializes the contract with the given total supply owned by the given `owner_id` with
/// default metadata (for example purposes only).
#[init]
pub fn new_default_meta(owner_id: AccountId, total_supply: U128) -> Self {
Self::new(
owner_id,
total_supply,
FungibleTokenMetadata {
spec: FT_METADATA_SPEC.to_string(),
name: "Example NEAR fungible token".to_string(),
symbol: "EXAMPLE".to_string(),
icon: None,
reference: None,
reference_hash: None,
decimals: 24,
},
)
}
/// Initializes the contract with the given total supply owned by the given `owner_id` with
/// the given fungible token metadata.
#[init]
pub fn new(
owner_id: AccountId,
total_supply: U128,
metadata: FungibleTokenMetadata,
) -> Self {
assert!(!env::state_exists(), "Already initialized");
metadata.assert_valid();
let mut this = Self {
token: FungibleToken::new(b"a".to_vec()),
metadata: LazyOption::new(b"m".to_vec(), Some(&metadata)),
};
this.token.internal_register_account(&owner_id);
this.token.internal_deposit(&owner_id, total_supply.into());
near_contract_standards::fungible_token::events::FtMint {
owner_id: &owner_id,
amount: &total_supply,
memo: Some("Initial tokens supply is minted"),
}
.emit();
this
}
}
And I want to make another contract which can do two things, send the ft, and receive an ft:
pub use crate::ft::*;
mod ft;
impl Contract {
#[payable]
fn receive_ft(&self) -> bool {
// user sends ft and this contract registers and stores it
}
#[payable]
fn send_ft(&self, receiver : AccountId, amount : U128) -> bool {
// user sends ft to external address
}
}
Anyone here to point me in the right direction?
your going to want to import the FungibleTokenReceiver from the near-contract-standards crate and then implement it in it's own impl block in something what would look like this:
use near_contract_standards::fungible_token::receiver::FungibleTokenReceiver;
use near_sdk::{near_bindgen, PromiseOrValue};
use near_sdk::json_types::{ValidAccountId, U128};
#[near_bindgen]
impl FungibleTokenReceiver for Contract {
pub fn ft_on_transfer(&mut self, sender_id: ValidAccountId, amount: U128, msg: String) -> PromiseOrValue<U128> {
// Your logic goes here make sure you return a promise that would
// be the value of the number of tokens you want to send back to
// the account [if any]
PromiseOrValue::Value(U128::from(0))
}
}
As for the sending out of tokens, I don't know the exact implementation, but you would have to implement a cross contract call on the fungible_token contract to transfer the tokens from the contract to the contract caller
Hope this helps :)

Anchor: Solana Account Initialization Issue

I'm using serum anchor framework for solana. I'm tring to initialize new account with #[account] attribute macro.
Each time i run anchor build i get the below error.
Error("the payer specified for an init constraint must be mutable.")
thread 'main' panicked at 'Code not parseable: Error("the payer specified for an init constraint must be mutable.")', lang/syn/src/idl/file.rs:360:58 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Following is the code snippet, where I'm attempting the account initialization
#[derive(Accounts)]
#[instruction(pool_nonce: u8, vault_nonce: u8)]
pub struct InitializePool<'info> {
/// CHECK: This is not dangerous because we don't read or write from this account
authority: UncheckedAccount<'info>,
#[account(
mut,
// constraint = lp_token_pool_vault.mint == LP_TOKEN_MINT_PUBKEY.parse::<Pubkey>().unwrap(),
constraint = lp_token_pool_vault.owner == pool_signer.key(),
)]
lp_token_pool_vault: Box<Account<'info, TokenAccount>>,
#[account(
mut,
// constraint = lp_token_depositor.mint == LP_TOKEN_MINT_PUBKEY.parse::<Pubkey>().unwrap()
)]
lp_token_depositor: Box<Account<'info, TokenAccount>>,
lp_token_deposit_authority: Signer<'info>,
reward_mint: Box<Account<'info, Mint>>,
#[account(
constraint = reward_vault.mint == reward_mint.key(),
constraint = reward_vault.owner == pool_signer.key(),
constraint = reward_vault.close_authority == COption::None,
)]
reward_vault: Box<Account<'info, TokenAccount>>,
#[account(
seeds = [
pool.to_account_info().key.as_ref()
],
bump,
)]
/// CHECK: This is not dangerous because we don't read or write from this account
pool_signer: UncheckedAccount<'info>,
#[account(
zero,
)]
pool: Box<Account<'info, Pool>>,
#[account(
init,
payer = owner,
seeds = [
owner.key.as_ref(),
pool.to_account_info().key.as_ref()
],
bump,
space = 10240,
)]
vault: Box<Account<'info, Vault>>,
owner: Signer<'info>,
token_program: Program<'info, Token>,
system_program: Program<'info, System>,
}
While initializing new account you need to pay fees and rent and by specifying payer in
#[account(
init,
payer = owner,
seeds = [
owner.key.as_ref(),
pool.to_account_info().key.as_ref()
],
bump,
space = 10240,
)]
vault: Box<Account<'info, Vault>>,
In order to modify payer account it has to be mutable.
We specify it as
#[account(mut)] // <== add this missing macro
owner: Signer<'info>,
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = owner, space = 9000)]
pub base_account: Account<'info, BaseAccount>,
#[account(mut)]
pub owner: Signer<'info>,
pub system_program: Program<'info, System>,
}
you should set the payer one of the Signers in account like above

Rust ink, Unable to use imported module due an "unresolved import" error

I'm trying to use my custom ERC20 contract that is stored in my local storage in the SWAP project that I'm working on.
the error I'm getting while trying to use the erc20 import inside the SWAP contract:
unresolved import `erc20::Erc20`
no `Erc20` in the root
help: a similar name exists in the module: `erc20`rustc(E0432)
swap.rs(10, 9): no `Erc20` in the root
Here is how I import the erc20 module in SWAP.rs:
#![cfg_attr(not(feature = "std"), no_std)]
use ink_lang as ink;
#[ink::contract]
mod swapv1 {
#[cfg(not(feature = "ink-as-dependency"))]
use erc20::Erc20; <-----
use ink_env::call::FromAccountId;
use ink_prelude::string::String;
// snip....
This is how I import the ERC20 module inside SWAP.rs .toml file:
erc20 = { path = "../erc20/", version="3.0.0-rc4", default-features = false, features = ["ink-as-dependency"] }
I've also added the following configuration to the ERC20 .toml file:
crate-type = [
"rlib",
]
Here is the content of the ERC20 module file:
#![cfg_attr(not(feature = "std"), no_std)]
pub use self::erc20::Erc20;
use ink_lang as ink;
#[ink::contract]
pub mod erc20 {
#[cfg(not(feature = "ink-as-dependency"))]
use ink_prelude::string::String;
use ink_storage::traits::SpreadAllocate;
#[ink(storage)]
#[derive(SpreadAllocate)]
pub struct Erc20 {
/// Total token supply.
total_supply: Balance,
/// Mapping from owner to number of owned token.
balances: ink_storage::Mapping<AccountId, Balance>,
/// Mapping of the token amount which an account is allowed to withdraw
/// from another account.
allowances: ink_storage::Mapping<(AccountId, AccountId), Balance>,
/// Token name
name: String,
/// Token symbol (ticker)
symbol:String,
}
/// Event emitted when a token transfer occurs.
#[ink(event)]
pub struct Transfer {
#[ink(topic)]
from: Option<AccountId>,
#[ink(topic)]
to: Option<AccountId>,
value: Balance,
}
/// Event emitted when an approval occurs that `spender` is allowed to withdraw
/// up to the amount of `value` tokens from `owner`.
#[ink(event)]
pub struct Approval {
#[ink(topic)]
owner: AccountId,
#[ink(topic)]
spender: AccountId,
value: Balance,
}
/// The ERC-20 error types.
#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub enum Error {
/// Returned if not enough balance to fulfill a request is available.
InsufficientBalance,
/// Returned if not enough allowance to fulfill a request is available.
InsufficientAllowance,
}
/// The ERC-20 result type.
pub type Result<T> = core::result::Result<T, Error>;
impl Erc20 {
/// Creates a new ERC-20 contract with the specified initial supply.
#[ink(constructor)]
pub fn new(name: String, symbol: String, initial_supply: Balance) -> Self {
let caller = Self::env().caller();
let me = ink_lang::utils::initialize_contract(|contract: &mut Self| {
contract.balances.insert(caller, &initial_supply);
contract.allowances.insert((caller,caller),&initial_supply);
contract.symbol = symbol;
contract.name = name;
});
Self::env().emit_event(Transfer {
from: None,
to: Some(caller),
value: initial_supply,
});
me
}
/// Returns the total token supply.
#[ink(message)]
pub fn total_supply(&self) -> Balance {
self.total_supply
}
/// Returns the account balance for the specified `owner`.
///
/// Returns `0` if the account is non-existent.
#[ink(message)]
pub fn balance_of(&self, owner: AccountId) -> Balance {
self.balances.get(&owner).unwrap_or(0)
}
/// Returns the amount which `spender` is still allowed to withdraw from `owner`.
///
/// Returns `0` if no allowance has been set `0`.
#[ink(message)]
pub fn allowance(&self, owner: AccountId, spender: AccountId) -> Balance {
self.allowances.get(&(owner, spender)).unwrap_or(0)
}
/// Transfers `value` amount of tokens from the caller's account to account `to`.
///
/// On success, a `Transfer` event is emitted.
///
/// # Errors
///
/// Returns `InsufficientBalance` error if there are not enough tokens on
/// the caller's account balance.
#[ink(message)]
pub fn transfer(&mut self, to: AccountId, value: Balance) -> Result<()> {
let from = self.env().caller();
self.transfer_from_to(from, to, value)
}
/// Allows `spender` to withdraw from the caller's account multiple times, up to
/// the `value` amount.
///
/// If this function is called again it overwrites the current allowance with `value`.
///
/// An `Approval` event is emitted.
#[ink(message)]
pub fn approve(&mut self, spender: AccountId, value: Balance) -> Result<()> {
let owner = self.env().caller();
self.allowances.insert((owner, spender), &value);
self.env().emit_event(Approval {
owner,
spender,
value,
});
Ok(())
}
/// Transfers `value` tokens on the behalf of `from` to the account `to`.
///
/// This can be used to allow a contract to transfer tokens on one's behalf and/or
/// to charge fees in sub-currencies, for example.
///
/// On success, a `Transfer` event is emitted.
///
/// # Errors
///
/// Returns `InsufficientAllowance` error if there are not enough tokens allowed
/// for the caller to withdraw from `from`.
///
/// Returns `InsufficientBalance` error if there are not enough tokens on
/// the the account balance of `from`.
#[ink(message)]
pub fn transfer_from(
&mut self,
from: AccountId,
to: AccountId,
value: Balance,
) -> Result<()> {
let caller = self.env().caller();
let allowance = self.allowance(from, caller);
if allowance < value {
return Err(Error::InsufficientAllowance)
}
self.transfer_from_to(from, to, value)?;
self.allowances.insert((from, caller), &(allowance - value));
Ok(())
}
/// Transfers `value` amount of tokens from the caller's account to account `to`.
///
/// On success, a `Transfer` event is emitted.
///
/// # Errors
///
/// Returns `InsufficientBalance` error if there are not enough tokens on
/// the caller's account balance.
fn transfer_from_to(
&mut self,
from: AccountId,
to: AccountId,
value: Balance,
) -> Result<()> {
let from_balance = self.balance_of(from);
if from_balance < value {
return Err(Error::InsufficientBalance)
}
self.balances.insert(from, &(from_balance - value));
let to_balance = self.balance_of(to);
self.balances.insert(to, &(to_balance + value));
self.env().emit_event(Transfer {
from: Some(from),
to: Some(to),
value,
});
Ok(())
}
}
/// The IERC20Metadata (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/IERC20Metadata.sol)
impl Erc20 {
/// Returns the token name.
#[ink(message)]
pub fn name(&self) -> String {
self.name.clone()
}
/// Returns the token name.
#[ink(message)]
pub fn symbol(&self) -> String {
self.symbol.clone()
}
/// Returns the decimal places of the token.
#[ink(message)]
pub fn decimals(&self) -> u8 {
18
}
}
}
Change
use erc20::Erc20;
to
use erc20::Erc20Ref;
Put this at the top of the ERC20 contract:
pub use self::erc20::{Erc20, Erc20Ref};
You can see an example of this in the subber contract where it specifies itself as SubberRef and is then called from the main delegator contract

Resources