Apprendre à rédiger des contrats intelligents Waves sur RIDE et RIDE4DAPPS. Partie 2 (DAO - Organisation autonome décentralisée)

Apprendre à rédiger des contrats intelligents Waves sur RIDE et RIDE4DAPPS. Partie 2 (DAO - Organisation autonome décentralisée)

Bonjour à tous!

В première Dans la partie, nous avons examiné en détail comment créer et utiliser dApp (application décentralisée) dans Vagues RIDE IDE.

Testons un peu celui démonté maintenant exemple.

Étape 3. Test du compte dApp

Apprendre à rédiger des contrats intelligents Waves sur RIDE et RIDE4DAPPS. Partie 2 (DAO - Organisation autonome décentralisée)

Quels problèmes vous sautent immédiatement aux yeux avec Alice ? dApp Compte?
Premièrement:
Boob et Cooper peuvent accidentellement envoyer des fonds à l'adresse dApp en utilisant des méthodes régulières. transférer transactions et ne pourra donc pas y accéder.

Deuxièmement:
Nous n'empêchons en aucune manière Alice de retirer des fonds sans l'approbation de Boob et/ou Cooper. Puisque, faites attention à vérifier, toutes les transactions d'Alice seront exécutées.

Réparons le 2ème en interdisant Alice transférer transactions. Déployons le script corrigé :
Apprendre à rédiger des contrats intelligents Waves sur RIDE et RIDE4DAPPS. Partie 2 (DAO - Organisation autonome décentralisée)

Nous essayons de retirer des pièces de dApp Alice et sa signature. Nous obtenons une erreur :
Apprendre à rédiger des contrats intelligents Waves sur RIDE et RIDE4DAPPS. Partie 2 (DAO - Organisation autonome décentralisée)

Essayons de nous retirer via retirer :

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

Le script fonctionne et nous avons compris le 2ème point !

Étape 4. Créer un DAO avec vote

Malheureusement, le langage RIDE n'offre pas encore la possibilité de travailler avec des collections (dictionnaires, itérateurs, réducteurs, etc.). Cependant, pour toute opération avec des collections plates valeur clé nous pouvons concevoir un système pour travailler avec des chaînes, en conséquence avec des clés et leur déchiffrement.

Les chaînes sont très faciles à concaténer ; les chaînes peuvent être séparées par des index.
Collectons et analysons une chaîne comme exemple de test et vérifions comment cela affecte le résultat de la transaction.
Nous avons retenu qu'Alice ne pouvait pas signer la transaction de transfert, puisque cette capacité était bloquée dans @verifier pour ce type de transaction.

Pratiquons avec les chaînes, puis résolvons ce problème.

Cordes RIDE

La transaction est à nouveau possible, on sait travailler avec des chaînes.
Apprendre à rédiger des contrats intelligents Waves sur RIDE et RIDE4DAPPS. Partie 2 (DAO - Organisation autonome décentralisée)


Au total, nous avons tout le nécessaire pour écrire une logique complexe DAO dApp.

Transactions de données

Transactions de données :
« La taille maximale d'une clé est de 100 caractères, et une clé peut contenir des points de code Unicode arbitraires, notamment des espaces et d'autres symboles non imprimables. Les valeurs de chaîne ont une limite de 32,768 100 octets et le nombre maximum d'entrées possibles dans une transaction de données est de 140. Dans l'ensemble, la taille maximale d'une transaction de données est d'environ XNUMX Ko — pour référence, presque exactement la durée de la pièce de Shakespeare « Roméo et Juliette ». '.»

Nous créons un DAO avec les conditions suivantes :
Pour qu'une startup reçoive un financement en appelant getFunds() le soutien d'au moins 2 participants - investisseurs DAO - est requis. retirer il sera possible exactement à hauteur du total indiqué sur vote Propriétaires de DAO.

Créons 3 types de clés et ajoutons une logique pour travailler avec les soldes dans 2 nouvelles fonctions vote et getFunds :
xx… xx_ia = investisseurs, solde disponible (vote, dépôt, retrait)
xx… xx_sv = startups, nombre de votes (votez, obtenez des fonds)
xx… xx_sf = startups, nombre de votes (votez, obtenez des fonds)
xx…xx = adresse publique (35 caractères)

Veuillez noter que dans Vote, nous devions mettre à jour plusieurs champs à la fois :

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

WriteSet nous permet de faire plusieurs enregistrements à la fois en un seul invoqueScript transactions.

Voici à quoi cela ressemble dans le stockage clé-valeur de la dApp DAO, après le réapprovisionnement de Bob et Cooper ia-dépôts :
Apprendre à rédiger des contrats intelligents Waves sur RIDE et RIDE4DAPPS. Partie 2 (DAO - Organisation autonome décentralisée)

Notre fonction de dépôt a légèrement changé :
Apprendre à rédiger des contrats intelligents Waves sur RIDE et RIDE4DAPPS. Partie 2 (DAO - Organisation autonome décentralisée)

Vient maintenant le moment le plus important dans les activités du DAO - vote pour les projets à financer.

Bob vote pour le projet 500000 XNUMX ondelettes de Neli :

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

Apprendre à rédiger des contrats intelligents Waves sur RIDE et RIDE4DAPPS. Partie 2 (DAO - Organisation autonome décentralisée)

Dans le magasin de données, nous voyons toutes les entrées nécessaires pour l'adresse de Neli :
Apprendre à rédiger des contrats intelligents Waves sur RIDE et RIDE4DAPPS. Partie 2 (DAO - Organisation autonome décentralisée)
Cooper a également voté pour le projet Neli.
Apprendre à rédiger des contrats intelligents Waves sur RIDE et RIDE4DAPPS. Partie 2 (DAO - Organisation autonome décentralisée)

Jetons un coup d'oeil au code de la fonction obtenir des fonds. Neli doit récolter un minimum de 2 votes pour pouvoir retirer des fonds du DAO.
Apprendre à rédiger des contrats intelligents Waves sur RIDE et RIDE4DAPPS. Partie 2 (DAO - Organisation autonome décentralisée)

Neli va retirer la moitié de la somme qui lui a été confiée :

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

Apprendre à rédiger des contrats intelligents Waves sur RIDE et RIDE4DAPPS. Partie 2 (DAO - Organisation autonome décentralisée)

Elle réussit, c'est-à-dire que DAO fonctionne !

Nous avons examiné le processus de création d'un DAO dans le langage RIDE4DAPPS.
Dans les parties suivantes, nous examinerons de plus près la refactorisation du code et les tests de cas.

Version complète du code dans IDE RIDE Waves :

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

La première partie
Coder sur GitHub
Vagues RIDE IDE
Annonce du programme de subvention

Source: habr.com

Ajouter un commentaire