원저자: @ Web3 Mario (https://x.com/web3_mario)
TON 기술 도입에 대한 이전 기사에 이어, 이 기간 동안 공식 TON 개발 문서를 깊이 있게 연구했는데, 현재 문서 내용은 내부 개발 문서에 더 가까운 것 같습니다. 별로 친숙하지 않은 내용이라, 저만의 학습 궤적을 바탕으로 TON Chain 프로젝트 개발에 대한 일련의 기사를 정리해 보았습니다. 모든 사람이 TON DApp 개발을 빨리 시작하는 데 도움이 되기를 바랍니다. . 혹시 글에 틀린 부분이 있으면 바로잡아주시고 함께 배워가셔도 좋습니다.
EVM에서 NFT를 개발하는 것과 TON Chain에서 NFT를 개발하는 것의 차이점은 무엇입니까?
FT 또는 NFT 발행은 일반적으로 DApp 개발자에게 가장 기본적인 요구 사항입니다. 그래서 저는 이것을 학습의 시작점으로도 사용합니다. 먼저 EVM 기술 스택에서 NFT를 개발하는 것과 TON Chain의 다음과 같은 차이점을 이해하겠습니다. EVM 기반 NFT는 일반적으로 ERC-721 표준을 상속하도록 선택합니다. 소위 NFT는 분할할 수 없는 유형의 암호화된 자산을 말하며 각 자산은 고유합니다. 즉, 특정 독점 특성을 가지고 있습니다. ERC-721은 이러한 유형의 자산에 대한 일반적인 개발 패러다임입니다. 일반적인 ERC 721 계약이 어떤 기능을 구현해야 하는지, 어떤 정보가 기록되는지 살펴보겠습니다. 아래 그림은 ERC 721 인터페이스입니다. FT와 달리 이체 인터페이스에 입력해야 하는 것은 금액이 아닌 이체할 tokenId임을 알 수 있다. 이 tokenId는 NFT 자산의 고유성을 나타내는 가장 기본적인 표현이기도 합니다. 물론 더 많은 속성을 전달하기 위해 일반적으로 각 tokenId에 대해 메타데이터가 기록됩니다. 이 메타데이터는 NFT의 다른 확장 가능한 데이터를 저장하는 외부 링크입니다. PFP 이미지에 대한 링크, 특정 속성의 이름 등
Solidity에 익숙하거나 객체지향에 익숙한 개발자라면 이러한 스마트 계약을 쉽게 구현할 수 있습니다. 일부 주요 매핑 관계 등 계약에 필요한 데이터 유형을 정의하고 필요에 따라 해당 항목을 개발하기만 하면 됩니다. 이러한 데이터의 수정 로직을 통해 NFT를 실현할 수 있습니다.
그러나 이는 TON 체인에서는 동일하지 않습니다. 차이점에는 두 가지 핵심 이유가 있습니다.
TON의 데이터 저장은 Cell을 기반으로 구현되며, 동일 계정의 Cell은 방향성 비순환 그래프를 통해 구현됩니다. 이는 저장해야 할 데이터가 경계 없이 커질 수 없음을 의미합니다. 왜냐하면 방향성 비순환 그래프의 경우 데이터의 깊이에 따라 쿼리 비용이 결정되기 때문입니다. 깊이가 무한히 확장되면 쿼리 비용이 너무 높아질 수 있습니다. 계약이 교착 상태 문제에 봉착했습니다.
높은 동시성 성능을 추구하기 위해 TON은 직렬 실행 아키텍처를 포기하고 대신 병렬성을 위해 특별히 설계된 개발 패러다임인 액터 모델을 채택하여 실행 환경을 재구성했습니다. 이는 영향을 미칩니다. 스마트 계약은 소위 내부 메시지를 전송하여 비동기식으로만 호출할 수 있습니다. 상태 수정 유형이든 읽기 전용 유형이든 이 원칙도 따라야 합니다. 비동기 호출이 실패할 경우 데이터 롤백을 처리하는 방법을 신중하게 고려해야 합니다.
물론 다른 기술적 차이점은 이전 기사에서 자세히 논의한 바 있으므로 이 기사에서는 스마트 계약 개발에 중점을 두기를 희망하므로 이에 대해서는 논의하지 않습니다. 위의 두 가지 설계 원칙은 TON과 EVM의 스마트 계약 개발 간에 큰 차이를 만듭니다. 초기 논의에서 우리는 NFT 관련 데이터를 저장하기 위해 NFT 계약이 일부 매핑 관계, 즉 매핑을 정의해야 한다는 것을 알고 있었습니다. 그 중 가장 중요한 것은 소유자이며, 이 매핑은 NFT의 소유권을 결정하는 특정 토큰 ID에 해당하는 NFT의 소유자 주소 매핑 관계를 저장합니다. 이는 이론상 무한할 수 있는 자료구조이므로 최대한 피하는 것이 필요하다. 따라서 무한한 데이터 구조의 존재를 샤딩의 표준으로 사용하는 것이 공식적으로 권장됩니다. 즉, 유사한 데이터 저장 요구사항이 있는 경우에는 마스터-슬레이브 계약 패러다임을 대신 사용하고, 하위 계약을 생성하여 각 키에 해당하는 데이터를 관리합니다. 그리고 주 계약을 통해 전역 매개 변수를 관리하거나 하위 계약 간의 내부 정보 상호 작용을 처리하는 데 도움을 줍니다.
이는 TON의 NFT도 유사한 아키텍처를 사용하여 설계되어야 함을 의미합니다. 각 NFT는 소유자 주소, 메타데이터 등과 같은 독점적인 데이터를 저장하고 주 계약을 통해 전체 상황을 관리하는 독립적인 하위 계약입니다. NFT 이름, 기호, 총 공급량 등
아키텍처를 명확히 한 후 다음 단계는 핵심 기능 요구 사항을 해결하는 것입니다. 이러한 마스터-슬레이브 계약 방식이 채택되므로 주 계약이 어떤 기능을 수행하고 하위 계약이 어떤 기능을 수행하는지 명확히 해야 합니다. 내부 정보는 무엇으로 전달되는지, 실행 오류 발생 시 이전 데이터를 어떻게 롤백하는지 등을 통해 전달됩니다. 일반적으로 복잡한 대규모 프로젝트를 개발하기 전에 클래스 다이어그램을 전달하고 서로 간의 정보 흐름을 명확히 해야 하며, 내부 호출이 실패한 후 롤백 로직을 신중하게 생각해야 합니다. 물론 위의 NFT 개발은 간단합니다. , 그러나 유사한 확인을 수행할 수도 있습니다.
소스 코드에서 TON 스마트 계약을 개발하는 방법을 알아보세요.
TON은 스마트 계약 개발 언어로 C와 유사한 정적인 유형의 언어를 선택했습니다. 그런 다음 소스 코드에서 TON 스마트 계약을 개발하는 방법을 알아보도록 하겠습니다. TON 공식 문서에서 이를 소개하겠습니다. 관심있는 친구들이 직접 확인해 보세요 . 이 경우 간단한 TON NFT 예제가 구현됩니다. 2개의 기능 계약과 3개의 필수 라이브러리로 구분된 계약 구조를 살펴보겠습니다.
이 두 가지 주요 기능 계약은 위의 원칙에 따라 설계되었습니다. 먼저 기본 계약 nft-collection의 코드를 살펴보겠습니다.
여기서는 TON 스마트 계약에 데이터를 지속적으로 저장하는 방법에 대한 첫 번째 지식 포인트를 소개합니다. 우리는 Solidity의 데이터 영구 저장이 일반적으로 스마트 계약의 상태 변수에 따라 EVM에 의해 자동으로 처리된다는 것을 알고 있습니다. 실행 후 최신 값을 기준으로 자동으로 유지되어 저장되므로 개발자는 이 프로세스를 고려할 필요가 없습니다. 하지만 Func에서는 그렇지 않습니다. 개발자는 해당 처리 로직을 직접 구현해야 합니다. 이 상황은 C 및 C++에서 GC 프로세스를 고려해야 하는 방식과 다소 유사하지만 다른 새로운 개발 언어는 일반적으로 이 로직 부분을 자동화합니다. . 먼저, 몇 가지 필수 라이브러리를 소개하고, 첫 번째 함수 load_data가 지속적으로 저장된 데이터를 읽는 데 사용되는 것을 확인합니다. 해당 논리는 먼저 get_data를 통해 영구 계약 저장 셀을 반환하는 것입니다. stdlib.fc 라이브러리에 의해 구현된 표준에 의해 수행되며 일부 기능은 일반적으로 시스템 기능으로 사용될 수 있습니다.
이 함수의 반환 값 유형은 TVM의 셀 유형인 cell입니다. 이전 소개에서 우리는 TON 블록체인의 모든 영구 데이터가 셀 트리에 저장된다는 것을 이미 알고 있었습니다. 각 셀에는 최대 1023비트의 임의 데이터와 다른 셀에 대한 최대 4개의 참조가 있습니다. 셀은 스택 기반 TVM에서 메모리로 사용됩니다. 셀에 저장되는 것은 압축적으로 인코딩된 데이터입니다. 특정 일반 텍스트 데이터를 얻으려면 셀을 슬라이스라는 유형으로 변환해야 합니다. Begin_parse 함수를 통해 셀을 슬라이스 형태로 변환한 후, 슬라이스에서 데이터 비트를 로드하고 다른 셀을 참조하여 셀 내의 데이터를 얻을 수 있다. 15번째 줄의 호출 메서드는 func의 설탕 구문이므로 첫 번째 함수의 값을 반환하는 두 번째 함수를 직접 호출할 수 있습니다. 그리고 마지막으로 데이터 지속 순서에 따라 해당 데이터를 순서대로 로드합니다. 이 프로세스는 Solidity와 다르며 해시맵을 기반으로 호출되지 않으므로 호출 순서가 엉망이 될 수 없습니다.
save_data 함수에서는 셀 빌더 유형인 새로운 유형 빌더인 다음 지식 포인트를 도입하는 역 프로세스라는 점을 제외하면 논리가 유사합니다. 데이터 비트와 다른 셀에 대한 참조는 빌더에 저장될 수 있으며, 그런 다음 새 셀로 마무리될 수 있습니다. 먼저 표준 함수인 Begin_cell을 통해 빌더를 생성하고, 차례로 Store 관련 함수를 통해 저장 관련 기능을 수행하며, 위의 호출 순서는 여기서의 저장 순서와 일치해야 합니다. 마지막으로 end_cell을 통해 새로운 셀이 구성되며, 마지막으로 가장 바깥쪽의 set_data를 통해 셀의 영속적 저장이 완료된다.
다음으로 비즈니스 관련 기능을 살펴보겠습니다. 먼저 방금 소개한 마스터-슬레이브 아키텍처에서 자주 사용하게 될 계약을 통해 새로운 계약을 생성하는 방법에 대한 지식 포인트를 소개해야 합니다. 우리는 TON에서 스마트 계약 간의 호출이 내부 메시지를 전송하여 구현된다는 것을 알고 있습니다. 이는 send_raw_message라는 메시지를 통해 이루어집니다. 첫 번째 매개변수는 메시지로 인코딩된 셀이고, 두 번째 매개변수는 트랜잭션 실행 방법의 차이를 나타내는 데 사용되는 식별 비트입니다. 서로 다른 내부 설정이 설정됩니다. TON에는 현재 메시지 전송 실행 모드에 대해 3개의 메시지 모드와 3개의 메시지 플래그가 있습니다. 단일 모드를 여러(아마도 없음) 플래그와 결합하여 원하는 모드를 얻을 수 있습니다. 결합한다는 것은 단지 그 값의 합을 채우는 것을 의미합니다. 모드 및 플래그에 대한 설명 테이블은 다음과 같습니다.
그럼 첫 번째 메인 함수인 destroy_nft_item을 살펴보겠습니다. 이름에서 알 수 있듯이 새로운 NFT 인스턴스를 생성하거나 전송하는 데 사용되는 함수입니다. msg를 인코딩한 후 send_raw_message를 통해 내부 계약이 전송됩니다. 플래그 비트 1은 인코딩에 지정된 수수료만 이 실행에 대한 가스 수수료로 사용합니다. 위의 소개 후에 우리는 이 코딩 규칙이 새로운 스마트 계약을 생성하는 방법과 일치해야 한다는 것을 쉽게 알 수 있습니다. 그럼 어떻게 구현되었는지 살펴보겠습니다.
51번째 줄을 직접 살펴보겠습니다. 위 두 함수는 메시지에 필요한 정보를 생성하는 데 사용되는 보조 함수이므로 나중에 살펴보겠습니다. 이는 스마트 계약의 내부 메시지를 생성하기 위한 인코딩 프로세스입니다. 중간은 실제로 일부 식별 비트가 내부 메시지의 요구 사항을 설명하는 데 사용됩니다. 다음 지식 포인트는 여기서 소개됩니다. TON은 메시지의 실행 방법을 설명하기 위해 TL-B라는 바이너리 언어를 선택하고 다른 플래그를 설정하여 구현합니다. 특정 특정 기능에 대한 내부 정보의 경우 가장 쉽게 생각할 수 있는 두 가지 사용 시나리오는 새로운 계약 생성과 배포된 계약 기능 호출입니다. 51행의 방법은 전자에 해당하며, 주로 55, 56, 57행을 통해 지정되는 새로운 nft 항목 계약을 생성합니다. 우선, 55행의 큰 숫자는 일련의 식별 비트입니다. store_uint의 첫 번째 입력 매개변수는 값이고, 두 번째는 계약에 의해 내부 메시지가 생성되는지 여부를 결정하는 비트 길이입니다. , 마지막 3개의 표시 비트 및 해당 이진 값 비트는 111(10진수로 4+ 2+ 1)이며, 처음 2개는 메시지에 새 소스 코드인 StateInit 데이터가 수반됨을 나타냅니다. 초기화에 필요한 계약 및 데이터입니다. 후자의 플래그 비트는 내부 메시지 첨부를 나타냅니다. 즉, 관련 논리 및 필수 매개변수가 실행될 것으로 예상됩니다. 따라서 코드의 66번째 줄에는 세 자리 데이터가 설정되어 있지 않은 것을 볼 수 있는데, 이는 배포된 계약에 대한 함수 호출을 나타냅니다. 자세한 코딩 규칙은 여기에서 확인할 수 있습니다.
그런 다음 StateInit의 인코딩 규칙은 계산_nft_item_state_init를 통해 계산된 49줄의 코드에 해당합니다. stateinit 데이터의 인코딩은 일부 플래그 비트 외에도 주로 새 계약의 두 부분과 관련됩니다. 코드 및 데이터 초기화. 데이터의 인코딩 순서는 새 계약에 지정된 지속성 셀의 저장 순서와 일치해야 합니다. 36행에서 볼 수 있듯이 초기화 데이터에는 ERC 721의 tokenId와 유사한 item_index와 표준 함수 my_address에서 반환된 현재 계약 주소(collection_address)가 포함되어 있습니다. nft 아이템에서.
다음 지식 포인트는 TON에서 생성되지 않은 모든 스마트 계약이 생성된 주소를 미리 계산할 수 있다는 것입니다. 이는 Solidity의 생성 2 기능과 유사합니다. 작업 체인 식별 비트는 두 부분으로 구성됩니다. stateinit의 해시 값으로 이전 소개에서 본 것처럼 TON 무한 샤딩 아키텍처에 대응하려면 전자를 지정해야 합니다. 이는 현재 통합된 값입니다. 표준 기능 작업 체인에서 가져옵니다. 후자는 표준 함수 cell_hash에 의해 얻어집니다. 다시 이 예로 돌아와서,calculate_nft_item_address는 새로운 컨트랙트 주소를 미리 계산하는 함수입니다. 그리고 생성된 값을 내부 메시지의 수신 주소로 53행의 메시지로 인코딩합니다. nft_content는 생성된 계약에 대한 초기화 호출에 해당합니다. 구체적인 구현은 다음 기사에서 소개됩니다.
send_royalty_params의 경우 읽기 전용 요청의 내부 메시지에 대한 응답이어야 합니다. 이전 소개에서 TON의 내부 메시지에는 데이터를 수정할 수 있는 작업뿐만 아니라 읽기 전용 작업도 포함되어 있다는 점을 특별히 강조했습니다. 이 방식으로만 작업을 수행하면 되므로 이 계약은 먼저 요청에 응답한 후의 요청자의 콜백 함수 표시를 나타낸다는 점에 유의할 필요가 있습니다. 요청된 항목 인덱스와 해당 로열티 데이터인 반환된 데이터가 됩니다.
다음으로, 다음 지식 포인트를 소개하겠습니다. TON에는 스마트 계약에 대한 통합 입구가 두 개뿐입니다. 이름은 recv_internal 및 recv_external입니다. 전자는 모든 내부 메시지에 대한 통합 호출 입구이고 후자는 모든 외부 메시지에 대한 통합 호출 입구입니다. 메시지 개발 사용자는 메시지에 지정된 다양한 플래그 비트에 따라 함수 내의 요구 사항에 따라 다양한 요청에 응답하기 위해 스위치와 같은 방법을 사용해야 합니다. 이 예제로 돌아가서 먼저 메시지에 대한 공석 검사를 수행한 다음 메시지의 정보를 각각 구문 분석하여 sender_address를 가져옵니다. 이 매개변수는 ~ 연산자에 사용됩니다. 여기에는 또 다른 구문이 포함됩니다. 여기서는 더 이상 설명하지 않겠습니다. 다음으로, 연산 연산 플래그 비트를 분석한 후 해당 요청을 다른 플래그 비트에 따라 처리합니다. 그 중 위의 함수들은 각각 일정한 논리에 따라 호출됩니다. 예를 들어 로열티 매개변수에 대한 요청에 응답하거나 새로운 nft를 캐스팅하고 글로벌 인덱스를 증가시킵니다.
다음 지식 포인트는 108번 라인에 해당합니다. 이 함수의 처리 논리는 Solidity의 require 함수와 유사하게 표준 함수 throw_unless를 통해 Func에서 발생합니다. 두 번째는 비트 부울 값을 확인하는 것입니다. 비트가 거짓이면 오류 코드와 함께 예외가 발생합니다. 이 줄에서는 위에서 구문 분석한 sender_address가 계약의 영구 저장소의 owner_address와 동일한지 여부를 확인하기 위해equal_slices를 사용하고 권한 판단이 이루어집니다.
마지막으로 코드 구조를 보다 명확하게 하기 위해 지속성 정보를 얻는 데 도움이 되는 일련의 보조 기능이 개발되었지만, 여기서는 소개하지 않습니다. 개발자는 이 구조를 참조하여 자신의 스마트 계약을 개발할 수 있습니다.
TON 생태계에서의 DApp 개발은 정말 흥미롭고 EVM의 개발 패러다임과는 매우 다르기 때문에 일련의 기사를 통해 TON Chain에서 DApp을 개발하는 방법을 소개하겠습니다. 모두와 함께 배우고 이러한 기회의 물결을 포착하세요. 새롭고 흥미로운 dapp 아이디어를 생각해내고 함께 개발하기 위해 트위터에서 저와 소통하실 수도 있습니다.