[Solana - Native vs Anchor] #3. Account 생성하기
목차
[Solana - Native vs Anchor] #3. Account 생성하기 이 글은 Solana - Native vs Anchor 시리즈의 3번째 글입니다. 이전 포스팅인 [Solana - Native vs Anchor] Account 확인하기에서 Account 하나를 생성하기 위해 Client에서 System Program으로 곧바로 생성을 요청했습니다. 이번 글 "Account 생성하기"에서는 Client가 Server Program으로 Account 생성을 요청하고, Server Program에서 System Program의 create_account Instruction을 호출해 Account를 생성하는 과정을 살펴보겠습니다. 이런 방법은 Solana에서 CPI(Cross-Program Invocation)라고 불립니다. CPI는 하나의 Program이 다른 Program의 Instruction을 호출하는 것을 말합니다. 이러면 Client 입장에서 하나의 Instruction으로 여러 작업을 처리를 할 수 있게 됩니다. Account 생성하기에서는 우리 Server Program이 System Program을 호출하는 CPI의 한 예를 설명합니다. Client Program은 하나의 Instruction을 갖는 Transaction을 보냅니다. 이 Instruction이 갖는 keys에는 payer와 System Program과 함께 새롭게 생성할 Account인 newKey가 포함되어 있습니다. Server Program은 Client로부터 3개의 Account를 받습니다. newKey Account를 생성하기 위해 System Program을 호출할 때 이 3개의 Account 정보를 이용합니다. Solana에서 Account의 생성은 System Program이 담당하고, 일반적인 Account는 모두 System Program이 소유합니다. 따라서, Server Program은 새로운 Account를 생성하기 위해 invoke 함수를 통해 System Program의 Instruction을 호출합니다. Client Program은 이전 포스팅들에서 설명했던 내용과 크게 다르지 않습니다. Server Program으로 보낼 Instruction을 생성할 때 payer, newKeypair, SystemProgram 세 개의 pubkey를 포함합니다. 이렇게 만들어진 하나의 Instruction을 Transaction에 담아 보냅니다. 테스트 결과 성공적으로 Account가 생성된 것을 볼 수 있습니다. 우리가 만든 Account의 Pubkey는 AGxcvfNVekDSParpMZiPihjJyazs2vm8Y1ZB52gZ6RSS 입니다. 생성된 결과를 아래와 같이 Solana Explorer를 통해서도 확인할 수 있습니다. Anchor 프로그램은 역시 자동으로 생성된 ts 파일을 통해 편하게 Server Program과 통신할 수 있게 도와줍니다. 전체 목차:Account 생성하기
프로그램 구조

Native 프로그래밍
Server Program
entrypoint!(process_instruction);
pub fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
let account_iter = &mut accounts.iter();
let payer = next_account_info(account_iter)?;
let new_account = next_account_info(account_iter)?;
let system_program = next_account_info(account_iter)?;
msg!("Program 호출됨. System Account 만드는 중...");
msg!("새로운 Public Key : {}", &new_account.key.to_string());
invoke(
&system_instruction::create_account(
&payer.key,
&new_account.key,
1 * LAMPORTS_PER_SOL,
0,
&system_program::ID,
),
&[
payer.clone(), new_account.clone(), system_program.clone()
],
)?;
msg!("Account created successfully.");
Ok(())
}Client Program
describe("Create a system account", async () => {
const connection = new Connection(`http://127.0.0.1:8899`, 'confirmed');
const payer = createKeypairFromFile(require('os').homedir() + '/.config/solana/id.json');
const PROGRAM_ID: PublicKey = new PublicKey("6Kv8wFQtRT8XeQd4sR4jQQefwL1oJC6xE36r6U1oRJ7Z");
it("Create the account", async () => {
const newKeypair = Keypair.generate();
let ix = new TransactionInstruction({
keys: [
{pubkey: payer.publicKey, isSigner: true, isWritable: true},
{pubkey: newKeypair.publicKey, isSigner: true, isWritable: true},
{pubkey: SystemProgram.programId, isSigner: false, isWritable: false}
],
programId: PROGRAM_ID,
data: Buffer.alloc(0),
});
await sendAndConfirmTransaction(
connection,
new Transaction().add(ix),
[payer, newKeypair]
);
});
});Test



Anchor 프로그래밍
Server Program
declare_id!("6KULEpud8tcYLZxrw2KUb1APZTeLCLdKtp2fhvVKQfqQ");
const LAMPORTS_PER_SOL: u64 = 1000000000;
#[program]
pub mod create_account {
use super::*;
pub fn create_system_account(ctx: Context<CreateSystemAccount>) -> Result<()> {
msg!("Program이 호출되었습니다. System Account 생성중...");
msg!(" 새로운 Public Key : {}", &ctx.accounts.new_account.key().to_string());
system_program::create_account(
CpiContext::new(
ctx.accounts.system_program.to_account_info(),
system_program::CreateAccount {
from: ctx.accounts.payer.to_account_info(), // From Pubkey
to: ctx.accounts.new_account.to_account_info(), // To Pubkey
},
),
1 * LAMPORTS_PER_SOL, // Lamports (1 SOL)
0, // Space
&ctx.accounts.system_program.key(), // Owner
)?;
msg!("Account가 생성되었습니다.");
Ok(())
}
}
#[derive(Accounts)]
pub struct CreateSystemAccount<'info> {
#[account(mut)]
pub payer: Signer<'info>,
#[account(mut)]
pub new_account: Signer<'info>,
pub system_program: Program<'info, System>,
}Client Program
describe("create-account", () => {
// Configure the client to use the local cluster.
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const wallet = provider.wallet as anchor.Wallet;
const program = anchor.workspace.CreateAccount as anchor.Program<CreateAccount>;
it("Account 생성하기", async () => {
const newKeypair = anchor.web3.Keypair.generate();
await program.methods.createSystemAccount()
.accounts({
payer: wallet.publicKey,
newAccount: newKeypair.publicKey,
systemProgram: anchor.web3.SystemProgram.programId
})
.signers([wallet.payer, newKeypair])
.rpc();
});
});Test

References