Аб тым як напісаць і апублікаваць смарт-кантракт у TON
Пра што гэты артыкул?
У артыкуле я распавяду аб тым, як паўдзельнічаў у першым (з двух) конкурсе Telegram па блокчейне, не заняў прызавое месца і вырашыў зафіксаваць досвед у артыкуле, каб ён не адышоў у Лёце і, магчыма, дапамог каму-небудзь.
Так як мне не хацелася пісаць абстрактны код, а зрабіць нешта працоўнае, для артыкула я напісаў смарт-кантракт маментальную латарэю і сайт, які паказвае дадзеныя смарт-кантракта напрамую з TON без выкарыстання прамежкавых сховішчаў.
Артыкул будзе карысны тым, хто хоча зрабіць свой першы смарт-кантракт у TON, але не ведае з чаго пачаць.
На прыкладзе латарэі я прайду ад усталёўкі асяроддзя да публікацыі смарт-кантракту, узаемадзеянні з ім і напішу сайт для атрымання і публікацыі дадзеных.
Аб удзеле ў конкурсе
У кастрычніку мінулага года Telegram аб'явіў конкурс па блокчэйне з новымі мовамі. Fift и FunC. Трэба было на выбар напісаць любыя з пяці прапанаваных смарт-кантрактаў. Я палічыў, што будзе нядрэнна заняцца чымсьці незвычайным, вывучыць мову і зрабіць што-небудзь, нават калі ў будучыні не давядзецца пісаць штосьці яшчэ. Плюс, тэма ўвесь час на слыху.
Варта сказаць, што досведу распрацоўкі смарт-кантрактаў у мяне не было.
Я планаваў удзельнічаць да самага канца пакуль атрымліваецца і пасля напісаць аглядны артыкул, але зафейліўся адразу на першым. Я з мульты-подпісам на FunC і ён увогуле працаваў. За аснову ўзяў .
На той момант я палічыў, гэтага дакладна дастаткова, каб заняць хаця б нейкае прызавое месца. У выніку каля 40 з 60 удзельнікаў сталі прызёрамі і мяне сярод іх не было. Увогуле, у гэтым нічога страшнага, але мяне напружыла адна рэч. На момант аб'ява вынікаў рэўю з тэстам да майго кантракту не было зроблена, я спытаў ва ўдзельнікаў у чаце ці ёсць хто яшчэ ў каго яго няма, такіх не было.
Мабыць звярнуўшы ўвагу на мае паведамленні праз два дні суддзі апублікавалі каментар і я так і не зразумеў, яны выпадкова прапусцілі мой смарт-кантракт падчас судзейства ці проста палічылі, што ён настолькі дрэнны, што не мае патрэбы ў каментары. Я задаў пытаньне на старонцы, але адказу не атрымаў. Хоць хто судзіў - не сакрэт, пісаць асабістыя паведамленні я палічыў лішнім.
Часу на разуменне было патрачана нямала, таму было вырашана напісаць артыкул. Паколькі інфармацыі пакуль не вельмі шмат, то артыкул дапаможа зэканоміць час усім зацікаўленым.
Канцэпт працы смарт-кантрактаў у TON
Перш чым нешта пісаць трэба разабрацца з якога боку ўвогуле падысці да гэтай штукі. Таму зараз я раскажу з якіх частак сістэма складаецца. Дакладней якія часткі трэба ведаць каб напісаць хаця б нейкі працоўны кантракт.
Мы засяродзімся на напісанні смарт-кантракту і працы з TON Virtual Machine (TVM), Fift и FunC, таму артыкул больш падобны на апісанне распрацоўкі звычайнай праграмы. На тым, як працуе сама платформа, тут спыняцца не будзем.
Наогул аб тым як працуе TVM і мова Fift ёсць добрая афіцыйная дакументацыя. Падчас удзелу ў конкурсе і зараз падчас напісання бягучага кантракту я часта звяртаўся да яе.
Асноўная мова на якой пішуцца смарт-кантракты. FunC. Дакументацыі па ім на дадзены момант няма, таму каб нешта напісаць трэба вывучаць прыклады смарт-кантрактаў з афіцыйнага рэпазітара і саму рэалізацыю мовы там жа, плюс можна глядзець прыклады смарт-кантрактаў за мінулыя два конкурсы. Спасылкі ў канцы артыкула.
Дапусцім мы ўжо напісалі смарт-кантракт на FunC, пасля гэтага мы кампілюем код у Fift-асэмблер.
Скампіляваны смарткантракт застаецца апублікаваць. Для гэтага трэба напісаць функцыю на Fift, які на ўваход будзе прымаць код смарт-кантракту і яшчэ некаторыя параметры, а на выхадзе атрымаецца файл з пашырэннем .boc (што азначае "bag of cells"), і, у залежнасці ад таго як напішам, прыватны ключ і адрас, які генеруецца на аснове кода смарт-кантракта. На адрас смарт-кантракта, які яшчэ не апублікаваны, ужо можна адпраўляць грамы.
Каб апублікаваць смарт-кантракт у TON атрыманы .boc файл трэба будзе адправіць у блокчейн з дапамогай лайт-кліента (пра што ніжэй). Але перад тым як публікаваць трэба перавесці грамаў на згенераваны адрас, інакш смарт-кантракт не будзе апублікаваны. Пасля публікацыі са смарт-кантрактам можна будзе ўзаемадзейнічаць, адпраўляючы яму паведамленні звонку (напрыклад, з дапамогай лайт-кліента) або знутры (напрыклад, адзін смарт-кантракт шле іншаму паведамленне ўнутры TON).
Пасля таго як мы зразумелі як публікуецца код, далей становіцца прасцей. Мы прыкладна ведаем, што хочам напісаць і як будзе працаваць наша праграма. І падчас напісання шукаем як гэта ўжо рэалізавана ў існуючых смарт-кантрактах, альбо зазіраем у код рэалізацыі Fift и FunC у афіцыйным рэпазітары, альбо глядзім у афіцыйнай дакументацыі.
Вельмі часта я шукаў па ключавых словах у Telegram чаце, дзе сабраліся ўсе ўдзельнікі конкурсу і супрацоўнікі Telegram у тым ліку, так атрымалася што падчас конкурсу ўсе сабраліся менавіта там і пачалі абмяркоўваць Fift і FunC.
Час перайсці ад тэорыі да практыкі.
Падрыхтоўка асяроддзя для працы з TON
Усё, што будзе апісана ў артыкуле я рабіў на MacOS і пераправерыў ў чыстай Ubuntu 18/04 LTS на Docker.
Першае што трэба зрабіць спампаваць і ўсталяваць lite-client з дапамогай якога можна адпраўляць запыты ў TON.
Інструкцыя на афіцыйным сайце даволі падрабязна і зразумела апісвае працэс усталёўкі і апускае некаторыя дэталі. Тут мы прытрымліваемся інструкцыі адначасна ўсталёўваючы адсутнічаюць залежнасці. Я не стаў сам кампіляваць кожны праект і ўсталёўваў з афіцыйнага рэпазітара Ubuntu (на MacOS я выкарыстаў 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 Пасля таго як усе залежнасці ўстаноўлены можна ўсталяваць lite-client, Fift, FunC.
Спачатку клануем рэпазітар TON разам з залежнасцямі. Для зручнасці ўсё будзем рабіць у тэчцы ~/TON.
cd ~/TON
git clone https://github.com/ton-blockchain/ton.git
cd ./ton
git submodule update --init --recursiveУ рэпазітары таксама захоўваюцца рэалізацыі Fift и FunC.
Цяпер мы гатовы сабраць праект. Код рэпазітара схіляваны ў тэчку ~/TON/ton. У ~/TON ствараем тэчку build і збіраем у ёй праект.
mkdir ~/TON/build
cd ~/TON/build
cmake ../tonБо мы збіраемся пісаць смарт-кантракт нам патрэбен не толькі lite-client, Але і Fift с FunC, таму кампілюем ўсё. Ня хуткі працэс таму чакаем.
cmake --build . --target lite-client
cmake --build . --target fift
cmake --build . --target funcДалей спампоўваем канфігурацыйны файл у якім ляжаць дадзеныя аб нодзе да якой lite-client будзе падключацца.
wget https://test.ton.org/ton-lite-client-test1.config.jsonЯкі робіцца першыя запыты ў TON
Цяпер запусцім lite-client.
cd ~/TON/build
./lite-client/lite-client -C ton-lite-client-test1.config.jsonКалі зборка прайшла паспяхова, то пасля запуску вы ўбачыце лог падключэння лайт кліента да нады.
[ 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)
...Можна выканаць каманду help і паглядзець якія каманды даступныя.
helpПералічоны каманды, якія мы будзем выкарыстоўваць у гэтым артыкуле.
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-методы смартконтракта. Цяпер мы гатовы да напісання самога кантракта.
Рэалізацыя
Ідэя
Як ужо пісаў вышэй, смарт-кантракт які мы пішам гэта латарэя.
Прычым гэта не латарэя, у якой трэба купіць білет і чакаць гадзіну, дзень ці месяц, а маментальная ў якой карыстальнік перакладае на адрас кантракта. N грамаў, і маментальна атрымлівае назад 2 * N грамаў або прайгравае. Верагоднасць перамогі зробім каля 40 працэнтаў. Калі грамаў для выплаты не дастаткова, то будзем лічыць транзакцыю папаўненнем.
Прычым важна каб стаўкі можна было бачыць у рэальным часе і ў зручным выглядзе, каб карыстач адразу мог зразумець выйграў ён ці прайграў. Таму трэба зрабіць вэб-сайт, які пакажа стаўкі і вынік напрамую з TON.
Напісанне смарт-кантракта
Для выгоды я зрабіў подстветку кода для FunC, убудова можна знайсці і ўсталяваць у пошуку Visual Studio Code, калі раптам захочацца дадаць нешта, то выклаў убудову ў адчынены доступ. Таксама раней кімсьці быў зроблены ўбудова для працы з Fift, таксама можна і ўсталяваць знайсці ў VSC.
Адразу створым рэпазітар куды будзем камітаваць прамежкавыя вынікі.
Каб аблегчыць сабе жыццё мы будзем пісаць смарт-кантракт і тэставаць лакальна, да таго часу, пакуль ён не будзе гатовы. Толькі пасля гэтага апублікуем яго ў TON.
У смарт-кантракта ёсць два вонкавыя метады да якіх можна звяртацца. Першы, recv_external() гэтая функцыя выконваецца калі запыт да кантракту паходзіць з навакольнага свету, гэта значыць не з TON, напрыклад калі мы самі фармуем паведамленне і адпраўляем яго праз lite-client. Другі, recv_internal() гэта калі ўсярэдзіне самога TON які-небудзь кантракт звяртаецца да нашага. У абодвух выпадках можна перадаць параметры ў функцыю.
Давайце пачнем з простага прыкладу, які будзе працаваць калі яго апублікаваць, але ніякай функцыянальнай нагрузкі ў ім няма.
() recv_internal(slice in_msg) impure {
;; TODO: implementation
}
() recv_external(slice in_msg) impure {
;; TODO: implementation
}Тут трэба растлумачыць што такое slice. Усе якія захоўваюцца дадзеныя ў TON Blockchain гэта калекцыя TVM cell ці проста cell, у такім вочку можна захоўваць да 1023 біт дадзеных і да 4 спасылак на іншыя вочкі.
TVM cell slice або slice гэта частка існуючай cell выкарыстоўваецца для яе парсінгу, далей будзе зразумела. Галоўнае для нас, што ў смарт-кантракт мы можам перадаць slice і ў залежнасці ад віду паведамлення апрацаваць дадзеныя ў recv_external() або recv_internal().
impure - ключавое слова, якое паказвае на тое, што функцыя змяняе дадзеныя смарт-кантракту.
Захаваем код кантракта ў lottery-code.fc і скампілюем.
~/TON/build/crypto/func -APSR -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc Значэнне сцягоў можна паглядзець з дапамогай каманды
~/TON/build/crypto/func -helpУ нас атрымаўся скампіляваны Fift-асэмблер код у 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>cЯго можна запусціць лакальна, для гэтага падрыхтуем асяроддзе.
Заўважым, першым радком падключаецца Asm.fif, гэта код напісаны на Fift для Fift-асэмблера.
Бо мы жадаем запускаць і тэставаць смарт-кантракт лакальна створым файл lottery-test-suite.fif і скапіюем туды скампіляваны код замяніўшы ў ім апошні радок, якая запісвае код смарткантракта ў канстанту code, каб потым перадаць яго ў віртуальную машыну:
"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
Пакуль накшталт зразумела, зараз дадамо ў той жа файл код, які мы будзем выкарыстоўваць для запуску 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 мы запісваем кантэкст, гэта значыць дадзеныя з якімі будзе запускацца TVM (ці стан сеткі). Яшчэ падчас конкурсу адзін з распрацоўшчыкаў паказаў як ствараецца c7 і я скапіяваў. У гэтым артыкуле нам магчыма трэба будзе мяняць rand_seed бо ад яго залежыць генерацыя выпадковага ліку і не змяняць, то кожны раз будзе вяртацца тое самае лік.
recv_internal и recv_external канстанты са значэннем 0 і -1 будуць адказваць за выклік адпаведных функцый у смарт-кантракце.
Цяпер мы гатовы стварыць першы тэст да нашага пустога смарт-кантракту. Для нагляднасці пакуль усе тэсты мы будзем дадаваць у гэты ж файл lottery-test-suite.fif.
Створым зменную storage і запішам у яе пусты cell, гэта будзе сховішча смарт-кантракту.
message гэтае паведамленне, якое мы перадамо смарт-кантакту звонку. Яго таксама зробім пакуль пустым.
variable storage
<b b> storage !
variable message
<b b> message ! Пасля таго як мы падрыхтавалі канастанты і зменныя мы запускаем TVM з дапамогай каманды runvmctx і перадаем створаныя параметры на ўваход.
message @
recv_external
code
storage @
c7
runvmctx У выніку ў нас атрымаецца прамежкавы код на Fift.
Цяпер мы можам запусціць палепшаны код.
export FIFTPATH=~/TON/ton/crypto/fift/lib // выполняем один раз для удобства
~/TON/build/crypto/fift -s lottery-test-suite.fif Праграма павінна адпрацаваць без памылак і ў выснове ўбачым лог выканання:
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=0Выдатна, мы напісалі першую працоўную версію смарт-кантракту.
Зараз трэба дадаваць функцыянал. Спачатку зоймемся паведамленнямі, якія прыходзіць са знешняга свету ў recv_external()
Распрацоўнік сам выбірае фармат паведамлення якое кантракт можа прыняць.
Але звычайна,
- па-першае, мы жадаем абараніць наш кантракт ад навакольнага свету і зрабіць так каб толькі ўладальнік кантракту мог адпраўляць на яго вонкавыя паведамленні.
- па-другое калі мы адпраўляем валіднае паведамленне ў TON мы жадаем каб гэта адбылося роўна адзін раз і пры паўторнай адпраўцы таго ж паведамлення смарт-кантракт адхіліў яго.
Таму амаль у кожным кантракце вырашаюцца гэтыя дзве праблемы, бо наш кантракт прымае вонкавыя паведамленні, нам таксама трэба паклапаціцца пра гэта.
Зробім мы ў зваротным парадку. Спачатку вырашым праблему з паўтарэннем, калі кантракт ужо атрымліваў такое паведамленне і апрацаваў яго, то не будзе яго выконваць другі раз. І потым вырашым праблему з тым каб толькі вызначанае кола асобаў мог адпраўляць паведамленні смарт-кантракту.
Ёсць розныя спосабы вырашыць праблему з паўтаральнымі паведамленнямі. Мы зробім вось як. У смарт-кантракце ініцыялізуем лічыльнік прынятых паведамленняў з першапачатковым значэннем 0. У кожным паведамленні смарт-кантракту будзем дадаваць бягучае значэнне лічыльніка. Калі значэнне лічыльніка ў паведамленні не супадае са значэннем у смарт-кантракце, то мы не апрацоўваем яго, калі супадае, то апрацоўваем і павялічваем лічыльнік у смарт-кантракце на 1.
Вяртаемся ў lottery-test-suite.fif і дапісваем у яго другі тэст. Адправім няправільны нумар, код павінен выкінуць выключэнне. Напрыклад няхай у дадзеных кантракта захоўваецца 166, а мы адправім 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"Запусцім.
~/TON/build/crypto/fift -s lottery-test-suite.fif І ўбачым, што тэст выконваецца з памылкай.
[ 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 passedНа гэтым этапе lottery-test-suite.fif павінен выглядаць як .
Цяпер давайце дапішам логіку лічыльніка ў смарт-кантракту ў 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 ляжыць паведамленне, якое мы адпраўляем.
Першае, што мы робім правяраем калі ў паведамленні ёсць дадзеныя, калі не, то проста выходзім.
Далей мы парсім паведамленне. in_msg~load_uint(32) загружае лік 165, 32-х бітнае unsigned int з перададзенага паведамлення.
Далей мы загружаем 32 біта са сховішча смарт-кантракту. Правяраем, што загружаны лік супадае з перададзеным, калі не выкідваем выключэнне. У нашым выпадку, бо мы перадаем несупаднае, павінна выкідвацца выключэнне.
Зараз скампілюем.
~/TON/build/crypto/func -APSR -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc Атрыманы код скапіюем у lottery-test-suite.fif, не забываючы замяніць апошні радок.
Правяраем, што тэст праходзіць:
~/TON/build/crypto/fift -s lottery-test-suite.fifможна паглядзець адпаведны коміт з бягучымі вынікамі.
Заўважым, што ўвесь час капіяваць скампіляваны код смарт-кантракту ў файл з тэстамі няёмка, таму напішам скрыпт, які будзе запісваць код у канстанту за нас, а мы проста падлучым скампіляваны код у нашы тэсты з дапамогай "include".
У тэчцы з праектам створым файл build.sh з наступным зместам.
#!/bin/bash
~/TON/build/crypto/func -SPA -R -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fcЗробім яго выкананым.
chmod +x ./build.shЦяпер, дастаткова запусціць наш скрыпт, каб скампіляваць кантракт. Але акрамя гэтага нам трэба запісаць яго ў канстанту code. Таму мы створым новы файл lotter-compiled-for-test.fif, які і ўключым у файле lottery-test-suite.fif.
Дадамо ў sh скирпт код, які будзе проста дубляваць скампляваны файл у lotter-compiled-for-test.fif і мяняць у ім апошні радок.
# 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.fifЗараз каб праверыць, запусцім атрыманы скрыпт і ў нас згенеруецца файл lottery-compiled-for-test.fif, які мы ўключым у наш lottery-test-suite.fif
В lottery-test-suite.fif выдаляем код кантракт і дадаем радок "lottery-compiled-for-test.fif" include.
Запускаем тэсты, каб праверыць, што яны праходзяць.
~/TON/build/crypto/fift -s lottery-test-suite.fifВыдатна, зараз, каб аўтаматызаваць запуск тэстаў створым файл test.sh, які спачатку будзе выконваць build.sh, А потым запускаць тэсты.
touch test.sh
chmod +x test.shУнутр пішам
./build.sh
echo "nCompilation completedn"
export FIFTPATH=~/TON/ton/crypto/fift/lib
~/TON/build/crypto/fift -s lottery-test-suite.fifЗробім test.sh і запусцім, каб пераканацца ў працаздольнасці тэстаў.
chmod +x ./test.sh
./test.shПравяраем, што кантракт кампілюецца і тэсты выконваюцца.
Выдатна, зараз пры запуску test.sh адразу будзе адбывацца кампіляцыя і запуск тэстаў. Вось спасылка на .
Окей, перш чым мы працягнем давайце для зручнасці зробім яшчэ адну рэч.
Створым тэчку build дзе будзем захоўваць скапіляваны кантракт і яго клон запісаны ў канстанту lottery-compiled.fif, lottery-compiled-for-test.fif. Таксама створым тэчку test дзе будуць захоўваецца файл з тэстамі lottery-test-suite.fif і патэнцыйна іншыя дапаможныя файлы. .
Працягнем распрацоўку смарт-кантракта.
Далей павінен быць тэст, які правярае, што паведамленне прымаецца і лічыльнік абнаўляецца ў сховішча, калі мы адпраўляем правільны лік.
Цяпер падумаем над тым, якая структура дадзеных і якія дадзеныя трэба захоўваць у смарт-кантракце.
Апішу ўсё што мы захоўваем.
`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` переменная типа словарь, хранит последние двадцать ставок. Далей трэба напісаць дзве функцыі. pack_state(), якая будзе пакаваць дадзеныя для наступнага захавання яго ў сховішча смарт-кантракту. Другую, назавем unpack_state() будзе счытваць і вяртаць дадзеныя са сховішча.
_ 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;
}Дадаем гэтыя дзве функцыі спачатку смарт-кантракту. Атрымаецца прамежкавы вынік.
Каб захаваць дадзеныя трэба будзе выклікаць убудаваную фунцкію set_data() і яна запіша дадзеныя з pack_state() у сховішча смарт-кантракта.
cell packed_state = pack_state(arg_1, .., arg_n);
set_data(packed_state);Цяпер калі ў нас ёсць зручныя функцыі запісу і чытання дадзеных мы можам рухацца далей.
Нам трэба праверыць, што ўваходнае звонку паведамленне падпісана ўладальнікам кантракту (ну ці іншым карыстальнікам, які мае доступ да прыватнага ключа).
Калі мы публікуем смарт-кантракт мы можам ініцыялізаваць яго з патрэбнымі нам дадзенымі ў сховішча, якія захаваюцца для будучага выкарыстання. Мы запішам туды публічны ключ, каб можна было праверыць, што подпіс уваходнага паведамлення быў зроблены адпаведным прыватным ключом.
Перад тым як працягнуць створым прыватны ключ і запішам яго ў test/keys/owner.pk. Для гэтага запусцім Fift у інтэрактыўным рэжыме і выканаем чатыры каманды.
`newkeypair` генерация публичного и приватного ключа и запись их в стек.
`drop` удаления из стека верхнего элемента (в данном случае публичный ключ)
`.s` просто посмотреть что лежит в стеке в данный момент
`"owner.pk" B>file` запись приватного ключа в файл с именем `owner.pk`.
`bye` завершает работу с Fift. Створым тэчку keys ўнутры тэчкі test і туды запішам прыватны ключ.
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
byeБачым у бягучай тэчцы файл owner.pk.
Мы выдаляем публічны ключ са стэка, калі спатрэбіцца можам атрымаць яго з прыватнага.
Цяпер нам трэба напісаць праверку подпісу. Пачнём з цеста. Спачатку мы счытваем прыватны ключ з файла з дапамогай функцыі file>B і запісваем яго ў зменную owner_private_key, далей з дапамогай функцыі priv>pub канвертуем прыватны ключ у публічны і запішам вынік у 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 !Абодва ключы нам спатрэбяцца.
Ініцыялізуем адвольнымі дадзенымі сховішча смарт-кантракту ў той жа самай паслядоўнасці, як у функцыі pack_state()і запішам у зменную 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 !Далей складзем падпісанае паведамленне, у ім будзе толькі подпіс і значэнне лічыльніка.
Спачатку ствараем дадзеныя, якія хочам перадаць, потым падпісваем іх прыватным ключом і нарэшце фармуем падпісанае паведамленне.
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 ! У выніку паведамленне якое мы адправім у смарт-кантракт запісана ў зменную message_to_send, пра функцыі hashu, ed25519_sign_uint можна пачытаць .
І для запуску цеста зноў выклікаем.
message_to_send @
recv_external
code
storage @
c7
runvmctxпавінен выглядаць файл з тэстамі на дадзеным этапе.
Запусцім тэст і ён упадзе, таму зменім смарт-кантракт, каб ён змог атрымліваць паведамленні такога фармату і правяраць подпіс.
Спачатку лічым з паведамлення 512 біт подпісы і запішам у зменную, далей лічым 32 біта зменнай лічыльніка.
Так як у нас ёсць функцыя счытвання дадзеных са сховішча смарт-канракту будзем выкарыстоўваць яе.
Далей праверка лічыльніка перададзенага са сховішчам і праверка подпісу. Калі нешта не супадзе, то выкідваем выключэнне з адпаведным кодам.
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));Супаведны коміт .
Запусцім тэсты і ўбачым, што другі тэст падае. Па двух прычынах, недахоп бітаў у паведамленні і недахоп бітаў у сховішчы, таму код падае пры парсінгу. Трэба дадаць подпіс паведамлення, якое мы адпраўляем і скапіяваць сховішча з апошняга тэста.
У другім тэсце дадамо подпіс паведамлення і зменім сховішча смарт-кантракту. выглядае файл з тэстамі ў дадзены момант.
Напішам чацвёрты тэст, у якім будзем адпраўляць паведамленне падпісанае чужым прыватным ключом. Створым яшчэ адзін прыватны ключ і захаваем у файл not-owner.pk. Гэтым прыватным ключом падпішам паведамленне. Запусцім тэсты і пераканаемся, што ўсе тэсты праходзяць. на бягучы момант.
Цяпер нарэшце мы можам перайсці да рэалізацыі логікі смарткантракта.
В recv_external() мы будзем прымаць два тыпы паведамленняў.
Паколькі наш кантракт будзе акумуляваць пройгрышы гульцоў, гэтыя грошы трэба пералічваць стваральніку латарэі. Адрас кашалька стваральніка латарэі запісваецца ў сховішча пры стварэнні кантракта.
На ўсялякі выпадак нам патрэбна магчымасць мяняць адрас на які адпраўляць грамы прайграўшых. Таксама мы павінны мець магчымасць адпраўляць грамы з латарэі на адрас уладальніка.
Пачнём з першага. Напішам спачатку тэст, які будзе правяраць, што пасля адпраўкі паведамлення смарт-кантракт захаваў новы адрас у сховішча. Звернем увагу, што ў паведамленне акрамя лічыльніка і новага адрасу мы перадаем яшчэ action 7-мі бітнае цэлае неадмоўнае лік, у залежнасці ад яго, мы будзем выбіраць як апрацоўваць паведамленне ў смарт-кантракце.
<b 0 32 u, 1 @ 7 u, new_owner_wc @ 32 i, new_owner_account_id @ 256 u, b> message_to_sign !У цесце можна ўбачыць як адбываецца дэсерэалізацыя сховішча смарткантратка storage у Fift. Дэсерыялізацыя зменных апісана ў дакументацыі па Fift.
з даданнем цеста.
Запускаем тэст і пераканаемся, што ён падае.
У смарт-кантракце мы працягваем парсіць message, счытваем у action. Нагадаем, што ў нас будзе два action: змена адрасу і адпраўка грамаў.
Потым счытваем новы адрас уладальніка кантракту і захоўваемы ў сховішчы.
Запускаем тэсты і бачым, што трэці тэст падае. Падае з-за таго, што кантракт зараз дадаткова парсіць 7 біт з паведамлення, якіх не хапае ў цесцю. Дадамо ў паведамленне неіснуючы action.Запусцім тэсты і бачым, што ўсе праходзяць. коміт на змены.
Цяпер напішам логіку адпраўкі названай колькасці грамаў на захаваны раней адрас.
Спачатку напішам тэст. Мы напішам два тэст адзін калі балансу не хапае, другі калі ўсё павінна прайсці паспяхова. Тэсты можна паглядзець .
Цяпер дапішам код. Спачатку напішам два дапаможныя метады. Першы гет метад для таго каб даведацца пра бягучы баланс смарт-кантракту.
int balance() inline_ref method_id {
return get_balance().pair_first();
}І другі для адпраўкі грамаў на іншы смарт-кантракт. Гэты метад я цалкам скапіяваў з іншага смарт-кантракта.
() 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
}Дадамо гэтыя два метады ў смарт-кантракт і напішам логіку. Спачатку парсім колькасць грамаў з паведамлення.
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));выглядае смарт-кантракт на дадзены момант. Запусцім тэсты і пераканаемся, што яны праходзяць.
Дарэчы, за апрацаванае паведамленне ў смарт-кантракта кожны раз спісваецца камісія. Каб паведамленні смарт-кантракт вылупіў запыт, пасля базавых праверак трэба выклікаць accept_message().
Цяпер зоймемся ўнутранымі паведамленнямі. Па факце мы будзем толькі прымаць грамы і адсылаць зваротна гульцу падвойную суму пры выйгрышы і траціна ўладальніку пры пройгрышы.
Спачатку напішам просты тэст. Для гэтага нам спатрэбіцца тэставы адрас смарт-кантракта, з якога мы быццам бы адпраўляем грамы на смарт-кантракт.
Адрас смарткантракта складаецца з двух лікаў, 32-х бітнае цэлае лік адказвае за workchain і 256-ці цэлае неадмоўнае унікальны нумар акаўнта ў гэтым workchain.
Я скапіяваў функцыю па захаванні адрасы з .
// ( wc addr fname -- ) Save address to file in 36-byte format
{ -rot 256 u>B swap 32 i>B B+ swap B>file } : save-addressДавайце разбяром, як працуе функцыя, гэта дасць разуменне як працуе Fift. Запускаем Fift у інтэрактыўным рэжыме.
~/TON/build/crypto/fift -i Спачатку мы кладзем у стэк -1, 12345 і назоў будучага файла "sender.addr":
-1 12345 "sender.addr" Наступным крокам выконваецца функцыя -rot, якая зрушвае стэк, такім вобразам, што наверсе стэка аказваецца унікальны нумар смарт-кантракту:
"sender.addr" -1 12345256 u>B канвертуе 256-ці бітнае неадмоўнае цэлае ў байты.
"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039swap мяняе месцамі два верхніх элемента стэка.
"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -132 i>B канвертуе 32-х бітнае цэлае ў байты.
"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFFB+ злучае дзве паслядоўнасці з байтаў.
"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFFзноў swap.
BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" І нарэшце выконваецца запіс байтаў у файл B>file.Пасля гэтага наш стэк пусты. Fift. У бягучай тэчцы створаны файл sender.addr. Перанясём файл у створаную тэчку test/addresses/.
Напішам просты тэст, які будзе адпраўляць грамы на смарт-кантракт. .
Цяпер зоймемся логікай латарэі.
Першае, што мы робім, правяраем паведамленне bounced ці не, калі bounced, то ігнаруем. bounced значыць, кантракт верне грамы, калі адбудзецца нейкая памылка. Мы вяртаць грамы калі раптам узнікне памылка, мы не будзем.
Правяраем, баланс калі менш чым паўграма, то проста прыманы паведамленне і ігнаруемы.
Далей парым адрас смарткантракта з якога прыйшло паведамленне.
Счытваем дадзеныя са сховішча і далей выдаляем старыя стаўкі з гісторыі калі іх больш за дваццаць. Для зручнасці я напісаў тры дадатковыя функцыі pack_order(), unpack_order(), remove_old_orders().
Далей мы глядзім калі балансу не хапае на выплату, то лічым, што гэта не стаўка, а папаленне і захоўваем папаўненне ў orders.
Далей нарэшце сутнасць смарт-кантракта.
Спачатку калі гулец прайграў мы захоўваем яго ў гісторыю ставак і калі сума больш за 3 грамаў адпраўляем 1/3 уладальніку смарт-кантракту.
Калі ж гулец выйграў, то мы адпраўляем падвоеную суму на адрас гульца і далей захоўваем інфармацыю аб стаўцы ў гісторыю.
() 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));
}Вось і ўсё. .
Цяпер застаецца простае, зробім гет-метады, каб са знешняга свету можна было атрымаць інфармацыю аб стане кантракта (па факце лічыць даныя іх сховішча смарткантракта).
. Аб тым як атрымліваць інфармацыю аб смарт-кантракце напішам ніжэй.
Яшчэ я забыўся дадаць код, які будзе апрацоўваць самы першы запыт, які адбываецца пры публікацыі смарт-кантракта. . І яшчэ баг з адпраўленнем 1/3 сумы на рахунак уладальніка.
Далей застаецца апублікаваць смарт-кантракт. Створым тэчку requests.
За аснову я ўзяў код публікацыі які у афіцыйным рэпазітары.
З таго на што варта звярнуць увагу. Мы фармуем сховішча смарт-кантракта і паведамленне на ўваход. Пасля гэта генеруецца адрас смарт-кантракта, гэта значыць адрас вядомы яшчэ да публікацыі ў TON. і выкарыстоўваюць смарткантракты). .
Далей мы выконваем код публікацыі і атрымліваем lottery-query.boc файл і адрас смарткантракта.
~/TON/build/crypto/fift -s requests/new-lottery.fif 0Не забываем захаваць згенераваныя файлы: lottery-query.boc, lottery.addr, lottery.pk.
Сярод іншага ў логах выканання ўбачым адрас смарт-кантракта.
new wallet address = 0:044910149dbeaf8eadbb2b28722e7d6a2dc6e264ec2f1d9bebd6fb209079bc2a
(Saving address to file lottery.addr)
Non-bounceable address (for init): 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd
Bounceable address (for later access): kQAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KpFYДзеля цікавасці зробім запыт у TON
$ ./lite-client/lite-client -C ton-lite-client-test1.config.json
getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KsydІ ўбачым, што акаўнт з такім адрасам пусты.
account state is emptyАдпраўляем на адрас 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 Gram і праз некалькі секунд выконваем тужэй каманду. Для адпраўкі грамаў я выкарыстоўваю , А тэставыя грамы можна папрасіць у каго-небудзь з чата, аб якім я скажу ў канцы артыкула.
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KsydГлядзіць, што ў сеціве зьявіўся неініцыялізаваны (state:account_uninit) смарткантракт з такім адрасам і балансам 1 000 000 000 нанаграм.
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 2000000000ngЦяпер апублікуем смарт-кантракт. Запусцім lite-client і выканаем.
> 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 Праверым, што кантракт апублікаваны.
> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KsydСярод іншага атрымаем.
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_activeБачым, што account_active.
Адпаведны коміт са зменамі .
Цяпер створым запыты для ўзаемадзеяння са смарт-кантрактам.
Дакладней першы для змены адраса мы пакінем у якасці самастойнай працы, а другі для адпраўкі грамаў на адрас уладальніка зробім. Па факце нам трэба будзе зрабіць тое самае, што і ў тэсце на адпраўку грамаў.
Вось такое паведамленне мы будзем адпраўляць на смарткантракт, дзе msg_seqno 165, action 2 і 9.5/XNUMX грам для адпраўкі.
<b 165 32 u, 2 7 u, 9500000000 Gram, b>Не забываем падпісаць паведамленне прыватным ключом lottery.pk, Які згенераваўся раней пры стварэнні смарт-кантракту. .
Атрымліваем іншармацыю з смарт-кантракту з дапамогай гет-метадаў
Цяпер разгледзім як запускаць гет-метады смарткантракта.
Запускаем lite-client і запускаем гет-метады, якія мы напісалі.
$ ./lite-client/lite-client -C ton-lite-client-test1.config.json
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd balance
arguments: [ 104128 ]
result: [ 64633878952 ]
...В result змяшчаецца значныя, якое вяртае функцыя balance() з нашага смарт-кантракта.
Тое ж самае выканаем яшчэ для некалькіх метадаў.
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_seqno
...
arguments: [ 77871 ]
result: [ 1 ] Запытаем гісторыю ставак.
> 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]) ] Мы будзем выкарыстоўваць lite-client і гет-метады каб выводзіць інфармацыю аб смарт-кантракце на сайце.
Паказваем дадзеныя смарт-кантракта на сайце
Я напісаў просты вэб-сайт на Python для таго, каб паказаць дадзеныя з смарт-кантракту ў зручным выглядзе. Тут я не буду падрабязна спыняцца на ім і апублікую сайт .
Запыты да TON робяцца з Python з дапамогай lite-client. Для зручнасці сайт пакуецца ў Docker і публікуецца на Google Cloud. .
Спрабуем
Цяпер паспрабуем адправіць туды грамаў для папаўнення з . Адправім 40 грам. І зробім пару ставак для навочнасці. Бачым, што сайт паказвае гісторыю ставак, бягучы працэнт выйгрышу і іншую карысную інфармацыю.
, Што першую мы выйгралі, другую прайгралі.
пасляслоўе
Артыкул атрымаўся нашмат даўжэй чым я меркаваў, можа можна было і карацей, а можа як раз для чалавека, які нічога не ведае пра TON і жадае напісаць і апублікаваць не самы просты смарт-кантракт з магчымасцю з ім узаемадзейнічаць. Магчыма нейкія рэчы можна было растлумачыць прасцей.
Магчыма некаторыя моманты ў рэалізацыі можна было зрабіць больш эфектыўна і элегантна, але тады б на падрыхтоўку артыкула спатрэбілася яшчэ больш часу. Таксама магчыма, што я недзе памыліўся ці нешта не зразумеў, таму калі вы робіце нешта сур'ёзнае трэба абапірацца на афіцыйную дакументацыю ці афіцыйны рэпазітар з кодам TON.
Трэба заўважыць, што бо сам TON яшчэ ў актыўнай стадыі распрацоўкі могуць адбыцца змены, якія зламаюць які-небудзь з крокаў у гэтым артыкуле (што і адбылося пакуль я пісаў, ужо выправіў), але агульны падыход ці наўрад зменіцца.
Пра будучыню TON разважаць не буду. Магчыма платформа стане нечым вялікім і нам варта выдаткаваць час на яе вывучэнне і заняць нішу сваімі прадуктамі ўжо зараз.
Ёсць яшчэ Libra ад Facebook, у якой патэнцыйная аўдыторыя карыстачоў больш за ў TON. Аб Libra я амаль нічога не ведаю, мяркуючы па форуме актыўнасці там нашмат больш чым у супольнасці TON. Хоць распрацоўшчыкі і супольнасць TON больш падобна на андэграўнд, што таксама крута.
Спасылкі
- Афіцыйная дакументацыя па TON:
- Афіцыйны рэпазітар TON:
- Афіцыйны кашалёк для розных платформ:
- Рэпазітар смарт-кантракту з гэтага артыкула:
- Спасылка на сайт смарт-кантракта:
- Рэпазітар на пашырэнне для Visual Studio Code для FunC:
- Чат пра ТON у Telegram, які вельмі дапамог разабрацца на пачатковым этапе. Я думаю не будзе памылкай, калі скажу, што там ёсць усе, хто пісаў нешта для TON. Тамсама можна папрасіць тэставых грамаў.
- Яшчэ адзін чат пра TON у якім я знаходзіў карысную інфармацыю:
- Першы этап конкурсу:
- Другі этап конкурсу:
Крыніца: habr.com
