Aprendendo a escrever contratos inteligentes Waves em RIDE e RIDE4DAPPS. Parte 2 (DAO — Organização Autônoma Descentralizada)

Aprendendo a escrever contratos inteligentes Waves em RIDE e RIDE4DAPPS. Parte 2 (DAO — Organização Autônoma Descentralizada)

Olá a todos!

Em primeiro parte, nós analisamos detalhadamente como criar e trabalhar com dApp (aplicativo descentralizado) em Waves RIDE IDE.

Vamos agora testar um pouco do que foi analisado exemplo.

Etapa 3. Testando a conta dApp

Aprendendo a escrever contratos inteligentes Waves em RIDE e RIDE4DAPPS. Parte 2 (DAO — Organização Autônoma Descentralizada)

Quais problemas imediatamente se destacam com Alice dApp Conta?
Primeiro:
Boob e Cooper podem acidentalmente enviar fundos para o endereço dApp através de uma transferência transação comum e, assim, não poderão acessá-los novamente.

Segundo:
Não restringimos Alice a retirar fundos sem a concordância de Boob ou Cooper. Observe que, ao verificar, todas as transações de Alice serão executadas.

Vamos corrigir o segundo ponto, proibindo Alice transferência transações. Implante o script corrigido:
Aprendendo a escrever contratos inteligentes Waves em RIDE e RIDE4DAPPS. Parte 2 (DAO — Organização Autônoma Descentralizada)

Tentamos retirar moedas do dApp de Alice com sua assinatura. Recebemos um erro:
Aprendendo a escrever contratos inteligentes Waves em RIDE e RIDE4DAPPS. Parte 2 (DAO — Organização Autônoma Descentralizada)

Tentamos retirar através do withdraw:

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

O script funciona e resolvemos o segundo ponto!

Etapa 4. Criando um DAO com votação

Infelizmente, a linguagem RIDE ainda não prevê recursos para trabalhar com coleções (dicionários, iteradores, redutores, etc.). No entanto, para quaisquer operações com coleções planas key-value podemos projetar um sistema que trabalhe com strings, correspondendo às chaves e sua decodificação.

É muito fácil concatenar strings, as strings podem ser divididas por índices.
Vamos usar um exemplo de teste para montar e desmontar uma string e verificar como isso afeta o resultado da transação.
Paramos no ponto em que Alice não conseguia assinar a transação de Transfer, pois essa possibilidade estava bloqueada em @verifier para esse tipo de transações.

Vamos praticar com strings e depois permitiremos isso.

RIDE Strings

A transação é mais uma vez possível, conseguimos trabalhar com strings.
Aprendendo a escrever contratos inteligentes Waves em RIDE e RIDE4DAPPS. Parte 2 (DAO — Organização Autônoma Descentralizada)


Portanto, temos tudo o que precisamos para escrever uma lógica complexa DAO dApp.

Transações de Dados

Transações de Dados:
“O tamanho máximo para uma chave é de 100 caracteres, e uma chave pode conter pontos de código Unicode arbitrários, incluindo espaços e outros símbolos não imprimíveis. Valores de string têm um limite de 32.768 bytes e o número máximo de entradas possíveis em uma transação de dados é de 100. No total, o tamanho máximo de uma transação de dados é de cerca de 140kb — para referência, quase exatamente o comprimento da peça de Shakespeare ‘Romeu e Julieta’.”

Criamos um DAO com as seguintes condições:
Para que a startup obtenha financiamento, chamando getFunds() é necessária o apoio de pelo menos 2 participantes — investidores do DAO. Retirar será possível exatamente o tanto quanto foi indicado no total. votação proprietários da DAO.

Vamos criar 3 tipos de chaves e adicionar a lógica para trabalhar com saldos em 2 novas funções: vote e getFunds:
xx…xx_ia = investidores, saldo disponível (vote, depósito, retirada)
xx…xx_sv = startups, número de votos (vote, getFunds)
xx…xx_sf = startups, número de votos (vote, getFunds)
xx…xx = endereço público (35 caracteres)

Note que na função Vote precisamos atualizar vários campos de uma vez:

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

WriteSet nos permite fazer várias entradas dentro de uma única invokeScript transação.

É assim que isso aparece no armazenamento key-value do dApp DAO, depois que Bob e Cooper fizeram seus depósitos: ia-depósitos:
Aprendendo a escrever contratos inteligentes Waves em RIDE e RIDE4DAPPS. Parte 2 (DAO — Organização Autônoma Descentralizada)

A função de depósito foi ligeiramente alterada:
Aprendendo a escrever contratos inteligentes Waves em RIDE e RIDE4DAPPS. Parte 2 (DAO — Organização Autônoma Descentralizada)

Agora chegamos ao ponto mais importante na operação da DAO — votação para projetos de financiamento.

Bob vota no projeto Neli com 500000 wavelets:

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

Aprendendo a escrever contratos inteligentes Waves em RIDE e RIDE4DAPPS. Parte 2 (DAO — Organização Autônoma Descentralizada)

No armazenamento de dados, vemos todos os registros necessários para o endereço Neli:
Aprendendo a escrever contratos inteligentes Waves em RIDE e RIDE4DAPPS. Parte 2 (DAO — Organização Autônoma Descentralizada)
Cooper também votou no projeto Neli.
Aprendendo a escrever contratos inteligentes Waves em RIDE e RIDE4DAPPS. Parte 2 (DAO — Organização Autônoma Descentralizada)

Vamos dar uma olhada no código da função getFunds. Neli precisa coletar pelo menos 2 votos para conseguir retirar fundos da DAO.
Aprendendo a escrever contratos inteligentes Waves em RIDE e RIDE4DAPPS. Parte 2 (DAO — Organização Autônoma Descentralizada)

Neli pretende retirar metade do valor que lhe foi confiado:

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

Aprendendo a escrever contratos inteligentes Waves em RIDE e RIDE4DAPPS. Parte 2 (DAO — Organização Autônoma Descentralizada)

Ela consegue, ou seja, a DAO está funcionando!

Examinamos o processo de criação de uma DAO na linguagem RIDE4DAPPS.
Nas próximas partes, abordaremos mais detalhadamente a refatoração do código e o teste de casos.

A versão completa do código está em 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
    }
}

Primeira parte
Código no GitHub
Waves RIDE IDE
Anúncio do programa de subsídios

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster