Învățăm să scriem smart contracte Waves folosind RIDE și RIDE4DAPPS. Partea 2 (DAO — Organizație Autonomă Descentralizată)

Învățăm să scriem smart contracte Waves folosind RIDE și RIDE4DAPPS. Partea 2 (DAO — Organizație Autonomă Descentralizată)

Salut tuturor!

În prima în aceste părți, am discutat în detaliu cum să creăm și să lucrăm cu dApp (aplicație descentralizată) în Waves RIDE IDE.

Hai să testăm puțin ceea ce am discutat exemplu.

Etapa 3. Testarea contului dApp

Învățăm să scriem smart contracte Waves folosind RIDE și RIDE4DAPPS. Partea 2 (DAO — Organizație Autonomă Descentralizată)

Ce probleme se remarcă imediat cu Alice dApp Cont?
În primul rând:
Boob și Cooper pot trimite din greșeală fonduri la adresa dApp printr-o transfer tranzacție obișnuită și, astfel, nu vor putea accesa aceste fonduri înapoi.

În al doilea rând:
Nu restricționăm în niciun fel accesul lui Alice la fonduri fără consimțământul lui Boob sau (și) Cooper. Așa că, rețineți că, prin verificare, toate tranzacțiile din partea lui Alice vor fi executate.

Hai să corectăm punctul 2, interzicând lui Alice transfer tranzacțiile. Implementăm scriptul corectat:
Învățăm să scriem smart contracte Waves folosind RIDE și RIDE4DAPPS. Partea 2 (DAO — Organizație Autonomă Descentralizată)

Încercăm să retragem monede cu semnătura Alice. Primim o eroare:
Învățăm să scriem smart contracte Waves folosind RIDE și RIDE4DAPPS. Partea 2 (DAO — Organizație Autonomă Descentralizată)

Încercăm să retragem prin withdraw:

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

Scriptul funcționează și am clarificat punctul 2!

Etapa 4. Creăm DAO cu votare

Din păcate, limbajul RIDE nu suportă momentan funcționalități pentru lucrul cu colecții (dictionare de dictionare, iteratoare, reduceli etc.). Totuși, pentru orice operațiune cu colecții plane key-value putem proiecta un sistem de lucru cu șiruri, respectiv cu chei și interpretarea lor.

Șirurile se pot concatena foarte simplu, acestea pot fi separate după indici.
Hai să construim și să analizăm un șir ca exemplu de test și să vedem cum aceasta afectează rezultatul tranzacției.
Ne-am oprit la faptul că Alice nu putea semna tranzacția de Transfer, deoarece această opțiune a fost blocată în @verifier pentru acest tip de tranzacții.

Hai să exersăm cu șirurile și apoi să permitem acest lucru.

RIDE Strings

Tranzacția este din nou posibilă, știm să lucrăm cu șiruri.
Învățăm să scriem smart contracte Waves folosind RIDE și RIDE4DAPPS. Partea 2 (DAO — Organizație Autonomă Descentralizată)


Astfel, avem tot ce ne trebuie pentru a scrie o logică complexă DAO dApp.

Tranzacții de date

Tranzacții de date:
„Dimensiunea maximă pentru o cheie este de 100 de caractere, iar o cheie poate conține puncte de cod Unicode arbitrare, inclusiv spații și alte simboluri neimprimabile. Valorile șirurilor au o limită de 32.768 de octeți, iar numărul maxim de intrări posibile într-o tranzacție de date este 100. În total, dimensiunea maximă a unei tranzacții de date este de aproximativ 140kb - pentru referință, aproape exact lungimea piesei lui Shakespeare 'Romeo și Julieta'.”

Creăm DAO cu următoarele condiții:
Pentru ca o startup să obțină finanțare, invocând getFunds() este nevoie de suportul a cel puțin două persoane - investitori DAO. Afișează se va putea exactly atâta cât am indicat în total pe votare deținătorii DAO.

Haideți să facem 3 tipuri de chei și să adăugăm logica de gestionare a soldurilor în 2 funcții noi: vote și getFunds:
xx…xx_ia = investitori, sold disponibil (vote, deposit, withdrawal)
xx…xx_sv = startup-uri, numărul de voturi (vote, getFunds)
xx…xx_sf = startup-uri, numărul de voturi (vote, getFunds)
xx…xx = adresă publică (35 de caractere)

Observați că în Vote a trebuit să actualizăm imediat câteva câmpuri:

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

WriteSet ne permite să facem mai multe înregistrări într-o singură invokeScript tranzacție.

Așa arată în stocarea key-value a DAO dApp, după ce Bob și Cooper au alimentat ia-depozite:
Învățăm să scriem smart contracte Waves folosind RIDE și RIDE4DAPPS. Partea 2 (DAO — Organizație Autonomă Descentralizată)

Funcția de depozit s-a schimbat ușor:
Învățăm să scriem smart contracte Waves folosind RIDE și RIDE4DAPPS. Partea 2 (DAO — Organizație Autonomă Descentralizată)

Acum ajungem la cel mai important moment din activitatea DAO — votul pentru proiectele de finanțare.

Bob votează pentru proiectul Neli cu 500000 wavelets:

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

Învățăm să scriem smart contracte Waves folosind RIDE și RIDE4DAPPS. Partea 2 (DAO — Organizație Autonomă Descentralizată)

În stocarea de date vedem toate înregistrările necesare pentru adresa Neli:
Învățăm să scriem smart contracte Waves folosind RIDE și RIDE4DAPPS. Partea 2 (DAO — Organizație Autonomă Descentralizată)
Cooper a votat de asemenea pentru proiectul Neli.
Învățăm să scriem smart contracte Waves folosind RIDE și RIDE4DAPPS. Partea 2 (DAO — Organizație Autonomă Descentralizată)

Să ne uităm la codul funcției getFunds. Neli trebuie să adune minim 2 voturi pentru a avea posibilitatea de a retrage fonduri din DAO.
Învățăm să scriem smart contracte Waves folosind RIDE și RIDE4DAPPS. Partea 2 (DAO — Organizație Autonomă Descentralizată)

Neli intenționează să retragă jumătate din suma de încredere:

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

Învățăm să scriem smart contracte Waves folosind RIDE și RIDE4DAPPS. Partea 2 (DAO — Organizație Autonomă Descentralizată)

Reușește, ceea ce înseamnă că DAO funcționează!

Am examinat procesul de creare a DAO în limbajul RIDE4DAPPS.
În următoarele părți ne vom ocupa mai în detaliu de refactorizarea codului și testarea cazurilor.

Versiunea completă a codului în 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
    }
}

Partea întâi
Codul pe GitHub
Waves RIDE IDE
Anunțul programului de granturi

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster