WaveFlow — dApp exchanger

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:

  1. Transfer 0.01 Waves for commission on generated account
  2. Deployment of the script below on this account
  3. 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

4 Likes

Is Liquid the only pairing supported. I would like to do my token and WBTC or WUSD.

2 Likes

good great project we will write too

1 Like

Hi, @AGAToken!
Yeah, right now it’s only possible to pair up with Liquid

Hello, @juandavidaristizabal !
In order to create an exchange, you need to have both tokens(Dimension Z and Liquid) on your address.
Is this condition met?

1 Like

Hey @jaguartoken
Thank you!

Good dApp but it’s a pity to have allowed the pairing only with Liquid. I think it’s a big limitation.

I hope you extend this function to other more popular cryptocurrencies or fiat such as BTC, ETH, USD, EUR.

Hi @Angry_Panda_ROAR!
One linking token can be very useful for further development,

It is already working for me, thanks!

In the future will you be able to exchange your custom coin for bitcoin?

Hi @jzurawski275!
The restriction is introduced for possible further development of the system. One option, however, is to create arbitrary pairs

What do you need to be able to click exhange token to liquid. I try and set it up but the create exchange botton does not work. Do i not have enough wave tokens to cover the fees?

Hey @jzurawski275!
To create exchanger you need have your token and Liquid on the account

how much liquid? I dont need the same amount do i? There would be no point exchanging one token to the other if i already have both amounts.

I am not trying to buy liquid I am trying to echange my crypto to liquid you should not need any in the account if you are trying to exchange. The hole point is you dont have any and you want to some. I guess I am confused why I need them if i am trying to get them.

@jzurawski275 I’m sorry about the inappropriate answer.

To make a swap one token to another you need only to have the specified number of tokens in the “You pay” field and 0.005 WAVES as fee.

If you have any more questions, I’d love to answer them in PM.