Skip to content

Commit

Permalink
proxy v3 support (#71)
Browse files Browse the repository at this point in the history
* SPF Fee added to amm bots

* Support eip-0003 derivation path for mnemonic from config

* docs
  • Loading branch information
GusevTimofey authored Apr 18, 2023
1 parent 3f35dcf commit d4aa18f
Show file tree
Hide file tree
Showing 100 changed files with 4,354 additions and 1,036 deletions.
1 change: 0 additions & 1 deletion .bsp/sbt.json

This file was deleted.

2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ project/metals.sbt

config.env

/.bsp/
/.bsp/
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ Windows:
cd ergo-dex-backend
copy ./config-example.env ./config.env
```
The 2 values that need to be changed in the `config.env` file are the address you want to recieve fees on and the URI to your node (localhost/127.0.0.1 might not be accessible from within a docker container, it is best to use the local lan ip if the node is running on the same host).
The 2 values that need to be changed in the `config.env` file are the mnemonic ([howto](https://github.com/spectrum-finance/ergo-dex-backend/blob/6c9fccfbd4de921d41343a5937153f1724408a10/modules/amm-executor/src/test/scala/org/ergoplatfrom/dex/executor/amm/HowTo.scala#L14)) from which bot will create the wallet to receive fees on and pay miner fees from (in SPF fee cases)
and the URI to your node (localhost/127.0.0.1 might not be accessible from within a docker container, it is best to use the local lan ip if the node is running on the same host).
### Running the services
Once the `config.env` file is created the only thing left to do is to run the containers:
Once the `config.env` file is created, make sure you have funds on expected address (you can check which address bot will use with `HowTo.scala` script). Next, the only thing left to do is to run the containers:

Linux:
```
Expand Down
3 changes: 3 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ lazy val utxoTracker = utils
.settings(commonSettings)
.settings(assembly / mainClass := Some("org.ergoplatform.dex.tracker.App"))
.settings(nativePackagerSettings("utxo-tracker"))
.settings(dockerBaseImage := "openjdk:11-jre-slim")
.enablePlugins(JavaAppPackaging, UniversalPlugin, DockerPlugin)
.dependsOn(Seq(core, cache).map(_ % allConfigDependency): _*)

Expand Down Expand Up @@ -157,6 +158,7 @@ lazy val ammExecutor = utils
assembly / mainClass := Some("org.ergoplatform.dex.executor.amm.App"),
libraryDependencies ++= SttpClientCE
)
.settings(dockerBaseImage := "openjdk:11-jre-slim")
.settings(nativePackagerSettings("amm-executor"))
.enablePlugins(JavaAppPackaging, UniversalPlugin, DockerPlugin)
.dependsOn(Seq(core, http).map(_ % allConfigDependency): _*)
Expand All @@ -168,6 +170,7 @@ lazy val poolResolver = utils
assembly / mainClass := Some("org.ergoplatform.dex.resolver.App"),
libraryDependencies ++= RocksDB
)
.settings(dockerBaseImage := "openjdk:11-jre-slim")
.settings(nativePackagerSettings("pool-resolver"))
.enablePlugins(JavaAppPackaging, UniversalPlugin, DockerPlugin)
.dependsOn(Seq(core, http).map(_ % allConfigDependency): _*)
Expand Down
2 changes: 1 addition & 1 deletion config-example.env
Original file line number Diff line number Diff line change
@@ -1 +1 @@
JAVA_TOOL_OPTIONS="-Dnetwork.node-uri=http://<my node ip>:9053 -Dexchange.reward-address=<my ergo wallet address>"
JAVA_TOOL_OPTIONS="-Dnetwork.node-uri=http://<my node ip>:9053 -Dexchange.mnemonic=<my ergo mnemonic>"
4 changes: 3 additions & 1 deletion modules/amm-executor/src/main/resources/application.conf
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
rotation.retry-delay = 120s

exchange.reward-address = "9gCigPc9cZNRhKgbgdmTkVxo1ZKgw79G8DvLjCcYWAvEF3XRUKy"
exchange.spectrum-token = "9a06d9e545a41fd51eeffc5e20d818073bf820c635e2a9d922269913e0de369d"
exchange.mnemonic = ""
monetary.min-dex-token-fee = 10

execution.order-lifetime = 300s

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ import org.ergoplatform.dex.configs.ConsumerConfig
import org.ergoplatform.dex.domain.amm.{CFMMOrder, OrderId}
import org.ergoplatform.dex.executor.amm.config.ConfigBundle
import org.ergoplatform.dex.executor.amm.context.AppContext
import org.ergoplatform.dex.executor.amm.interpreters.{CFMMInterpreter, N2TCFMMInterpreter, T2TCFMMInterpreter}
import org.ergoplatform.dex.executor.amm.processes.Executor
import org.ergoplatform.dex.executor.amm.interpreters.v1.{InterpreterV1, N2TCFMMInterpreter, T2TCFMMInterpreter}
import org.ergoplatform.dex.executor.amm.interpreters.v3.InterpreterV3
import org.ergoplatform.dex.executor.amm.interpreters.v3.n2t.N2TV3
import org.ergoplatform.dex.executor.amm.interpreters.v3.t2t.T2TV3
import org.ergoplatform.dex.executor.amm.interpreters.CFMMInterpreter
import org.ergoplatform.dex.executor.amm.processes.{Executor, NetworkContextUpdater}
import org.ergoplatform.dex.executor.amm.repositories.CFMMPools
import org.ergoplatform.dex.executor.amm.services.Execution
import org.ergoplatform.dex.executor.amm.services.{DexOutputResolver, Execution}
import org.ergoplatform.dex.executor.amm.streaming._
import org.ergoplatform.dex.protocol.amm.AMMType.{CFMMType, N2T_CFMM, T2T_CFMM}
import org.ergoplatform.ergo.modules.ErgoNetwork
Expand All @@ -34,40 +38,50 @@ import zio.{ExitCode, URIO, ZEnv}
object App extends EnvApp[AppContext] {

def run(args: List[String]): URIO[ZEnv, ExitCode] =
init(args.headOption).use { case (executor, ctx) =>
val appF = executor.run.compile.drain
init(args.headOption).use { case (tasks, ctx) =>
val appF = fs2.Stream(tasks: _*).parJoinUnbounded.compile.drain
appF.run(ctx) as ExitCode.success
}.orDie

private def init(configPathOpt: Option[String]): Resource[InitF, (Executor[StreamF], AppContext)] =
private def init(configPathOpt: Option[String]) =
for {
blocker <- Blocker[InitF]
configs <- Resource.eval(ConfigBundle.load[InitF](configPathOpt, blocker))
ctx = AppContext.init(configs)
implicit0(isoKRun: IsoK[RunF, InitF]) = isoKRunByContext(ctx)
implicit0(e: ErgoAddressEncoder) = ErgoAddressEncoder(configs.protocol.networkType.prefix)
implicit0(confirmedOrders: CFMMConsumerIn[StreamF, RunF, Confirmed]) =
makeConsumer[OrderId, Confirmed[CFMMOrder.Any]](configs.consumers.confirmedOrders)
makeConsumer[OrderId, Confirmed[CFMMOrder.AnyOrder]](configs.consumers.confirmedOrders)
implicit0(unconfirmedOrders: CFMMConsumerIn[StreamF, RunF, Unconfirmed]) =
makeConsumer[OrderId, Unconfirmed[CFMMOrder.Any]](configs.consumers.unconfirmedOrders)
makeConsumer[OrderId, Unconfirmed[CFMMOrder.AnyOrder]](configs.consumers.unconfirmedOrders)
implicit0(consumerRetries: CFMMConsumerRetries[StreamF, RunF]) =
makeConsumer[OrderId, Delayed[CFMMOrder.Any]](configs.consumers.ordersRetry)
makeConsumer[OrderId, Delayed[CFMMOrder.AnyOrder]](configs.consumers.ordersRetry)
implicit0(orders: CFMMConsumerIn[StreamF, RunF, Id]) =
Consumer.combine2(confirmedOrders, unconfirmedOrders)(_.entity, _.entity)
implicit0(producerRetries: CFMMProducerRetries[StreamF]) <-
Producer.make[InitF, StreamF, RunF, OrderId, Delayed[CFMMOrder.Any]](configs.producers.ordersRetry)
implicit0(consumer: CFMMCircuit[StreamF, RunF]) = StreamingCircuit.make[StreamF, RunF, OrderId, CFMMOrder.Any]
Producer.make[InitF, StreamF, RunF, OrderId, Delayed[CFMMOrder.AnyOrder]](configs.producers.ordersRetry)
implicit0(consumer: CFMMCircuit[StreamF, RunF]) =
StreamingCircuit.make[StreamF, RunF, OrderId, CFMMOrder.AnyOrder]
implicit0(backend: SttpBackend[RunF, Fs2Streams[RunF]]) <- makeBackend(ctx, blocker)
implicit0(explorer: ErgoExplorer[RunF]) = ErgoExplorerStreaming.make[StreamF, RunF]
implicit0(node: ErgoNode[RunF]) <- Resource.eval(ErgoNode.make[InitF, RunF])
implicit0(network: ErgoNetwork[RunF]) = ErgoNetwork.make[RunF]
implicit0(pools: CFMMPools[RunF]) <- Resource.eval(CFMMPools.make[InitF, RunF])
implicit0(t2tInt: CFMMInterpreter[T2T_CFMM, RunF]) <- Resource.eval(T2TCFMMInterpreter.make[InitF, RunF])
implicit0(n2tInt: CFMMInterpreter[N2T_CFMM, RunF]) <- Resource.eval(N2TCFMMInterpreter.make[InitF, RunF])
implicit0(interpreter: CFMMInterpreter[CFMMType, RunF]) = CFMMInterpreter.make[RunF]
implicit0(pools: CFMMPools[RunF]) <- Resource.eval(CFMMPools.make[InitF, RunF])
(networkContextUpdater, context) <- Resource.eval(NetworkContextUpdater.make[InitF, StreamF, RunF])
implicit0(resolver: DexOutputResolver[RunF]) <-
Resource.eval(DexOutputResolver.make[InitF, RunF](configs.exchange))
implicit0(t2tInt: InterpreterV1[T2T_CFMM, RunF]) <-
Resource.eval(T2TCFMMInterpreter.make[InitF, RunF])
implicit0(n2tInt: InterpreterV1[N2T_CFMM, RunF]) <-
Resource.eval(N2TCFMMInterpreter.make[InitF, RunF])
implicit0(n2tInt: InterpreterV3[N2T_CFMM, RunF]) <-
Resource.eval(N2TV3.make[InitF, RunF](configs.exchange, configs.monetary, context))
implicit0(n2tInt: InterpreterV3[T2T_CFMM, RunF]) <-
Resource.eval(T2TV3.make[InitF, RunF](configs.exchange, configs.monetary, context))
implicit0(interpreter: CFMMInterpreter[CFMMType, RunF]) <-Resource.eval(CFMMInterpreter.make[InitF, RunF])
implicit0(execution: Execution[RunF]) <- Resource.eval(Execution.make[InitF, RunF])
executor <- Resource.eval(Executor.make[InitF, StreamF, RunF])
} yield executor -> ctx
} yield List(executor.run, networkContextUpdater.run) -> ctx

private def makeBackend(
ctx: AppContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package org.ergoplatform.dex.executor.amm.config

import derevo.derive
import derevo.pureconfig.pureconfigReader
import org.ergoplatform.ergo.Address
import org.ergoplatform.ergo.{Address, TokenId}
import sigmastate.basics.DLogProtocol.DLogProverInput
import tofu.Context
import tofu.logging.derivation.loggable

@derive(pureconfigReader, loggable)
final case class ExchangeConfig(rewardAddress: Address)
final case class ExchangeConfig(spectrumToken: TokenId, mnemonic: String)

object ExchangeConfig extends Context.Companion[ExchangeConfig]
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.ergoplatform.dex.executor.amm.domain
import cats.syntax.show._
import org.ergoplatform.dex.domain.AssetAmount
import org.ergoplatform.dex.domain.amm.PoolId
import org.ergoplatform.dex.domain.amm.OrderId
import org.ergoplatform.ergo.{BoxId, SErgoTree}
import tofu.Errors

Expand All @@ -17,13 +18,23 @@ object errors {
s"Price slipped up too much for Pool{id=$poolId}. {minOutput=${minOutput.show}, actualOutput=${actualOutput.show}}"
)

final case class PriceTooLow(poolId: PoolId, maxDexFee: Long, actualDexFee: Long)
final case class PriceTooLow(poolId: PoolId, maxDexFee: Long, actualDexFee: Long, quote: Long)
extends ExecutionFailed(
s"Price slipped down too much for Pool{id=$poolId}. {maxDexFee=${maxDexFee.show}, actualDexFee=${actualDexFee.show}}"
s"Price slipped down too much for Pool{id=$poolId}. {maxDexFee=${maxDexFee.show}, actualDexFee=${actualDexFee.show}, quote=${quote}"
)

final case class IncorrectMultiAddressSwapTree(poolId: PoolId, orderId: BoxId, tree: SErgoTree, err: String)
final case class IncorrectMultiAddressTree(poolId: PoolId, orderId: BoxId, tree: SErgoTree, err: String)
extends ExecutionFailed(
s"Incorrect multi address tree for pool $poolId and order $orderId: $tree. Err is: $err"
)

final case class EmptyOutputForDexTokenFee(poolId: PoolId, orderId: BoxId)
extends ExecutionFailed(
s"There is no output for make box with spf fee."
)

final case class NegativeDexFee(poolId: PoolId, orderId: OrderId, dexFee: Long)
extends ExecutionFailed(
s"Dex fee is negative ${dexFee} for pool: $poolId, order: $orderId."
)
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,39 @@
package org.ergoplatform.dex.executor.amm.interpreters

import cats.FlatMap
import cats.{Functor, Monad}
import org.ergoplatform.ErgoLikeTransaction
import org.ergoplatform.ergo.state.{Predicted, Traced}
import org.ergoplatform.dex.domain.amm.CFMMPool
import org.ergoplatform.dex.domain.DexOperatorOutput
import org.ergoplatform.dex.domain.amm.CFMMOrder._
import org.ergoplatform.dex.domain.amm.CFMMOrderType.{DepositType, RedeemType, SwapType}
import org.ergoplatform.dex.domain.amm.{CFMMOrder, CFMMPool}
import org.ergoplatform.dex.executor.amm.interpreters.v1.InterpreterV1
import org.ergoplatform.dex.executor.amm.interpreters.v3.InterpreterV3
import org.ergoplatform.dex.protocol.amm.AMMType.{CFMMType, N2T_CFMM, T2T_CFMM}
import org.ergoplatform.dex.protocol.instances._
import org.ergoplatform.ergo.domain.Output
import org.ergoplatform.ergo.state.{Predicted, Traced}
import tofu.higherKind.{Mid, RepresentableK}
import tofu.logging.Logging
import tofu.logging.{Logging, Logs}
import tofu.syntax.logging._
import tofu.syntax.monadic._

/** Interprets CFMM operations to a transaction
*/
trait CFMMInterpreter[CT <: CFMMType, F[_]] {

def deposit(in: Deposit, pool: CFMMPool): F[(ErgoLikeTransaction, Traced[Predicted[CFMMPool]])]
def deposit(
in: CFMMOrder.AnyDeposit,
pool: CFMMPool
): F[(ErgoLikeTransaction, Traced[Predicted[CFMMPool]], Traced[Predicted[DexOperatorOutput]])]

def redeem(in: Redeem, pool: CFMMPool): F[(ErgoLikeTransaction, Traced[Predicted[CFMMPool]])]
def redeem(
in: CFMMOrder.AnyRedeem,
pool: CFMMPool
): F[(ErgoLikeTransaction, Traced[Predicted[CFMMPool]], Traced[Predicted[DexOperatorOutput]])]

def swap(in: SwapAny, pool: CFMMPool): F[(ErgoLikeTransaction, Traced[Predicted[CFMMPool]])]
def swap(
in: CFMMOrder.AnySwap,
pool: CFMMPool
): F[(ErgoLikeTransaction, Traced[Predicted[CFMMPool]], Traced[Predicted[DexOperatorOutput]])]
}

object CFMMInterpreter {
Expand All @@ -30,37 +43,89 @@ object CFMMInterpreter {
tofu.higherKind.derived.genRepresentableK[Rep]
}

def make[F[_]](implicit
n2t: CFMMInterpreter[N2T_CFMM, F],
t2t: CFMMInterpreter[T2T_CFMM, F]
): CFMMInterpreter[CFMMType, F] =
new Proxy[F]

final class Proxy[F[_]](implicit n2t: CFMMInterpreter[N2T_CFMM, F], t2t: CFMMInterpreter[T2T_CFMM, F])
extends CFMMInterpreter[CFMMType, F] {

def deposit(in: Deposit, pool: CFMMPool): F[(ErgoLikeTransaction, Traced[Predicted[CFMMPool]])] =
if (pool.isNative) n2t.deposit(in, pool)
else t2t.deposit(in, pool)
def make[I[_]: Functor, F[_]: Monad](implicit
n2t: InterpreterV1[N2T_CFMM, F],
t2t: InterpreterV1[T2T_CFMM, F],
n2tV3: InterpreterV3[N2T_CFMM, F],
t2tV3: InterpreterV3[T2T_CFMM, F],
logs: Logs[I, F]
): I[CFMMInterpreter[CFMMType, F]] =
logs.forService[CFMMInterpreter[CFMMType, F]].map { implicit __ =>
new Tracing[F] attach new Proxy[F]
}

final class Proxy[F[_]: Functor](implicit
n2tV1: InterpreterV1[N2T_CFMM, F],
t2tV1: InterpreterV1[T2T_CFMM, F],
n2tV3: InterpreterV3[N2T_CFMM, F],
t2tV3: InterpreterV3[T2T_CFMM, F]
) extends CFMMInterpreter[CFMMType, F] {

def deposit(
in: CFMMOrder[DepositType],
pool: CFMMPool
): F[(ErgoLikeTransaction, Traced[Predicted[CFMMPool]], Traced[Predicted[DexOperatorOutput]])] =
in match {
case d: DepositErgFee =>
if (pool.isNative) n2tV1.deposit(d, pool)
else t2tV1.deposit(d, pool)
case d: DepositTokenFee => if (pool.isNative) n2tV3.deposit(d, pool) else t2tV3.deposit(d, pool)
}

def redeem(
in: CFMMOrder[RedeemType],
pool: CFMMPool
): F[(ErgoLikeTransaction, Traced[Predicted[CFMMPool]], Traced[Predicted[DexOperatorOutput]])] =
in match {
case r: RedeemErgFee =>
if (pool.isNative) n2tV1.redeem(r, pool)
else t2tV1.redeem(r, pool)
case r: RedeemTokenFee => if (pool.isNative) n2tV3.redeem(r, pool) else t2tV3.redeem(r, pool)
}

def swap(
in: CFMMOrder[SwapType],
pool: CFMMPool
): F[(ErgoLikeTransaction, Traced[Predicted[CFMMPool]], Traced[Predicted[DexOperatorOutput]])] =
in match {
case s: SwapErg =>
if (pool.isNative) n2tV1.swap(s, pool)
else t2tV1.swap(s, pool)
case s: SwapTokenFee => if (pool.isNative) n2tV3.swap(s, pool) else t2tV3.swap(s, pool)
}

def redeem(in: Redeem, pool: CFMMPool): F[(ErgoLikeTransaction, Traced[Predicted[CFMMPool]])] =
if (pool.isNative) n2t.redeem(in, pool)
else t2t.redeem(in, pool)

def swap(in: SwapAny, pool: CFMMPool): F[(ErgoLikeTransaction, Traced[Predicted[CFMMPool]])] =
if (pool.isNative) n2t.swap(in, pool)
else t2t.swap(in, pool)
}

final class CFMMInterpreterTracing[CT <: CFMMType, F[_]: FlatMap: Logging] extends CFMMInterpreter[CT, Mid[F, *]] {

def deposit(in: Deposit, pool: CFMMPool): Mid[F, (ErgoLikeTransaction, Traced[Predicted[CFMMPool]])] =
_ >>= (r => trace"deposit(in=$in, pool=$pool) = (${r._1}, ${r._2})" as r)

def redeem(in: Redeem, pool: CFMMPool): Mid[F, (ErgoLikeTransaction, Traced[Predicted[CFMMPool]])] =
_ >>= (r => trace"redeem(in=$in, pool=$pool) = (${r._1}, ${r._2})" as r)

def swap(in: SwapAny, pool: CFMMPool): Mid[F, (ErgoLikeTransaction, Traced[Predicted[CFMMPool]])] =
_ >>= (r => trace"swap(in=$in, pool=$pool) = (${r._1}, ${r._2})" as r)
final private class Tracing[F[_]: Monad: Logging] extends CFMMInterpreter[CFMMType, Mid[F, *]] {

def deposit(
in: AnyDeposit,
pool: CFMMPool
): Mid[F, (ErgoLikeTransaction, Traced[Predicted[CFMMPool]], Traced[Predicted[DexOperatorOutput]])] =
for {
_ <- info"deposit(${in.box.boxId}, ${pool.box.boxId})"
r <- _
_ <- info"deposit(${in.box.boxId}, ${pool.box.boxId}) -> ${s"${r._1.id}"}"
} yield r

def redeem(
in: AnyRedeem,
pool: CFMMPool
): Mid[F, (ErgoLikeTransaction, Traced[Predicted[CFMMPool]], Traced[Predicted[DexOperatorOutput]])] =
for {
_ <- info"redeem(${in.box.boxId}, ${pool.box.boxId})"
r <- _
_ <- info"redeem(${in.box.boxId}, ${pool.box.boxId}) -> ${s"${r._1.id}"}"
} yield r

def swap(
in: AnySwap,
pool: CFMMPool
): Mid[F, (ErgoLikeTransaction, Traced[Predicted[CFMMPool]], Traced[Predicted[DexOperatorOutput]])] =
for {
_ <- info"swap(${in.box.boxId}, ${pool.box.boxId})"
r <- _
_ <- info"swap(${in.box.boxId}, ${pool.box.boxId}) -> ${s"${r._1.id}"}"
} yield r
}
}
Loading

0 comments on commit d4aa18f

Please sign in to comment.