Naučte se psát chytré smlouvy Waves na RIDE a RIDE4DAPPS. Část 2 (DAO – Decentralizovaná autonomní organizace)

Naučte se psát chytré smlouvy Waves na RIDE a RIDE4DAPPS. Část 2 (DAO – Decentralizovaná autonomní organizace)

Ahoj všichni!

В první části jsme podrobně prozkoumali, jak vytvořit a pracovat s dApp (decentralizovaná aplikace) v Vlny RIDE IDE.

Pojďme si nyní rozebrané trochu otestovat příklad.

Fáze 3. Testování účtu dApp

Naučte se psát chytré smlouvy Waves na RIDE a RIDE4DAPPS. Část 2 (DAO – Decentralizovaná autonomní organizace)

Jaké problémy okamžitě spěchají k hlasům s Alicí dApp Účet?
Za prvé:
Boob a Cooper mohou omylem poslat prostředky na adresu dApp pomocí normálního transfer transakce, a proto k nim nebude mít zpětný přístup.

Za druhé:
Alici žádným způsobem neomezujeme ve výběru finančních prostředků bez souhlasu Boob a/nebo Coopera. Protože věnujte pozornost ověření, všechny transakce od Alice budou provedeny.

Opravme 2 zákazem Alice transfer transakce. Nasaďte opravený skript:
Naučte se psát chytré smlouvy Waves na RIDE a RIDE4DAPPS. Část 2 (DAO – Decentralizovaná autonomní organizace)

Snažíme se vybírat mince s dApp Alice a jejím podpisem. Dostaneme chybu:
Naučte se psát chytré smlouvy Waves na RIDE a RIDE4DAPPS. Část 2 (DAO – Decentralizovaná autonomní organizace)

Pokus o výběr prostřednictvím výběru:

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

Scénář funguje a my jsme přišli na 2. bod!

Fáze 4. Vytvořte DAO s hlasováním

Jazyk RIDE bohužel zatím neposkytuje možnost práce s kolekcemi (slovníky, slovníky, iterátory, redukce atd.). Nicméně pro jakékoli operace na plochých kolekcích klíč – hodnota umíme navrhnout systém pro práci s řetězci, respektive s klíči a jejich dešifrováním.

Řetězce se velmi snadno spojují, řetězce lze oddělovat indexy.
Pojďme shromáždit a analyzovat řetězec jako testovací případ a zkontrolovat, jak to ovlivní výsledek transakce.
Vyrovnali jsme se s tím, že Alice nemohla podepsat transakci Transfer, protože tato možnost byla u tohoto typu transakce zablokována v @verifier.

Pojďme cvičit se strunami a pak to vyřešíme.

Struny RIDE

Transakce je opět možná, umíme pracovat s řetězci.
Naučte se psát chytré smlouvy Waves na RIDE a RIDE4DAPPS. Část 2 (DAO – Decentralizovaná autonomní organizace)


Celkově máme vše, co potřebujete k psaní složité logiky DAO dApp.

Datové transakce

Datové transakce:
„Maximální velikost klíče je 100 znaků a klíč může obsahovat libovolné body v kódu Unicode včetně mezer a dalších netisknutelných symbolů. Hodnoty řetězce mají limit 32,768 100 bajtů a maximální počet možných záznamů v datové transakci je 140. Celkově je maximální velikost datové transakce kolem XNUMX kb – pro informaci, téměř přesně délka Shakespearovy hry 'Romeo a Julie '.“

Vytváříme DAO s následujícími podmínkami:
Aby startup získal finance zavoláním getFunds() je nutná podpora minimálně 2 účastníků - DAO investorů. ustoupit bude možné přesně tolik, kolik je uvedeno na hlasování Majitelé DAO.

Vytvoříme 3 typy klíčů a přidáme logiku pro práci se zůstatky ve 2 nových funkcích hlasování a získávání prostředků:
xx…xx_IA = investoři, disponibilní zůstatek (hlasování, vklad, výběr)
xx…xx_sv = startupy, počet hlasů (hlasujte, získejte peníze)
xx…xx_sf = startupy, počet hlasů (hlasujte, získejte peníze)
xx…xx = veřejná adresa (35 znaků)

Upozornění v Hlasování, že jsme museli aktualizovat několik polí najednou:

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

WriteSet nám umožňuje vytvořit několik záznamů najednou v rámci jednoho invokeScript transakce.

Takto to vypadá v úložišti klíč-hodnota DAO dApp poté, co Bob a Cooper doplnili ia- vklady:
Naučte se psát chytré smlouvy Waves na RIDE a RIDE4DAPPS. Část 2 (DAO – Decentralizovaná autonomní organizace)

Naše funkce vkladu se mírně změnila:
Naučte se psát chytré smlouvy Waves na RIDE a RIDE4DAPPS. Část 2 (DAO – Decentralizovaná autonomní organizace)

Nyní přichází nejdůležitější okamžik v činnosti DAO - hlasování na projekty k financování.

Bob hlasuje pro projekt Neli na 500000 XNUMX vlnkách:

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

Naučte se psát chytré smlouvy Waves na RIDE a RIDE4DAPPS. Část 2 (DAO – Decentralizovaná autonomní organizace)

V úložišti dat vidíme všechny potřebné položky pro adresu Neli:
Naučte se psát chytré smlouvy Waves na RIDE a RIDE4DAPPS. Část 2 (DAO – Decentralizovaná autonomní organizace)
Cooper také hlasoval pro projekt Neli.
Naučte se psát chytré smlouvy Waves na RIDE a RIDE4DAPPS. Část 2 (DAO – Decentralizovaná autonomní organizace)

Pojďme se podívat na kód funkce getFunds. Neli musí nasbírat alespoň 2 hlasy, aby mohla vybrat prostředky z DAO.
Naučte se psát chytré smlouvy Waves na RIDE a RIDE4DAPPS. Část 2 (DAO – Decentralizovaná autonomní organizace)

Neli se chystá vybrat polovinu ze svěřené částky:

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

Naučte se psát chytré smlouvy Waves na RIDE a RIDE4DAPPS. Část 2 (DAO – Decentralizovaná autonomní organizace)

Uspěje, to znamená, že DAO funguje!

Přezkoumali jsme proces vytváření DAO v jazyce RIDE4DAPPS.
V následujících dílech se blíže podíváme na refaktoring kódu a testování případů.

Celý kód 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
    }
}

První část
Kód na githubu
Vlny RIDE IDE
Vyhlášení grantového programu

Zdroj: www.habr.com

Přidat komentář