Skip to content

Commit

Permalink
Merge pull request #777 from radixdlt/fix/ABW-2889-unstake-values-fix
Browse files Browse the repository at this point in the history
[ABW-2889] properly compute unstake values.
  • Loading branch information
jakub-rdx authored Feb 1, 2024
2 parents 5121f27 + e904e21 commit 99a40cd
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ import com.babylon.wallet.android.domain.model.Transferable
import com.babylon.wallet.android.domain.model.TransferableAsset
import com.babylon.wallet.android.domain.model.assets.Asset
import com.babylon.wallet.android.domain.model.assets.PoolUnit
import com.babylon.wallet.android.domain.model.resources.Resource
import com.babylon.wallet.android.domain.model.resources.metadata.poolUnit
import com.babylon.wallet.android.domain.usecases.assets.GetPoolDetailsUseCase
import com.babylon.wallet.android.domain.usecases.assets.ResolveAssetsFromAddressUseCase
import com.babylon.wallet.android.presentation.transaction.AccountWithTransferableResources
import com.babylon.wallet.android.presentation.transaction.PreviewType
Expand All @@ -22,50 +19,54 @@ import javax.inject.Inject

class PoolContributionProcessor @Inject constructor(
private val resolveAssetsFromAddressUseCase: ResolveAssetsFromAddressUseCase,
private val getPoolDetailsUseCase: GetPoolDetailsUseCase,
private val getProfileUseCase: GetProfileUseCase
) : PreviewTypeProcessor<DetailedManifestClass.PoolContribution> {
@Suppress("LongMethod")

override suspend fun process(summary: ExecutionSummary, classification: DetailedManifestClass.PoolContribution): PreviewType {
val assets = resolveAssetsFromAddressUseCase(
fungibleAddresses = summary.involvedFungibleAddresses(),
nonFungibleIds = summary.involvedNonFungibleIds()
).getOrThrow()
val involvedPools = getPoolDetailsUseCase(classification.poolAddresses.map { it.addressString() }.toSet()).getOrThrow()
val defaultDepositGuarantee = getProfileUseCase.invoke().first().appPreferences.transaction.defaultDepositGuarantee
val accountsWithdrawnFrom = summary.accountWithdraws.keys
val ownedAccountsWithdrawnFrom = getProfileUseCase.accountsOnCurrentNetwork().filter {
accountsWithdrawnFrom.contains(it.address)
}
val from = summary.extractWithdraws(ownedAccountsWithdrawnFrom, assets)
val to = summary.accountDeposits.map { depositsPerAddress ->
val to = summary.extractDeposits(classification, assets, defaultDepositGuarantee)
return PreviewType.Transfer.Pool(
from = from,
to = to,
actionType = PreviewType.Transfer.Pool.ActionType.Contribution
)
}

private suspend fun ExecutionSummary.extractDeposits(
classification: DetailedManifestClass.PoolContribution,
assets: List<Asset>,
defaultDepositGuarantee: Double
): List<AccountWithTransferableResources.Owned> {
val to = accountDeposits.map { depositsPerAddress ->
val ownedAccount = getProfileUseCase.accountOnCurrentNetwork(depositsPerAddress.key) ?: error("No account found")
val deposits = depositsPerAddress.value.map { deposit ->
val resourceAddress = deposit.resourceAddress
val contributions = classification.poolContributions.filter {
it.poolUnitsResourceAddress.addressString() == resourceAddress
}
if (contributions.isEmpty()) {
resolveGeneralAsset(deposit, summary, assets, defaultDepositGuarantee)
resolveGeneralAsset(deposit, this, assets, defaultDepositGuarantee)
} else {
val pool = involvedPools.find { it.address == contributions.first().poolAddress.addressString() }
?: error("No pool found")
val poolResource = assets.find {
it.resource.resourceAddress == pool.metadata.poolUnit()
}?.resource as? Resource.FungibleResource
?: error("No pool resource found")
val poolUnit = assets.find { it.resource.resourceAddress == resourceAddress } as? PoolUnit
?: error("No pool unit found")
val contributedResourceAddresses = contributions.first().contributedResources.keys
val guaranteeType = deposit.guaranteeType(defaultDepositGuarantee)
val poolUnitAmount = contributions.find {
it.poolUnitsResourceAddress.addressString() == poolResource.resourceAddress
it.poolUnitsResourceAddress.addressString() == poolUnit.resourceAddress
}?.poolUnitsAmount?.asStr()?.toBigDecimalOrNull()
Transferable.Depositing(
transferable = TransferableAsset.Fungible.PoolUnitAsset(
amount = contributions.map { it.poolUnitsAmount.asStr().toBigDecimal() }.sumOf { it },
unit = PoolUnit(
stake = poolResource.copy(ownedAmount = poolUnitAmount),
pool = pool
),
unit = poolUnit.copy(stake = poolUnit.stake.copy(ownedAmount = poolUnitAmount)),
contributionPerResource = contributedResourceAddresses.associateWith { contributedResourceAddress ->
contributions.mapNotNull { it.contributedResources[contributedResourceAddress]?.asStr()?.toBigDecimal() }
.sumOf { it }
Expand All @@ -80,11 +81,7 @@ class PoolContributionProcessor @Inject constructor(
resources = deposits
)
}
return PreviewType.Transfer.Pool(
from = from,
to = to,
actionType = PreviewType.Transfer.Pool.ActionType.Contribution
)
return to
}

private fun resolveGeneralAsset(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ val NonFungibleResourceIndicator.nonFungibleLocalIds: List<NonFungibleLocalId>
is NonFungibleResourceIndicator.ByIds -> ids
}

private fun Map<String, MetadataValue?>.toMetadata(): List<Metadata> = mapNotNull { it.toMetadata() }
fun Map<String, MetadataValue?>.toMetadata(): List<Metadata> = mapNotNull { it.toMetadata() }

@Suppress("CyclomaticComplexMethod", "LongMethod")
private fun Map.Entry<String, MetadataValue?>.toMetadata(): Metadata? = when (val typed = value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class ValidatorStakeProcessor @Inject constructor(
val involvedValidators = getValidatorsUseCase(classification.involvedValidatorAddresses).getOrThrow()

val fromAccounts = extractWithdrawals(summary, getProfileUseCase, resources)
val toAccounts = classification.extractDeposits(
val toAccounts = extractDeposits(
executionSummary = summary,
getProfileUseCase = getProfileUseCase,
resources = resources,
Expand All @@ -45,29 +45,30 @@ class ValidatorStakeProcessor @Inject constructor(
)
}

private suspend fun DetailedManifestClass.ValidatorStake.extractDeposits(
private suspend fun extractDeposits(
executionSummary: ExecutionSummary,
getProfileUseCase: GetProfileUseCase,
resources: List<Resource>,
involvedValidators: List<ValidatorDetail>
) = executionSummary.accountDeposits.map { depositsPerAccount ->
val ownedAccount = getProfileUseCase.accountOnCurrentNetwork(depositsPerAccount.key) ?: error("No account found")
val defaultDepositGuarantees = getProfileUseCase.invoke().first().appPreferences.transaction.defaultDepositGuarantee
val depositingLsu = depositsPerAccount.value.map { depositedResource ->
val resourceAddress = depositedResource.resourceAddress
val depositingLsu = depositsPerAccount.value.groupBy { it.resourceAddress }.map { depositedResources ->
val lsuResource = resources.find {
it.resourceAddress == resourceAddress
it.resourceAddress == depositedResources.key
} as? Resource.FungibleResource ?: error("No resource found")
val stakes = validatorStakes.filter { it.liquidStakeUnitAddress.asStr() == resourceAddress }
val validatorAddress = lsuResource.validatorAddress ?: error("No validator address found")
val validator =
involvedValidators.find { it.address == stakes.first().validatorAddress.addressString() } ?: error("No validator found")
val amount = stakes.sumOf { it.liquidStakeUnitAmount.asStr().toBigDecimal() }
val guaranteeType = depositedResource.guaranteeType(defaultDepositGuarantees)
involvedValidators.find { it.address == validatorAddress } ?: error("No validator found")
val lsuAmount = depositedResources.value.sumOf { it.amount }
val xrdWorth = lsuAmount.divide(lsuResource.currentSupply, lsuResource.mathContext)
.multiply(validator.totalXrdStake, lsuResource.mathContext)
val guaranteeType = depositedResources.value.first().guaranteeType(defaultDepositGuarantees)
Transferable.Depositing(
transferable = TransferableAsset.Fungible.LSUAsset(
amount = amount,
lsu = LiquidStakeUnit(lsuResource.copy(ownedAmount = amount), validator),
xrdWorth = stakes.sumOf { it.xrdAmount.asStr().toBigDecimal() },
amount = lsuAmount,
lsu = LiquidStakeUnit(lsuResource.copy(ownedAmount = lsuAmount), validator),
xrdWorth = xrdWorth,
),
guaranteeType = guaranteeType
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import com.babylon.wallet.android.presentation.transaction.AccountWithTransferab
import com.babylon.wallet.android.presentation.transaction.PreviewType
import com.radixdlt.ret.DetailedManifestClass
import com.radixdlt.ret.ExecutionSummary
import com.radixdlt.ret.NonFungibleGlobalId
import com.radixdlt.ret.ResourceIndicator
import kotlinx.coroutines.flow.first
import rdx.works.profile.domain.GetProfileUseCase
import rdx.works.profile.domain.accountOnCurrentNetwork
Expand All @@ -30,7 +32,7 @@ class ValidatorUnstakeProcessor @Inject constructor(
val resources = getResourcesUseCase(addresses = summary.involvedResourceAddresses + xrdAddress, withDetails = true).getOrThrow()
val involvedValidators = getValidatorsUseCase(classification.involvedValidatorAddresses).getOrThrow()

val fromAccounts = classification.extractWithdrawals(summary, getProfileUseCase, resources, involvedValidators)
val fromAccounts = extractWithdrawals(summary, getProfileUseCase, resources, involvedValidators)
val toAccounts = classification.extractDeposits(summary, getProfileUseCase, resources, involvedValidators)
val validatorAddressesSet = classification.validatorAddresses.map { it.addressString() }.toSet()
return PreviewType.Transfer.Staking(
Expand All @@ -49,26 +51,23 @@ class ValidatorUnstakeProcessor @Inject constructor(
) = executionSummary.accountDeposits.map { claimsPerAddress ->
val ownedAccount = getProfileUseCase.accountOnCurrentNetwork(claimsPerAddress.key) ?: error("No account found")
val defaultDepositGuarantees = getProfileUseCase.invoke().first().appPreferences.transaction.defaultDepositGuarantee
val depositingNfts = claimsPerAddress.value.map { claimedResource ->
val resourceAddress = claimedResource.resourceAddress
val depositingNfts = claimsPerAddress.value.filterIsInstance<ResourceIndicator.NonFungible>().map { claimedResource ->
val resourceAddress = claimedResource.resourceAddress.addressString()
val nftResource =
resources.find { it.resourceAddress == resourceAddress } as? Resource.NonFungibleResource
?: error("No resource found")
val validatorUnstake = validatorUnstakes.find { it.claimNftAddress.addressString() == resourceAddress }
?: error("No validator claim found")
val lsuResource = resources.find {
it.resourceAddress == validatorUnstake.liquidStakeUnitAddress.addressString()
} as? Resource.FungibleResource ?: error("No resource found")
val validator =
involvedValidators.find { validatorUnstake.validatorAddress.addressString() == it.address } ?: error("No validator found")
val lsuAmount = validatorUnstake.liquidStakeUnitAmount.asStr().toBigDecimal()
val xrdWorth = lsuAmount.divide(lsuResource.currentSupply, lsuResource.mathContext)
.multiply(validator.totalXrdStake, lsuResource.mathContext)
val stakeClaimNftItems = validatorUnstake.claimNftIds.map { localId ->
involvedValidators.find { nftResource.validatorAddress == it.address } ?: error("No validator found")
val stakeClaimNftItems = claimedResource.indicator.nonFungibleLocalIds.map { localId ->
val globalId = NonFungibleGlobalId.fromParts(claimedResource.resourceAddress, localId)
val claimAmount =
claimsNonFungibleData.find { it.nonFungibleGlobalId.asStr() == globalId.asStr() }?.data?.claimAmount?.asStr()
?.toBigDecimal()
?: error("No claim amount found")
Resource.NonFungibleResource.Item(
collectionAddress = resourceAddress,
localId = Resource.NonFungibleResource.Item.ID.from(localId)
) to xrdWorth
) to claimAmount
}
val guaranteeType = claimedResource.guaranteeType(defaultDepositGuarantees)
Transferable.Depositing(
Expand All @@ -91,22 +90,21 @@ class ValidatorUnstakeProcessor @Inject constructor(
)
}

private suspend fun DetailedManifestClass.ValidatorUnstake.extractWithdrawals(
private suspend fun extractWithdrawals(
executionSummary: ExecutionSummary,
getProfileUseCase: GetProfileUseCase,
resources: List<Resource>,
involvedValidators: List<ValidatorDetail>
) = executionSummary.accountWithdraws.map { withdrawalsPerAccount ->
val ownedAccount = getProfileUseCase.accountOnCurrentNetwork(withdrawalsPerAccount.key) ?: error("No account found")
val withdrawingLsu = withdrawalsPerAccount.value.map { depositedResource ->
val resourceAddress = depositedResource.resourceAddress
val withdrawingLsu = withdrawalsPerAccount.value.groupBy { it.resourceAddress }.map { depositedResources ->
val resourceAddress = depositedResources.key
val lsuResource = resources.find {
it.resourceAddress == resourceAddress
} as? Resource.FungibleResource ?: error("No resource found")
val unstakes = validatorUnstakes.filter { it.liquidStakeUnitAddress.asStr() == resourceAddress }
val validator =
involvedValidators.find { it.address == unstakes.first().validatorAddress.addressString() } ?: error("No validator found")
val totalLSU = unstakes.sumOf { it.liquidStakeUnitAmount.asStr().toBigDecimal() }
val validatorAddress = lsuResource.validatorAddress ?: error("No validator address found")
val validator = involvedValidators.find { it.address == validatorAddress } ?: error("No validator found")
val totalLSU = depositedResources.value.sumOf { it.amount }
val xrdWorth = totalLSU.divide(lsuResource.currentSupply, lsuResource.mathContext)
.multiply(validator.totalXrdStake, lsuResource.mathContext)
Transferable.Withdrawing(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ internal class TransactionReviewViewModelTest : StateViewModelTest<TransactionRe
resolveAssetsFromAddressUseCase = resolveAssetsFromAddressUseCase
),
poolContributionProcessor = PoolContributionProcessor(
getPoolDetailsUseCase = getPoolDetailsUseCase,
getProfileUseCase = getProfileUseCase,
resolveAssetsFromAddressUseCase = resolveAssetsFromAddressUseCase
),
Expand Down Expand Up @@ -232,7 +231,8 @@ internal class TransactionReviewViewModelTest : StateViewModelTest<TransactionRe
packageAddresses = listOf(),
metadata = mapOf()
),
presentedProofs = listOf()
presentedProofs = listOf(),
newlyCreatedNonFungibles = listOf()
)

@Before
Expand Down
2 changes: 1 addition & 1 deletion gradle/libraries.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ hiltNavigation = "1.1.0"
biometricKtx = "1.2.0-alpha05"
coilCompose = "2.5.0"
kotlinxSerialization = "1.6.1"
ret = "v1.0.8"
ret = "v1.0.9"
slip10 = "1.2-snapshot"
crypto = "1.72"
okhttpBom = "5.0.0-alpha.11"
Expand Down

0 comments on commit 99a40cd

Please sign in to comment.