Naučte sa písať Waves smart kontrakty na RIDE a RIDE4DAPPS. Časť 2 (DAO - Decentralizovaná autonómna organizácia)

Naučte sa písať Waves smart kontrakty na RIDE a RIDE4DAPPS. Časť 2 (DAO - Decentralizovaná autonómna organizácia)

Ahoj všetci!

В prvý časti sme sa podrobne pozreli na to, ako vytvárať a pracovať s dApp (decentralizovaná aplikácia) v Vlny RIDE IDE.

Ten rozobratý si teraz trochu otestujeme príklad.

Fáza 3. Testovanie účtu dApp

Naučte sa písať Waves smart kontrakty na RIDE a RIDE4DAPPS. Časť 2 (DAO - Decentralizovaná autonómna organizácia)

Aké problémy na vás s Alicou okamžite vyskočia? Dapp Účet?
Po prvé:
Boob a Cooper môžu náhodne poslať prostriedky na adresu dApp pomocou bežného prevod transakcie, a preto k nim nebude mať prístup späť.

Po druhé:
Alicu žiadnym spôsobom neobmedzujeme vo výbere prostriedkov bez súhlasu Booba a/alebo Coopera. Keďže dávajte pozor na overenie, všetky transakcie od Alice sa vykonajú.

Napravme 2. zákazom Alice prevod transakcií. Poďme nasadiť opravený skript:
Naučte sa písať Waves smart kontrakty na RIDE a RIDE4DAPPS. Časť 2 (DAO - Decentralizovaná autonómna organizácia)

Snažíme sa stiahnuť mince z dApp Alice a jej podpis. Dostaneme chybu:
Naučte sa písať Waves smart kontrakty na RIDE a RIDE4DAPPS. Časť 2 (DAO - Decentralizovaná autonómna organizácia)

Skúsme vybrať výberom:

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

Scenár funguje a my sme prišli na druhý bod!

Fáza 4. Vytvorte DAO s hlasovaním

Žiaľ, jazyk RIDE zatiaľ neposkytuje možnosť práce s kolekciami (slovníky, iterátory, redukcie a pod.). Avšak pre akékoľvek prevádzky s plochými zbierkami kľúč-hodnota vieme navrhnúť systém pre prácu s reťazcami, podľa toho s kľúčmi a ich dešifrovaním.

Reťazce sa dajú veľmi ľahko zreťaziť, reťazce možno oddeliť indexmi.
Poďme zhromaždiť a analyzovať reťazec ako testovací príklad a skontrolovať, ako to ovplyvňuje výsledok transakcie.
Vyrovnali sme sa s tým, že Alice nemohla podpísať transakciu Transfer, pretože táto možnosť bola pre tento typ transakcie zablokovaná v @verifier.

Poďme cvičiť so strunami a potom to vyriešime.

RIDE struny

Transakcia je opäť možná, vieme pracovať s reťazcami.
Naučte sa písať Waves smart kontrakty na RIDE a RIDE4DAPPS. Časť 2 (DAO - Decentralizovaná autonómna organizácia)


Celkovo máme všetko potrebné na napísanie komplexnej logiky DAO dApp.

Dátové transakcie

Dátové transakcie:
„Maximálna veľkosť kľúča je 100 znakov a kľúč môže obsahovať ľubovoľné body kódu Unicode vrátane medzier a iných netlačiteľných symbolov. Hodnoty reťazca majú limit 32,768 100 bajtov a maximálny počet možných záznamov v dátovej transakcii je 140. Celkovo je maximálna veľkosť dátovej transakcie okolo XNUMX kb – pre porovnanie, takmer presne toľko, koľko má Shakespearova hra Romeo a Júlia. '.“

Vytvárame DAO s nasledujúcimi podmienkami:
Aby startup získal financie zavolaním getFunds() je potrebná podpora minimálne 2 účastníkov - DAO investorov. ustúpiť bude možné presne toľko, koľko je uvedené na hlasovanie Majitelia DAO.

Vytvorme 3 typy kľúčov a pridáme logiku pre prácu so zostatkami v 2 nových funkciách hlasovanie a získavanie prostriedkov:
xx…xx_ia = investori, disponibilný zostatok (hlasovanie, vklad, výber)
xx…xx_sv = startupy, počet hlasov (hlasujte, získajte prostriedky)
xx…xx_sf = startupy, počet hlasov (hlasujte, získajte prostriedky)
xx…xx = verejná adresa (35 znakov)

Upozorňujeme, že pri hlasovaní sme potrebovali aktualizovať niekoľko polí naraz:

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

WriteSet nám umožňuje robiť niekoľko záznamov naraz v rámci jedného invokeScript transakcií.

Takto to vyzerá v úložisku kľúč-hodnota DAO dApp po doplnení Boba a Coopera ia- vklady:
Naučte sa písať Waves smart kontrakty na RIDE a RIDE4DAPPS. Časť 2 (DAO - Decentralizovaná autonómna organizácia)

Naša funkcia vkladu sa mierne zmenila:
Naučte sa písať Waves smart kontrakty na RIDE a RIDE4DAPPS. Časť 2 (DAO - Decentralizovaná autonómna organizácia)

Teraz prichádza najdôležitejší moment v aktivitách DAO – hlasovať pre projekty, ktoré sa majú financovať.

Bob hlasuje za Neliho projekt 500000 XNUMX vlniek:

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

Naučte sa písať Waves smart kontrakty na RIDE a RIDE4DAPPS. Časť 2 (DAO - Decentralizovaná autonómna organizácia)

V dátovom úložisku vidíme všetky potrebné záznamy pre Neliinu adresu:
Naučte sa písať Waves smart kontrakty na RIDE a RIDE4DAPPS. Časť 2 (DAO - Decentralizovaná autonómna organizácia)
Cooper tiež hlasoval za projekt Neli.
Naučte sa písať Waves smart kontrakty na RIDE a RIDE4DAPPS. Časť 2 (DAO - Decentralizovaná autonómna organizácia)

Poďme sa pozrieť na kód funkcie getFunds. Neli musí nazbierať minimálne 2 hlasy, aby si mohla vybrať prostriedky z DAO.
Naučte sa písať Waves smart kontrakty na RIDE a RIDE4DAPPS. Časť 2 (DAO - Decentralizovaná autonómna organizácia)

Neli si vyberie polovicu sumy, ktorá jej bola zverená:

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

Naučte sa písať Waves smart kontrakty na RIDE a RIDE4DAPPS. Časť 2 (DAO - Decentralizovaná autonómna organizácia)

Darí sa jej, to znamená, že DAO funguje!

Pozreli sme sa na proces vytvárania DAO v jazyku RIDE4DAPPS.
V nasledujúcich častiach sa bližšie pozrieme na refaktorovanie kódu a testovanie prípadov.

Plná verzia kódu v 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
    }
}

Prvá časť
Kód na GitHub
Vlny RIDE IDE
Vyhlásenie grantového programu

Zdroj: hab.com

Pridať komentár