Autore: @Web3Mario (https://x.com/web3_mario)

Abstract: In seguito al precedente articolo sull'introduzione della tecnologia TON, durante questo periodo ho studiato in modo approfondito i documenti ufficiali di sviluppo di TON. Sento che ci sono ancora alcuni ostacoli all'apprendimento. Il contenuto del documento attuale sembra essere più simile a uno sviluppo interno documento, per un nuovo sviluppo entry-level. Non è molto amichevole per i lettori, quindi cerco di organizzare una serie di articoli sullo sviluppo del progetto TON Chain in base al mio percorso di apprendimento, spero che possa essere utile per tutti è iniziato con lo sviluppo di TON DApp. Se ci sono errori nella scrittura, potete correggermi e imparare insieme.

Quali sono le differenze tra lo sviluppo di NFT in EVM e lo sviluppo di NFT su TON Chain?

L'emissione di un FT o NFT è solitamente l'esigenza più elementare per gli sviluppatori DApp. Quindi lo uso anche come punto di ingresso per l'apprendimento. Innanzitutto, comprendiamo le seguenti differenze tra lo sviluppo di un NFT nello stack tecnologico EVM e in TON Chain. Gli NFT basati su EVM di solito scelgono di ereditare lo standard ERC-721. La cosiddetta NFT si riferisce a un tipo di asset crittografato indivisibile e ogni asset è unico, ovvero presenta determinate caratteristiche esclusive. ERC-721 è un paradigma di sviluppo comune per questo tipo di asset. Vediamo quali funzioni deve implementare un contratto ERC721 comune e quali informazioni vengono registrate. L'immagine seguente è un'interfaccia ERC721. Si può notare che a differenza di FT, ciò che deve essere inserito nell'interfaccia di trasferimento è il tokenId da trasferire anziché l'importo. Questo tokenId è anche la manifestazione più elementare dell'unicità delle risorse NFT Naturalmente, per trasportare più attributi, di solito viene registrato un metadato per ciascun tokenId. Questo metadato è un collegamento esterno che salva altri dati scalabili dell'NFT, ad esempio come collegamenti a immagini PFP, determinati nomi di attributi, ecc.

Per gli sviluppatori che hanno familiarità con Solidity o con l'orientamento agli oggetti, è facile implementare un contratto così intelligente. Basta definire i tipi di dati richiesti nel contratto, come alcune relazioni di mappatura chiave, e sviluppare i corrispondenti in base alle funzioni richieste La logica di modifica di questi dati può realizzare una NFT.

Tuttavia, in TON Chain tutto è diverso. Le ragioni principali della differenza sono due:

  • La memorizzazione dei dati in TON è basata su Cell, e la Cell dello stesso conto è implementata tramite un grafico aciclico diretto. Ciò significa che i dati che devono essere archiviati non possono crescere senza limiti, perché per un grafico aciclico diretto, il costo della query è determinato dalla profondità dei dati. Quando la profondità si estende all'infinito, il costo della query potrebbe essere troppo alto, portando a Il contratto è bloccato in una situazione di stallo.

  • Per perseguire elevate prestazioni di concorrenza, TON ha abbandonato l'architettura di esecuzione seriale e ha invece adottato un paradigma di sviluppo appositamente progettato per il parallelismo, il modello Actor, per ricostruire l'ambiente di esecuzione. Ciò ha un impatto. I contratti intelligenti possono essere chiamati solo in modo asincrono inviando i cosiddetti messaggi interni. Tieni presente che, sia che si tratti di una chiamata di tipo modifica di stato o di sola lettura, è necessario seguire anche questo principio deve essere considerato attentamente come gestire il rollback dei dati se una chiamata asincrona fallisce.

Naturalmente, altre differenze tecniche sono state discusse in dettaglio nell’articolo precedente. Questo articolo spera di concentrarsi sullo sviluppo del contratto intelligente, quindi non verrà discusso. I due principi di progettazione di cui sopra fanno una grande differenza tra lo sviluppo di contratti intelligenti in TON ed EVM. Nella discussione iniziale, sappiamo che un contratto NFT deve definire alcune relazioni di mappatura, ovvero la mappatura, per salvare i dati relativi a NFT. Il più importante di questi sono i proprietari. Questa mappatura memorizza la relazione di mappatura dell'indirizzo del proprietario dell'NFT corrispondente a un determinato tokenID, che determina la proprietà dell'NFT. Il trasferimento è una modifica della proprietà. Poiché si tratta di una struttura dati che in teoria può essere illimitata, è necessario evitarla il più possibile. Pertanto, si consiglia ufficialmente di utilizzare l'esistenza di strutture dati illimitate come standard per lo sharding. Quando cioè ci sono esigenze simili di archiviazione dei dati, viene utilizzato il paradigma del contratto master-slave e i dati corrispondenti a ciascuna chiave vengono gestiti creando sub-contratti. E gestisci i parametri globali attraverso il contratto principale o aiuta a gestire l'interazione delle informazioni interne tra i subappalti.

Ciò significa che anche gli NFT in TON devono essere progettati utilizzando un'architettura simile. Ogni NFT è un sub-contratto indipendente, che salva dati esclusivi come indirizzo del proprietario, metadati, ecc., e gestisce la situazione complessiva attraverso un contratto principale come nome NFT, simbolo, fornitura totale, ecc.

Dopo aver chiarito l'architettura, il passo successivo è risolvere i requisiti funzionali principali. Poiché viene adottato questo metodo di contratto master-slave, è necessario chiarire quali funzioni sono svolte dal contratto principale e quali funzioni sono svolte dal subappalto. e i due vengono passati attraverso Quali informazioni interne vengono comunicate e come ripristinare i dati precedenti quando si verifica un errore di esecuzione. Di solito, prima di sviluppare un progetto complesso su larga scala, è necessario passare un diagramma di classe e chiarire il flusso di informazioni tra loro, e pensare attentamente alla logica di rollback dopo che la chiamata interna fallisce. Naturalmente, il suddetto sviluppo NFT è semplice, ma può anche eseguire una verifica simile.

Impara a sviluppare contratti intelligenti TON dal codice sorgente

TON ha scelto di progettare un linguaggio tipo C, tipizzato staticamente, denominato Func come linguaggio di sviluppo dei contratti intelligenti. Quindi impariamo come sviluppare i contratti intelligenti TON dal codice sorgente. Ho scelto l'esempio NFT nel documento ufficiale di TON Gli amici interessati possono verificarlo da soli. In questo caso viene implementato un semplice esempio TON NFT. Diamo un'occhiata alla struttura del contratto, che è suddiviso in due contratti funzionali e tre librerie necessarie.

Questi due principali contratti funzionali sono progettati secondo i principi di cui sopra Innanzitutto, diamo un'occhiata al codice del contratto principale nft-collection:

Questo introduce il primo punto di conoscenza, come archiviare in modo persistente i dati nei contratti intelligenti TON. Sappiamo che l'archiviazione persistente dei dati in Solidity viene gestita automaticamente da EVM in base al tipo di parametri che normalmente saranno le variabili di stato degli smart contract automaticamente persistenti e archiviati in base al valore più recente dopo l'esecuzione e gli sviluppatori non devono considerare questo processo. Ma questo non è il caso in Func. Gli sviluppatori devono implementare da soli la logica di elaborazione corrispondente. Questa situazione è in qualche modo simile alla necessità di considerare il processo GC in C e C++, ma altri nuovi linguaggi di sviluppo solitamente automatizzano questa parte del processo. logica. Diamo un'occhiata al codice. Innanzitutto, introduciamo alcune librerie richieste e poi vediamo che la prima funzione load_data viene utilizzata per leggere i dati archiviati in modo persistente. La sua logica è restituire prima la cella di archiviazione del contratto persistente tramite get_data viene eseguito dallo standard Implementato dalla libreria stdlib.fc, alcune di queste funzioni possono solitamente essere utilizzate come funzioni di sistema.

Il tipo di valore restituito da questa funzione è cell, che è il tipo di cella in TVM. Nell’introduzione precedente, sappiamo già che tutti i dati persistenti nella blockchain TON sono archiviati nell’albero delle celle. Ogni cella ha fino a 1023 bit di dati arbitrari e fino a quattro riferimenti ad altre celle. Le celle vengono utilizzate come memoria nella TVM basata su stack. Ciò che viene memorizzato nella cella sono dati codificati in modo compatto. Per ottenere dati di testo in chiaro specifici, la cella deve essere convertita in un tipo chiamato slice. La cella può essere convertita nel tipo slice tramite la funzione Begin_parse, quindi i dati nella cella possono essere ottenuti caricando i bit di dati dalla slice e i riferimenti ad altre celle. Nota che questo metodo di chiamata nella riga 15 è zucchero sintattico in func e puoi chiamare direttamente la seconda funzione che restituisce il valore della prima funzione. E infine carica i dati corrispondenti in base all'ordine di persistenza dei dati. Tieni presente che questo processo è diverso da Solidity e non viene chiamato in base all'hashmap, quindi l'ordine delle chiamate non può essere incasinato.

Nella funzione save_data, la logica è simile, tranne per il fatto che si tratta di un processo inverso, che introduce il punto di conoscenza successivo, un nuovo generatore di tipi, che è il tipo di generatore di celle. Bit di dati e riferimenti ad altre celle possono essere archiviati nei builder, che possono quindi essere finalizzati in nuove celle. Per prima cosa crea un builder tramite la funzione standard Begin_cell e memorizza a turno le funzioni correlate tramite le funzioni relative al negozio. Tieni presente che l'ordine di chiamata sopra deve essere coerente con l'ordine di archiviazione qui. Infine, la costruzione della nuova cella viene completata tramite end_cell. A questo punto, la cella viene gestita in memoria. Infine, tramite il set_data più esterno, può essere completata la memorizzazione persistente della cella.

Successivamente, diamo un'occhiata alle funzioni legate al business. Innanzitutto, dobbiamo introdurre il prossimo punto di conoscenza, come creare un nuovo contratto tramite un contratto, che verrà utilizzato frequentemente nell'architettura master-slave appena introdotta. Sappiamo che in TON le chiamate tra contratti intelligenti vengono implementate inviando messaggi interni. Ciò si ottiene tramite un messaggio chiamato send_raw_message. Nota che il primo parametro è la cella con codifica del messaggio e il secondo parametro è il bit di identificazione, che viene utilizzato per indicare la differenza nel metodo di esecuzione della transazione in TON Attualmente sono disponibili 3 modalità di messaggio e 3 flag di messaggio per la modalità di esecuzione dell'invio del messaggio. Una singola modalità può essere combinata con più flag (magari nessuno) per ottenere la modalità desiderata. Combinare significa semplicemente riempire la somma dei loro valori. Di seguito la tabella descrittiva delle Modalità e dei Flag:

Diamo quindi un'occhiata alla prima funzione principale, deploy_nft_item. Come suggerisce il nome, questa è una funzione utilizzata per creare o trasmettere una nuova istanza NFT Dopo aver codificato un messaggio, il contratto interno viene inviato tramite send_raw_message e il flag è selezionato il bit flag pari a 1 utilizza solo la tariffa specificata nella codifica come tariffa del gas per questa esecuzione. Dopo l’introduzione di cui sopra, possiamo facilmente renderci conto che questa regola di codifica dovrebbe corrispondere al modo di creare un nuovo contratto intelligente. Diamo quindi un'occhiata a come viene implementato.

Diamo un'occhiata direttamente alla riga 51. Le due funzioni precedenti sono funzioni ausiliarie utilizzate per generare le informazioni richieste per il messaggio, quindi le vedremo più tardi. Questo è un processo di codifica per creare messaggi interni del contratto intelligente nel mezzo ci sono in realtà alcuni bit di identificazione utilizzati per descrivere i requisiti del messaggio interno. Il punto di conoscenza successivo viene introdotto qui. TON ha scelto un linguaggio binario chiamato TL-B per descrivere il metodo di esecuzione del messaggio e lo implementa di conseguenza impostazione di bit di flag diversi. Per informazioni interne su determinate funzioni specifiche, i due scenari di utilizzo più semplici a cui pensare sono la creazione di un nuovo contratto e le chiamate di funzione del contratto distribuito. Il metodo alla riga 51 corrisponde al primo, creando un nuovo contratto per articolo nft, specificato principalmente attraverso le righe 55, 56 e 57. Prima di tutto, la lunga serie di numeri nella riga 55 è una serie di bit di identificazione. Si noti che il primo parametro di input di store_uint è il valore e il secondo è la lunghezza del bit, che determina se il messaggio interno viene creato dal contratto. , gli ultimi tre bit di marcatura e il corrispondente Il bit del valore binario è 111 (il decimale è 4+2+1), i primi due dei quali indicano che il messaggio sarà accompagnato dai dati StateInit. Questi dati sono il codice sorgente del nuovo contratto e i dati necessari per l'inizializzazione. L'ultimo bit di flag indica l'allegato del messaggio interno, cioè è prevista l'esecuzione della logica pertinente e dei parametri richiesti. Pertanto, vedrai che i dati a tre cifre non sono impostati nella riga 66 del codice, che indica una chiamata di funzione al contratto distribuito. Le regole di codifica dettagliate possono essere trovate qui.

Quindi le regole di codifica di StateInit corrispondono a 49 righe di codice, calcolate tramite calcola_nft_item_state_init. Si noti che anche la codifica dei dati stateinit segue una regola di codifica TL-B stabilita. Oltre ad alcuni bit di flag, coinvolge principalmente due parti del nuovo contratto codice e E inizializza i dati. L'ordine di codifica dei dati deve essere coerente con l'ordine di archiviazione delle celle di persistenza specificato dal nuovo contratto. Come puoi vedere alla riga 36, ​​i dati di inizializzazione includono item_index, che è simile al tokenId in ERC721, e l'indirizzo del contratto corrente restituito dalla funzione standard my_address, che è collection_address. L'ordine di questi dati è coerente con la dichiarazione in articolo nft.

Il successivo punto di conoscenza è che in TON, tutti i contratti intelligenti non generati possono pre-calcolare i propri indirizzi generati. Questo è simile alla funzione create2 in Solidity. La generazione di nuovi indirizzi in TON è composta da due parti, l'identificatore della catena di lavoro Bit e l'hash di stateinit. sono uniti insieme. Nell'introduzione precedente, sappiamo già che il primo deve essere specificato per corrispondere all'architettura di sharding infinito TON. Attualmente è un valore unificato. Ottenuto dalla catena di lavoro con funzioni standard. Quest'ultimo è ottenuto dalla funzione standard cell_hash. Quindi, tornando a questo esempio,culate_nft_item_address è una funzione che precalcola il nuovo indirizzo del contratto. E codificare il valore generato nel messaggio alla riga 53 come indirizzo di ricezione del messaggio interno. nft_content corrisponde alla chiamata di inizializzazione del contratto creato. L'implementazione specifica verrà introdotta nel prossimo articolo.

Per quanto riguarda send_royalty_params, deve essere una risposta al messaggio interno di una richiesta di sola lettura Nell'introduzione precedente, abbiamo sottolineato in particolare che il messaggio interno in TON non contiene solo operazioni che possono modificare i dati, ma anche la lettura. l'unica operazione deve essere eseguita in questo modo Implementazione, quindi questo contratto è un'operazione di questo tipo. Prima di tutto, vale la pena notare che la riga 67 rappresenta il segno della funzione di callback del richiedente dopo aver risposto alla richiesta saranno i dati restituiti, ovvero l'indice dell'articolo richiesto e i dati della royalty corrispondente.

Successivamente, introduciamo il successivo punto di conoscenza. Esistono solo due ingressi unificati per i contratti intelligenti in TON, denominati recv_internal e recv_external. Il primo è l'ingresso di chiamata unificato per tutti i messaggi interni e il secondo è l'ingresso di chiamata unificato per tutti quelli esterni Messaggi Sviluppo L'utente deve utilizzare un metodo simile a un interruttore per rispondere a diverse richieste in base ai diversi bit di contrassegno specificati dal messaggio all'interno della funzione in base ai requisiti. Il bit di contrassegno qui è il contrassegno della funzione di callback nella riga 67 sopra. Tornando a questo esempio, esegui prima un controllo di disponibilità sul messaggio, quindi analizza rispettivamente le informazioni nel messaggio. Innanzitutto, analizza la riga 83 per ottenere il mittente_indirizzo. Questo parametro verrà utilizzato per i successivi controlli dei permessi qui appartiene ad un'altra sintassi zucchero. Non lo espanderò qui. Successivamente, vengono analizzati i bit del flag dell'operazione operativa e quindi le richieste corrispondenti vengono elaborate in base ai diversi bit del flag. Tra queste, le funzioni di cui sopra sono chiamate rispettivamente secondo una certa logica. Ad esempio, rispondi a una richiesta per il parametro royalty o lancia un nuovo nft e incrementa l'indice globale.

Il punto di conoscenza successivo corrisponde alla riga 108. Penso che tu possa anche conoscere la logica di elaborazione di questa funzione nominandola. È simile alla funzione require in Solidity. Le eccezioni vengono lanciate in Func tramite la funzione standard Throw_unless è il codice di errore, il secondo è controllare il valore booleano del bit, se il bit è falso, verrà generata un'eccezione con il codice di errore. In questa riga, equal_slices viene utilizzato per determinare se il sender_address analizzato sopra è uguale all'owner_address dell'archivio persistente del contratto e viene effettuato il giudizio sull'autorizzazione.

Infine, per rendere più chiara la struttura del codice, sono state sviluppate una serie di funzioni ausiliarie per aiutare ad ottenere informazioni sulla persistenza, che non verranno introdotte in questa sede. Gli sviluppatori possono fare riferimento a questa struttura per sviluppare i propri smart contract.

Lo sviluppo di DApp nell'ecosistema TON è davvero interessante ed è molto diverso dal paradigma di sviluppo di EVM, quindi presenterò come sviluppare DApp in TON Chain attraverso una serie di articoli. Imparare insieme a tutti e cogliere questa ondata di opportunità. Puoi anche interagire con me su Twitter per trovare alcune idee nuove e interessanti per le dapp e svilupparle insieme.