Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CMC YF API Rebase #74

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/api/src/main/scala/fi/spectrum/api/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ object Main extends EnvApp[AppContext] {
implicit0(httpCache: CachingMiddleware[F]) = CacheMiddleware.make[F]
implicit0(metricsMiddleware: MetricsMiddleware[F]) = MetricsMiddleware.make[F]
implicit0(ammStats: AmmStats[F]) <- AmmStats.make[I, F, xa.DB].toResource
implicit0(priceTracking: PriceTracking[F]) <- PriceTracking.make[I, F, xa.DB].toResource
implicit0(lmStats: LmStatsApi[F]) <- LmStatsApi.make[I, F, xa.DB].toResource
implicit0(priceTracking: PriceTracking[F]) <- PriceTracking.make[I, F, xa.DB].toResource
implicit0(mempool: MempoolApi[F]) <- MempoolApi.make[I, F, xa.DB].toResource
implicit0(historyApi: HistoryApi[F]) <- HistoryApi.make[I, F, xa.DB].toResource
serverProc = HttpServer.make[I, F](config.http)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import fi.spectrum.api.v1.endpoints.models.{CMCMarket, CoinGeckoPairs, CoinGecko
import fi.spectrum.api.v1.models.amm._
import fi.spectrum.common.http.{HttpError, baseEndpoint}
import sttp.tapir._
import io.circe.generic.auto._
import sttp.tapir.generic.auto._
import sttp.tapir.json.circe.jsonBody

final class PriceTrackingEndpoints {
Expand All @@ -17,7 +19,8 @@ final class PriceTrackingEndpoints {
getVerifiedMarketsE,
getMarketsE,
getPairsCoinGeckoE,
getTickersCoinGeckoE
getTickersCoinGeckoE,
getCmcYFInfoE
)

def getVerifiedMarketsE: Endpoint[Unit, Unit, HttpError, List[CMCMarket], Any] =
Expand Down Expand Up @@ -50,4 +53,11 @@ final class PriceTrackingEndpoints {
.out(jsonBody[List[CoinGeckoTicker]])
.tag(Group)
.name("Coin Gecko tickers API")

def getCmcYFInfoE: Endpoint[Unit, Unit, HttpError, CMCYFInfo, Any] =
baseEndpoint.get
.in(PathPrefixPriceTracking / "cmc" / "yf")
.out(jsonBody[CMCYFInfo])
.tag(Group)
.name("Coin market cap yield farming API")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package fi.spectrum.api.v1.models.amm

import derevo.derive
import tofu.logging.derivation.loggable

@derive(loggable)
case class CMCYFInfo(
provider: String,
providerLogo: String,
providerUrl: String,
links: List[MediaLink],
pools: List[PoolCMCInfo]
)

@derive(loggable)
case class MediaLink(title: String, link: String)

@derive(loggable)
case class PoolCMCInfo(
name: String,
pair: String,
pairLink: String,
logo: String,
poolRewards: List[String],
apr: BigDecimal,
totalStaked: Long
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ final class PriceTrackingRoutes[

private val interpreter = Http4sServerInterpreter(opts)

def routes: HttpRoutes[F] = getVerifiedMarketsR <+> getMarketsR <+> getPairsCoinGeckoR <+> getTickersCoinGeckoR
def routes: HttpRoutes[F] =
getCmcYFInfoR <+> getVerifiedMarketsR <+> getMarketsR <+> getPairsCoinGeckoR <+> getTickersCoinGeckoR

def getCmcYFInfoR: HttpRoutes[F] = interpreter.toRoutes(getCmcYFInfoE.serverLogic { _ =>
pt.getCmcYFInfo.adaptThrowable.value
})

def getVerifiedMarketsR: HttpRoutes[F] = interpreter.toRoutes(getVerifiedMarketsE.serverLogic { _ =>
pt.getVerifiedMarkets.adaptThrowable.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ import tofu.syntax.time.now.millis
import tofu.time.Clock
import fi.spectrum.api.v1.models.amm.types.MarketId
import tofu.syntax.foption._

import fi.spectrum.api.db.models.lm.LmPoolSnapshot
import fi.spectrum.api.v1.models.lm.LMPoolStat
import scala.math.BigDecimal.RoundingMode
import fi.spectrum.api.db.models.amm.AssetInfo
import cats.data.OptionT

@derive(representableK)
trait PriceTracking[F[_]] {
Expand All @@ -37,6 +40,7 @@ trait PriceTracking[F[_]] {

def getTickersCoinGecko: F[List[CoinGeckoTicker]]

def getCmcYFInfo: F[CMCYFInfo]
}

object PriceTracking {
Expand All @@ -49,6 +53,9 @@ object PriceTracking {
volumes24H: Volumes24H[F],
solver: FiatPriceSolver[F],
metrics: Metrics[F],
lmStats: LmStatsApi[F],
lmSnapshots: LMSnapshots[F],
assets: Assets[F],
logs: Logs[I, F]
): I[PriceTracking[F]] =
logs
Expand All @@ -61,9 +68,58 @@ object PriceTracking {
tokens: VerifiedTokens[F],
solver: FiatPriceSolver[F],
snapshots: Snapshots[F],
volumes24H: Volumes24H[F]
volumes24H: Volumes24H[F],
lmStats: LmStatsApi[F],
lmSnapshots: LMSnapshots[F],
assets: Assets[F]
) extends PriceTracking[F] {

def getCmcYFInfo: F[CMCYFInfo] =
for {
ammPools <- snapshots.get
lmPools <- lmSnapshots.get
assets <- assets.get
lmPoolStats <- lmStats.lmStatsApi
result <- lmPools.flatTraverse(snap => processLmPoolInfo(snap, assets, lmPoolStats, ammPools).map(_.toList))
} yield CMCYFInfo(
"",
"",
"",
List.empty,
result
)

private def processLmPoolInfo(
pool: LmPoolSnapshot,
assets: List[AssetInfo],
poolStats: List[LMPoolStat],
ammPools: List[PoolSnapshot]
) =
(for {
ammPool <- OptionT.fromOption[F](ammPools.find(_.lp.tokenId == pool.lq.tokenId))
apy <- OptionT.fromOption[F](poolStats.find(_.poolId == pool.poolId).flatMap(_.yearProfit))
rewardTicker <- OptionT.fromOption[F](assets.find(_.id == pool.reward.tokenId).flatMap(_.ticker))
lockedX <- OptionT(solver.convert(ammPool.lockedX, UsdUnits, ammPools))
lockedY <- OptionT(solver.convert(ammPool.lockedY, UsdUnits, ammPools))
pairTicker <- OptionT.fromOption[F](evalPairTicker(ammPool))
tvl = lockedX.value + lockedY.value
staked = tvl.toLong * pool.lq.amount / (Long.MaxValue - ammPool.lp.amount)
} yield PoolCMCInfo(
"",
pairTicker,
"",
"",
List(rewardTicker.value),
apy.setScale(1),
staked
)).value

private def evalPairTicker(pool: PoolSnapshot) =
for {
xTicker <- pool.lockedX.ticker
yTicker <- pool.lockedY.ticker
} yield s"${xTicker.value}-${yTicker.value}"

def getPairsCoinGecko: F[List[CoinGeckoPairs]] =
for {
tw <- resolveTimeWindow(TimeWindow.empty)
Expand Down Expand Up @@ -247,6 +303,14 @@ object PriceTracking {
_ <- info"getPairsCoinGecko() finished"
_ <- trace"getPairsCoinGecko() - $r"
} yield r

def getCmcYFInfo: Mid[F, CMCYFInfo] =
for {
_ <- info"getCmcYFInfo()"
r <- _
_ <- info"getCmcYFInfo() finished"
_ <- trace"getCmcYFInfo() - $r"
} yield r
}

final private class PriceTrackingMetrics[F[_]: Monad: Clock](implicit metrics: Metrics[F])
Expand All @@ -269,5 +333,7 @@ object PriceTracking {
def getPairsCoinGecko: Mid[F, List[CoinGeckoPairs]] = unit >> _

def getTickersCoinGecko: Mid[F, List[CoinGeckoTicker]] = unit >> _

def getCmcYFInfo: Mid[F, CMCYFInfo] = unit >> _
}
}