Nauka pisania inteligentnych kontraktów Waves na RIDE i RIDE4DAPPS. Część 2 (DAO – Zdecentralizowana Organizacja Autonomiczna)

Nauka pisania inteligentnych kontraktów Waves na RIDE i RIDE4DAPPS. Część 2 (DAO – Zdecentralizowana Organizacja Autonomiczna)

Witam wszystkich!

В pierwszy w części szczegółowo przyjrzeliśmy się, jak tworzyć i pracować z dApp (aplikacją zdecentralizowaną) w Wave RIDE IDE.

Przetestujmy teraz trochę zdemontowany przykład.

Etap 3. Testowanie konta dApp

Nauka pisania inteligentnych kontraktów Waves na RIDE i RIDE4DAPPS. Część 2 (DAO – Zdecentralizowana Organizacja Autonomiczna)

Jakie problemy od razu rzucają Ci się w oczy z Alicją? dApp Konto?
Po pierwsze:
Boob i Cooper mogą przypadkowo wysłać środki na adres dApp przy użyciu zwykłego konta przenieść transakcji i w związku z tym nie będzie możliwości odzyskania do nich dostępu.

Po drugie:
Nie ograniczamy w żaden sposób Alicji w zakresie wypłacania środków bez zgody Booba i/lub Coopera. Ponieważ zwróć uwagę na weryfikację, wszystkie transakcje od Alicji zostaną zrealizowane.

Naprawmy drugą kwestię, banując Alice przenieść transakcje. Wdróżmy poprawiony skrypt:
Nauka pisania inteligentnych kontraktów Waves na RIDE i RIDE4DAPPS. Część 2 (DAO – Zdecentralizowana Organizacja Autonomiczna)

Próbujemy wypłacić monety z dApp Alice i jej podpisu. Otrzymujemy błąd:
Nauka pisania inteligentnych kontraktów Waves na RIDE i RIDE4DAPPS. Część 2 (DAO – Zdecentralizowana Organizacja Autonomiczna)

Spróbujmy wypłacić poprzez wycofanie:

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

Skrypt działa i odkryliśmy drugi punkt!

Etap 4. Utwórz DAO z głosowaniem

Niestety język RIDE nie zapewnia jeszcze możliwości pracy z kolekcjami (słowniki słownikowe, iteratory, reduktory itp.). Jednak w przypadku wszelkich operacji z kolekcjami płaskimi kluczowa wartość możemy zaprojektować system do pracy z ciągami znaków, odpowiednio z kluczami i ich deszyfrowaniem.

Łańcuchy można bardzo łatwo łączyć; ciągi można oddzielać indeksami.
Zbierzmy i przeanalizujmy ciąg znaków jako przykład testowy i sprawdźmy, jak wpływa to na wynik transakcji.
Zdecydowaliśmy się na to, że Alicja nie mogła podpisać transakcji Przelewu, gdyż możliwość ta została zablokowana w @weryfikatorze dla tego typu transakcji.

Poćwiczmy z ciągami znaków, a następnie rozwiążmy ten problem.

JAZDA na sznurkach

Transakcja jest ponownie możliwa, wiemy jak pracować z ciągami znaków.
Nauka pisania inteligentnych kontraktów Waves na RIDE i RIDE4DAPPS. Część 2 (DAO – Zdecentralizowana Organizacja Autonomiczna)


W sumie mamy wszystko co potrzebne do napisania złożonej logiki Aplikacja DAO.

Transakcje danych

Transakcje danych:
„Maksymalny rozmiar klucza to 100 znaków, a klucz może zawierać dowolne punkty kodu Unicode, w tym spacje i inne symbole niedrukowalne. Wartości ciągów mają limit 32,768 100 bajtów, a maksymalna liczba możliwych wpisów w transakcji danych wynosi 140. Ogółem maksymalny rozmiar transakcji danych wynosi około XNUMX kb — dla porównania, prawie dokładnie tyle samo długości sztuki Szekspira „Romeo i Julia” „.”

Tworzymy DAO z następującymi warunkami:
Aby startup mógł otrzymać dofinansowanie dzwoniąc pobierz fundusze() wymagane jest wsparcie przynajmniej 2 uczestników – inwestorów DAO. wycofać będzie możliwe dokładnie tyle, ile suma jest wskazana głosowanie Właściciele DAO.

Stwórzmy 3 typy kluczy i dodajmy logikę do pracy z saldami w 2 nowych funkcjach głosowania i getFunds:
xx… xx_ja = inwestorzy, dostępne saldo (głosowanie, wpłata, wycofanie)
xx… xx_sv = startupy, liczba głosów (głosuj, zdobądź fundusze)
xx… xx_sf = startupy, liczba głosów (głosuj, zdobądź fundusze)
xx…xx = adres publiczny (35 znaków)

Pamiętaj, że w Vote musieliśmy zaktualizować kilka pól jednocześnie:

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

WriteSet pozwala nam na utworzenie kilku rekordów jednocześnie w jednym wywołaj skrypt transakcje.

Tak to wygląda w magazynie klucz-wartość aplikacji DAO dApp po uzupełnieniu zapasów przez Boba i Coopera ia-depozyty:
Nauka pisania inteligentnych kontraktów Waves na RIDE i RIDE4DAPPS. Część 2 (DAO – Zdecentralizowana Organizacja Autonomiczna)

Nasza funkcja depozytowa uległa niewielkim zmianom:
Nauka pisania inteligentnych kontraktów Waves na RIDE i RIDE4DAPPS. Część 2 (DAO – Zdecentralizowana Organizacja Autonomiczna)

Teraz nadchodzi najważniejszy moment w działalności DAO - głosować dla projektów, które mają zostać dofinansowane.

Bob głosuje na projekt Neli dotyczący 500000 XNUMX falek:

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

Nauka pisania inteligentnych kontraktów Waves na RIDE i RIDE4DAPPS. Część 2 (DAO – Zdecentralizowana Organizacja Autonomiczna)

W magazynie danych widzimy wszystkie niezbędne wpisy dotyczące adresu Neli:
Nauka pisania inteligentnych kontraktów Waves na RIDE i RIDE4DAPPS. Część 2 (DAO – Zdecentralizowana Organizacja Autonomiczna)
Cooper również głosował za projektem Neli.
Nauka pisania inteligentnych kontraktów Waves na RIDE i RIDE4DAPPS. Część 2 (DAO – Zdecentralizowana Organizacja Autonomiczna)

Przyjrzyjmy się kodowi funkcji getFunds. Neli musi zebrać minimum 2 głosy, aby móc wypłacić środki z DAO.
Nauka pisania inteligentnych kontraktów Waves na RIDE i RIDE4DAPPS. Część 2 (DAO – Zdecentralizowana Organizacja Autonomiczna)

Neli zamierza wypłacić połowę powierzonej jej kwoty:

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

Nauka pisania inteligentnych kontraktów Waves na RIDE i RIDE4DAPPS. Część 2 (DAO – Zdecentralizowana Organizacja Autonomiczna)

Udaje się, czyli DAO działa!

Przyjrzeliśmy się procesowi tworzenia DAO w tym języku RIDE4DAPPS.
W kolejnych częściach przyjrzymy się bliżej refaktoryzacji kodu i testowaniu przypadków.

Pełna wersja kodu w IDE JAZDY Falami:

# 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
    }
}

Pierwsza część
Kod na GitHubie
Wave RIDE IDE
Ogłoszenie programu dotacyjnego

Źródło: www.habr.com

Dodaj komentarz