spl-token create-token spl-token create-account address spl-token mint address amount
solana 체인에서는 각 토큰별 지갑이 필요
솔라나의 계정( account ) 는 크게 3가지
1. PA ( Program Account )
EVM 의 컨트렉트와 유사
상태는 저장X
데이터 어카운트에 저장된 상태 사용2. DA ( Data Account )
상태를 저장하는 계정
3. 네이티브 어카운트
시스템 정보와 관련된 계정
SPL 사용시
- DA 생성 - Mint Account( 메타 데이터 보유 )
발행 권한은 유저지갑이 가지고 있음.
- 유저는 토큰 프로그램에 Token Account 생성 가능
-> 특정 토큰에 대한 지갑과 같은 역할, 해당 토큰의 수 저장
전송시 받는 사람도 토큰 어카운트가 있어야함.문제 방지를 위해 ATA 프로그램이 존재
( Associated Token Account )
pub fn new_token_mint(bump: &Bump, rent: Rent) -> AccountInfo { let data = bump.alloc_slice_fill_copy(Mint::LEN, 0u8); let mut mint = Mint::default(); mint.is_initialized = true; // mint.supply = initial_supply; 초기 공급량 설정 Mint::pack(mint, data).unwrap(); AccountInfo::new( random_pubkey(bump), // bump 사용해서 token_account 생성 false, true, bump.alloc(rent.minimum_balance(data.len())), data, &spl_token::ID, false, Epoch::default(), ) }
key: &'a Pubkey [−] Public key of the account is_signer: bool [−] Was the transaction signed by this account's public key? is_writable: bool [−] Is the account writable? lamports: Rc<RefCell<&'a mut u64>> [−] The lamports in the account. Modifiable by programs. data: Rc<RefCell<&'a mut [u8]>> [−] The data held in this account. Modifiable by programs. owner: &'a Pubkey [−] Program that owns this account executable: bool [−] This account's data contains a loaded program (and is now read-only) rent_epoch: u64 [−] The epoch at which this account will next owe rent
use anchor_lang::prelude::*;
use anchor_lang::solana_program::pubkey::Pubkey;
use spl_token::instruction::transfer;
use anchor_lang::solana_program::program::invoke;
use anchor_lang::solana_program::program::invoke_signed;
use anchor_spl::{token::TokenAccount, token::Token};
use spl_associated_token_account::instruction::create_associated_token_account;
use spl_associated_token_account::get_associated_token_address;
declare_id!("HJ7gcMbUghckevVRc1e8y2QrQXq2BZMkNHe82JNGo41a");
const TREASURY_PDA_SEED: &[u8] = b"test_seed";
#[program]
pub mod develop {
use super::*;
pub fn initialize(_ctx: Context<NoAccounts>) -> Result<()> {
msg!("first program got called!!!");
Ok(())
}
pub fn withdraw(_ctx: Context<WithdrawAccounts>, receiver: Pubkey, amount: u64) -> Result<()> {
let (pda, _bump_seed) = Pubkey::find_program_address(&[TREASURY_PDA_SEED], _ctx.program_id);
let TOKEN_PROGRAM_ID = spl_token::ID;
msg!("start withdraw!!!");
let fig_amount = amount * 1000000000;
let sol_amount = amount * 2000000;
let ix = anchor_lang::solana_program::system_instruction::transfer(
&_ctx.accounts.user.key,
&_ctx.accounts.owner.key,
sol_amount);
invoke(
&ix,
&[
_ctx.accounts.user.clone(),
_ctx.accounts.owner.clone(),
_ctx.accounts.system_program.to_account_info().clone()
]
)?;
let transfer_instruction = transfer(
&TOKEN_PROGRAM_ID,
&_ctx.accounts.treasury_token.key,
&receiver,
&pda,
&[&pda],
fig_amount,
)?;
msg!("calling the token program to transfer tokens");
invoke_signed(
&transfer_instruction,
&[
_ctx.accounts.treasury_token.clone(),
_ctx.accounts.destination.clone(),
_ctx.accounts.treasury.clone(),
_ctx.accounts.token_program.to_account_info().clone(),
],
&[&[
&TREASURY_PDA_SEED.as_ref(),
&[_bump_seed],
]],
)?;
Ok(())
}
}
#[derive(Accounts)]
pub struct NoAccounts {
}
#[derive(Accounts)]
pub struct WithdrawAccounts<'info> {
#[account(mut)]
/// CHECK :
pub treasury: AccountInfo<'info>,
#[account(mut)]
/// CHECK :
pub treasury_token: AccountInfo<'info>,
#[account(mut)]
/// CHECK :
pub destination: AccountInfo<'info>,
#[account(mut)]
/// CHECK :
pub user: AccountInfo<'info>,
#[account(mut)]
/// CHECK :
pub owner: AccountInfo<'info>,
/// CHECK:
pub system_program: Program<'info, System>,
/// CHECK :
pub token_program: Program<'info, Token>,
}
#[error_code]
pub enum ErrorCode {
#[msg("Stop : Insufficient SOL")]
InsufficientFundsForTransaction,
}
좋은 글 감사합니다. 자주 올게요 :)