Вучымся пісаць Waves смарт-кантракты на RIDE і RIDE4DAPPS. Частка 2 (DAO - Decentralized Autonomous Organization)

Вучымся пісаць Waves смарт-кантракты на RIDE і RIDE4DAPPS. Частка 2 (DAO - Decentralized Autonomous Organization)

Усім прывітанне!

В першай часткі мы падрабязна разгледзелі як ствараць і працаваць з dApp (дэцэнтралізаваным дадаткам) у Waves RIDE IDE.

Давайце зараз крыху патэстуем разабраны прыклад.

Этап 3. Тэставанне dApp акаўнта

Вучымся пісаць Waves смарт-кантракты на RIDE і RIDE4DAPPS. Частка 2 (DAO - Decentralized Autonomous Organization)

Якія праблемы адразу кідаюцца на галосы з Alice DAPP Рахунак?
Па-першае:
Boob і Cooper могуць выпадкова адправіць на адрас dApp сродкі з дапамогай звычайнай пераклад транзакцыі і, такім чынам, не змогуць атрымаць да іх доступ зваротна.

Па-другое:
Мы ніяк не абмяжоўваем Alice у выснове сродкаў без узгаднення з Boob ці(і) Cooper. Бо, звярніце ўвагу на verify, усе транзакцыі ад Alice будуць выконвацца.

Давайце выправім 2-е, забараніўшы Alice пераклад транзакцыі. Дэплоім выпраўлены скрыпт:
Вучымся пісаць Waves смарт-кантракты на RIDE і RIDE4DAPPS. Частка 2 (DAO - Decentralized Autonomous Organization)

Спрабуем вывесці манеты з dApp Alice і яе подпісам. Атрымліваем памылку:
Вучымся пісаць Waves смарт-кантракты на RIDE і RIDE4DAPPS. Частка 2 (DAO - Decentralized Autonomous Organization)

Спрабуем вывесці праз withdraw:

broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"withdraw",args:[{type:"integer", value: 1000000}]}, payment: []}))

Скрыпт працуе і з 2-м пунктам мы разабраліся!

Этап 4. Ствараем DAO з галасаваннем

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

Радкі вельмі проста канкатэнаваць, радкі можна падзяляць па індэксах.
Давайце ў якасці тэставага прыкладу збяром і разбяром радок і праверым як гэта паўплывае на зыход транзакцыі.
Мы спыніліся на тым, што Alice не магла падпісаць Transfer транзакцыю, бо гэтая магчымасць была заблакаваная ў @verifier для такога тыпу транзакцый.

Давайце папрактыкуемся з радкамі і потым вырашым гэта.

RIDE Strings

Транзакцыя зноў магчымая, мы ўмеем працаваць з радкамі.
Вучымся пісаць Waves смарт-кантракты на RIDE і RIDE4DAPPS. Частка 2 (DAO - Decentralized Autonomous Organization)


Разам мы маем усё неабходнае для напісання складанай логікі DAO dApp.

Data Transactions

Data Transactions:
“Максымальны памер для кнопкі з'яўляецца 100 characters, і key можа быць arbitrary Unicode code points including spaces and other non-printable symbols. String values ​​мае ліміт 32,768 байт і максімум нумара магчымых накладаў у data transaction is 100. Overall, maximum size of data transaction is 140kb — for reference, all exactly length of Shakespeare's play ”

Ствараем DAO з наступнымі ўмовамі:
Для таго, каб стартапу атрымаць фінансаванне, выклікаўшы getFunds() неабходна падтрымка мінімум 2-х удзельнікаў – інвестараў DAO. вывесці можна будзе роўна столькі, колькі ў суме паказалі на галасаванні уладальнікі DAO.

Давайце зробім 3 тыпы ключоў і дадамо логіку па працы з балансамі ў 2-х новых функцыях vote і getFunds:
хх…хх_ia = інвестары, даступны баланс (vote, deposit, withdrawal)
хх…хх_sv = стартапы, колькасць галасоў (vote, getFunds)
хх…хх_sf = стартапы, колькасць галасоў (vote, getFunds)
xx…xx = публічны адрас (35 сімвалаў)

Заўважце ў Vote нам спатрэбілася абнаўляць адразу некалькі палёў:

WriteSet([DataEntry(key1, value1), DataEntry(key2, value2)]),

WriteSet дазваляе нам рабіць адразу некалькі запісаў у рамках адной invokeScript транзакцыі.

Вось так гэта выглядае ў key-value сховішча DAO dApp, пасля таго як Bob і Cooper папоўнілі ia-дэпазіты:
Вучымся пісаць Waves смарт-кантракты на RIDE і RIDE4DAPPS. Частка 2 (DAO - Decentralized Autonomous Organization)

Функцыя дэпазіту ў нас крыху змянілася:
Вучымся пісаць Waves смарт-кантракты на RIDE і RIDE4DAPPS. Частка 2 (DAO - Decentralized Autonomous Organization)

Цяпер надыходзіць найважнейшы момант у дзейнасці DAO. галасаванне за праекты для фінансавання.

Bob галасуе за праект Neli на 500000 wavelets:

broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []}))

Вучымся пісаць Waves смарт-кантракты на RIDE і RIDE4DAPPS. Частка 2 (DAO - Decentralized Autonomous Organization)

У сховішчы дадзеных мы бачым усе неабходныя запісы для адраса Neli:
Вучымся пісаць Waves смарт-кантракты на RIDE і RIDE4DAPPS. Частка 2 (DAO - Decentralized Autonomous Organization)
Купер таксама прагаласаваў за праект Neli.
Вучымся пісаць Waves смарт-кантракты на RIDE і RIDE4DAPPS. Частка 2 (DAO - Decentralized Autonomous Organization)

Давайце зірнем на код функцыі getFunds. Neli павінна сабраць мінімум 2 галасы, каб мець магчымасць вывесці сродкі з DAO.
Вучымся пісаць Waves смарт-кантракты на RIDE і RIDE4DAPPS. Частка 2 (DAO - Decentralized Autonomous Organization)

Neli збіраецца вывесці палову даверанай ёй сумы:

broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []}))

Вучымся пісаць Waves смарт-кантракты на RIDE і RIDE4DAPPS. Частка 2 (DAO - Decentralized Autonomous Organization)

Ёй гэта ўдаецца, гэта значыць DAO працуе!

Мы разгледзелі працэс стварэння DAO на мове RIDE4DAPPS.
У наступных частках мы падрабязней зоймемся рэфактарынгам кода і тэставаннем кейсаў.

Поўная версія кода ў Waves RIDE IDE:

# In this example multiple accounts can deposit their funds to DAO and safely take them back, no one can interfere with this.
# DAO participants can also vote for particular addresses and let them withdraw invested funds then quorum has reached.
# An inner state is maintained as mapping `address=>waves`.
# https://medium.com/waves-lab/waves-announces-funding-for-ride-for-dapps-developers-f724095fdbe1

# You can try this contract by following commands in the IDE (ide.wavesplatform.com)
# Run commands as listed below
# From account #0:
#      deploy()
# From account #1: deposit funds
#      broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"deposit",args:[]}, payment: [{amount: 100000000, asset:null }]}))
# From account #2: deposit funds
#      broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"deposit",args:[]}, payment: [{amount: 100000000, asset:null }]}))
# From account #1: vote for startup
#      broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []}))
# From account #2: vote for startup
#      broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []}))
# From account #3: get invested funds
#      broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []}))

{-# STDLIB_VERSION 3 #-}
{-# CONTENT_TYPE DAPP #-}
{-# SCRIPT_TYPE ACCOUNT #-}

@Callable(i)
func deposit() = {
   let pmt = extract(i.payment)
   if (isDefined(pmt.assetId)) then throw("can hodl waves only at the moment")
   else {
        let currentKey = toBase58String(i.caller.bytes)
        let xxxInvestorBalance = currentKey + "_" + "ib"
        let currentAmount = match getInteger(this, xxxInvestorBalance) {
            case a:Int => a
            case _ => 0
        }
        let newAmount = currentAmount + pmt.amount
        WriteSet([DataEntry(xxxInvestorBalance, newAmount)])
   }
}
@Callable(i)
func withdraw(amount: Int) = {
        let currentKey = toBase58String(i.caller.bytes)
        let xxxInvestorBalance = currentKey + "_" + "ib"
        let currentAmount = match getInteger(this, xxxInvestorBalance) {
            case a:Int => a
            case _ => 0
        }
        let newAmount = currentAmount - amount
     if (amount < 0)
            then throw("Can't withdraw negative amount")
    else if (newAmount < 0)
            then throw("Not enough balance")
            else ScriptResult(
                    WriteSet([DataEntry(xxxInvestorBalance, newAmount)]),
                    TransferSet([ScriptTransfer(i.caller, amount, unit)])
                )
    }
@Callable(i)
func getFunds(amount: Int) = {
        let quorum = 2
        let currentKey = toBase58String(i.caller.bytes)
        let xxxStartupFund = currentKey + "_" + "sf"
        let xxxStartupVotes = currentKey + "_" + "sv"
        let currentAmount = match getInteger(this, xxxStartupFund) {
            case a:Int => a
            case _ => 0
        }
        let totalVotes = match getInteger(this, xxxStartupVotes) {
            case a:Int => a
            case _ => 0
        }
        let newAmount = currentAmount - amount
    if (amount < 0)
            then throw("Can't withdraw negative amount")
    else if (newAmount < 0)
            then throw("Not enough balance")
    else if (totalVotes < quorum)
            then throw("Not enough votes. At least 2 votes required!")
    else ScriptResult(
                    WriteSet([
                        DataEntry(xxxStartupFund, newAmount)
                        ]),
                    TransferSet([ScriptTransfer(i.caller, amount, unit)])
                )
    }
@Callable(i)
func vote(amount: Int, address: String) = {
        let currentKey = toBase58String(i.caller.bytes)
        let xxxInvestorBalance = currentKey + "_" + "ib"
        let xxxStartupFund = address + "_" + "sf"
        let xxxStartupVotes = address + "_" + "sv"
        let currentAmount = match getInteger(this, xxxInvestorBalance) {
            case a:Int => a
            case _ => 0
        }
        let currentVotes = match getInteger(this, xxxStartupVotes) {
            case a:Int => a
            case _ => 0
        }
        let currentFund = match getInteger(this, xxxStartupFund) {
            case a:Int => a
            case _ => 0
        }
    if (amount <= 0)
            then throw("Can't withdraw negative amount")
    else if (amount > currentAmount)
            then throw("Not enough balance")
    else ScriptResult(
                    WriteSet([
                        DataEntry(xxxInvestorBalance, currentAmount - amount),
                        DataEntry(xxxStartupVotes, currentVotes + 1),
                        DataEntry(xxxStartupFund, currentFund + amount)
                        ]),
                    TransferSet([ScriptTransfer(i.caller, amount, unit)])
            )
    }
@Verifier(tx)
func verify() = {
    match tx {
        case t: TransferTransaction =>false
        case _ => true
    }
}

Першая частка
Код на гітхабе
Waves RIDE IDE
Анонс грантавай праграмы

Крыніца: habr.com

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