데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      OpenLab 2기 - 누구나 하는 Smart Contract 개발 5회차 모임

      exitso5l 24.11.05
      237 1 0
      DEVOTEE 요약
      오픈랩 5차 모임에서는 Smart Contract 개발에 관한 스터디를 진행하며 "Token"이라는 주제를 깊이 있게 다루었습니다. 다양한 방법으로 토큰을 생성하고 전송 및 소각하는 과정을 공유하며, Solana 블록체인에서 SPL 토큰을 효과적으로 관리하고자 하는 방법들을 학습했습니다.
      DEVOTEE 추천 블로그

      안녕하세요!

      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


      How to verify accounts in a Solana program

      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에서 빠르고 효율적인 데이터 관리 가능

        • 직렬화 목적: 프로그램 상태를 저장하기 위한 데이터 포장 // 구조체 데이터를 직렬화 해서 데이터를 전달

        • 역직렬화 목적: 포장된 데이터를 원래 상태로 복원

      How to create a Token

      SPL 토큰(Solana Program Library Token)은 Solana 블록체인에서 생성되고 관리되는 토큰의 표준입니다.

      SPL 토큰은 Ethereum의 ERC-20 토큰과 유사한 개념으로, Solana 생태계에서 다양한 디지털 자산을 표현하고 거래하는 데 사용됩니다.

      다음은 SPL 토큰에 대한 주요 특징입니다:

      ECR 20과 공통점 및 차이점

      공통점

      • 토큰 표준: 두 표준 모두 특정 규칙에 따라 토큰을 발행하고 관리하는 방법을 정의.

      • 전송 기능: 사용자가 토큰을 다른 계정으로 전송할 수 있는 기능을 제공.

      • 민트 및 소각 기능: 새로운 토큰을 생성(민트)하거나 기존 토큰을 소멸(소각)할 수 있는 기능을 포함.

      • 잔액 확인: 특정 주소의 토큰 잔액을 조회할 수 있는 기능 제공.

      • 이벤트 로깅: 토큰 전송 및 민트/소각과 같은 주요 작업에 대해 이벤트를 기록하여 블록체인에서 확인 가능.

      차이점

      • 블록체인 플랫폼:

        • 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}`);
      })();

      How to create a Token

      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 Core Concepts

      SPL Token 3가지 개념 (Token Program, Mint Account, Token Account)

      • Token Program : 네트워크에서 토큰이 상호작용하기 위한 모든 명령과 로직을 포함

      • Mint Account : 특정 유형의 토큰을 나타내며, 총 공급량 및 민트 권한

        (새로운 토큰 단위를 생성할 수 있는 권한이 있는 주소)과 같은 토큰에 대한 전역 메타데이터를 저장합니다.

      • Token Account : 특정 주소가 소유한 특정 유형의 토큰(민트 계정)의 단위 수를 추적

      Token Program



      Mint Account



      Token Account



      계정들의 관계 (Account Relationship)


      How to create a Token account

      // 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 EMUe3nsCWaipWpmaDFypxjXDE6XEYWTSNNKe3tvFEbgx

      How to get a Token account

      import { 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);
      })();

      그럼 아래와 같이 결과가 나옵니다~


      How to get a Token account's balance

      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


      How to mint tokens


      How to transfer tokens



      Cookbook에서 쓴 코드인 transferChecked 가 에러가 떠서 아래 사진과 같이 transfer 코드로 수정해서 코드를 진행합니다~ 그럼 아래와 같이 결과를 얻을 수 있었어요!


      How to Burn tokens


      Solana blockchain on flutter



      이번 스터디는 블록체인에 대한 개념을 이해하는데 유용한 시간이었습니다.

      다음에는 dApp, pay 등 실제 앱을 만들어보는 시간을 가지면 더욱 풍성한 스터디가 될꺼 같습니다.


      P.S.

      저희 모임은 1,2차를 제외하고는 다 스터디 이후에 간단하게 맥주를 마시면서 서로를 알아가고 관계를 쌓아가는 시간을 가졌습니다~

      다음이 마지막 모임이라는게 아쉽네요 ㅠㅠ


      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      exitso5l 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기