[KO] 솔라나에서의 NFT 구현 1편: 솔라나 기본 개념 이해하기 — 컨트랙트와 스토리지의 구조

c0wjay
DSRV
Published in
24 min readJun 9, 2022

--

DSRV Dev Guild에서는 더 많은 개발자들과 Web3 인프라를 만들어가기 위해, 다양한 메인넷과 스마트 컨트랙트에 대한 가이드를 연재합니다.

Disclaimer: 이 글은 정보 전달을 위한 목적으로 작성되었으며, 특정 프로젝트에 대한 투자 권고, 법률적 자문 등 목적으로 하지 않습니다. 모든 투자의 책임은 개인에게 있으며, 이로 발생된 결과에 대해 어떤 부분에서도 DSRV는 책임을 지지 않습니다. 본문이 포괄하는 내용들은 특정 자산에 대한 투자를 추천하는 것이 아니며, 언제나 본문의 내용만을 통한 의사결정은 지양하시길 바랍니다.

[솔라나 NFT 시리즈]

  1. 솔라나 기본 개념 이해하기 — 컨트랙트와 스토리지의 구조
  2. 캔디머신을 활용한 NFT 민팅 과정 이해하기
  3. 캔디머신 코드 분석해보기

솔라나(Solana)의 등장

솔라나(Solana)는 2021년 여름 “DeFi Summer” 시점에 혜성처럼 등장해, 단 기간내에 크립토 업계의 많은 주목을 받았습니다. 비슷한 시기에 출시된 다른 레이어1 블록체인들처럼 솔라나는 확장성 문제로 대변되는 높은 수수료 문제 및 거래처리 속도 향상을 꾀하고자 등장하였습니다. 지난 “DeFi Summer” 이후, 이더리움에서의 디앱 사용량이 급증함에 따라 이더리움의 가스비도 급격하게 상승하였는데요, 솔라나는 이와 크게 대비되는 초당 최대 5만 건의 거래 처리 능력과 평균 거래 수수료가 약 $0.0001 정도만 사용된다는 장점으로 큰 주목을 받았습니다.

솔라나는 역사증명(Proof of History)이라는 방식을 도입하여 노드 간의 시간을 동기화하는 방식을 채택하고, 기존 PBFT (Practical Byzantine Fault Tolerance)방식을 응용한 Tower BFT를 활용하며, EVM과 구현체가 다른 Sealevel 이라는 별도의 런타임 엔진을 도입했습니다. 앞으로 Web3의 대중화가 이루어지며 디앱 사용량이 증가할 것이라 생각한다면, 솔라나와 같이 이더리움을 대체할 수 있는 다른 레이어1 블록체인을 관심 있게 지켜 보는 것도 좋지 않을까요? 이번 시리즈를 통해서는 디앱을 만드는 개발자 입장에서 솔라나가 어떻게 구현되어 있는지, NFT를 민팅하는 과정은 어떻게 구성되어 있는 지 살펴보겠습니다.

솔라나는 이더리움과 많은 것이 다르게 구현되어 있습니다. 가장 큰 차이는 컨트랙트가 상태를 저장하지 않는다는 점인데요. 솔라나에서 스마트 컨트랙트를 개발하기 위해서는 Accounts, Instructions, PDA와 CPI 에 대한 개념 이해가 필수적이며, 본 글을 통해 해당 개념을 이해할 수 있습니다. 참고로, 솔라나에서 컨트랙트(Contract)를 프로그램(Program)이라고 부릅니다.

1. 계정(Accounts)

솔라나의 계정(Accounts, 이하 어카운트)는 크게 3가지로 구분하고 있습니다.

Program Account는 Data Account에 저장된 State를 가져와 실행합니다.
[그림 1–1] Program Account는 Data Account에 저장된 State를 가져와 실행합니다.[1]

1.1. Program Account

솔라나는 이더리움의 컨트랙트와 다르게, 프로그램(컨트랙트)이 상태를 저장하지 않습니다.
[좌: 그림 1–2] 이더리움의 컨트랙트(Contract) ㅤ [우: 그림 1–3] 솔라나의 프로그램(Program), 출처: Banksea Finance Medium

Program Account는 솔라나에서 일반적으로 Program이라 불리는 계정으로, Accounts의 metadata에 executable이라 표시된 Accounts를 의미합니다. 이더리움에서 컨트랙트에 해당되는 개념이지만, Program Accounts에 상태를 저장하지는 않고, data accounts에 저장된 상태를 사용합니다. 위의 도표를 참고하시면 이해가 될 것입니다.

우리가 일반적으로 알고 있는 이더리움의 컨트랙트는 기본적으로 수정이 불가(immutable)하며, Upgradeable Pattern을 통해 컨트랙트를 작성해야 컨트랙트 수정이 가능합니다. 하지만 솔라나의 Program은 권장사항인 solana program deploy로 배포 시, 수정 가능한 패턴으로 배포됩니다. 단, 솔라나 Program이 final 로 표시하면 업그레이드 불가능(non-upgradeable)하도록 설정할 수 있습니다. 이는 Program이 immutable 함을 의미하며, upgrade는 불가합니다. 다만, 로더를 통해 완전히 새로운 프로그램을 다시 업로드할 수 있습니다.

💡 DSRV’s Tip: Loader란?솔라나 공식문서에서 Loader는 “A program with the ability to interpret the binary encoding of other on-chain programs.”[2] 라고 정의합니다.프로그램의 배포란, 쉽게 얘기하자면 Program Account의 데이터에 코드를 업로드하는 과정입니다.
클라이언트가 프로그램 데이터를 serialize하여, write instruction에 담아 로더에 보내면, 로더는 instruction에 담겨있는 데이터를 Program account에 쓰고, 해당 account를 executable로 마킹합니다.
즉, 로더란 프로그램의 배포와 실행을 수행하는 Native Program 입니다.
로더의 종류에는 BPF Loader 2, BPF Upgradeable Loader, Native Loader 등이 있습니다. [3][4][5]
- BPF Loader 2: BPF Program의 loading, finalizing, executing을 수행합니다. [6]
- BPF Upgradeable Loader: BPF Program의 deploying, upgrading, executing을 수행합니다. [7]
- Native Loader: Native program의 loading, executing을 수행합니다.
참고로, `solana deploy` 명령어를 사용하여 프로그램을 배포하면, BPF Loader 2를 사용하며, 프로그램은 수정 불가능합니다.
`solana program deploy` 명령어를 사용하여 프로그램을 배포하면, BPF Upgradeable Loader 를 사용하며, 프로그램은 수정 가능합니다.
따라서 Ownership의 관계는 아래와 같습니다. 여기서 Ownership이라 함은 쉽게 설명하자면 Account의 쓰기 권한을 의미합니다. 아래 관계에서 왼쪽 Account에 대한 쓰기 권한은 오른쪽 Account에 있습니다.- Data Account → custom program (ex. Candy Machine, Token Program)
- Upgradeable custom program (ex. Candy Machine) → BPF Upgradeable Loader
- Immutable custom program (ex. Token Program) → BPF Loader
- Native program [8] (ex. BPF Loader) → Native Loader
💡 DSRV’s Tip: Executable Account에 딥 다이브해보기

솔라나의 Contract 런타임 환경인 Sealevel에서 Executable Accounts는 수정 불가능(immutable)하게 데이터를 저장합니다.
다만 프로그램이 수정 가능(mutable)하게 설정되면, 저장된 실행 코드가 나중에 수정할 수 있어야 하는데요,
그 과정에 대해선 아래에 설명하겠습니다.

1) `solana deploy` 명령어로 배포한 경우 (immutable program accounts)
Sealevel의 executable account에 바로 실행 코드가 byte code 형태로 저장됩니다.
저장된 실행 코드 데이터는 immutable하기 때문에, Program Account를 업데이트 할 수 없습니다.

2) `solana program deploy` 명령어로 배포한 경우 (mutable program accounts)
Sealevel의 executable account에는 실행 코드가 아닌 프록시(proxy) 주소가 저장됩니다.

해당 프록시 주소는 mutable account를 가리키며, 해당 계정 (mutable account)에 실행 코드가 byte code 형태로 저장됩니다. Program Account를 업데이트하고자 하면, 위 mutable account에 저장된 실행 코드가 수정되지 때문에, 수정 가능합니다.

1.2. Data Account

데이터 계정(Data Account)는 상태를 저장하는 계정을 의미합니다. 이는 운영체제의 file과 유사한 개념으로, Validator(검증인 노드, 이하 밸리데이터)들의 Memory에 저장됩니다. 운영체제의 file에서 data의 lifetime은 해당 Data를 구동하는 Program의 Lifetime과 같은데, 솔라나에서 Data의 Lifetime은 Data Account에 저장된 Lamports 수에 의해 결정됩니다(참고: 1 SOL = 10⁹ lamports). 스토리지 비용을 따로 청구하지 않는 이더리움과 달리, 솔라나에서는 스토리지를 밸리데이터에게 수수료를 따로 지불하고 Rent 하는 개념입니다.[9]

Data Account는 크게 read-only | writable, initialized | deinitialized 로 구분할 수 있습니다.

  • Read-only Account는 프로그램에 의해 수정이 불가한 immutable account이며, read-only이기 때문에 병렬적으로 실행됩니다.
  • Writable Account는 프로그램에 의해 수정이 가능한 Mutable account입니다. 여러 프로그램이 하나의 Writable Account에 할당이 된다면, 동시성 문제(Race condition)가 생길 수 있으므로 Serialize되어 단일 프로그램에 할당됩니다. 이 과정을 런타임이 관여합니다.
  • Writable Account에 데이터를 수정하는 프로그램은 소유권(Ownership)이 있는 프로그램만 접근(Access)이 가능합니다. 즉, 프로그램의 ID와 계정의 owner가 일치해야 수정이 가능합니다. (참고 1, 참고 2)
  • Initialized는 결국 Data Account가 활성화되려면 rent 하기 위해 계정에 돈이 있어야 합니다. 이 과정을 Initialize라 하며, 반대로 계정에서 돈을 빼서 0원을 만드는 과정을 Deinitialize라 부르며 이에 해당하는 Deinitialized 상태가 있습니다.

1.3. Native Account

Native Account는 System, Stake, Vote, Clock 등과 같이 시스템 정보와 관련된 account를 말합니다. [1]

2. Transactions & Instructions

트랜잭션은 크게 Instruction을 담고 있는 메세지(message)와 Signature들로 구성됩니다. 대략적인 형태는 아래 도표를 참고하면 좋습니다.

트랜잭션은 서명과 메세지로 구성되며, 메세지는 다시 인스트럭션들의 집합으로, 인스트럭션들은 이 인스트럭션을 수행할 program Id와 어카운트 주소, 데이터로 구성됩니다.
[그림 1–4] 트랜잭션 도표, 출처 @c0wjay, DSRV

여기서 Instruction이란 프로그램이 실행하는 Logic의 최소 단위입니다. Transaction에는 Instruction, Signature, 계정 주소 등을 담고 있으며, Solana Runtime은 트랜잭션을 검증하고, 각 Instruction들을 순서대로, atomic하게 처리(processing)합니다. 즉, 하나의 Transaction이 복수의 Instruction을 담고 실행되기 때문에, 이 점에서 이더리움과의 차이가 있습니다. 각 Instruction들은 적절한 Program들에 배정되어 Program이 해당 Instruction들을 수행합니다.

Program 내부에서 Instruction이 처리되는 과정은 위 도표를 참고하면 좋습니다. Program에 입력된 Instruction은 Deserialize 과정을 거칩니다. 솔라나에서 계정 간의 메세지 통신은 byte stream으로 이루어집니다. 따라서 Program은 byte stream으로 입력받은 내용을 사람이 읽을 수 있는 형태로 deserialize하는 과정을 거치며, 반대로 Program으로부터 출력할 때는 다시 byte stream으로 변환하는 serialize 과정을 거칩니다.

해당 Instruction에 담긴 내용에 따라 프로그램 내부 함수들이 호출, 수행됩니다. 또한 Instruction에는 계정 주소와 Signature 등도 담겨져 있는데, Instruction 처리 과정에서 필요한 Data Account들을 불러와, 상태를 변경하게 됩니다.

프로그램은 인스트럭션을 받은 후, 내부에서 실행하기 위해 역직렬화 과정을 거치며, 다시 직렬화 과정을 거쳐 인스트럭션을 생성합니다.
[그림 1–6] Instruction의 serialization & deserialization 출처: 솔라나 쿡북
💡 DSRV’s Tip: Serde에 대해 알아보기

위 설명에 대한 코드 수준의 이해는 솔라나 쿡북을 참고하면 좋습니다. [9]
짧게 Serialization 과 Deserialization 에 대해 설명해보도록 하겠습니다.

전반적인 프로세스는 위 도표(그림 1-6)와 같은데, 클라이언트 ↔ 프로그램 또는 프로그램 ↔ 프로그램 간의 RPC 통신은 Serialized 된 byte stream으로 주고 받습니다. 프로그램은 받은 byte의 flag에 따라 Instruction의 종류를 분류하고, Payload를 Deserialize하는 과정을 거칩니다.
Borsh Deserialize 과정 살펴보기위와 같은 코드를 실행하면, 다음과 같은 결과가 출력됩니다. Primitive(255, 65535, 4294967295, "hello", "world", [1, 2, 3, 4, 5], {"cookbook": "recipe", "recipe": "ingredient"}) 이는 byte array인 prim을 Primitive struct로 deserialize하는 과정의 예시입니다.  어떻게 대응되는 지는 아래 도표를 참고해주세요.
Borsh Deserialization 모식도
[그림 1–7] Borsh Deserialization 예시, 출처 @c0wjay

3. Token Program

이더리움에서는 ERC20 규격 등의 스마트 컨트랙트를 이용하여 새로운 토큰(Token)을 발행하는 것이 가능합니다. 반면, 솔라나에서는 토큰을 발행하는 프로그램(스마트 컨트랙트)은 1개만 존재하며(SPL Token Program), 해당 프로그램에 create-token이라는 instruction을 보내어 새로운 토큰을 발행합니다.

Mint Account 관계도
[그림 1–8] Mint Account, 출처: Twitter, @pencilflip

새로운 토큰 발행을 위해, Token Program에 create-token instruction을 보내면 어떤 일이 일어날까요?Token Program은 Mint Account 또는 Token Mint 라 불리는 Data account를 생성합니다. 각 타입의 토큰은 모두 어떤 한 종류의 Mint Account 과 관련되어 있으며, Mint Account는 토큰에 대한 metadata를 저장하고 있습니다 해당 토큰의 Mint Authority(발행 권한)는 User Account가 가지고 있습니다. 단, owner는 프로그램의 쓰기 권한이므로 Mint Authority와는 다릅니다. 이는 Mint Authority를 가진 User Account만 해당 Token의 발행이 가능하다는 뜻입니다.

Token Account 관계도
[그림 1–9] Token Account, 출처: Twitter, @pencilflip

또한 User는 Token Program으로 하여금, Token Account를 생성할 수 있습니다. Token Account는 특정 토큰에 대한 특정 User의 지갑과 같은 역할을 하는 Data Account로, 해당 토큰의 갯수를 저장합니다.

[그림 1–10] 여러 유저들의 Token 구조, 출처: Twitter, @pencilflip

토큰의 전송은 Token Program에 transfer instruction을 보내면 가능합니다. 이 때, 받는 사람도 해당 토큰에 대한 Token Account를 가지고 있어야 합니다. (이것에 대한 설명은 추후에 다른 컨텐츠로 다루도록 하겠습니다.)

만약 받는 사람이 해당 토큰에 대한 Token Account를 가지고 있지 않다면, 문제가 생길 수 있습니다. 특히 받는 사람이 off-line일 경우엔, 받는 사람이 계정을 만들 때까지 보내는 사람이 기다리기가 어려울 수 있기 때문입니다.

이를 위해 솔라나에서는 Associated Token Account (ATA) Program이 있습니다. 이 프로그램은 받는 사람이 해당 토큰에 대한 Token Account가 없더라도, 보내는 사람이 받는 사람에 대한 Token Account를 미리 만들어서 그 곳으로 토큰을 전송할 수 있도록 토큰 계정을 미리 만들어주는 기능을 합니다. [10] 이는 해당 프로그램이 Mint Address와 User Address로부터 Associated Token Address를 해시 함수 등을 사용해서 결정론적으로 도출해내기 때문에 가능해집니다.

4. Program Derived Address & Cross-Program Invocation

용어 정리: PDA란?본 글에서 PDA는 Program-Derived Address를 칭하고, 해당 Program-Derived Address를 주소로 하는 계정을 PDA Account라 칭합니다.

프로그램은 Cross-Program Invocation(CPI)을 통해 다른 프로그램을 호출할 수 있습니다. [11] 솔라나 프로그램 코드에서는 invoke 함수를 흔히 보실 수 있는데요. 이 메소드를 활용하면 다른 프로그램의 함수를 실행할 수 있습니다. 또한 Invocation은 호출한 프로그램의 추가 Invocation을 만들 수 있지만, 호출 횟수(call depth)는 4회로 제한됩니다. 이를 통해 재진입 공격(Re-entrancy Attack)을 방지합니다.

💡 DSRV’s Tip: 재진입 공격(Re-entrancy Attack)에 대해 알아보기재진입 공격(Re-entrancy Attack)이란, 컨트랙트 A가 외부 컨트랙트 B를 호출하는 구조에서,
컨트랙트 A의 실행이 완료되기 전에, 컨트랙트 B에서 다시 컨트랙트 A를 재귀적으로 호출하여 컨트랙트 A의 금액을 모두 인출하는 공격입니다.
예를 들자면, 아래와 같은 로직으로 공격이 수행됩니다. ([참고 예제](<https://solidity-by-example.org/hacks/re-entrancy>))1. 컨트랙트 A 에 해커의 돈이 100원이 있고, 이를 돌려받고자 한다.
2. 돌려받는 주소를 해커가 작성한 컨트랙트 B로 한다.
3. 컨트랙트 B에는 fallback function(돈이 입금되면 실행되는 함수)이 있고, 이 함수는 다시 컨트랙트 A를 호출하여 돈을 돌려받는 과정을 재실행한다.
4. 아직 컨트랙트 A의 실행은 완료되지 않았으므로(즉 컨트랙트 A에는 해커의 돈이 100원 그대로 있다고 상태가 저장되어 있으므로), 다시 100원의 송금이 실행된다.
5. 이는 다시 재귀 호출을 만들어, 계속해서 컨트랙트 A에서 B로 100원씩 송금이 실행된다.

만약 PDA Account가 다른 Program의 함수를 실행할 때, 별도의 Signature 가 필요하다면 어떻게 처리해야 할까요? 이럴 때에는invoke_signed 함수를 사용할 수 있습니다.

이는 Program으로 하여금 PDA라 하는 공개키를 Signature 처럼 사용할 수 있도록 합니다. 다른 외부 유저에서는 사용이 불가합니다. 이를 활용하여 Instruction을 만들어서 자산을 전송하는 것과 같은 행동을 할 수 있도록 합니다.

PDA는 Program ID와 Seeds를 해시 함수(sha256)에 넣어 생성됩니다. 정확히는 생성하는 것이 아니라, 위 함수에 넣어 ed25519 타원 곡선 위에 있는 공개 키 값을 찾는 것입니다. 그 값은 Program이 존재하기 전부터 이미 해시 함수 위에 Input에 대응되는 값으로 있기 때문입니다. [12] 실제로 solana.js 등에 있는 create_program_addressfind_program_address 함수는 같은 기능을 합니다.

PDA에 대응되는 Data Account를 만들 수 있으며, 이 때 Account의 주소는 PDA입니다. 이 때 PDA Account는 두 가지 기능을 갖습니다.

  1. 프로그램의 상태를 저장한다.
  2. Cross-Program Invocation에 서명(sign)한다.

create_program_address로 생성된 Program 주소는 지갑의 공개 키와 구분할 수 없습니다. 따라서 Program은 런타임에게 프로그램 주소 생성에 사용된 Seed 구문을 제공하고, 런타임은 해당 Program ID와 제공받은 Seed 구문을 ed25519 알고리즘을 통해 검증합니다.

PDA를 통해 토큰 B에 대한 Account(Token Account)를 만든다면, 이 Account에 대한 소유권은 PDA가 갖고, PDA에 대한 소유권은 해당 프로그램이 갖습니다. 즉, PDA는 일종의 해당 프로그램만 사용 가능한 Account의 공개키이자 서명(Signature)인 것입니다.

💡 DSRV’s Tip: 왜 Program-Derived Address(PDA)가 필요하나요?

아래와 같은 중고거래 예시를 가정하여 설명해보겠습니다.

"밥이 앨리스한테 물건을 보내고, 돈을 받는다"


그렇다면 밥 입장에서는 물건을 보내기 전에, 앨리스가 돈을 주겠다는 확약을 받아야하고, 앨리스는 정상적인 물건인지 확인 전까지는 돈을 보내주기가 꺼려집니다.
이 때 3자에게 돈을 맡겨서 물건의 배송이 완료된 후에, 돈을 보내는 구조로 처리하는 것이 일반적입니다.

온라인 상의 거래도 항상 양쪽이 모두 온라인인 상황에서만 거래되는 것이 아니라, 한쪽이 오프라인이어도 거래가 진행되는 상황이 있을 수 있습니다. 이 때 제 3자가 돈을 보관해야하는데, 솔라나에선 프로그램이 그 역할을 담당합니다. 문제는 그 프로그램이 특정 유저에 의해 작동이 된다면, 매 거래시마다 유저가 서명해야 한다는 문제가 생기고, 또한 그 유저가 나쁜 마음을 먹게 된다면 악의적으로 거래가 이루어질 수 있습니다. 예를 들면, 위에서 앨리스에게 뒷돈을 받아 밥에게 송금을 취소하는 경우가 있을 것입니다.

그렇다고 프로그램이 프로그램 소유의 개인키를 갖고 있기에는, 오픈소스 코드라면 개인키가 공개되거나 탈취될 수 있다는 문제점을 갖고 있습니다. 이러한 문제인식에서 PDA라는 개념이 나오게 되었고, 개인키가 존재하지 않으면서도 “프로그램”만 사용할 수 있고, 이를 상호 검증할 수 있는 서명(Signature)의 개념이 생긴 것입니다.

5. Wallet

[그림 1–10] 여러 유저들의 Token 구조, 출처: Twitter, @pencilflip

솔라나에서 지갑(Wallet)은 Keypair들의 모음입니다. 위 그림 [1–10]에서의 User Account의 주소가 공개키가 되며, 해당 계정에는 Lamports를 저장합니다. Token Account는 특정 Token의 주소와 유저 지갑의 공개키를 입력하여 생성 가능하며, 지갑의 UI는 공개키에 대응되는 Token Account들의 잔고를 조회 및 합하여 총계를 보여줍니다.

6. NFT in Solana

Solana에서 NFT의 개념이란, Mint Account에 연결된 ATA에 토큰을 단 한 개만 발급한 후, 추후 발행을 금지시키는 형태로 구현합니다. [13]

6.1. NFT의 메타데이터

메타데이터의 처리는 메타플렉스(Metaplex)에 의해 구현된 token-metadata-program에 의해 작동됩니다. [14] 메타플렉스(Metaplex)는 스스로 Metadata Program을 Token Mint의 Decorator라 칭합니다. Metadata Program은 해당 Mint Account의 Key를 Seed에 포함시켜 만든 Metadata PDA account를 만듭니다.

온체인 상에 저장되는 Metadata는 하기와 같습니다.

여기서 URI는 오프체인(off-chain) JSON metadata를 가리키는 포인터입니다(예시). NFT에 대한 attributes 등 세부 특성에 대한 metadata는 보통 URI를 통한 오프체인(Arweave 등)에 저장됩니다.

6.2. NFT의 발행 프로세스

  1. Arweave에 이미지를 업로드합니다.
  2. 이미지 주소가 담긴 json 형식의 metadata를 다시 Arweave에 업로드합니다.
  3. 해당 Arweave URI가 포함된 NFT를 발행합니다.
  4. 발행 후 생성된 Mint Account 주소를 통해 NFT metadata를 조회 가능합니다.
NFT 어카운트들의 관계도
[그림 1–11] NFT account들의 구조, 출처: Twitter, @pencilflip

보다 상세한 내용은 솔라나 쿡북에서 확인하실 수 있습니다. [15] 하나의 NFT를 위해 솔라나에서 어떤 Account들이 구성되어 있는지 자세히 알아보고 싶다면, pencilflip의 트위터 글을 참고하면 좋습니다. (참고: Sigrid Jin님의 트위터 번역본)

글을 마무리하며

이번 시간에는 솔라나에서 Program AccountData Account 가 각각 어떻게 구현되어 있는지 알아봤으며, 트랜잭션의 구성과 Program이 어떻게 실행되는지 알아보았습니다. 그 과정에서 가장 중요한 개념인 PDA와 CPI에 대해 다루었으며, 마지막으로는 솔라나에서 NFT가 어떻게 구현되어 있는 지 다루어 보았습니다.

다음 시간에는 메타플렉스(Metaplex)의 캔디머신이라는 일종의 SaaS 툴을 이용하여 솔라나에서 NFT를 민팅하는 과정을 high level에서 다룹니다. 이 글이 솔라나에서 NFT 프로젝트를 시작하고자 하는 많은 개발자분들에게 도움이 되었기를 바라며, 다음 글로 찾아오도록 하겠습니다. 감사합니다.

--

--

c0wjay
DSRV

Rustacean interested in Programming Languages.