WaveFlow is a resource that allows you to create new or use existing exchangers that provide a constant supply and demand for certain traded pairs. Algorithmic pricing is used to ensure consistency of supply and demand - the more popular the token is, the higher its price is set. Each exchanger is a dApp written in RIDE.
The algorithm of calculations is extremely simple:
X — amount of token A which dApp has
Y — amount of token A which dApp has
a — you paid with token of sort A
b — payout with token of sort B
X ⋅ Y = const
(X + a) ⋅ (Y - b) = const
Any user can make an exchange in the existing exchangers.
The process of your exchanger creation consists of the following items:
- Transfer 0.01 Waves for commission on generated account
- Deployment of the script below on this account
- Funding
After that, this exchange starts to appear on the site
{-# STDLIB_VERSION 3 #-}
{-# CONTENT_TYPE DAPP #-}
{-# SCRIPT_TYPE ACCOUNT #-}
let owner = base58'${ownerAddress}'
let IdTokenA = "${assetIdA}"
let IdTokenB = "${assetIdB}"
let comission = ${fee}
let version = "0.0.1"
func assetId (asset: String) =
{
if asset == "WAVES" then unit else asset.fromBase58String()
}
let assetIdTokenA = assetId(IdTokenA)
let assetIdTokenB = assetId(IdTokenB)
@Callable(contextObj)
func fund() = {
let payment = match(contextObj.payment) {
case p:AttachedPayment => p
case _ => throw("Payment not attached")
}
let assetIdReceived = payment.assetId
let tokenReceiveAmount = payment.amount
if (assetIdReceived == assetIdTokenA && !isDefined(getBoolean(this, "fundA"))) then
(
if (isDefined(getBoolean(this, "fundB"))) then
WriteSet([DataEntry("amountTokenA", tokenReceiveAmount ),DataEntry("fundA", true ),DataEntry("exchange_count", 0 ) ,DataEntry("status", true ),
DataEntry("assetIdTokenA", IdTokenA), DataEntry("assetIdTokenB", IdTokenB), DataEntry("comission",comission),DataEntry("version", version), DataEntry("owner", toBase58String(owner) )])
else {
WriteSet([DataEntry("amountTokenA", tokenReceiveAmount ),DataEntry("assetIdTokenA", IdTokenA),DataEntry("fundA", true )])
}
)
else (if (assetIdReceived == assetIdTokenB && !isDefined(getBoolean(this, "fundB")))
then(
if (isDefined(getBoolean(this, "fundA"))) then
WriteSet([DataEntry("amountTokenB", tokenReceiveAmount ),DataEntry("fundB", true ),DataEntry("exchange_count", 0),DataEntry("status", true ),
DataEntry("assetIdTokenB",IdTokenB), DataEntry("assetIdTokenB", IdTokenB),DataEntry("comission",comission),DataEntry("version", version),DataEntry("owner", toBase58String(owner) )])
else {
WriteSet([DataEntry("amountTokenB", tokenReceiveAmount ),DataEntry("assetIdTokenB",IdTokenB),DataEntry("fundB", true )])
}
)
else throw("already funded or you try to fund with incorrect asset")
)
}
@Callable(contextObj)
func replenishment() = {
let payment = match(contextObj.payment) {
case p:AttachedPayment => p
case _ => throw("Payment not attached")
}
let assetIdReceived = payment.assetId
let tokenReceiveAmount = payment.amount
if (assetIdReceived == assetIdTokenA && isDefined(getBoolean(this, "fundA"))) then
(
let amountTokenA = getIntegerValue(this, "amountTokenA")
WriteSet([DataEntry("amountTokenA", amountTokenA+tokenReceiveAmount )])
)
else (if (assetIdReceived == assetIdTokenB && isDefined(getBoolean(this, "fundB")))
then(
let amountTokenB = getIntegerValue(this, "amountTokenB")
WriteSet([DataEntry("amountTokenB", amountTokenB+tokenReceiveAmount )])
)
else throw("there was an error in the replenishment")
)
}
@Callable(contextObj)
func exchanger( minTokenRecieve: Int) = {
if(comission < 0 || comission >100) then(throw("incorrect comission value")) else
#let amountTokenB = this.wavesBalance()
#let amountTokenA = this.assetbalance(assetIdTokenA)
let amountTokenB = getIntegerValue(this, "amountTokenB")
let amountTokenA = getIntegerValue(this, "amountTokenA")
#let digitIdTokenA = if(!isDefined(assetIdTokenA)) then 8 else extract(assetInfo(extract(assetIdTokenA))).decimals
#let digitIdTokenB = if(!isDefined(assetIdTokenB)) then 8 else extract(assetInfo(extract(assetIdTokenB))).decimals
# check and extract info about payment
let payment = match(contextObj.payment) {
case p:AttachedPayment => p
case _ => throw("Payment not attached")
}
let assetIdReceived = payment.assetId
let tokenReceiveAmount = payment.amount
let count = match(getInteger(this, "exchange_count")){
case i: Int => value(getInteger(this, "exchange_count"))
case _=> 0
}
# the amount of sent assets are determined in the next block
if(assetIdReceived == assetIdTokenB)
then
(
#let tokenSendAmount = (AmountTokenA*scale - fraction(amountTokenB,scale*AmountTokenA,tokenReceiveAmount + amountTokenB))/scale
let tokenSendAmount = (fraction(amountTokenA,tokenReceiveAmount,tokenReceiveAmount + amountTokenB)*(100-comission))/100
if(tokenSendAmount < minTokenRecieve) then(throw("Price has changed dramaticaly. Receiving token amount don't satisfy specified price level")) else
let assetIdSent = assetIdTokenA
# successful execution result is updating information about actual balance and supply into the state and transfer tokens to the caller
ScriptResult(
WriteSet([DataEntry("amountTokenA", amountTokenA - tokenSendAmount ),DataEntry("amountTokenB", amountTokenB + tokenReceiveAmount ),DataEntry("exchange_count", count + 1)]),
TransferSet([ScriptTransfer(contextObj.caller, tokenSendAmount, assetIdSent)])
)
)
else ( if (assetIdReceived == assetIdTokenA)
then
(
#let tokenSendAmount = (amountTokenB*scale - fraction(amountTokenA,scale*amountTokenB,tokenReceiveAmount + amountTokenA))/scale
let tokenSendAmount = (fraction(amountTokenB,tokenReceiveAmount,tokenReceiveAmount + amountTokenA)*(100-comission))/100
if(tokenSendAmount < minTokenRecieve) then(throw("Price has changed dramaticaly. Receiving token amount don't satisfy specified price level")) else
let assetIdSent = assetIdTokenB
# successful execution result is updating information about actual balance and supply into the state and transfer tokens to the caller
ScriptResult(
WriteSet([DataEntry("amountTokenA", amountTokenA + tokenReceiveAmount ),DataEntry("amountTokenB", amountTokenB - tokenSendAmount),DataEntry("exchange_count", count + 1)]),
TransferSet([ScriptTransfer(contextObj.caller, tokenSendAmount, assetIdSent)])
)
)
else throw("Asset is not allowed"))
}
@Callable(contextObj)
func withdraw() = {
let BalanceTokenA = this.assetBalance(assetIdTokenA)
let BalanceTokenB = this.assetBalance(assetIdTokenB)
if (contextObj.caller.bytes == owner)
then (
ScriptResult(
WriteSet([DataEntry("amountTokenA", "withdrawn" ),DataEntry("amountTokenB", "withdrawn"),DataEntry("status", false)]),
TransferSet([ScriptTransfer(contextObj.caller,BalanceTokenA, assetIdTokenA), ScriptTransfer(contextObj.caller,BalanceTokenB,assetIdTokenB)])
)
)
else throw("You are not exchanger's owner")
}
@Verifier(contextObj)
func verify() = false