From d43db09d66fe233cdf30c58404a4f00719a6eae8 Mon Sep 17 00:00:00 2001 From: Bill Date: Wed, 29 May 2024 10:27:52 -0700 Subject: [PATCH] Parse delegations for account staking balances (#395) Co-authored-by: mobile-build-bot-git --- build.gradle.kts | 2 +- .../exchange.dydx.abacus/output/Account.kt | 124 +++++++++++++++--- .../wallet/account/AccountProcessor.kt | 38 +++++- .../state/model/TradingStateMachine.kt | 1 + 4 files changed, 140 insertions(+), 25 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 00084403b..2ce50ac20 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -51,7 +51,7 @@ allprojects { } group = "exchange.dydx.abacus" -version = "1.7.34" +version = "1.7.35" repositories { google() diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/output/Account.kt b/src/commonMain/kotlin/exchange.dydx.abacus/output/Account.kt index f010bb6de..15015187f 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/output/Account.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/output/Account.kt @@ -1567,6 +1567,38 @@ data class AccountBalance( } } +@JsExport +@Serializable +data class StakingDelegation( + var validator: String, + var amount: String, +) { + companion object { + internal fun create( + existing: StakingDelegation?, + parser: ParserProtocol, + data: Map, + decimals: Int, + ): StakingDelegation? { + Logger.d { "creating Staking Delegation\n" } + + val validator = parser.asString(data["validator"]) + val amount = parser.asDecimal(data["amount"]) + if (validator != null && amount != null) { + val decimalAmount = amount * Numeric.decimal.TEN.pow(-1 * decimals) + val decimalAmountString = parser.asString(decimalAmount)!! + return if (existing?.validator != validator || existing.amount != decimalAmountString) { + StakingDelegation(validator, decimalAmountString) + } else { + existing + } + } + Logger.d { "Staking Delegation not valid" } + return null + } + } +} + @JsExport @Serializable data class HistoricalTradingReward( @@ -2020,6 +2052,7 @@ data class TradingRewards( data class Account( var balances: IMap?, var stakingBalances: IMap?, + var stakingDelegations: IList?, var subaccounts: IMap?, var groupedSubaccounts: IMap?, var tradingRewards: TradingRewards?, @@ -2057,26 +2090,20 @@ data class Account( } val stakingBalances: IMutableMap = - iMutableMapOf() - val stakingBalancesData = parser.asMap(data["stakingBalances"]) - if (stakingBalancesData != null) { - for ((key, value) in stakingBalancesData) { - // key is the denom - // It should be chain token denom here - val tokenInfo = findTokenInfo(tokensInfo, key) - if (tokenInfo != null) { - val balanceData = parser.asMap(value) ?: iMapOf() - AccountBalance.create( - existing?.stakingBalances?.get(key), - parser, - balanceData, - tokenInfo.decimals, - )?.let { balance -> - stakingBalances[key] = balance - } - } - } - } + processStakingBalance( + existing, + parser, + data, + tokensInfo, + ) + + val stakingDelegations: IMutableList = + processStakingDelegations( + existing, + parser, + data, + tokensInfo, + ) val tradingRewardsData = parser.asMap(data["tradingRewards"]) val tradingRewards = if (tradingRewardsData != null) { @@ -2132,6 +2159,7 @@ data class Account( return Account( balances, stakingBalances, + stakingDelegations, subaccounts, groupedSubaccounts, tradingRewards, @@ -2142,5 +2170,61 @@ data class Account( private fun findTokenInfo(tokensInfo: Map, denom: String): TokenInfo? { return tokensInfo.firstNotNullOfOrNull { if (it.value.denom == denom) it.value else null } } + + private fun processStakingDelegations( + existing: Account?, + parser: ParserProtocol, + data: Map, + tokensInfo: Map, + ): IMutableList { + val stakingDelegations: IMutableList = + iMutableListOf() + val stakingDelegationsData = parser.asList(data["stakingDelegations"]) + stakingDelegationsData?.forEachIndexed { index, value -> + val stakingDelegationData = parser.asMap(value) ?: iMapOf() + val tokenInfo = findTokenInfo(tokensInfo, stakingDelegationData["denom"] as String) + if (tokenInfo != null) { + StakingDelegation.create( + existing?.stakingDelegations?.getOrNull(index), + parser, + stakingDelegationData, + tokenInfo.decimals, + )?.let { stakingDelegation -> + stakingDelegations.add(stakingDelegation) + } + } + } + return stakingDelegations + } + + private fun processStakingBalance( + existing: Account?, + parser: ParserProtocol, + data: Map, + tokensInfo: Map, + ): IMutableMap { + val stakingBalances: IMutableMap = + iMutableMapOf() + val stakingBalancesData = parser.asMap(data["stakingBalances"]) + if (stakingBalancesData != null) { + for ((key, value) in stakingBalancesData) { + // key is the denom + // It should be chain token denom here + val tokenInfo = findTokenInfo(tokensInfo, key) + if (tokenInfo != null) { + val balanceData = parser.asMap(value) ?: iMapOf() + AccountBalance.create( + existing?.stakingBalances?.get(key), + parser, + balanceData, + tokenInfo.decimals, + )?.let { balance -> + stakingBalances[key] = balance + } + } + } + } + return stakingBalances + } } } diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/processor/wallet/account/AccountProcessor.kt b/src/commonMain/kotlin/exchange.dydx.abacus/processor/wallet/account/AccountProcessor.kt index 8bd017a8e..81c7af5c6 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/processor/wallet/account/AccountProcessor.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/processor/wallet/account/AccountProcessor.kt @@ -809,8 +809,9 @@ private class V4AccountDelegationsProcessor(parser: ParserProtocol) : BaseProces return if (payload != null) { val modified = mutableMapOf() for (itemPayload in payload) { - val delegation = parser.asNativeMap(itemPayload) - val balance = parser.asNativeMap(delegation?.get("balance")) + val item = parser.asNativeMap(itemPayload) + val balance = parser.asNativeMap(item?.get("balance")) + if (balance != null) { val denom = parser.asString(balance["denom"]) if (denom != null) { @@ -842,6 +843,33 @@ private class V4AccountDelegationsProcessor(parser: ParserProtocol) : BaseProces null } } + + fun receivedDelegations( + existing: Map?, + payload: List?, + ): List? { + return if (payload != null) { + val modified = mutableListOf() + for (itemPayload in payload) { + val item = parser.asNativeMap(itemPayload) + val validator = parser.asString(parser.value(item, "delegation.validatorAddress")) + val amount = parser.asDecimal(parser.value(item, "balance.amount")) + val denom = parser.asString(parser.value(item, "balance.denom")) + if (validator != null && amount != null) { + modified.add( + mapOf( + "validator" to validator, + "amount" to amount, + "denom" to denom, + ), + ) + } + } + return modified + } else { + null + } + } } private class V4AccountTradingRewardsProcessor(parser: ParserProtocol) : BaseProcessor(parser) { @@ -914,8 +942,10 @@ internal class V4AccountProcessor(parser: ParserProtocol) : BaseProcessor(parser ): Map? { val modified = existing?.mutable() ?: mutableMapOf() val delegations = parser.asNativeMap(parser.value(existing, "stakingBalances")) - val modifiedDelegations = delegationsProcessor.received(delegations, payload) - modified.safeSet("stakingBalances", modifiedDelegations) + val modifiedStakingBalance = delegationsProcessor.received(delegations, payload) + modified.safeSet("stakingBalances", modifiedStakingBalance) + val modifiedDelegations = delegationsProcessor.receivedDelegations(delegations, payload) + modified.safeSet("stakingDelegations", modifiedDelegations) return modified } diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine.kt b/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine.kt index e0cd84253..c97b0b406 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine.kt @@ -1175,6 +1175,7 @@ open class TradingStateMachine( Account( account.balances, account.stakingBalances, + account.stakingDelegations, subaccounts, groupedSubaccounts, account.tradingRewards,