Дэцэнтралізаваная афілейт-праграма з адкрытым зыходным кодам на блокчейне Waves

Дэцэнтралізаваная афілейт-праграма на блокчэйне Waves, рэалізаваная ў рамках гранта Waves Labs камандай Bettex.

Пост не з'яўляецца рэкламным! Праграма мае адкрыты зыходны код, яе выкарыстанне і распаўсюджванне бясплатна. Ужыванне праграмы стымулюе развіццё dApp прыкладанняў і ў цэлым спрыяе дэцэнтралізацыі, што ідзе на карысць кожнаму карыстачу Сеткі.

Дэцэнтралізаваная афілейт-праграма з адкрытым зыходным кодам на блокчейне Waves

Прадстаўлены dApp для affiliate-праграм з'яўляецца шаблонам для праектаў, якія ўключаюць affiliate як частка свайго функцыяналу. Код можа быць скарыстаны як шаблон для капіявання, як бібліятэка ці як набор ідэй для тэхнічнай рэалізацыі.

Па функцыянале гэта звычайная affiliate-сістэма, якая рэалізуе рэгістрацыю з указаннем реферера, шматузроўневае налічэнне ўзнагарод за Рэфералы і матывацыю для рэгістрацыі ў сістэме (кешбэк). Сістэма з'яўляецца "чыстым" dApp, гэта значыць вэб-дадатак ўзаемадзейнічае напрамую з блокчейном, не маючы свайго бэкэнда, базы дадзеных і да т.п.

Выкарыстоўваюцца тэхнікі, якія таксама могуць быць карысныя ў шматлікіх іншых праектах:

  • Выклік смарт-акаўнта ў доўг з неадкладным пагашэннем (у момант выкліку на акаўнце няма токенаў для аплаты выкліку, але яны з'яўляюцца там у выніку выкліку).
  • PoW-captcha – абарона ад высокачашчыннага аўтаматызаванага выкліку функцый смарт-акаўнта – аналаг капчы, але праз доказ выкарыстання вылічальных рэсурсаў.
  • Запыт да ключоў data па шаблоне.

Прыкладанне складаецца з:

  • кода смарт-акаўнта на мове ride4dapps (які па задумцы мерзецца ў асноўны смарт-акаўнт, для якога трэба рэалізаваць affiliate-функцыянал);
  • js-абгорткі, якая рэалізуе ўзровень абстракцыі над WAVES NODE REST API;
  • кода на vuejs framework, які з'яўляецца прыкладам выкарыстання бібліятэкі і RIDE-кода.

Апішам усе пералічаныя асаблівасці.

Выклік смарт-акаўнта ў доўг з неадкладным пагашэннем

Выклік InvokeScript патрабуе аплаты камісіі з які ініцыюе транзакцыю акаўнта. Гэта не праблема, калі вы робіце праект для блокчейн-гікаў, у якіх на ўліковым запісе ёсць нейкая колькасць токенаў WAVES, але калі прадукт арыентаваны на выкарыстанне ў шырокіх масах – гэта становіцца сур'ёзнай праблемай. Бо карыстач павінен заклапаціцца купляй токенаў WAVES (ці іншага падыходнага асета, якім можна аплачваць транзакцыі), што павялічвае і без таго немалы парог уваходу ў праект. Мы можам раздаць карыстальнікам асэт, якім дазволіць аплаціць транзакцыі і сутыкнемся з рызыкай іх нямэтавага выкарыстання, калі ствараюцца аўтаматызаваныя сістэмы для выпампоўвання ліквіднага асета з нашай сістэмы.

Было б вельмі зручна, калі была б магчымасць вырабляць выклік InvokeScript "за кошт атрымальніка" (таго смарт-акаўнта, на якім усталяваны скрыпт), і такая магчымасць, хоць і не відавочнай выявай, існуе.

Калі ўсярэдзіне InvokeScript вырабіць ScriptTransfer на адрас выклікалага, які кампенсуе выдаткаваныя на fee токены, то такі выклік мінуе паспяхова, нават калі ў момант выкліку на выклікалым акаўнце не было ніякіх ассетаў. Гэта магчыма дзякуючы таму, што праверка наяўнасці дастатковай колькасці токенаў ажыццяўляецца пасля выкліку транзакцыі, а не перад ёй, так што можна рабіць транзакцыі ў крэдыт пры ўмове іх неадкладнага пагашэння.

ScriptTransfer(i.caller, i.fee, unit)

Прыведзены ніжэй код пакрывае выдаткаванае fee за кошт сродкаў смарт-акаўнта. Для абароны ад нямэтавага выкарыстання гэтай магчымасці неабходна выкарыстоўваць праверку, што выклікае марнуе fee у патрэбным асеце і ў разумных межах:

func checkFee(i:Invocation) = {
if i.fee > maxFee then throw(“unreasonable large fee”) else
if i.feeAssetId != unit then throw(“fee must be in WAVES”) else true
}

Таксама для абароны ад зламыснай і бессэнсоўнай растраты сродкаў неабходна абарона ад аўтаматычнага выкліку (PoW-captcha)

PoW-captcha

Сама ідэя proof-of-work captcha не новая і ўжо рэалізавана ў розных праектах, у тым ліку рэалізаваных на базе WAVES. Сэнс ідэі ў тым, каб для здзяйснення дзеяння, які траціць рэсурсы нашага праекта, які выклікае павінен выдаткаваць і свае ўласныя рэсурсы, што робіць атаку на знясіленне рэсурсаў даволі затратнай. Для вельмі лёгкай і малазатратнай валідацыі таго, што адпраўнік транзакцыі вырашыў PoW-задачу, існуе праверка id транзакцыі:

if take(toBase58String(i.transactionId), 3) != “123” then throw(“proof of work failed”) else

Для таго, каб правесці транзакцыю, які выклікае павінен падабраць такія параметры, каб яе base58 код (id) пачынаўся на лічбы 123, што адпавядае ў сярэднім пары дзясяткаў секунд працэсарнага часу і ў цэлым разумна для нашай задачы. Калі патрабуецца прасцейшы ці больш складаны PoW, то задачу лёгка дапрацаваць відавочным спосабам.

Запыт да ключоў data па шаблоне

Для таго, каб выкарыстоўваць blockchain у якасці базы дадзеных, жыццёва неабходна мець прылады API для запытаў да базы як key-val па шаблонах. Такі інструментарый з'явіўся ў пачатку ліпеня 2019 г. у выглядзе параметру ?matches у запыту REST API /addresses/data?matches=regexp. Цяпер, калі нам трэба з вэб-прыкладанні атрымаць не адзін ключ і не ўсе ключы адразу, а толькі нейкую групу, то можна зрабіць выбарку па імі ключа. Напрыклад, у дадзеным праекце транзакцыі вываду сродкаў кадуюцца ў выглядзе

withdraw_${userAddress}_${txid}

што дазваляе атрымаць спіс транзакцый на вывад сродкаў для любога зададзенага адраса па шаблоне:

?matches=withdraw_${userAddress}_.*

Цяпер разбяром кампаненты гатовага рашэння.

Код на vuejs

Код з'яўляецца працоўнай дэманстрацыяй, блізкай да рэальнага праекту. Ён рэалізуе ўваход праз Waves Keeper і працу з бібліятэкай affiliate.js, з дапамогай якой ён рэгіструе карыстальніка ў сістэме, апытвае дадзеныя аб транзакцыях, а таксама дазваляе выводзіць заробленыя сродкі на акаўнт карыстальніка.

Дэцэнтралізаваная афілейт-праграма з адкрытым зыходным кодам на блокчейне Waves

Код на RIDE

Складаецца з функцый register, fund і withdraw.

Функцыя register рэгіструе ў сістэме карыстальніка. У яе два параметры: referer (адрас рефера) і не выкарыстоўваны ў кодзе функцыі параметр salt, які патрэбен для падбору id транзакцыі (задача PoW-captcha).

Функцыя (як і астатнія функцыі з дадзенага праекту) выкарыстоўвае тэхніку выкліку ў доўг, вынікам функцыі з'яўляецца фінансаванне выплаты fee за выклік гэтай функцыі. Дзякуючы гэтаму рашэнню, толькі што які стварыў кашалёк карыстач адразу можа працаваць з сістэмай і яму не трэба бянтэжыцца пытаннем набыцця ці атрыманні асета, які дазваляе аплаціць камісію за транзакцыю.

Вынікам працы функцыі рэгістрацыі з'яўляюцца два запісы:

${owner)_referer = referer
${referer}_referral_${owner} = owner

Гэта дазваляе праводзіць прамы і зваротны пошукі (рэферэр дадзенага карыстальніка і ўсе Рэфералы дадзенага карыстальніка).

Функцыя fund з'яўляецца хутчэй шаблонам для распрацоўкі сапраўднага функцыяналу. У прадстаўленым выглядзе яна бярэ ўсе пералічаныя транзакцыяй сродкі і размяркоўвае іх на акаўнты рэферэраў 1, 2, 3 узроўня, на акаўнт "кешбэка" і акаўнт "здачы" (сюды пападае ўсё, што засталося пры размеркаванні па папярэдніх акаўнтах).

Кешбэк - гэта сродак стымулявання канчатковага карыстальніка для ўдзелу ў реферальной сістэме. Якая выплачваецца частка камісіі сістэмай у выглядзе "кешбэка" карыстальнік можа зняць гэтак жа, як ўзнагароды за Рэфералы.

Пры выкарыстанні реферальной сістэмы функцыю fund варта перайначыць, убудаваць у асноўную логіку таго смарт-акаўнта, на якім будзе працаваць сістэма. Напрыклад, калі реферальное ўзнагароджанне выплачваецца за зробленую стаўку, то функцыя fund павінна быць убудаваная ў логіку, дзе робіцца стаўка (ці вырабляецца іншае мэтавае дзеянне, за якое і выплачваецца ўзнагароджанне). У дадзеную функцыю закадзіравана тры ўзроўню реферальных узнагарод. Калі патрабуецца зрабіць больш ці менш узроўняў, тое гэта таксама кіруецца ў кодзе. Працэнт узнагароджання задаецца канстантамі level1-level3, у кодзе ён лічыцца як amount * level / 1000, гэта значыць значэнне 1 адпавядае 0,1% (гэта таксама можна мяняць у кодзе).

Выклік функцыі змяняе баланс уліковага запісу і таксама стварае запісы для мэт лагагавання выгляду:

fund_address_txid = address:owner:inc:level:timestamp
Для получения timestamp (текущего времени) используется такая вот связка
func getTimestamp() = {
let block = extract(blockInfoByHeight(height))
toString(block.timestamp)
}

Гэта значыць час транзакцыі - гэта час блока, у якой ён знаходзіцца. Гэта надзейней, чым выкарыстоўваць timestamp з самай транзакцыі, тым больш, што ен не даступны з callable.
Функцыя withdraw выводзіць усе назапашаныя ўзнагароды на ўліковы запіс карыстальніка. Стварае запісы для мэт лагіравання:

# withdraw log: withdraw_user_txid=amount:timestamp

Дадатак

Асноўная частка прыкладання - бібліятэка affiliate.js, якая з'яўляецца мастом паміж мадэлямі дадзеных affiliate і WAVES NODE REST API. Рэалізуе ўзровень абстракцыі, незалежны ад фрэймворка (можа быць выкарыстаны любы). Актыўныя функцыі (register, withdraw) мяркуюць, што ў сістэме ўсталяваны Waves Keeper, сама бібліятэка гэта не правярае.

Рэалізуе метады:

fetchReferralTransactions
fetchWithdrawTransactions
fetchMyBalance
fetchReferrals
fetchReferer
withdraw
register

Функцыянал метадаў відавочны з назваў, параметры і якія вяртаюцца дадзеныя апісаны ў кодзе. Дадатковых каментароў патрабуе функцыя register - яна запускае цыкл падбору id транзакцыі, каб ён пачынаўся на 123 - гэта апісаная вышэй PoW-captcha, якая абараняе ад масавых рэгістрацый. Функцыя знаходзіць транзакцыю з патрэбным ID, а потым падпісвае праз Waves Keeper.

DEX affiliate праграма даступная на GitHub.com.

Крыніца: habr.com

Дадаць каментар