23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
안녕하세요!
OpenLab 2 기에 누구나 하는 Smart Contract 개발에 참여중인 MiN입니다.
5차 모임은 11월 4일 삼화빌딩에서 진행을 하였고,
정태님, 정호님, 민석님 순으로 발표를 진행하였습니다!
이번 스터디 발표는 “Token”이라는 큰 카테고리를 구체적으로 다루는 시간이었습니다.
구체적으로는 Token을 만들기 위한 account 생성부터 만든 token을 태우는 것까지 다루었습니다.
이제 스터디가 이번을 포함해 2번 밖에 안남은만큼 남은 시간 열심히해야겠다는 생각이 들었고
혼자 공부하는것보다 다양한 관점에서 바라보는 것도
같은 내용을 듣더라도 구체적인 부분을 더욱 채우면서 완벽해지는 것도
물론 복습도 되고요! 이런게 스터디의 매력이 아닌가 싶습니다 ㅎㅎ
그럼 정태님의 발표로 시작하겠습니다~
How to verify accounts in a Solana program
How to create a token
How to get a Token mint
use borsh::{BorshDeserialize, BorshSerialize}; // Borsh 라이브러리를 가져와서 직렬화 및 역직렬화 기능을 사용합니다.
use solana_program::{
account_info::{next_account_info, AccountInfo}, // 계정 정보를 가져오는 모듈
clock::Clock, // 시스템 클락 정보를 가져오는 모듈
entrypoint, // 프로그램의 진입점을 정의하는 모듈
entrypoint::ProgramResult, // 프로그램의 결과를 정의하는 타입
msg, // 로그 메시지를 출력하는 매크로
program_error::ProgramError, // 프로그램 오류를 정의하는 모듈
pubkey::Pubkey, // 공개 키 타입
rent::Rent, // 임대료 관련 정보를 가져오는 모듈
system_program::ID as SYSTEM_PROGRAM_ID, // 시스템 프로그램의 ID를 가져옴
sysvar::Sysvar, // 시스템 변수 관련 모듈
};
// 프로그램의 진입점을 process_instruction 함수로 설정
entrypoint!(process_instruction);
// HelloState 구조체 정의: 프로그램의 상태를 나타냅니다.
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct HelloState {
is_initialized: bool, // 초기화 여부를 나타내는 필드
}
// process_instruction 함수: 프로그램이 호출될 때 실행되는 주요 함수
pub fn process_instruction(
program_id: &Pubkey, // 호출된 프로그램의 공개 키
accounts: &[AccountInfo], // 프로그램에서 사용할 계정 목록
_instruction_data: &[u8], // 추가 실행 데이터 (사용되지 않음)
) -> ProgramResult {
let accounts_iter = &mut accounts.iter(); // 계정 목록을 순회하기 위한 반복자
// Payer 계정을 가져옴 (서명자)
let payer_account = next_account_info(accounts_iter)?;
// Hello 상태 계정을 가져옴
let hello_state_account = next_account_info(accounts_iter)?;
// 시스템 프로그램 계정을 가져옴
let system_program = next_account_info(accounts_iter)?;
let rent = Rent::get()?; // 현재의 임대료 정보를 가져옴
// Payer 계정이 서명자인지 확인
if !payer_account.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
// Hello 상태 계정이 임대료 면제인지 확인
if !rent.is_exempt(hello_state_account.lamports(), 1) {
return Err(ProgramError::AccountNotRentExempt);
}
// Hello 상태 계정이 쓰기가 가능한지 확인
if !hello_state_account.is_writable {
return Err(ProgramError::InvalidAccountData);
}
// Hello 상태 계정의 소유자가 현재 프로그램인지 확인
if hello_state_account.owner.ne(&program_id) {
return Err(ProgramError::IllegalOwner);
}
// 시스템 프로그램이 예상한 ID와 일치하는지 확인
if system_program.key.ne(&SYSTEM_PROGRAM_ID) {
return Err(ProgramError::IncorrectProgramId);
}
// Hello 상태 계정에서 데이터를 읽어 HelloState 구조체로 역직렬화
let mut hello_state = HelloState::try_from_slice(&hello_state_account.data.borrow())?;
// 상태가 이미 초기화되었는지 확인
if hello_state.is_initialized {
return Err(ProgramError::AccountAlreadyInitialized);
}
// 상태를 초기화
hello_state.is_initialized = true;
// 초기화된 상태를 Hello 상태 계정에 직렬화하여 저장
hello_state.serialize(&mut &mut hello_state_account.data.borrow_mut()[..])?;
msg!("Account initialized :)"); // 계정 초기화 메시지 출력
Ok(()) // 정상적으로 종료
}서명자 검증: 프로그램에서 계정이 실제로 서명했는지 확인함으로써 트랜잭션의 권한을 보호함. is_signer로 서명 여부를 확인함.
계정 쓰기 권한 확인: 상태를 저장할 계정이 쓰기 가능한지 검증함으로써 프로그램이 데이터를 안전하게 저장할 수 있도록 보장함. is_writable로 확인함.
계정 소유자 검증: 프로그램의 상태를 저장할 계정이 현재 프로그램에 속해 있는지 확인함. 이를 통해 다른 프로그램이 계정을 조작하지 못하게 함. owner.ne(&program_id)로 확인함.
임대료 면제 여부 확인: 상태 계정이 충분한 자산을 보유해 임대료 면제를 받는지 검증함. 이는 Solana의 계정 시스템에서 필수적인 부분임. rent.is_exempt로 확인함.
초기화 상태 검증: 계정이 이미 초기화된 경우를 방지하기 위해, 상태가 초기화된 적이 있는지 확인함. is_initialized로 확인함.
시스템 프로그램 검증: 프로그램에서 시스템 프로그램이 올바르게 설정되었는지 검증함으로써 안정적인 프로그램 실행을 보장함. SYSTEM_PROGRAM_ID로 검증함.
계정 데이터 직렬화 및 역직렬화: 프로그램 상태를 저장하거나 불러오기 위해 Borsh 라이브러리를 사용해 데이터를 직렬화하고 역직렬화함. 이는 Solana에서 효율적인 데이터 관리를 가능하게 함.
직렬화와 역직렬화 개념: 데이터를 프로그램이 이해할 수 있는 형식으로 변환하거나, 다시 원래 형태로 복원하는 과정
Borsh 사용 이유: Solana에서 빠르고 효율적인 데이터 관리 가능
직렬화 목적: 프로그램 상태를 저장하기 위한 데이터 포장 // 구조체 데이터를 직렬화 해서 데이터를 전달
역직렬화 목적: 포장된 데이터를 원래 상태로 복원
SPL 토큰(Solana Program Library Token)은 Solana 블록체인에서 생성되고 관리되는 토큰의 표준입니다.
SPL 토큰은 Ethereum의 ERC-20 토큰과 유사한 개념으로, Solana 생태계에서 다양한 디지털 자산을 표현하고 거래하는 데 사용됩니다.
다음은 SPL 토큰에 대한 주요 특징입니다:
공통점
토큰 표준: 두 표준 모두 특정 규칙에 따라 토큰을 발행하고 관리하는 방법을 정의.
전송 기능: 사용자가 토큰을 다른 계정으로 전송할 수 있는 기능을 제공.
민트 및 소각 기능: 새로운 토큰을 생성(민트)하거나 기존 토큰을 소멸(소각)할 수 있는 기능을 포함.
잔액 확인: 특정 주소의 토큰 잔액을 조회할 수 있는 기능 제공.
이벤트 로깅: 토큰 전송 및 민트/소각과 같은 주요 작업에 대해 이벤트를 기록하여 블록체인에서 확인 가능.
차이점
블록체인 플랫폼:
ERC-20: Ethereum 블록체인에서 사용됨.
SPL: Solana 블록체인에서 사용됨.
거래 속도 및 수수료:
ERC-20: Ethereum의 블록 생성 속도와 수수료 변동성이 존재.
SPL: Solana는 높은 거래 속도와 낮은 수수료를 제공, 초당 수천 건의 거래 처리 가능.
기술적 구현:
ERC-20: Solidity로 작성되며, Ethereum Virtual Machine(EVM)에서 실행.
SPL: Rust나 C로 작성되며, Solana의 독자적인 실행 환경에서 운영.
거래 메커니즘:
ERC-20: 이더리움의 가스 메커니즘을 사용하여 거래 수수료 발생.
SPL: Solana의 거래 모델을 따르며, 사용자가 특정 수수료를 지불하지 않고도 빠른 거래 가능.
SPL 토큰은 Solana 블록체인에서 자산을 생성하고 관리하기 위한 강력하고 유연한 도구로, 많은 개발자와 기업들이 이를 사용하여 혁신적인 블록체인 솔루션을 구축하고 있습니다.
// Solana 웹3 라이브러리 및 SPL 토큰 관련 모듈 가져오기
import {
clusterApiUrl, // 클러스터의 URL을 가져오는 함수
Connection, // Solana 네트워크와의 연결을 위한 클래스
Keypair, // 키 쌍을 생성하고 관리하는 클래스
sendAndConfirmTransaction, // 트랜잭션을 보내고 확인하는 함수
SystemProgram, // 시스템 프로그램을 통해 계정을 생성하거나 관리하는 기능
Transaction, // 트랜잭션을 생성하고 조작하는 클래스
} from "@solana/web3.js";
import {
createInitializeMintInstruction, // 민트 초기화 지시어를 생성하는 함수
TOKEN_PROGRAM_ID, // SPL 토큰 프로그램의 ID
MINT_SIZE, // 민트 계정의 크기
getMinimumBalanceForRentExemptMint, // 임대료 면제 민트를 위한 최소 잔액을 얻는 함수
createMint, // 민트를 생성하는 함수
} from "@solana/spl-token";
import bs58 from "bs58"; // Base58 인코딩/디코딩을 위한 라이브러리
// 비동기 함수 정의
(async () => {
// Devnet 클러스터와 연결
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
// 최신 블록 해시 가져오기
const recentBlockhash = await connection.getLatestBlockhash();
// 수수료를 지불할 계정 생성 (비밀키를 Base58로 디코딩)
const feePayer = Keypair.fromSecretKey(
bs58.decode(
"588FU4PktJWfGfxtzpAAXywSNt74AvtroVzGfKkVN1LwRuvHwKGr851uH8czM5qm4iqLbs1kKoMKtMJG4ATR7Ld2",
),
);
// 민트 권한을 가진 계정 생성 (비밀키를 Base58로 디코딩)
const alice = Keypair.fromSecretKey(
bs58.decode(
"4NMwxzmYj2uvHuq8xoqhY8RXg63KSVJM1DXkpbmkUY7YQWuoyQgFnnzn6yo3CMnqZasnNPNuAT2TLwQsCaKkUddp",
),
);
// 1) 내장 함수를 사용하여 민트 생성
let mintPubkey = await createMint(
connection, // 연결 객체
feePayer, // 수수료 지불자
alice.publicKey, // 민트 권한
alice.publicKey, // 동결 권한 (null을 사용하여 비활성화 가능, 비활성화하면 다시 활성화할 수 없음)
8, // 소수점 자리수
);
// 생성된 Mint account의 퍼블릭키 출력
console.log(`mint: ${mintPubkey.toBase58()}`);
// 또는
// 2) 직접 구성하여 민트 생성
const mint = Keypair.generate(); // 새로운 키 쌍 생성
console.log(`mint: ${mint.publicKey.toBase58()}`); // 생성된 민트의 퍼블릭키 출력
// 트랜잭션 생성
const transaction = new Transaction().add(
// 민트 계정 생성
SystemProgram.createAccount({
fromPubkey: feePayer.publicKey, // 수수료 지불자의 퍼블릭키
newAccountPubkey: mint.publicKey, // 새로 생성할 민트 계정의 퍼블릭키
space: MINT_SIZE, // 민트 계정의 크기
lamports: await getMinimumBalanceForRentExemptMint(connection), // 임대료 면제 민트를 위한 최소 잔액
programId: TOKEN_PROGRAM_ID, // SPL 토큰 프로그램 ID
}),
// 민트 계정 초기화
createInitializeMintInstruction(
mint.publicKey, // 민트 계정 퍼블릭키
8, // 소수점 자리수
alice.publicKey, // 민트 권한
alice.publicKey, // 동결 권한
),
);
// 트랜잭션 전송 및 확인
const transactionSignature = await sendAndConfirmTransaction(
connection,
transaction, // 생성된 트랜잭션
[feePayer, mint], // 서명자
);
// 트랜잭션 해시 출력
console.log(`txhash: ${transactionSignature}`);
})();import { clusterApiUrl, Connection, PublicKey } from "@solana/web3.js"; // Solana 관련 모듈 임포트
import { getMint } from "@solana/spl-token"; // SPL 토큰 관련 기능 임포트
(async () => {
// Solana devnet에 연결
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
// 조회할 민트 계정의 공개 키 생성
const mintAccountPublicKey = new PublicKey(
"계정 공개 키 값 입력", // 예시 민트 계정의 공개 키
);
// 민트 계정 정보 가져오기
let mintAccount = await getMint(connection, mintAccountPublicKey);
// 가져온 민트 계정 정보 출력
console.log(mintAccount);
/*
{
address: PublicKey {
_bn: <BN: 7351e5e067cc7cfefef42e78915d3c513edbb8adeeab4d9092e814fe68c39fec>
},
mintAuthority: PublicKey {
_bn: <BN: df30e6ca0981c1a677eed6f7cb46b2aa442ca9b7a10a10e494badea4b9b6944f>
},
supply: 0n, // 현재 민트된 토큰의 공급량
decimals: 8, // 소수점 이하 자리 수
isInitialized: true, // 민트 계정이 초기화되었는지 여부
freezeAuthority: PublicKey {
_bn: <BN: df30e6ca0981c1a677eed6f7cb46b2aa442ca9b7a10a10e494badea4b9b6944f>
} // 동결 권한을 가진 공개 키
}
*/
})();이어서 정호님의 발표입니다~
How to create a Token account
How to get a Token account
How to get a Token account's balance
SPL Token 3가지 개념 (Token Program, Mint Account, Token Account)
Token Program : 네트워크에서 토큰이 상호작용하기 위한 모든 명령과 로직을 포함
Mint Account : 특정 유형의 토큰을 나타내며, 총 공급량 및 민트 권한
(새로운 토큰 단위를 생성할 수 있는 권한이 있는 주소)과 같은 토큰에 대한 전역 메타데이터를 저장합니다.
Token Account : 특정 주소가 소유한 특정 유형의 토큰(민트 계정)의 단위 수를 추적
Token Program
Mint Account
Token Account
계정들의 관계 (Account Relationship)
// import 생략… (2번 수행함, 전송 테스트 위해서 A, B 생성)
(async()=> {
// connection
const connection= new Connection(clusterApiUrl( "devnet" ), "confirmed" );
const feePayer= getKeypairFromEnvironment( "SECRET_KEY" );
const mintPubkey= new PublicKey( "9fEVbr7V1VtdorTNg68HWkjtXzDaujWgnLLA4nTgk1oT" ,);
const myKey= new PublicKey( "3BZy93Tr3wUYmLKwwGkMAKVRLgYMY4mi6reivNdcKuFj" ,);
// 2 ) composed by yourself
{
// calculate ATA
let ata= await getAssociatedTokenAddress(
mintPubkey,// mint
myKey,// Second account
);
console.log(`ATA: ${ata.toBase58()}`);
let transaction= new Transaction().add(
createAssociatedTokenAccountInstruction(
feePayer.publicKey,// payer
ata,// ata
myKey,// Second account
mintPubkey,// mint
),
);
const signature= await sendAndConfirmTransaction(
connection,
transaction,
[feePayer],// Signers
);
console.log(`txhash: ${await signature}`);
}
})();
// result CUykwgksaBKTtyuxfhg6b7njdvwqwYkxAuN51G6H7ica
// result EMUe3nsCWaipWpmaDFypxjXDE6XEYWTSNNKe3tvFEbgximport { clusterApiUrl, Connection, PublicKey } from "@solana/web3.js" ;
import { getAccount } from "@solana/spl-token" ;
(async()=> {
const connection= new Connection(clusterApiUrl( "devnet" ), "confirmed" );
const tokenAccountPubkey= new PublicKey(
"CUykwgksaBKTtyuxfhg6b7njdvwqwYkxAuN51G6H7ica" ,
);
let tokenAccount= await getAccount(connection, tokenAccountPubkey);
console.log(tokenAccount);
})();그럼 아래와 같이 결과가 나옵니다~
import { clusterApiUrl, Connection, PublicKey } from "@solana/web3.js" ;
(async()=> {
const connection= new Connection(clusterApiUrl( "devnet" ), "confirmed" );
const tokenAccount= new PublicKey(
"CUykwgksaBKTtyuxfhg6b7njdvwqwYkxAuN51G6H7ica" ,
);
let tokenAmount= await connection.getTokenAccountBalance(tokenAccount);
console.log(`amount: ${tokenAmount.value.amount}`);
console.log(`decimals: ${tokenAmount.value.decimals}`);
})();똑같이 실행을 하면 아래와 같이 결과를 볼 수 있어요~
마지막으로 민석님의 발표입니다~
How to mint tokens
How to transfer tokens
How to Burn tokens
Cookbook에서 쓴 코드인 transferChecked 가 에러가 떠서 아래 사진과 같이 transfer 코드로 수정해서 코드를 진행합니다~ 그럼 아래와 같이 결과를 얻을 수 있었어요!
이번 스터디는 블록체인에 대한 개념을 이해하는데 유용한 시간이었습니다.
다음에는 dApp, pay 등 실제 앱을 만들어보는 시간을 가지면 더욱 풍성한 스터디가 될꺼 같습니다.
P.S.
저희 모임은 1,2차를 제외하고는 다 스터디 이후에 간단하게 맥주를 마시면서 서로를 알아가고 관계를 쌓아가는 시간을 가졌습니다~
다음이 마지막 모임이라는게 아쉽네요 ㅠㅠ
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.