Be a crypto hero in the market by logging in

Don't you have an account? Sign in

Token price

  • BTC

    11,970,630.7KRW

    -0.0%

  • ETH

    248,986.9KRW

    -3.5%

  • XRP

    329.0KRW

    -5.0%

  • BCH

    363,794.4KRW

    -2.3%

  • LTC

    86,146.2KRW

    -1.4%

  • USDT

    1,199.7KRW

    0.2%

  • EOS

    4,528.6KRW

    -5.6%

  • BNB

    24,240.9KRW

    -3.6%

  • BSV

    140,861.8KRW

    -4.0%

  • XLM

    82.2KRW

    -4.4%

  • XMR

    86,478.6KRW

    -0.9%

  • ADA

    57.4KRW

    -6.9%

  • TRX

    19.9KRW

    -7.2%

  • HT

    4,797.9KRW

    -1.2%

  • DASH

    110,305.6KRW

    -1.9%

  • XTZ

    1,256.0KRW

    -0.9%

  • ETC

    7,146.6KRW

    -2.0%

  • NEO

    10,884.0KRW

    -3.6%

  • MKR

    635,925.7KRW

    -2.6%

  • ONT

    929.1KRW

    -4.2%

  • XEM

    53.1KRW

    -1.9%

  • USDC

    1,200.1KRW

    0.2%

  • ZEC

    55,138.9KRW

    -3.7%

  • DOGE

    3.1KRW

    -0.2%

  • BAT

    231.9KRW

    -8.6%

  • DCR

    26,461.5KRW

    -2.0%

  • PAX

    1,200.4KRW

    0.2%

  • VET

    4.8KRW

    -3.0%

  • QTUM

    2,483.5KRW

    -3.8%

  • TUSD

    1,201.5KRW

    0.3%

  • BTG

    12,078.5KRW

    -4.1%

  • OMG

    1,287.1KRW

    -3.8%

  • RVN

    39.5KRW

    -6.2%

  • LINK

    485.1KRW

    2.0%

  • ZRX

    282.0KRW

    -2.6%

  • KCS

    1,806.1KRW

    -0.4%

  • NANO

    1,087.0KRW

    -1.9%

  • REP

    12,619.4KRW

    -1.2%

  • BCD

    738.8KRW

    -2.2%

  • LSK

    1,143.6KRW

    -6.5%

  • INB

    386.2KRW

    1.0%

  • QNT

    10,053.0KRW

    -3.6%

  • ICX

    238.3KRW

    -3.3%

  • WAVES

    1,173.0KRW

    -4.1%

  • DGB

    9.3KRW

    -4.2%

  • XIN

    247,864.9KRW

    -0.2%

  • XIN

    247,864.9KRW

    -0.2%

  • HC

    2,525.1KRW

    -1.5%

  • THETA

    125.8KRW

    -2.0%

  • BTS

    39.8KRW

    -1.7%

  • BCN

    0.6KRW

    -4.6%

  • DAI

    1,202.9KRW

    0.1%

  • MAID

    226.7KRW

    -0.4%

  • SC

    2.3KRW

    4.0%

  • NPXS

    0.4KRW

    -2.3%

  • BTM

    96.2KRW

    -2.4%

  • BTM

    96.2KRW

    -2.4%

  • IOST

    7.8KRW

    -5.7%

  • KMD

    808.4KRW

    -2.6%

  • MONA

    1,321.0KRW

    -4.5%

  • XVG

    5.2KRW

    -3.7%

  • ARDR

    78.3KRW

    6.8%

  • AE

    256.3KRW

    -1.2%

  • GNT

    73.4KRW

    -2.0%

  • ZIL

    8.1KRW

    -2.4%

  • AOA

    10.6KRW

    -4.0%

  • STEEM

    194.8KRW

    -2.1%

  • NEXO

    118.7KRW

    -2.4%

  • ENJ

    82.2KRW

    -1.8%

  • SNT

    18.2KRW

    -0.9%

  • MCO

    3,931.9KRW

    -0.7%

  • XZC

    6,330.9KRW

    -4.1%

  • ELF

    98.5KRW

    -0.8%

  • WTC

    1,176.6KRW

    -2.3%

  • ETN

    5.0KRW

    0.3%

  • RDD

    1.7KRW

    5.1%

  • ELA

    2,893.0KRW

    3.2%

  • STRAT

    416.6KRW

    -4.2%

  • DENT

    0.6KRW

    -3.1%

  • LRC

    43.9KRW

    1.2%

  • FCT

    3,883.9KRW

    3.0%

  • PAI

    25.9KRW

    -3.2%

  • PAI

    25.9KRW

    -3.2%

  • FNB

    132.4KRW

    0.5%

  • WAX

    42.5KRW

    -3.1%

  • R

    71.5KRW

    -2.9%

  • ANT

    1,108.5KRW

    4.4%

  • AION

    94.8KRW

    -2.4%

  • ODE

    132.9KRW

    -1.8%

  • ARK

    263.8KRW

    -2.3%

  • BNT

    475.5KRW

    -4.0%

  • LOOM

    28.2KRW

    -2.2%

  • POWR

    65.1KRW

    -2.8%

  • PPT

    460.2KRW

    -3.7%

  • MOAC

    389.9KRW

    -4.1%

  • PAY

    202.5KRW

    2.2%

  • TTC

    53.9KRW

    -0.6%

  • PIVX

    310.0KRW

    -2.7%

  • POLY

    39.7KRW

    -1.7%

  • CNX

    309.4KRW

    -1.3%

  • HUM

    0.4KRW

    --%

  • REPO

    115.5KRW

    11.6%

  • GUSD

    1,194.6KRW

    0.2%

  • CPT

    3.2KRW

    -6.2%

  • LKY

    182.9KRW

    -1.4%

  • AERGO

    83.9KRW

    -3.5%

  • COSM

    12.1KRW

    -2.2%

  • MBL

    2.1KRW

    -1.1%

  • APIS

    0.9KRW

    4.9%

  • AKRO

    10.7KRW

    -0.2%

  • TEMCO

    1.5KRW

    -1.9%

  • UPP

    13.8KRW

    -3.1%

  • MEETONE

    1.9KRW

    -4.7%

  • BAAS

    0.8KRW

    2.5%

  • MVL

    0.4KRW

    -1.7%

  • ABL

    10.5KRW

    -8.7%

  • DCC

    0.9KRW

    --%

  • eDEL

    2.4KRW

    -0.7%

  • AID

    11.2KRW

    -2.4%

  • PTON

    0.2KRW

    -12.1%

  • WET

    11.4KRW

    1.6%

  • VRA

    0.7KRW

    -5.6%

  • CLB

    2.9KRW

    -2.0%

  • XRA

    11.6KRW

    -0.1%

  • KARMA

    0.2KRW

    -1.5%

  • SEAL

    1.0KRW

    --%

  • PXL

    8.8KRW

    -0.4%

  • HORUS

    0.5KRW

    -13.8%

  • PUB

    0.3KRW

    -23.2%

  • NPER

    3.5KRW

    --%

  • PUT

    2.3KRW

    -23.3%

  • CCH

    0.1KRW

    -10.5%

  • KNT

    0.1KRW

    -39.2%

  • IQ

    6.2KRW

    -8.2%

  • RBTC

    11,947,102.0KRW

    -0.1%

  • BLACK

    1.3KRW

    -3.6%

  • CET

    23.1KRW

    2.8%

  • RCD

    3.0KRW

    --%

  • MCC

    3.0KRW

    --%

  • INC

    2.3KRW

    -0.4%

  • BZKY

    0.3KRW

    --%

  • BORA

    17.1KRW

    -4.8%

  • CRE

    2.9KRW

    0.6%

Community

RIDE for dApps hits Waves TestNet!

Waves | 03.18| 166

The upgrade to Waves’ native blockchain language is a key step along the way to full dApp implementation and Web 3.0 development.

Earlier in January we released our roadmap. We have now reached the first milestone on this, and the release of Node update 0.17 brings RIDE for dApps to TestNet!

As we’ve discussed at length before, blockchain really isn’t designed for carrying out complicated computations. Blockchain architecture simply isn’t suited to this. After seeing the vulnerabilities and edge cases that have arisen on Ethereum, Waves has always maintained that Turing-completeness should not be essential for on-chain blockchain computations. The RIDE language itself is deliberately non-Turing complete for this reason. However, Turing complete computations can still be achieved by spreading operations over consecutive blocks, if such functionality is required. RIDE therefore offers a flexible but safe solution for on-blockchain computation.

RIDE for dApps is being introduced to grant an account a way of assigning a programmable function to itself, with callable functions that are able to:

  1. Receive payments
  2. Change the account’s state
  3. Send WAVES and tokens from the account

To initiate the call, we have added a new command: InvokeScriptTransaction. This needs to be put on the blockchain to call a function, and the sender pays fees to the miner for the invocation to be executed. The sender can optionally attach payment in WAVES or tokens, and upon invocation the contract state can be changed and the contract can make multiple payments.

The existing mechanics for authorisation scripts will be maintained under the @Verifier function of the account. This can be thought of as an ‘admin’ function for the contract’s owner or owners. By default, the contract, contract data and contract tokens are all controlled by the private key for the account. Multisig control is possible. If @Verifier is always false, then the contract is sealed — immutable.

We will now give an overview of some of the use cases that this new functionality makes possible. As we know, these functions can generate transfers (transfer of funds from the contract address) and change a dApp’s state (via DataTransactions from the contract account). Moreover, RIDE for dApps makes implementation of certain use cases far easier and more convenient.

Dividing funds into two equal parts

Suppose we need to implement a mechanism to split the funds in an account equally between two specific addresses.

Consider an implementation using a smart account: the script allows the account to send only MassTransfer transactions that meet the specified conditions:

  1. There are two recipients: Alice and Bob
  2. Fee = 0.006 WAVES
  3. Each of them is sent exactly ((balance-fee) / 2) WAVES

The account can only send a transaction that meets the conditions described above. Other transactions will not be accepted on the blockchain.

# Predefined addresses of recipients
let Alice = base58'3NBVqYXrapgJP9atQccdBPAgJPwHDKkh6A8
let Bob = base58'3N78bNBYhT6pt6nugc6ay1uW1nLEfnRWkJd'
match(tx) {
# only MassTransferTransactions are allowed
case tx : MassTransferTransaction =>
# check if the transaction has exactly two predefined recipients
tx.transferCount == 2
&& tx.transfers[0].recipient == Alice
&& tx.transfers[1].recipient == Bob
# check if WAVES are distributed evenly
&& tx.assetId == unit
&& tx.transfers[0].amount == (wavesBalance(tx.sender) - fee) / 2
&& tx.transfers[0].amount == tx.transfers[1].amount
# check if the fee is equal to 0.006 WAVES
&& tx.fee == 600000
# the other types of transactions are prohibited
case _ => false
}

In RIDE 4 dApps we do this differently: you can divide the funds between two given addresses by calling the ‘split’ function described in the script. This function divides all the funds of the account-contract in half, sending them to two addresses — Alice and Bob. You can call this function by sending InvokeScriptTransaction. In this case, the fee is paid by the one who sends the transaction, so you do not need to check the fee in the script code.

# predefined addresses of recipients
let Alice = base58'3NBVqYXrapgJP9atQccdBPAgJPwHDKkh6A8'
let Bob = base58'3N78bNBYhT6pt6nugc6ay1uW1nLEfnRWkJd'
@Callable(i)
func split() = {
# calculate the amount of WAVES that will be transferred to Alice and Bob
let transferAmount = wavesBalance(this) / 2
# the result of a contract invocation contains two transfers (to Alice and to Bob)
TransferSet(List(
ContractTransfer(Alice, transferAmount, unit),
ContractTransfer(Bob, transferAmount, unit)
))
}

Register of addresses that have paid for a service

Imagine that we want to create a service that allows you to register payment for services by users (for example, a monthly subscription). Subscribers each pay 10 WAVES, with the time of the last payment being entered into the register.

Consider the implementation using a smart account. The account script only allows DataTransactions that meet the following conditions:

  1. 0.005 WAVES fee
  2. Two entries
  • <transfer_id> : “used”
  • <payment_sender_address> : <current_height>

3. In the proofs array, the first element is the transfer transaction id (payment for services), which includes:

  • The recipient address for the contract
  • Amount >= 10 WAVES + 0.005 WAVES (to pay for DataTx fee)
  • Id was not previously used as a key (to prevent double-spending)
# fixed payment and fee values
let dataTxFee = 500000
let paymentAmount = 1000000000
match (tx) {
# only DataTransactions are allowed
case  t: DataTransaction =>
# extract the payment transaction's id from the DataTransaction
let paymentTxId = toBase58String(t.proofs[0])
# extract the provided payment transaction from the blockchain
let paymentTx = transactionById(fromBase58String(paymentTxId))
match (paymentTx) {
# the provided payment transaction should be a TransferTransaction
case paymentTx : TransferTransaction =>
# check if the payment transaction was not used before (to prevent double-spending)
&& !isDefined(getString(this, paymentTxId))
# check if the payment recipient is this account
&& paymentTx.recipient == this
# check if the transfer amount exceeds the required minimum
&& paymentTx.amount >= paymentAmount + dataTxFee
# check if the transferred asset is WAVES
&& paymentTx.assetId == unit
# check if the data satisfies the specified format
&& size(t.data) == 2
&& getString(t.data, paymentTxId) == "used"
&& getInteger(t.data, toBase58String(tx.sender.bytes)) == height
# check if the fee is correct
&& t.fee == dataTxFee
case _ => false
}
# the other types of transactions are prohibited
case _ => false
}

The main disadvantage of this approach is the complex mechanics: you need to send two transactions, one of which refers to the other, as well as send transactions on behalf of other account (specify the sender address of the contract).

Using RIDE for dApps everything is much easier. The payment can be attached to InvokeScriptTransaction. We need only check in the script that the payment was attached. If the address calling the script pays at least 10 WAVES, it is entered into the registry.

# fixed payment amount
let paymentAmount = 1000000000
@Callable(i)
func pay() = {
# check if the attached payment is at least 10 WAVES
let payment = extract(i.payment)
if (payment.asset != unit || payment.amount < paymentAmount)
then throw("You have to provide a payment of at least 10 WAVES")
# the result of a contract invocation contains one dataEntry(<caller_address>, <current_height>)
else WriteSet(List(DataEntry(toBase58String(i.caller.bytes), height)))
}

Voting

In this example, a non-anonymous vote is implemented based on known voting lists and candidates. An account ballot is prepared in advance (sent to the list via DataTransaction), including:

  1. The list of voters (for each voter create an entry { <voter_address_voted>, false })
  2. The list of candidates (for each candidate create an entry { <candidate_address>, 0 }).

To implement using a smart account, the account script allows only DataTransactions:

  1. Submitted by voters who have not already voted. The voter’s public key goes in proofs[1] and signs this DataTransactions (the signature itself goes in the proofs[0]).
  2. The DataTx contains exactly two entries:
  • <candidate> : <current_value> + 1
  • <voter_address_voted> : true
match(tx) {
# only DataTransactions are allowed
case tx : DataTransaction =>
# extract the chosen candidate from the transaction
let candidate = tx.data[0].key
# check if the transaction was signed by the voter
let voterPublicKey = tx.proofs[1]
let voterKey = toBase58String(addressFromPublicKey(voterPublicKey)) + "_voted"
sigVerify(tx.bodyBytes, tx.proofs[0], voterPublicKey)
# check if the voter has not voted yet
&& extract(getBoolean(this, voterKey)) == false
# check if the data satisfies the specified format
&& size(tx.data) == 2
&& extract(getInteger(tx.data, candidate)) == extract(getInteger(this, candidate)) + 1
&& extract(getBoolean(tx.data, voterKey)) == true
case _ => false
}

In RIDE for dApps, in the contract script we describe the vote function, which can be called to vote for a certain candidate.

@Callable(i)
func vote(candidate : String) = {
let voterKey = toBase58String(i.caller.bytes) + "_voted"
# check if the caller is registered as a voter
if (!isDefined(getBoolean(this, voterKey))) then throw("You are not registered as a voter")
# check if the voter has not voted yet
else if (extract(getBoolean(this, voterKey)) == true) then throw("You have already voted")
# check if the candidate is in the voting list
else match (getInteger(this, candidate)) {
case r : Integer =>
WriteSet(List(DataEntry(candidate, r + 1),
DataEntry(voterKey, true)))
case _ => throw ("Candidate is not in the voting list")
}
}

Wallet

In this example, a dApp wallet is implemented: you can send a WAVES payment, and it will save it in the wallet (deposit function), and you can take deposited WAVES back out of the wallet (withdraw function).

func getBalance(address: Address) : Int = {
match getInteger(this, toBase58String(address.bytes)) {
case a: Int => a
case _ => 0
}
}
@Callable(i)
func deposit() = {
let caller = toBase58String(i.caller.bytes)
let currentBalance = getBalance(caller)
let payment = match(i.payment) {
case p : AttachedPayment => p
case _ => throw("You have to provide a payment to deposit")
}
if (payment.asset != unit) then throw("This wallet cannot hold assets other than WAVES")
else {
let newBalance = currentBalance + payment.amount
WriteSet(List(DataEntry(caller, newBalance)))
}
}
@Callable(i)
func withdraw(amount: Int) = {
let caller = toBase58String(i.caller.bytes)
let currentBalance = getBalance(caller)
if (amount < 0) then throw("Can't withdraw negative amount")
else if (amount > currentBalance) then throw("Insufficient balance")
else {
let newBalance = currentBalance - amount
ContractResult(
WriteSet(List(DataEntry(currentKey, newBalance))),
TransferSet(List(ContractTransfer(i.caller, amount, unit)))
)
}
}

Exchanger

This example shows implementation for a dApp-exchanger, which buys/sells WAVES and USD at a fixed price. It describes the functions ‘buyWAVESforUSD’ and ‘sellWAVESforUSD’. Users need to attach payment, and the dApp sends a transfer in response.

let WAVES = unit                                                    # amount asset
let USD = base58'Ft8X1v1LTa1ABafufpaCWyVj8KkaxUWE6xBhW6sNFJck'      # price asset
let buyPrice = 301
let sellPrice = 299
let scale = 100000000
@Callable(i)
func buyWAVESforUSD() = {
let payment = extract(i.payment)
if (payment.asset != USD) then throw("To buy WAVES for USD you have to provide USD")
else {
let amount = payment.amount * scale / buyPrice
TransferSet(List(ContractTransfer(i.caller, amount, USD)))
}
}
@Callable(i)
func sellWAVESforUSD() = {
let payment = extract(i.payment)
if (payment.asset != WAVES) then throw("To sell WAVES for USD you have to provide WAVES")
else {
let amount = payment.amount / (scale / sellPrice)
TransferSet(List(ContractTransfer(i.caller, amount, WAVES)))
}
}

This is by no means a complete list of possible use cases. Check out RIDE for dApps on TestNet and let us know your thoughts and your own use cases!

Join Waves Community
Read Waves News channel
Follow Waves Twitter
Subscribe to Waves Subreddit


RIDE for dApps hits Waves TestNet! was originally published in Waves Platform on Medium, where people are continuing the conversation by highlighting and responding to this story.

Comment 0

delete

Are you sure you want to delete this post?