Informazioni su come scrivere e pubblicare uno smart contract in TON
Di cosa parla questo articolo?
Nell'articolo parlerò di come ho preso parte al primo (di due) concorso blockchain di Telegram, non ho vinto alcun premio e ho deciso di registrare la mia esperienza in un articolo in modo che non cada nell'oblio e, forse, mi aiuti qualcuno.
Dato che non volevo scrivere codice astratto, ma fare qualcosa che funzionasse, per l'articolo ho scritto uno smart contract per una lotteria istantanea e un sito web che mostra i dati dello smart contract direttamente da TON senza utilizzare storage intermedio.
L'articolo sarà utile a chi vuole stipulare il suo primo smart contract in TON, ma non sa da dove cominciare.
Usando la lotteria come esempio, passerò dall'installazione dell'ambiente alla pubblicazione di un contratto intelligente, all'interazione con esso e alla scrittura di un sito Web per ricevere e pubblicare dati.
Sulla partecipazione al concorso
Lo scorso ottobre, Telegram ha annunciato un concorso blockchain con nuove lingue Fift и FunC. Era necessario scegliere tra scrivere uno dei cinque contratti intelligenti proposti. Ho pensato che sarebbe stato carino fare qualcosa di diverso, imparare una lingua e realizzare qualcosa, anche se non dovessi scrivere nient'altro in futuro. Inoltre, l'argomento è costantemente sulle labbra.
Vale la pena dire che non avevo esperienza nello sviluppo di contratti intelligenti.
Avevo programmato di partecipare fino alla fine finché avrei potuto e poi scrivere un articolo di revisione, ma al primo tentativo ho fallito subito. IO con firma multipla attiva FunC e generalmente ha funzionato. L'ho preso come base .
A quel tempo, pensavo che questo fosse decisamente sufficiente per conquistare almeno un posto in premio. Di conseguenza, circa 40 partecipanti su 60 sono diventati vincitori e io non ero tra questi. In generale, non c'è niente di sbagliato in questo, ma una cosa mi ha infastidito. Al momento dell'annuncio dei risultati non era stata fatta la revisione del test per il mio contratto, ho chiesto ai partecipanti alla chat se c'era qualcun altro che non ce l'aveva, non c'erano.
Apparentemente prestando attenzione ai miei messaggi, due giorni dopo i giudici hanno pubblicato un commento e ancora non capisco se si siano persi accidentalmente il mio smart contract durante il giudizio o semplicemente abbiano pensato che fosse così brutto da non aver bisogno di un commento. Ho posto una domanda sulla pagina, ma non ho ricevuto risposta. Anche se non è un segreto chi ha giudicato, ho ritenuto non necessario scrivere messaggi personali.
È stato dedicato molto tempo alla comprensione, quindi si è deciso di scrivere un articolo. Poiché non ci sono ancora molte informazioni, questo articolo aiuterà a risparmiare tempo a tutti gli interessati.
Il concetto di contratti intelligenti in TON
Prima di scrivere qualsiasi cosa, devi capire da che parte affrontare questa cosa. Pertanto, ora ti dirò da quali parti è composto il sistema. Più precisamente, quali parti devi sapere per scrivere almeno una sorta di contratto di lavoro.
Ci concentreremo sulla scrittura di un contratto intelligente e sulla collaborazione TON Virtual Machine (TVM), Fift и FunC, quindi l'articolo è più simile a una descrizione dello sviluppo di un programma normale. Non ci soffermeremo su come funziona la piattaforma stessa qui.
In generale su come funziona TVM e lingua Fift c'è una buona documentazione ufficiale. Durante la partecipazione al concorso ed ora mentre scrivo il presente contratto, mi sono rivolto spesso a lei.
La lingua principale in cui sono scritti i contratti intelligenti è FunC. Al momento non c'è documentazione a riguardo, quindi per scrivere qualcosa devi studiare esempi di contratti intelligenti dal repository ufficiale e l'implementazione del linguaggio stesso lì, inoltre puoi guardare esempi di contratti intelligenti degli ultimi due concorsi. Link a fine articolo.
Diciamo che abbiamo già scritto uno smart contract per FunC, dopodiché compiliamo il codice nell'assemblatore Fift.
Il contratto intelligente compilato deve ancora essere pubblicato. Per fare questo è necessario scrivere una funzione in Fift, che prenderà il codice del contratto intelligente e alcuni altri parametri come input e l'output sarà un file con estensione .boc (che significa “sacchetto di celle”) e, a seconda di come lo scriviamo, una chiave privata e un indirizzo, che viene generato in base al codice del contratto intelligente. Puoi già inviare grammi all'indirizzo di uno smart contract che non è stato ancora pubblicato.
Per pubblicare un contratto intelligente in TON ricevuto .boc il file dovrà essere inviato alla blockchain utilizzando un light client (ne parleremo più avanti). Ma prima della pubblicazione è necessario trasferire i grammi all'indirizzo generato, altrimenti il contratto intelligente non verrà pubblicato. Dopo la pubblicazione, puoi interagire con lo smart contract inviandogli messaggi dall'esterno (ad esempio, utilizzando un light client) o dall'interno (ad esempio, uno smart contract invia un messaggio a un altro all'interno di TON).
Una volta capito come viene pubblicato il codice, tutto diventa più semplice. Sappiamo approssimativamente cosa vogliamo scrivere e come funzionerà il nostro programma. E mentre scriviamo, cerchiamo come questo sia già implementato nei contratti intelligenti esistenti, oppure esaminiamo il codice di implementazione Fift и FunC nel repository ufficiale, oppure cerca nella documentazione ufficiale.
Molto spesso ho cercato parole chiave nella chat di Telegram dove si riunivano tutti i partecipanti al concorso e i dipendenti di Telegram, ed è successo che durante il concorso tutti si sono riuniti lì e hanno iniziato a discutere di Fift e FunC. Link a fine articolo.
È tempo di passare dalla teoria alla pratica.
Preparare l'ambiente per lavorare con TON
Ho fatto tutto ciò che verrà descritto nell'articolo su MacOS e l'ho ricontrollato in un ambiente pulito Ubuntu Ubuntu 18.04 LTS su Docker.
La prima cosa che devi fare è scaricare e installare lite-client con cui potrai inviare richieste a TON.
Le istruzioni sul sito ufficiale descrivono il processo di installazione in modo piuttosto completo e chiaro, omettendo alcuni dettagli. Qui seguiamo le istruzioni, installando le dipendenze mancanti man mano. Non ho compilato personalmente ciascun progetto, ma ho installato i pacchetti dai repository ufficiali. Ubuntu (su MacOS ho usato brew).
apt -y install git
apt -y install wget
apt -y install cmake
apt -y install g++
apt -y install zlib1g-dev
apt -y install libssl-dev Una volta installate tutte le dipendenze, è possibile installare lite-client, Fift, FunC.
Per prima cosa cloniamo il repository TON insieme alle sue dipendenze. Per comodità, faremo tutto in una cartella ~/TON.
cd ~/TON
git clone https://github.com/ton-blockchain/ton.git
cd ./ton
git submodule update --init --recursiveIl repository memorizza anche le implementazioni Fift и FunC.
Ora siamo pronti per assemblare il progetto. Il codice del repository viene clonato in una cartella ~/TON/ton. In ~/TON creare una cartella build e raccogliere al suo interno il progetto.
mkdir ~/TON/build
cd ~/TON/build
cmake ../tonDal momento che scriveremo un contratto intelligente, non abbiamo bisogno solo di questo lite-clientMa Fift с FunC, quindi compiliamo tutto. Non è un processo veloce, quindi stiamo aspettando.
cmake --build . --target lite-client
cmake --build . --target fift
cmake --build . --target funcSuccessivamente, scarica il file di configurazione che contiene i dati sul nodo a cui lite-client si collegherà.
wget https://test.ton.org/ton-lite-client-test1.config.jsonAvanzando le prime richieste a TON
Ora lanciamo lite-client.
cd ~/TON/build
./lite-client/lite-client -C ton-lite-client-test1.config.jsonSe la compilazione ha avuto esito positivo, dopo il lancio vedrai un registro della connessione del light client al nodo.
[ 1][t 2][1582054822.963129282][lite-client.h:201][!testnode] conn ready
[ 2][t 2][1582054823.085654020][lite-client.cpp:277][!testnode] server version is 1.1, capabilities 7
[ 3][t 2][1582054823.085725069][lite-client.cpp:286][!testnode] server time is 1582054823 (delta 0)
...È possibile eseguire il comando help e vedere quali comandi sono disponibili.
helpElenchiamo i comandi che utilizzeremo in questo articolo.
list of available commands:
last Get last block and state info from server
sendfile <filename> Load a serialized message from <filename> and send it to server
getaccount <addr> [<block-id-ext>] Loads the most recent state of specified account; <addr> is in [<workchain>:]<hex-or-base64-addr> format
runmethod <addr> [<block-id-ext>] <method-id> <params>... Runs GET method <method-id> of account <addr> with specified parameterslast получает последний созданный блок с сервера.
sendfile <filename> отправляет в TON файл с сообщением, именно с помощью этой команды публикуется смарт-контракт и запрсосы к нему.
getaccount <addr> загружает текущее состояние смарт-контракта с указанным адресом.
runmethod <addr> [<block-id-ext>] <method-id> <params> запускает get-методы смартконтракта. Ora siamo pronti per scrivere il contratto vero e proprio.
implementazione
Idea
Come ho scritto sopra, lo smart contract che stiamo scrivendo è una lotteria.
Inoltre, questa non è una lotteria in cui è necessario acquistare un biglietto e attendere un'ora, un giorno o un mese, ma una lotteria istantanea in cui l'utente si trasferisce all'indirizzo del contratto N grammi e lo recupera immediatamente 2 * N grammi o perdite. Aumenteremo la probabilità di vincita al 40% circa. Se non ci sono abbastanza grammi per il pagamento, considereremo la transazione come una ricarica.
Inoltre, è importante che le scommesse possano essere visualizzate in tempo reale e in una forma comoda, in modo che l'utente possa capire immediatamente se ha vinto o perso. Pertanto, è necessario creare un sito Web che mostri scommesse e risultati direttamente da TON.
Scrivere un contratto intelligente
Per comodità, ho evidenziato il codice per FunC; il plugin può essere trovato e installato nella ricerca di Visual Studio Code; se all'improvviso vuoi aggiungere qualcosa, ho reso il plugin pubblicamente disponibile. Inoltre, qualcuno ha precedentemente creato un plug-in per lavorare con Fift, puoi anche installarlo e trovarlo in VSC.
Creiamo subito un repository in cui commetteremo i risultati intermedi.
Per semplificarci la vita, scriveremo un contratto intelligente e lo testeremo localmente finché non sarà pronto. Solo dopo lo pubblicheremo su TON.
Il contratto intelligente ha due metodi esterni a cui è possibile accedere. Primo, recv_external() questa funzione viene eseguita quando una richiesta al contratto arriva dal mondo esterno, cioè non da TON, ad esempio quando generiamo noi stessi un messaggio e lo inviamo tramite il lite-client. Secondo, recv_internal() è allora che, all'interno di TON, qualsiasi contratto fa riferimento al nostro. In entrambi i casi è possibile passare parametri alla funzione.
Cominciamo con un semplice esempio che funzionerà se pubblicato, ma non contiene alcun carico funzionale.
() recv_internal(slice in_msg) impure {
;; TODO: implementation
}
() recv_external(slice in_msg) impure {
;; TODO: implementation
}Qui dobbiamo spiegare di cosa si tratta slice. Tutti i dati archiviati in TON Blockchain costituiscono una raccolta TVM cell o semplicemente cell, in una cella di questo tipo è possibile memorizzare fino a 1023 bit di dati e fino a 4 collegamenti ad altre celle.
TVM cell slice o slice questo fa parte di quello esistente cell viene utilizzato per analizzarlo, diventerà chiaro in seguito. La cosa principale per noi è che possiamo trasferirci slice e, a seconda del tipo di messaggio, elaborare i dati recv_external() o recv_internal().
impure — una parola chiave che indica che la funzione modifica i dati del contratto intelligente.
Salviamo il codice contratto in lottery-code.fc e compilare.
~/TON/build/crypto/func -APSR -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc Il significato dei flag può essere visualizzato utilizzando il comando
~/TON/build/crypto/func -helpAbbiamo compilato il codice assembler Fift in lottery-compiled.fif:
// lottery-compiled.fif
"Asm.fif" include
// automatically generated from `/Users/rajymbekkapisev/TON/ton/crypto/smartcont/stdlib.fc` `./lottery-code.fc`
PROGRAM{
DECLPROC recv_internal
DECLPROC recv_external
recv_internal PROC:<{
// in_msg
DROP //
}>
recv_external PROC:<{
// in_msg
DROP //
}>
}END>cPuò essere lanciato localmente, per questo prepareremo l’ambiente.
Tieni presente che la prima linea si connette Asm.fif, questo è il codice scritto in Fift per l'assemblatore Fift.
Poiché vogliamo eseguire e testare il contratto intelligente localmente, creeremo un file lottery-test-suite.fif e copia lì il codice compilato, sostituendo l'ultima riga in esso, che scrive il codice del contratto intelligente su una costante codeper poi trasferirlo sulla macchina virtuale:
"TonUtil.fif" include
"Asm.fif" include
PROGRAM{
DECLPROC recv_internal
DECLPROC recv_external
recv_internal PROC:<{
// in_msg
DROP //
}>
recv_external PROC:<{
// in_msg
DROP //
}>
}END>s constant code
Fin qui sembra chiaro, ora aggiungiamo allo stesso file il codice che utilizzeremo per avviare TVM.
0 tuple 0x076ef1ea , // magic
0 , 0 , // actions msg_sents
1570998536 , // unix_time
1 , 1 , 3 , // block_lt, trans_lt, rand_seed
0 tuple 100000000000000 , dictnew , , // remaining balance
0 , dictnew , // contract_address, global_config
1 tuple // wrap to another tuple
constant c7
0 constant recv_internal // to run recv_internal()
-1 constant recv_external // to invoke recv_external()В c7 registriamo il contesto, cioè i dati con cui verrà lanciato il TVM (o stato della rete). Anche durante la competizione, uno degli sviluppatori ha mostrato come creare c7 e ho copiato. In questo articolo potrebbe essere necessario cambiare rand_seed poiché da esso dipende la generazione di un numero casuale e se non viene modificato verrà restituito ogni volta lo stesso numero.
recv_internal и recv_external le costanti con valori 0 e -1 saranno responsabili della chiamata delle funzioni corrispondenti nello smart contract.
Ora siamo pronti per creare il primo test per il nostro contratto intelligente vuoto. Per chiarezza, per ora aggiungeremo tutti i test allo stesso file lottery-test-suite.fif.
Creiamo una variabile storage e scriverne uno vuoto cell, questa sarà l'archiviazione del contratto intelligente.
message Questo è il messaggio che trasmetteremo al contatto smart dall'esterno. Lo renderemo anche vuoto per ora.
variable storage
<b b> storage !
variable message
<b b> message ! Dopo aver preparato le costanti e le variabili, lanciamo TVM utilizzando il comando runvmctx e passare i parametri creati all'input.
message @
recv_external
code
storage @
c7
runvmctx Alla fine ci riusciremo codice intermedio per Fift.
Ora possiamo eseguire il codice risultante.
export FIFTPATH=~/TON/ton/crypto/fift/lib // выполняем один раз для удобства
~/TON/build/crypto/fift -s lottery-test-suite.fif Il programma dovrebbe funzionare senza errori e nell'output vedremo il log di esecuzione:
execute SETCP 0
execute DICTPUSHCONST 19 (xC_,1)
execute DICTIGETJMPZ
execute DROP
execute implicit RET
[ 3][t 0][1582281699.325381279][vm.cpp:479] steps: 5 gas: used=304, max=9223372036854775807, limit=9223372036854775807, credit=0Ottimo, abbiamo scritto la prima versione funzionante dello smart contract.
Ora dobbiamo aggiungere funzionalità. Per prima cosa affrontiamo i messaggi che arrivano dal mondo esterno recv_external()
Lo sviluppatore stesso sceglie il formato del messaggio che il contratto può accettare.
Ma di solito
- in primo luogo, vogliamo proteggere il nostro contratto dal mondo esterno e fare in modo che solo il titolare del contratto possa inviargli messaggi esterni.
- in secondo luogo, quando inviamo un messaggio valido a TON, vogliamo che ciò accada esattamente una volta e quando inviamo nuovamente lo stesso messaggio, lo smart contract lo rifiuta.
Quindi quasi tutti i contratti risolvono questi due problemi, poiché il nostro contratto accetta messaggi esterni, dobbiamo occuparci anche di questo.
Lo faremo in ordine inverso. Per prima cosa risolviamo il problema della ripetizione: se il contratto ha già ricevuto un messaggio del genere e lo ha elaborato, non lo eseguirà una seconda volta. E poi risolveremo il problema in modo che solo una certa cerchia di persone possa inviare messaggi al contratto intelligente.
Esistono diversi modi per risolvere il problema con i messaggi duplicati. Ecco come lo faremo. Nello smart contract, inizializziamo il contatore dei messaggi ricevuti con il valore iniziale 0. In ogni messaggio nello smart contract, aggiungeremo il valore del contatore corrente. Se il valore del contatore nel messaggio non corrisponde al valore nello smart contract, non lo elaboriamo; se lo fa, lo elaboriamo e aumentiamo il contatore nello smart contract di 1.
Torniamo a lottery-test-suite.fif e aggiungi un secondo test. Se inviamo un numero errato, il codice dovrebbe generare un'eccezione. Ad esempio, lascia che i dati del contratto memorizzino 166 e invieremo 165.
<b 166 32 u, b> storage !
<b 165 32 u, b> message !
message @
recv_external
code
storage @
c7
runvmctx
drop
exit_code !
."Exit code " exit_code @ . cr
exit_code @ 33 - abort"Test #2 Not passed"Lanciamo.
~/TON/build/crypto/fift -s lottery-test-suite.fif E vedremo che il test viene eseguito con un errore.
[ 1][t 0][1582283084.210902214][words.cpp:3046] lottery-test-suite.fif:67: abort": Test #2 Not passed
[ 1][t 0][1582283084.210941076][fift-main.cpp:196] Error interpreting file `lottery-test-suite.fif`: error interpreting included file `lottery-test-suite.fif` : lottery-test-suite.fif:67: abort": Test #2 Not passedIn questa fase lottery-test-suite.fif dovrebbe assomigliare .
Ora aggiungiamo la contrologica al contratto intelligente lottery-code.fc.
() recv_internal(slice in_msg) impure {
;; TODO: implementation
}
() recv_external(slice in_msg) impure {
if (slice_empty?(in_msg)) {
return ();
}
int msg_seqno = in_msg~load_uint(32);
var ds = begin_parse(get_data());
int stored_seqno = ds~load_uint(32);
throw_unless(33, msg_seqno == stored_seqno);
}В slice in_msg sta il messaggio che inviamo.
La prima cosa che facciamo è controllare se il messaggio contiene dati, in caso contrario usciremo semplicemente.
Successivamente analizziamo il messaggio. in_msg~load_uint(32) carica il numero 165, 32 bit unsigned int dal messaggio trasmesso.
Successivamente carichiamo 32 bit dallo spazio di archiviazione del contratto intelligente. Controlliamo che il numero caricato corrisponda a quello passato; in caso contrario lanciamo un'eccezione. Nel nostro caso, poiché stiamo superando una non corrispondenza, dovrebbe essere lanciata un'eccezione.
Ora compiliamo.
~/TON/build/crypto/func -APSR -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc Copia il codice risultante in lottery-test-suite.fif, senza dimenticare di sostituire l'ultima riga.
Controlliamo che il test sia superato:
~/TON/build/crypto/fift -s lottery-test-suite.fifPuoi vedere il commit corrispondente con i risultati attuali.
Tieni presente che è scomodo copiare costantemente il codice compilato di uno smart contract in un file con test, quindi scriveremo uno script che scriverà il codice in una costante per noi e collegheremo semplicemente il codice compilato ai nostri test utilizzando "include".
Creare un file nella cartella del progetto build.sh con il seguente contenuto.
#!/bin/bash
~/TON/build/crypto/func -SPA -R -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fcRendiamolo eseguibile.
chmod +x ./build.shOra esegui semplicemente il nostro script per compilare il contratto. Ma oltre a questo, dobbiamo scriverlo in una costante code. Quindi creeremo un nuovo file lotter-compiled-for-test.fif, che includeremo nel file lottery-test-suite.fif.
Aggiungiamo il codice skipt a sh, che duplicherà semplicemente il file compilato lotter-compiled-for-test.fif e cambia l'ultima riga al suo interno.
# copy and change for test
cp lottery-compiled.fif lottery-compiled-for-test.fif
sed '$d' lottery-compiled-for-test.fif > test.fif
rm lottery-compiled-for-test.fif
mv test.fif lottery-compiled-for-test.fif
echo -n "}END>s constant code" >> lottery-compiled-for-test.fifOra, per verificare, eseguiamo lo script risultante e verrà generato un file lottery-compiled-for-test.fif, che inseriremo nel ns lottery-test-suite.fif
В lottery-test-suite.fif eliminare il codice contratto e aggiungere la riga "lottery-compiled-for-test.fif" include.
Eseguiamo test per verificare che passino.
~/TON/build/crypto/fift -s lottery-test-suite.fifOttimo, ora per automatizzare l'avvio dei test, creiamo un file test.sh, che verrà eseguito per primo build.sh, quindi eseguire i test.
touch test.sh
chmod +x test.shScriviamo dentro
./build.sh
echo "nCompilation completedn"
export FIFTPATH=~/TON/ton/crypto/fift/lib
~/TON/build/crypto/fift -s lottery-test-suite.fifFacciamolo test.sh ed eseguirlo per assicurarsi che i test funzionino.
chmod +x ./test.sh
./test.shControlliamo che il contratto venga compilato e che i test vengano eseguiti.
Ottimo, ora all'avvio test.sh I test verranno compilati ed eseguiti immediatamente. Ecco il collegamento a .
Ok, prima di continuare, facciamo ancora una cosa per comodità.
Creiamo una cartella build dove memorizzeremo il contratto copiato e il suo clone scritto in una costante lottery-compiled.fif, lottery-compiled-for-test.fif. Creiamo anche una cartella test dove verrà archiviato il file di test? lottery-test-suite.fif e potenzialmente altri file di supporto. .
Continuiamo a sviluppare il contratto intelligente.
Successivamente dovrebbe esserci un test che controlli che il messaggio venga ricevuto e che il contatore venga aggiornato nel negozio quando inviamo il numero corretto. Ma lo faremo più tardi.
Ora pensiamo a quale struttura dei dati e quali dati devono essere archiviati nel contratto intelligente.
Descriverò tutto ciò che conserviamo.
`seqno` 32-х битное целое положительное число счетчик.
`pubkey` 256-ти битное целое положительное число публичный ключ, с помощью которого, мы будем проверять подпись отправленного извне сообщения, о чем ниже.
`order_seqno` 32-х битное целое положительное число хранит счетчик количества ставок.
`number_of_wins` 32-х битное целое положительное число хранит количество побед.
`incoming_amount` тип данных Gram (первые 4 бита отвечает за длину), хранит общее количество грамов, которые были отправлены на контртакт.
`outgoing_amount` общее количество грамов, которое было отправлено победителям.
`owner_wc` номер воркчейна, 32-х битное (в некоторых местах написано, что 8-ми битное) целое число. В данный момент всего два -1 и 0.
`owner_account_id` 256-ти битное целое положительное число, адрес контракта в текущем воркчейне.
`orders` переменная типа словарь, хранит последние двадцать ставок. Successivamente è necessario scrivere due funzioni. Chiamiamo il primo pack_state(), che impacchetterà i dati per il successivo salvataggio nello spazio di archiviazione del contratto intelligente. Chiamiamo il secondo unpack_state() leggerà e restituirà i dati dalla memoria.
_ pack_state(int seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) inline_ref {
return begin_cell()
.store_uint(seqno, 32)
.store_uint(pubkey, 256)
.store_uint(order_seqno, 32)
.store_uint(number_of_wins, 32)
.store_grams(incoming_amount)
.store_grams(outgoing_amount)
.store_int(owner_wc, 32)
.store_uint(owner_account_id, 256)
.store_dict(orders)
.end_cell();
}
_ unpack_state() inline_ref {
var ds = begin_parse(get_data());
var unpacked = (ds~load_uint(32), ds~load_uint(256), ds~load_uint(32), ds~load_uint(32), ds~load_grams(), ds~load_grams(), ds~load_int(32), ds~load_uint(256), ds~load_dict());
ds.end_parse();
return unpacked;
}Aggiungiamo queste due funzioni all'inizio dello smart contract. Funzionerà risultato intermedio.
Per salvare i dati dovrai chiamare la funzione integrata set_data() e scriverà i dati da pack_state() nello stoccaggio del contratto intelligente.
cell packed_state = pack_state(arg_1, .., arg_n);
set_data(packed_state);Ora che disponiamo di comode funzioni per scrivere e leggere i dati, possiamo andare avanti.
Dobbiamo verificare che il messaggio in arrivo dall'esterno sia firmato dal titolare del contratto (o da un altro utente che ha accesso alla chiave privata).
Quando pubblichiamo uno smart contract, possiamo inizializzarlo con i dati di cui abbiamo bisogno in archivio, che verranno salvati per un uso futuro. Registreremo lì la chiave pubblica in modo da poter verificare che il messaggio in arrivo sia stato firmato con la chiave privata corrispondente.
Prima di continuare, creiamo una chiave privata e scriviamola test/keys/owner.pk. Per fare ciò, avviamo Fift in modalità interattiva ed eseguiamo quattro comandi.
`newkeypair` генерация публичного и приватного ключа и запись их в стек.
`drop` удаления из стека верхнего элемента (в данном случае публичный ключ)
`.s` просто посмотреть что лежит в стеке в данный момент
`"owner.pk" B>file` запись приватного ключа в файл с именем `owner.pk`.
`bye` завершает работу с Fift. Creiamo una cartella keys all'interno della cartella test e scrivi lì la chiave privata.
mkdir test/keys
cd test/keys
~/TON/build/crypto/fift -i
newkeypair
ok
.s
BYTES:128DB222CEB6CF5722021C3F21D4DF391CE6D5F70C874097E28D06FCE9FD6917 BYTES:DD0A81AAF5C07AAAA0C7772BB274E494E93BB0123AA1B29ECE7D42AE45184128
drop
ok
"owner.pk" B>file
ok
byeVediamo un file nella cartella corrente owner.pk.
Togliamo la chiave pubblica dallo stack e quando necessario possiamo recuperarla da quella privata.
Ora dobbiamo scrivere una verifica della firma. Cominciamo con la prova. Per prima cosa leggiamo la chiave privata dal file utilizzando la funzione file>B e scriverlo in una variabile owner_private_key, quindi utilizzando la funzione priv>pub converti la chiave privata in una chiave pubblica e scrivi il risultato owner_public_key.
variable owner_private_key
variable owner_public_key
"./keys/owner.pk" file>B owner_private_key !
owner_private_key @ priv>pub owner_public_key !Avremo bisogno di entrambe le chiavi.
Inizializziamo l'archiviazione del contratto intelligente con dati arbitrari nella stessa sequenza della funzione pack_state()e scriverlo in una variabile storage.
variable owner_private_key
variable owner_public_key
variable orders
variable owner_wc
variable owner_account_id
"./keys/owner.pk" file>B owner_private_key !
owner_private_key @ priv>pub owner_public_key !
dictnew orders !
0 owner_wc !
0 owner_account_id !
<b 0 32 u, owner_public_key @ B, 0 32 u, 0 32 u, 0 Gram, 0 Gram, owner_wc @ 32 i, owner_account_id @ 256 u, orders @ dict, b> storage !Successivamente, comporremo un messaggio firmato, conterrà solo la firma e il controvalore.
Per prima cosa creiamo i dati che vogliamo trasmettere, poi li firmiamo con una chiave privata e infine generiamo un messaggio firmato.
variable message_to_sign
variable message_to_send
variable signature
<b 0 32 u, b> message_to_sign !
message_to_sign @ hashu owner_private_key @ ed25519_sign_uint signature !
<b signature @ B, 0 32 u, b> <s message_to_send ! Di conseguenza, il messaggio che invieremo allo smart contract verrà registrato in una variabile message_to_send, sulle funzioni hashu, ed25519_sign_uint puoi leggere .
E per eseguire il test chiamiamo di nuovo.
message_to_send @
recv_external
code
storage @
c7
runvmctxIl file con i test dovrebbe assomigliare a questo in questa fase.
Eseguiamo il test e fallirà, quindi modificheremo lo smart contract in modo che possa ricevere messaggi di questo formato e verificare la firma.
Innanzitutto contiamo 512 bit della firma del messaggio e li scriviamo in una variabile, quindi contiamo 32 bit della variabile contatore.
Poiché disponiamo di una funzione per leggere i dati dall'archiviazione del contratto intelligente, la utilizzeremo.
Il prossimo è controllare il contatore trasferito con l'archiviazione e controllare la firma. Se qualcosa non corrisponde, lanciamo un'eccezione con il codice appropriato.
var signature = in_msg~load_bits(512);
var message = in_msg;
int msg_seqno = message~load_uint(32);
(int stored_seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) = unpack_state();
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, check_signature(slice_hash(in_msg), signature, pubkey));Impegno pertinente .
Eseguiamo i test e vediamo che il secondo test fallisce. Per due motivi, non ci sono abbastanza bit nel messaggio e non ci sono abbastanza bit nella memoria, quindi il codice si blocca durante l'analisi. Dobbiamo aggiungere una firma al messaggio che stiamo inviando e copiare la memoria dell'ultimo test.
Nel secondo test, aggiungeremo una firma al messaggio e modificheremo l'archiviazione dello smart contract. il file con i test appare al momento.
Scriviamo un quarto test, in cui invieremo un messaggio firmato con la chiave privata di qualcun altro. Creiamo un'altra chiave privata e salviamola in un file not-owner.pk. Firmeremo il messaggio con questa chiave privata. Eseguiamo i test e assicuriamoci che tutti i test vengano superati. al momento.
Ora possiamo finalmente passare all’implementazione della logica dello smart contract.
В recv_external() accetteremo due tipi di messaggi.
Poiché il nostro contratto accumulerà le perdite dei giocatori, questo denaro dovrà essere trasferito al creatore della lotteria. L'indirizzo del portafoglio del creatore della lotteria viene registrato nell'archivio al momento della creazione del contratto.
Per ogni evenienza, abbiamo bisogno della possibilità di modificare l'indirizzo a cui inviamo i grammi dei perdenti. Dovremmo anche essere in grado di inviare grammi dalla lotteria all’indirizzo del proprietario.
Cominciamo con il primo. Scriviamo prima un test che verificherà che dopo aver inviato il messaggio, lo smart contract abbia salvato il nuovo indirizzo nell'archivio. Tieni presente che nel messaggio, oltre allo sportello e al nuovo indirizzo, trasmettiamo anche action Un numero intero non negativo a 7 bit, a seconda di esso sceglieremo come elaborare il messaggio nello smart contract.
<b 0 32 u, 1 @ 7 u, new_owner_wc @ 32 i, new_owner_account_id @ 256 u, b> message_to_sign !Nel test puoi vedere come viene deserializzato lo storage dello smartcontract storage in Quinto. La deserializzazione delle variabili è descritta nella documentazione Fift.
con aggiunta di impasto.
Eseguiamo il test e assicuriamoci che fallisca. Ora aggiungiamo la logica per modificare l'indirizzo del proprietario della lotteria.
Nel contratto intelligente continuiamo ad analizzare message, leggi action. Ricordiamo che ne avremo due action: cambia indirizzo e invia grammi.
Successivamente leggiamo il nuovo indirizzo del titolare del contratto e lo salviamo in archivio.
Eseguiamo i test e vediamo che il terzo test fallisce. Si blocca perché il contratto ora analizza inoltre dal messaggio 7 bit che mancano nel test. Aggiungine uno inesistente al messaggio action. Facciamo i test e vediamo che tutto passi. impegnarsi nei cambiamenti. Grande.
Ora scriviamo la logica per inviare il numero di grammi specificato all'indirizzo precedentemente salvato.
Per prima cosa, scriviamo un test. Scriveremo due test, uno quando non c'è abbastanza equilibrio, il secondo quando tutto dovrebbe passare con successo. È possibile visualizzare i test .
Ora aggiungiamo il codice. Innanzitutto, scriviamo due metodi di supporto. Il primo metodo è scoprire il saldo attuale di un contratto intelligente.
int balance() inline_ref method_id {
return get_balance().pair_first();
}E il secondo serve per inviare grammi a un altro contratto intelligente. Ho copiato completamente questo metodo da un altro contratto intelligente.
() send_grams(int wc, int addr, int grams) impure {
;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
cell msg = begin_cell()
;; .store_uint(0, 1) ;; 0 <= format indicator int_msg_info$0
;; .store_uint(1, 1) ;; 1 <= ihr disabled
;; .store_uint(1, 1) ;; 1 <= bounce = true
;; .store_uint(0, 1) ;; 0 <= bounced = false
;; .store_uint(4, 5) ;; 00100 <= address flags, anycast = false, 8-bit workchain
.store_uint (196, 9)
.store_int(wc, 8)
.store_uint(addr, 256)
.store_grams(grams)
.store_uint(0, 107) ;; 106 zeroes + 0 as an indicator that there is no cell with the data.
.end_cell();
send_raw_message(msg, 3); ;; mode, 2 for ignoring errors, 1 for sender pays fees, 64 for returning inbound message value
}Aggiungiamo questi due metodi allo smart contract e scriviamo la logica. Per prima cosa analizziamo il numero di grammi del messaggio. Successivamente controlliamo il saldo, se non è sufficiente lanciamo un'eccezione. Se tutto va bene, inviamo i grammi all'indirizzo salvato e aggiorniamo il contatore.
int amount_to_send = message~load_grams();
throw_if(36, amount_to_send + 500000000 > balance());
accept_message();
send_grams(owner_wc, owner_account_id, amount_to_send);
set_data(pack_state(stored_seqno + 1, pubkey, order_seqno, number_of_wins, incoming_amount, outgoing_amount, owner_wc, owner_account_id, orders));sembra il contratto intelligente al momento. Eseguiamo i test e assicuriamoci che passino.
A proposito, ogni volta che viene elaborato un messaggio viene detratta una commissione dallo smart contract. Affinché i messaggi del contratto intelligente eseguano la richiesta, dopo i controlli di base è necessario chiamare accept_message().
Passiamo ora ai messaggi interni. Infatti accetteremo solo grammi e restituiremo il doppio della somma al giocatore se vince e un terzo al proprietario se perde.
Per prima cosa, scriviamo un semplice test. Per fare ciò, abbiamo bisogno di un indirizzo di prova dello smart contract dal quale presumibilmente inviamo grammi allo smart contract.
L'indirizzo del contratto intelligente è composto da due numeri, un numero intero a 32 bit responsabile della catena di lavoro e un numero di account univoco intero non negativo a 256 bit in questa catena di lavoro. Ad esempio, -1 e 12345, questo è l'indirizzo che salveremo in un file.
Ho copiato la funzione per salvare l'indirizzo da .
// ( wc addr fname -- ) Save address to file in 36-byte format
{ -rot 256 u>B swap 32 i>B B+ swap B>file } : save-addressDiamo un'occhiata a come funziona la funzione, questo darà una comprensione di come funziona Fift. Avvia Fift in modalità interattiva.
~/TON/build/crypto/fift -i Per prima cosa inseriamo nello stack -1, 12345 e il nome del futuro file "sender.addr":
-1 12345 "sender.addr" Il passo successivo è eseguire la funzione -rot, che sposta lo stack in modo tale che in cima allo stack ci sia un numero di contratto intelligente univoco:
"sender.addr" -1 12345256 u>B converte un numero intero non negativo a 256 bit in byte.
"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039swap scambia i primi due elementi dello stack.
"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -132 i>B converte un numero intero a 32 bit in byte.
"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFFB+ collega due sequenze di byte.
"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFFdi nuovo swap.
BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" E infine i byte vengono scritti nel file B>file. Dopodiché il nostro stack è vuoto. Fermiamoci Fift. È stato creato un file nella cartella corrente sender.addr. Spostiamo il file nella cartella creata test/addresses/.
Scriviamo un semplice test che invierà grammi a un contratto intelligente. .
Ora diamo un'occhiata alla logica della lotteria.
La prima cosa che facciamo è controllare il messaggio bounced o no se bounced, allora lo ignoriamo. bounced significa che il contratto restituirà grammi se si verifica qualche errore. Non restituiremo grammi se si verifica improvvisamente un errore.
Controlliamo, se il saldo è inferiore a mezzo grammo, accettiamo semplicemente il messaggio e lo ignoriamo.
Successivamente, analizziamo l'indirizzo dello smart contract da cui proviene il messaggio.
Leggiamo i dati dalla memoria e poi cancelliamo le vecchie scommesse dallo storico se ce ne sono più di venti. Per comodità, ho scritto tre funzioni aggiuntive pack_order(), unpack_order(), remove_old_orders().
Successivamente, controlliamo se il saldo non è sufficiente per il pagamento, quindi consideriamo che questa non è una scommessa, ma un rifornimento e salviamo il rifornimento in orders.
Poi finalmente l'essenza dello smart contract.
Innanzitutto, se il giocatore perde, lo salviamo nella cronologia delle scommesse e se l'importo è superiore a 3 grammi, inviamo 1/3 al proprietario dello smart contract.
Se il giocatore vince, inviamo il doppio dell'importo all'indirizzo del giocatore e quindi salviamo le informazioni sulla scommessa nella cronologia.
() recv_internal(int order_amount, cell in_msg_cell, slice in_msg) impure {
var cs = in_msg_cell.begin_parse();
int flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
if (flags & 1) { ;; ignore bounced
return ();
}
if (order_amount < 500000000) { ;; just receive grams without changing state
return ();
}
slice src_addr_slice = cs~load_msg_addr();
(int src_wc, int src_addr) = parse_std_addr(src_addr_slice);
(int stored_seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) = unpack_state();
orders = remove_old_orders(orders, order_seqno);
if (balance() < 2 * order_amount + 500000000) { ;; not enough grams to pay the bet back, so this is re-fill
builder order = pack_order(order_seqno, 1, now(), order_amount, src_wc, src_addr);
orders~udict_set_builder(32, order_seqno, order);
set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins, incoming_amount + order_amount, outgoing_amount, owner_wc, owner_account_id, orders));
return ();
}
if (rand(10) >= 4) {
builder order = pack_order(order_seqno, 3, now(), order_amount, src_wc, src_addr);
orders~udict_set_builder(32, order_seqno, order);
set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins, incoming_amount + order_amount, outgoing_amount, owner_wc, owner_account_id, orders));
if (order_amount > 3000000000) {
send_grams(owner_wc, owner_account_id, order_amount / 3);
}
return ();
}
send_grams(src_wc, src_addr, 2 * order_amount);
builder order = pack_order(order_seqno, 2, now(), order_amount, src_wc, src_addr);
orders~udict_set_builder(32, order_seqno, order);
set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins + 1, incoming_amount, outgoing_amount + 2 * order_amount, owner_wc, owner_account_id, orders));
}Tutto qui. .
Ora non resta che semplice, creiamo dei metodi get in modo da poter ottenere informazioni sullo stato del contratto dal mondo esterno (infatti, leggere i dati dal loro archivio smart contract).
. Scriveremo di seguito su come ricevere informazioni su uno smart contract.
Ho anche dimenticato di aggiungere il codice che elaborerà la primissima richiesta che si verifica quando si pubblica uno smart contract. . E inoltre bug con l'invio di 1/3 dell'importo sul conto del proprietario.
Il prossimo passo è pubblicare lo smart contract. Creiamo una cartella requests.
Ho preso come base il codice di pubblicazione который nel repository ufficiale.
Qualcosa a cui vale la pena prestare attenzione. Generiamo uno storage del contratto intelligente e un messaggio di input. Successivamente viene generato l'indirizzo dello smart contract, ovvero l'indirizzo è noto anche prima della pubblicazione in TON. Successivamente, è necessario inviare diversi grammi a questo indirizzo e solo dopo è necessario inviare un file con lo smart contract stesso, poiché la rete prende una commissione per l'archiviazione dello smart contract e delle operazioni in esso (validatori che archiviano ed eseguono smart contratti). .
Successivamente eseguiamo il codice di pubblicazione e otteniamo lottery-query.boc file e indirizzo del contratto intelligente.
~/TON/build/crypto/fift -s requests/new-lottery.fif 0Non dimenticare di salvare i file generati: lottery-query.boc, lottery.addr, lottery.pk.
Tra le altre cose, vedremo l'indirizzo dello smart contract nei log di esecuzione.
new wallet address = 0:044910149dbeaf8eadbb2b28722e7d6a2dc6e264ec2f1d9bebd6fb209079bc2a
(Saving address to file lottery.addr)
Non-bounceable address (for init): 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd
Bounceable address (for later access): kQAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KpFYGiusto per divertimento, facciamo una richiesta a TON
$ ./lite-client/lite-client -C ton-lite-client-test1.config.json
getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KsydE vedremo che l'account con questo indirizzo è vuoto.
account state is emptyInviamo all'indirizzo 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gram e dopo pochi secondi eseguiamo lo stesso comando. Per inviare grammi utilizzo , e puoi chiedere a qualcuno della chat i grammi di prova, di cui parlerò alla fine dell'articolo.
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KsydSembra un non inizializzato (state:account_uninit) un contratto intelligente con lo stesso indirizzo e un saldo di 1 di nanogrammi.
account state is (account
addr:(addr_std
anycast:nothing workchain_id:0 address:x044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A)
storage_stat:(storage_info
used:(storage_used
cells:(var_uint len:1 value:1)
bits:(var_uint len:1 value:103)
public_cells:(var_uint len:0 value:0)) last_paid:1583257959
due_payment:nothing)
storage:(account_storage last_trans_lt:3825478000002
balance:(currencies
grams:(nanograms
amount:(var_uint len:4 value:2000000000))
other:(extra_currencies
dict:hme_empty))
state:account_uninit))
x{C00044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A20259C2F2F4CB3800000DEAC10776091DCD650004_}
last transaction lt = 3825478000001 hash = B043616AE016682699477FFF01E6E903878CDFD6846042BA1BFC64775E7AC6C4
account balance is 2000000000ngAdesso pubblichiamo lo smart contract. Lanciamo lite-client ed eseguiamo.
> sendfile lottery-query.boc
[ 1][t 2][1583008371.631410122][lite-client.cpp:966][!testnode] sending query from file lottery-query.boc
[ 3][t 1][1583008371.828550100][lite-client.cpp:976][!query] external message status is 1 Controlliamo che il contratto sia stato pubblicato.
> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KsydTra le altre cose otteniamo.
storage:(account_storage last_trans_lt:3825499000002
balance:(currencies
grams:(nanograms
amount:(var_uint len:4 value:1987150999))
other:(extra_currencies
dict:hme_empty))
state:(account_activeLo vediamo account_active.
Commit corrispondente con modifiche .
Ora creiamo le richieste per interagire con lo smart contract.
Più precisamente, lasceremo il primo per modificare l'indirizzo come lavoro indipendente, e faremo il secondo per inviare grammi all'indirizzo del proprietario. In effetti, dovremo fare la stessa cosa del test per l'invio di grammi.
Questo è il messaggio che invieremo allo smart contract, dove msg_seqno 165 action 2 e 9.5 grammi per l'invio.
<b 165 32 u, 2 7 u, 9500000000 Gram, b>Non dimenticare di firmare il messaggio con la tua chiave privata lottery.pk, che è stato generato in precedenza durante la creazione del contratto intelligente. .
Ricevere informazioni da un contratto intelligente utilizzando i metodi get
Ora diamo un'occhiata a come eseguire i metodi di acquisizione del contratto intelligente.
Lanciamo lite-client ed esegui i metodi get che abbiamo scritto.
$ ./lite-client/lite-client -C ton-lite-client-test1.config.json
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd balance
arguments: [ 104128 ]
result: [ 64633878952 ]
...В result contiene il valore restituito dalla funzione balance() dal nostro contratto intelligente.
Faremo lo stesso per molti altri metodi.
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_seqno
...
arguments: [ 77871 ]
result: [ 1 ] Richiediamo la cronologia delle tue scommesse.
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_orders
...
arguments: [ 67442 ]
result: [ ([0 1 1583258284 10000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [1 3 1583258347 4000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [2 1 1583259901 50000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308]) ] Utilizzeremo il client lite e otterremo metodi per visualizzare le informazioni sul contratto intelligente sul sito.
Visualizzazione dei dati del contratto intelligente sul sito web
Ho scritto un semplice sito Web in Python per visualizzare i dati del contratto intelligente in modo conveniente. Qui non mi dilungherò nel dettaglio e pubblicherò il sito .
Le richieste a TON vengono effettuate da Python via lite-client. Per comodità, il sito è impacchettato in Docker e pubblicato su Google Cloud. .
Provare
Ora proviamo a inviare grammi lì per il rifornimento . Invieremo 40 grammi. E facciamo un paio di puntate per chiarezza. Vediamo che il sito mostra la cronologia delle scommesse, l'attuale percentuale di vincita e altre informazioni utili.
che abbiamo vinto il primo, perso il secondo.
postfazione
L'articolo si è rivelato molto più lungo di quanto mi aspettassi, forse poteva essere più breve, o forse solo per una persona che non sa nulla di TON e vuole scrivere e pubblicare uno smart contract non così semplice con la possibilità di interagire con Esso. Forse alcune cose si potevano spiegare più semplicemente.
Forse alcuni aspetti dell'implementazione avrebbero potuto essere eseguiti in modo più efficiente ed elegante, ma in tal caso ci sarebbe voluto ancora più tempo per preparare l'articolo. È anche possibile che io abbia commesso un errore da qualche parte o non abbia capito qualcosa, quindi se stai facendo qualcosa di serio devi fare affidamento sulla documentazione ufficiale o sul repository ufficiale con il codice TON.
Va notato che poiché TON stesso è ancora in una fase attiva di sviluppo, potrebbero verificarsi modifiche che interromperanno uno qualsiasi dei passaggi di questo articolo (cosa che è avvenuta mentre stavo scrivendo, è già stato corretto), ma l'approccio generale è improbabile che cambi.
Non parlerò del futuro di TON. Forse la piattaforma diventerà qualcosa di grande e dovremmo dedicare del tempo a studiarla e riempire una nicchia con i nostri prodotti adesso.
C'è anche Libra di Facebook, che ha un pubblico potenziale di utenti più grande di TON. Non so quasi nulla di Libra, a giudicare dal forum c'è molta più attività lì che nella comunità TON. Anche se gli sviluppatori e la comunità di TON sono più simili all'underground, il che è anche bello.
riferimenti
- Documentazione ufficiale TON:
- Repository ufficiale TON:
- Portafoglio ufficiale per diverse piattaforme:
- Repository di contratti intelligenti da questo articolo:
- Link al sito web del contratto intelligente:
- Repository per l'estensione per Visual Studio Code per FunC:
- Chatta su TON in Telegram, che mi ha davvero aiutato a capirlo nella fase iniziale. Penso che non sia un errore se dico che tutti quelli che hanno scritto qualcosa per TON sono lì. Puoi anche chiedere i grammi di prova lì.
- Un'altra chiacchierata su TON in cui ho trovato informazioni utili:
- Prima fase del concorso:
- Seconda fase del concorso:
Fonte: habr.com
