Impariamo a scrivere smart contract Waves su RIDE e RIDE4DAPPS. Parte 2 (DAO — Organizzazione Autonoma Decentralizzata)

Impariamo a scrivere smart contract Waves su RIDE e RIDE4DAPPS. Parte 2 (DAO — Organizzazione Autonoma Decentralizzata)

Ciao a tutti!

In prima parte abbiamo esaminato in dettaglio come creare e lavorare con dApp (applicazioni decentralizzate) in Waves RIDE IDE.

Ora proviamo a testare ciò che abbiamo esaminato esempio.

Fase 3. Testare l'account dApp

Impariamo a scrivere smart contract Waves su RIDE e RIDE4DAPPS. Parte 2 (DAO — Organizzazione Autonoma Decentralizzata)

Quali problemi saltano subito all'occhio con Alice dApp Account?
In primo luogo:
Boob e Cooper potrebbero accidentalmente inviare fondi all'indirizzo dApp tramite una normale transfer transazione e, in tal modo, non poterli recuperare.

In secondo luogo:
Non limitiamo in alcun modo Alice nel prelevare fondi senza l'approvazione di Boob o/ed Cooper. Infatti, si noti che tutte le transazioni di Alice saranno eseguite.

Correggiamo il secondo punto, vietando ad Alice transfer le transazioni. Deployiamo lo script corretto:
Impariamo a scrivere smart contract Waves su RIDE e RIDE4DAPPS. Parte 2 (DAO — Organizzazione Autonoma Decentralizzata)

Provando a prelevare monete con dApp Alice e la sua firma. Otteniamo un errore:
Impariamo a scrivere smart contract Waves su RIDE e RIDE4DAPPS. Parte 2 (DAO — Organizzazione Autonoma Decentralizzata)

Provando a prelevare tramite withdraw:

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

Lo script funziona e abbiamo risolto anche il secondo punto!

Fase 4. Creiamo un DAO con voto

Purtroppo, nel linguaggio RIDE non sono ancora disponibili le funzionalità per lavorare con collezioni (dizionari-dizionari, iteratori, riduttori e simili). Tuttavia, per tutte le operazioni con collezioni piatte key-value possiamo progettare un sistema di gestione delle stringhe, inclusi le chiavi e la loro decodifica.

Le stringhe si concatenano facilmente e possono essere suddivise per indici.
Facciamo un esempio pratico raccogliendo e analizzando una stringa per vedere come influisce sulla transazione originale.
Abbiamo scoperto che Alice non poteva firmare la transazione di trasferimento poiché questa possibilità era bloccata in @verifier per questo tipo di transazioni.

Esercitiamoci con le stringhe e poi risolveremo questo problema.

RIDE Strings

La transazione è di nuovo possibile, sappiamo come gestire le stringhe.
Impariamo a scrivere smart contract Waves su RIDE e RIDE4DAPPS. Parte 2 (DAO — Organizzazione Autonoma Decentralizzata)


In sintesi, abbiamo tutto il necessario per scrivere logiche complesse. DAO dApp.

Transazioni di dati

Transazioni di dati:
“La dimensione massima per una chiave è di 100 caratteri, e una chiave può contenere punti di codice Unicode arbitrari, compresi spazi e altri simboli non stampabili. I valori delle stringhe hanno un limite di 32.768 byte e il numero massimo di voci possibili nelle transazioni di dati è 100. In generale, la dimensione massima di una transazione di dati è di circa 140 kb — per riferimento, quasi esattamente la lunghezza dell'opera di Shakespeare 'Romeo e Giulietta'.”

Creiamo un DAO con le seguenti condizioni:
Perché una startup ottenga finanziamenti, deve attivare getFunds() il supporto di almeno 2 membri — investitori DAO. Visualizza sarà possibile esattamente quanto indicato in totale su votazione i proprietari del DAO.

Creiamo 3 tipi di chiavi e aggiungiamo la logica per gestire i saldi in due nuove funzioni vote e getFunds:
xx…xx_ia = investitori, saldo disponibile (vote, deposit, withdrawal)
xx…xx_sv = startup, numero di voti (vote, getFunds)
xx…xx_sf = startup, numero di voti (vote, getFunds)
xx…xx = indirizzo pubblico (35 caratteri)

Nota che in Vote abbiamo dovuto aggiornare immediatamente diversi campi:

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

WriteSet ci consente di effettuare più registrazioni all'interno di un'unica invokeScript transazione.

Ecco come appare nel key-value store dell'app DAO, dopo che Bob e Cooper hanno depositato ia-depositi:
Impariamo a scrivere smart contract Waves su RIDE e RIDE4DAPPS. Parte 2 (DAO — Organizzazione Autonoma Decentralizzata)

La funzione di deposito è leggermente cambiata:
Impariamo a scrivere smart contract Waves su RIDE e RIDE4DAPPS. Parte 2 (DAO — Organizzazione Autonoma Decentralizzata)

Adesso arriva il momento più importante per il DAO — votazione per i progetti da finanziare.

Bob vota per il progetto Neli con 500000 wavelets:

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

Impariamo a scrivere smart contract Waves su RIDE e RIDE4DAPPS. Parte 2 (DAO — Organizzazione Autonoma Decentralizzata)

Nel data store vediamo tutte le registrazioni necessarie per l'indirizzo Neli:
Impariamo a scrivere smart contract Waves su RIDE e RIDE4DAPPS. Parte 2 (DAO — Organizzazione Autonoma Decentralizzata)
Cooper ha anche votato per il progetto Neli.
Impariamo a scrivere smart contract Waves su RIDE e RIDE4DAPPS. Parte 2 (DAO — Organizzazione Autonoma Decentralizzata)

Diamo un'occhiata al codice della funzione getFunds. Neli deve raccogliere almeno 2 voti per poter prelevare fondi dal DAO.
Impariamo a scrivere smart contract Waves su RIDE e RIDE4DAPPS. Parte 2 (DAO — Organizzazione Autonoma Decentralizzata)

Neli intende prelevare metà della somma a lei affidata:

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

Impariamo a scrivere smart contract Waves su RIDE e RIDE4DAPPS. Parte 2 (DAO — Organizzazione Autonoma Decentralizzata)

Ci riesce, il che significa che il DAO è attivo!

Abbiamo esaminato il processo di creazione di un DAO in RIDE4DAPPS.
Nelle prossime parti ci occuperemo più dettagliatamente del refactoring del codice e del testing dei casi.

Versione completa del codice in 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
    }
}

Prima parte
Codice su GitHub
Waves RIDE IDE
Annuncio del programma di sovvenzioni

Fonte: habr.com

Acquista un hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista un hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster