
Tere kÔigile!
Uues osa, me kĂ€sitleme ĂŒksikasjalikult, kuidas luua ja töötada dApp-i (detsentraliseeritud rakenduse) juures .
Testime nĂŒĂŒd veidi lahti vĂ”etud .
Etapp 3. dApp konto testimine

Millised probleemid esmapilgul Alice'i juures silma hakkavad dApp Konto?
Esiteks:
Boob ja Cooper vĂ”ivad juhuslikult saata dApp aadressile raha tavalise ĂŒlekandmine tehingu kaudu ja seega ei pÀÀse nad sellele enam juurde.Teiseks:
Me ei piira Alice'i raha vÀljavÔtmist Boobi vÔi Cooperiga kooskÔlastamise nÔudmisest. Pidage meeles, et kÔik Alice'i tehingud tÀidetakse.
Parandame teise, keelates Alice'il ĂŒlekandmine tehingud. Rakendame parandatud skripti:

ïżŒ
Proovime vÀljavÔtteid Alice'i dAppist ja tema allkirjast. Saame vea:

Proovime vÀljavÔttes:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"withdraw",args:[{type:"integer", value: 1000000}]}, payment: []}))Skript töötab ja 2. punkt on selge!
Etapp 4. Loome DAO hÀÀltega
Kahjuks ei saa RIDE keeles praegu töötada kollektsioonidega (sĂ”nastikud, iteratorid, vĂ€hendajad jne). Siiski, kĂ”iki operatsioone tasapinnaliste kollektsioonidega key-value saame planeerida sĂŒstemaatika stringide, vastavalt vĂ”tmete ja nende deĆĄifreerimisega.
Stringe on vÀga lihtne konkateneerida, stringe saab jagada indeksite jÀrgi.
Kogume ja analĂŒĂŒsime stringi testnĂ€itena ning kontrollime, kuidas see mĂ”jutab tehingu tulemust.
Keskendume sellele, et Alice ei saanud Transfer tehingut allkirjastada, kuna see vÔimalus oli @verifieris blokeeritud selliste tehingute jaoks.
Haarame stringide kasutamisega ja seejÀrel lubame selle.
RIDE stringid
Tehing on taas vÔimalik, oskame töötada stringidega.

ïżŒ
KokkuvÔttes on meil kÔik vajalik keerulise loogika kirjutamiseks DAO dApp.
Andmete tehingud
Andmete tehingud:
âVĂ”tme maksimaalne suurus on 100 tĂ€hemĂ€rki, ja vĂ”tmel vĂ”ivad olla juhuslikud Unicode koodipunktid, sealhulgas tĂŒhikud ja muud mitteprinditavad sĂŒmbolid. Stringi vÀÀrtustel on piirang 32 768 baiti ja andmete tehingus on maksimaalne sisestuste arv 100. Ăldiselt on andmete tehingu maksimaalne suurus umbes 140 kbâââviiteks, justkui William Shakespeare'i nĂ€idend 'Romeo ja Julia' pikkus.â
Loome DAO jÀrgmiste tingimustega:
Et startup saaks rahastust, kutsudes esile getFunds() on vajalik vĂ€hemalt 2 DAO osalise â investorite toetust. VĂ€ljasta saab olema tĂ€pselt nii palju, kui kokku hÀÀletati DAO omanike poolt. ĐČлаЎДлŃŃŃ DAO.
Teeme 3 tĂŒĂŒpi vĂ”tmeid ja lisame tasakaalude haldamise loogika kahe uue funktsiooni, vote ja getFunds, kaudu:
xxâŠxx_ia = investorid, saadaval tasakaal (vote, deposit, withdrawal)
xxâŠxx_sv = startupid, hÀÀlte arv (vote, getFunds)
xxâŠxx_sf = startupid, hÀÀlte arv (vote, getFunds)
xxâŠxx = avalik aadress (35 tĂ€hte)
Pange tÀhele, et Vote'is pidi me vÀrskendama korraga mitmeid vÀlju:
WriteSet([DataEntry(key1, value1), DataEntry(key2, value2)]),WriteSet vĂ”imaldab meil teha korraga mitu kirjet ĂŒhe invokeScript tehingu raames.
Nii see vÀlja nÀeb DAO dApp key-value salvestuses, pÀrast seda, kui Bob ja Cooper on fondi tÀiendanud ia-deposiid:

Deposiidi funktsioon on meil pisut muutunud:

NĂŒĂŒd on kĂ€es kĂ”ige olulisem moment DAO tegevuses â hÀÀletamine projektide rahastamise poolt.
Bob hÀÀletab projekti Neli poolt 500000 waveletiga:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []}))
Andmehoidlas nÀeme kÔik vajalikud kirjed Neli aadressi jaoks:

Cooper hÀÀletas samuti projekti Neli poolt.

Vaatame koodi funktsioonist getFunds. Neli peab koguma vÀhemalt 2 hÀÀlt, et saada vÔimalus eraldada vahendeid DAO-st.

Neli kavatseb vÀlja vÔtta poole talle usaldatud summast:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []}))
Tal Ônnestub see, mis tÀhendab, et DAO töötab!
Me kÀsitlesime DAO loomise protsessi keeles RIDE4DAPPS.
JÀrgmistes osades kÀsitleme koodi refaktoreerimist ja testimist.
Koodi tÀisversioon
# 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
}
}
Allikas: habr.com
