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

chore(metadata-service): Fetch Asset information from MetadataService #710

Merged
merged 2 commits into from
Oct 15, 2024
Merged
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 build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ allprojects {
}

group = "exchange.dydx.abacus"
version = "1.12.25"
version = "1.12.26"

repositories {
google()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import exchange.dydx.abacus.protocols.LocalizerProtocol
import exchange.dydx.abacus.protocols.ParserProtocol
import exchange.dydx.abacus.utils.mutable
import exchange.dydx.abacus.utils.safeSet
import indexer.models.configs.ConfigsAssetMetadata
import indexer.models.configs.ConfigsMarketAsset

internal interface AssetProcessorProtocol {
Expand Down Expand Up @@ -97,3 +98,89 @@ internal class AssetProcessor(
return received
}
}
internal interface AssetMetadataProcessorProtocol {
fun process(
assetId: String,
payload: ConfigsAssetMetadata,
): Asset
}

internal class AssetMetadataProcessor(
parser: ParserProtocol,
private val localizer: LocalizerProtocol?
) : BaseProcessor(parser), AssetMetadataProcessorProtocol {
private val assetConfigurationsResourcesKeyMap = mapOf(
"string" to mapOf(
"website" to "websiteLink",
"technical_doc" to "whitepaperLink",
"cmc" to "coinMarketCapsLink",
),
)

private val assetConfigurationsKeyMap = mapOf(
"string" to mapOf(
"name" to "name",
),
"strings" to mapOf(
"sector_tags" to "tags",
),
)

override fun process(
assetId: String,
payload: ConfigsAssetMetadata,
): Asset {
val imageUrl = "https://mainnet-metadata-service-logos.s3.ap-northeast-1.amazonaws.com/$assetId.png"
val primaryDescriptionKey = "__ASSETS.$assetId.PRIMARY"
val secondaryDescriptionKey = "__ASSETS.$assetId.SECONDARY"
val primaryDescription = localizer?.localize(primaryDescriptionKey)
val secondaryDescription = localizer?.localize(secondaryDescriptionKey)

return Asset(
id = assetId,
name = payload.name,
tags = payload.sector_tags,
resources = AssetResources(
websiteLink = payload.urls["website"],
whitepaperLink = payload.urls["technical_doc"],
coinMarketCapsLink = payload.urls["cmc"],
imageUrl = imageUrl,
primaryDescriptionKey = primaryDescriptionKey,
secondaryDescriptionKey = secondaryDescriptionKey,
primaryDescription = primaryDescription,
secondaryDescription = secondaryDescription,
),
)
}

override fun received(
existing: Map<String, Any>?,
payload: Map<String, Any>
): Map<String, Any>? {
return existing
}

internal fun receivedConfigurations(
assetId: String,
asset: Map<String, Any>?,
payload: Map<String, Any>,
): Map<String, Any> {
val received = transform(asset, payload, assetConfigurationsKeyMap)
val urls = payload["urls"] as Map<String, String?>
val resources = transform(
parser.asNativeMap(asset?.get("resources")),
urls,
assetConfigurationsResourcesKeyMap,
).mutable()
val imageUrl = "https://mainnet-metadata-service-logos.s3.ap-northeast-1.amazonaws.com/$assetId.png"
val primaryDescriptionKey = "__ASSETS.$assetId.PRIMARY"
val secondaryDescriptionKey = "__ASSETS.$assetId.SECONDARY"
resources.safeSet("imageUrl", imageUrl)
resources.safeSet("primaryDescriptionKey", primaryDescriptionKey)
resources.safeSet("secondaryDescriptionKey", secondaryDescriptionKey)
received["id"] = assetId
received["resources"] = resources

return received
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,36 @@ import exchange.dydx.abacus.processor.utils.MarketId
import exchange.dydx.abacus.protocols.LocalizerProtocol
import exchange.dydx.abacus.protocols.ParserProtocol
import exchange.dydx.abacus.utils.mutable
import indexer.models.configs.ConfigsAssetMetadata
import indexer.models.configs.ConfigsMarketAsset

internal class AssetsProcessor(
parser: ParserProtocol,
localizer: LocalizerProtocol?
localizer: LocalizerProtocol?,
val metadataService: Boolean = false,
) : BaseProcessor(parser) {
private val assetProcessor = AssetProcessor(parser = parser, localizer = localizer)
private val assetMetadataProcessor = AssetMetadataProcessor(parser = parser, localizer = localizer)

override fun environmentChanged() {
assetProcessor.environment = environment
assetMetadataProcessor.environment = environment
}

internal fun processMetadataConfigurations(
existing: MutableMap<String, Asset>,
payload: Map<String, ConfigsAssetMetadata>,
): MutableMap<String, Asset> {
for ((assetId, data) in payload) {
val asset = assetMetadataProcessor.process(
assetId = assetId,
payload = data,
)

existing[assetId] = asset
}

return existing
}

internal fun processConfigurations(
Expand Down Expand Up @@ -50,12 +70,20 @@ internal class AssetsProcessor(
if (assetId != null) {
val marketPayload = parser.asNativeMap(data)
if (marketPayload != null) {
val receivedAsset = assetProcessor.receivedConfigurations(
assetId,
parser.asNativeMap(existing?.get(assetId)),
marketPayload,
deploymentUri,
)
val receivedAsset = if (metadataService) {
assetMetadataProcessor.receivedConfigurations(
assetId,
parser.asNativeMap(existing?.get(assetId)),
marketPayload,
)
} else {
assetProcessor.receivedConfigurations(
assetId,
parser.asNativeMap(existing?.get(assetId)),
marketPayload,
deploymentUri,
)
}
assets[assetId] = receivedAsset
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ class V4StateManagerConfigs(
"status":"/v1/status"
},
"configs":{
"markets":"/configs/markets.json"
"markets":"/configs/markets.json",
"assets": "https://66iv2m87ol.execute-api.ap-northeast-1.amazonaws.com/mainnet/metadata-service/v1/info"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it's better to put this into env.json? In case the host ever changes, we can just update the config json. Assets are essential to the app.

},
"launchIncentive":{
"graphql":"/query/ccar-perpetuals",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ class PerpTradingStateMachine(
useParentSubaccount: Boolean,
staticTyping: Boolean = false,
trackingProtocol: TrackingProtocol?,
metadataService: Boolean = false,
) :
TradingStateMachine(environment, localizer, formatter, maxSubaccountNumber, useParentSubaccount, staticTyping, trackingProtocol) {
TradingStateMachine(environment, localizer, formatter, maxSubaccountNumber, useParentSubaccount, staticTyping, trackingProtocol, metadataService) {
/*
Placeholder for now. Eventually, the code specifically for Perpetual will be in this class
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import exchange.dydx.abacus.state.changes.Changes
import exchange.dydx.abacus.state.changes.StateChanges
import indexer.models.IndexerCompositeMarketObject
import indexer.models.IndexerWsMarketUpdateResponse
import indexer.models.configs.ConfigsAssetMetadata
import indexer.models.configs.ConfigsMarketAsset
import kollections.iListOf
import kollections.toIList
Expand Down Expand Up @@ -211,6 +212,38 @@ internal fun TradingStateMachine.receivedBatchedMarketsChanges(
}
}

internal fun TradingStateMachine.processMarketsConfigurationsWithMetadataService(
payload: Map<String, ConfigsAssetMetadata>,
subaccountNumber: Int?,
deploymentUri: String,
): StateChanges {
internalState.assets = assetsProcessor.processMetadataConfigurations(
existing = internalState.assets,
payload = payload,
)

marketsCalculator.calculate(internalState.marketsSummary)
val subaccountNumbers = MarginCalculator.getChangedSubaccountNumbers(
parser = parser,
subaccounts = internalState.wallet.account.subaccounts,
subaccountNumber = subaccountNumber ?: 0,
tradeInput = internalState.input.trade,
)
return if (subaccountNumber != null) {
StateChanges(
changes = iListOf(Changes.markets, Changes.assets, Changes.subaccount, Changes.input),
markets = null,
subaccountNumbers = subaccountNumbers,
)
} else {
StateChanges(
changes = iListOf(Changes.markets, Changes.assets),
markets = null,
subaccountNumbers = null,
)
}
}

internal fun TradingStateMachine.processMarketsConfigurations(
payload: Map<String, ConfigsMarketAsset>,
subaccountNumber: Int?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import exchange.dydx.abacus.utils.mutableMapOf
import exchange.dydx.abacus.utils.safeSet
import exchange.dydx.abacus.utils.typedSafeSet
import exchange.dydx.abacus.validator.InputValidator
import indexer.models.configs.ConfigsAssetMetadata
import indexer.models.configs.ConfigsMarketAsset
import kollections.JsExport
import kollections.iListOf
Expand All @@ -100,6 +101,7 @@ open class TradingStateMachine(
private val useParentSubaccount: Boolean,
val staticTyping: Boolean = false,
private val trackingProtocol: TrackingProtocol?,
val metadataService: Boolean = false,
) {
internal var internalState: InternalState = InternalState()

Expand All @@ -113,6 +115,7 @@ open class TradingStateMachine(
val processor = AssetsProcessor(
parser = parser,
localizer = localizer,
metadataService = metadataService,
)
processor.environment = environment
processor
Expand Down Expand Up @@ -314,17 +317,30 @@ open class TradingStateMachine(
): StateChanges {
val json = parser.decodeJsonObject(payload)
if (staticTyping) {
val parsedAssetPayload = parser.asTypedStringMap<ConfigsMarketAsset>(json)
if (parsedAssetPayload == null) {
Logger.e { "Error parsing asset payload" }
return StateChanges.noChange
}
if (metadataService) {
val parsedAssetPayload = parser.asTypedStringMap<ConfigsAssetMetadata>(json)
if (parsedAssetPayload == null) {
Logger.e { "Error parsing asset payload" }
return StateChanges.noChange
}
return processMarketsConfigurationsWithMetadataService(
payload = parsedAssetPayload,
subaccountNumber = subaccountNumber,
deploymentUri = deploymentUri,
)
} else {
val parsedAssetPayload = parser.asTypedStringMap<ConfigsMarketAsset>(json)
if (parsedAssetPayload == null) {
Logger.e { "Error parsing asset payload" }
return StateChanges.noChange
}

return processMarketsConfigurations(
payload = parsedAssetPayload,
subaccountNumber = subaccountNumber,
deploymentUri = deploymentUri,
)
return processMarketsConfigurations(
payload = parsedAssetPayload,
subaccountNumber = subaccountNumber,
deploymentUri = deploymentUri,
)
}
} else {
return if (json != null) {
receivedMarketsConfigurationsDeprecated(json, subaccountNumber, deploymentUri)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ internal class StateManagerAdaptorV2(
useParentSubaccount = appConfigs.accountConfigs.subaccountConfigs.useParentSubaccount,
staticTyping = appConfigs.staticTyping,
trackingProtocol = ioImplementations.tracking,
metadataService = appConfigs.metadataService,
)

internal val jsonEncoder = JsonEncoder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ data class AppConfigsV2(
var enableLogger: Boolean = false,
var triggerOrderToast: Boolean = false,
var staticTyping: Boolean = false,
var metadataService: Boolean = false,
) {
companion object {
val forApp = AppConfigsV2(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,31 @@ internal class SystemSupervisor(
}

private fun retrieveMarketConfigs() {
val oldState = stateMachine.state
val url = helper.configs.configsUrl("markets")
if (url != null) {
helper.get(url, null, null) { _, response, httpCode, _ ->
if (helper.success(httpCode) && response != null) {
update(
// TODO, subaccountNumber required to refresh
stateMachine.configurations(response, null, helper.deploymentUri),
oldState,
)
if (stateMachine.metadataService) {
val oldState = stateMachine.state
val url = helper.configs.configsUrl("assets")
if (url != null) {
helper.post(url, null, null) { _, response, httpCode, _ ->
if (helper.success(httpCode) && response != null) {
update(
stateMachine.configurations(response, null, helper.deploymentUri),
oldState,
)
}
}
}
} else {
val oldState = stateMachine.state
val url = helper.configs.configsUrl("markets")
if (url != null) {
helper.get(url, null, null) { _, response, httpCode, _ ->
if (helper.success(httpCode) && response != null) {
update(
// TODO, subaccountNumber required to refresh
stateMachine.configurations(response, null, helper.deploymentUri),
oldState,
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package indexer.models.configs

import exchange.dydx.abacus.utils.IList
import exchange.dydx.abacus.utils.IMap
import kotlinx.serialization.Serializable

/**
Expand All @@ -14,3 +15,16 @@ data class ConfigsMarketAsset(
val coinMarketCapsLink: String? = null,
val tags: IList<String>? = null,
)

/**
* @description Asset from MetadataService Info response
*/
@Suppress("ConstructorParameterNaming")
@Serializable
data class ConfigsAssetMetadata(
val name: String,
val logo: String,
val urls: IMap<String, String?>,
val sector_tags: IList<String>? = null,
// val exchanges: IList<Any>
)