Skip to content

Commit

Permalink
Merge pull request #774 from radixdlt/fix/ABW-2846-guarantees
Browse files Browse the repository at this point in the history
Compute stake/pool preview guarantees state
  • Loading branch information
jakub-rdx authored Jan 30, 2024
2 parents 343ae28 + a525dca commit c44540b
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 53 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.babylon.wallet.android.presentation.transaction.analysis.processor

import com.babylon.wallet.android.domain.model.GuaranteeType
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.GetResourcesUseCase
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
import com.radixdlt.ret.DetailedManifestClass
Expand All @@ -21,37 +21,41 @@ import rdx.works.profile.domain.accountsOnCurrentNetwork
import javax.inject.Inject

class PoolContributionProcessor @Inject constructor(
private val getResourcesUseCase: GetResourcesUseCase,
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 resources = getResourcesUseCase(addresses = summary.involvedResourceAddresses).getOrThrow()
val assets = resolveAssetsFromAddressUseCase(
fungibleAddresses = summary.involvedFungibleAddresses(),
nonFungibleIds = summary.involvedNonFungibleIds()
).getOrThrow()
val involvedPools = getPoolDetailsUseCase(classification.poolAddresses.map { it.addressString() }.toSet()).getOrThrow()
val defaultDepositGuarantees = getProfileUseCase.invoke().first().appPreferences.transaction.defaultDepositGuarantee
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, resources)
val from = summary.extractWithdraws(ownedAccountsWithdrawnFrom, assets)
val to = summary.accountDeposits.map { depositsPerAddress ->
val ownedAccount = getProfileUseCase.accountOnCurrentNetwork(depositsPerAddress.key) ?: error("No account found")
val deposits = depositsPerAddress.value.mapNotNull { deposit ->
val deposits = depositsPerAddress.value.map { deposit ->
val resourceAddress = deposit.resourceAddress
val contributions = classification.poolContributions.filter {
it.poolUnitsResourceAddress.addressString() == resourceAddress
}
if (contributions.isEmpty()) {
null
resolveGeneralAsset(deposit, summary, assets, defaultDepositGuarantee)
} else {
val pool = involvedPools.find { it.address == contributions.first().poolAddress.addressString() }
?: error("No pool found")
val poolResource = resources.find { it.resourceAddress == pool.metadata.poolUnit() } as? Resource.FungibleResource
val poolResource = assets.find {
it.resource.resourceAddress == pool.metadata.poolUnit()
}?.resource as? Resource.FungibleResource
?: error("No pool resource found")
val contributedResourceAddresses = contributions.first().contributedResources.keys
val guaranteeType = (deposit as? ResourceIndicator.Fungible)?.guaranteeType(defaultDepositGuarantees)
?: GuaranteeType.Guaranteed

val guaranteeType = deposit.guaranteeType(defaultDepositGuarantee)
val poolUnitAmount = contributions.find {
it.poolUnitsResourceAddress.addressString() == poolResource.resourceAddress
}?.poolUnitsAmount?.asStr()?.toBigDecimalOrNull()
Expand Down Expand Up @@ -83,12 +87,30 @@ class PoolContributionProcessor @Inject constructor(
)
}

private fun ExecutionSummary.extractWithdraws(allOwnedAccounts: List<Network.Account>, resources: List<Resource>) =
private fun resolveGeneralAsset(
deposit: ResourceIndicator,
summary: ExecutionSummary,
involvedAssets: List<Asset>,
defaultDepositGuarantee: Double
): Transferable.Depositing {
val asset = if (deposit.isNewlyCreated(summary = summary)) {
deposit.toNewlyCreatedTransferableAsset(deposit.newlyCreatedMetadata(summary = summary))
} else {
deposit.toTransferableAsset(involvedAssets)
}

return Transferable.Depositing(
transferable = asset,
guaranteeType = deposit.guaranteeType(defaultDepositGuarantee)
)
}

private fun ExecutionSummary.extractWithdraws(allOwnedAccounts: List<Network.Account>, assets: List<Asset>) =
accountWithdraws.entries.map { transferEntry ->
val accountOnNetwork = allOwnedAccounts.find { it.address == transferEntry.key }

val withdrawing = transferEntry.value.map { resourceIndicator ->
Transferable.Withdrawing(resourceIndicator.toTransferableResource(resources))
Transferable.Withdrawing(resourceIndicator.toTransferableAsset(assets))
}
accountOnNetwork?.let { account ->
AccountWithTransferableResources.Owned(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.babylon.wallet.android.presentation.transaction.analysis.processor

import com.babylon.wallet.android.domain.model.GuaranteeType
import com.babylon.wallet.android.domain.model.Transferable
import com.babylon.wallet.android.domain.model.TransferableAsset
import com.babylon.wallet.android.domain.model.assets.StakeClaim
Expand All @@ -13,11 +14,14 @@ 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.ResourceIndicator
import com.radixdlt.ret.nonFungibleLocalIdAsStr
import kotlinx.coroutines.flow.first
import rdx.works.profile.derivation.model.NetworkId
import rdx.works.profile.domain.GetProfileUseCase
import rdx.works.profile.domain.accountOnCurrentNetwork
import rdx.works.profile.domain.currentNetwork
import java.math.BigDecimal
import javax.inject.Inject

class ValidatorClaimProcessor @Inject constructor(
Expand All @@ -36,7 +40,7 @@ class ValidatorClaimProcessor @Inject constructor(
}.flatten()

val toAccounts = extractDeposits(summary, getProfileUseCase, resources)
val fromAccounts = classification.extractWithdrawals(
val fromAccounts = extractWithdrawals(
executionSummary = summary,
getProfileUseCase = getProfileUseCase,
resources = resources,
Expand All @@ -51,7 +55,7 @@ class ValidatorClaimProcessor @Inject constructor(
)
}

private suspend fun DetailedManifestClass.ValidatorClaim.extractWithdrawals(
private suspend fun extractWithdrawals(
executionSummary: ExecutionSummary,
getProfileUseCase: GetProfileUseCase,
resources: List<Resource>,
Expand All @@ -60,39 +64,38 @@ class ValidatorClaimProcessor @Inject constructor(
): List<AccountWithTransferableResources.Owned> {
return executionSummary.accountWithdraws.map { claimsPerAddress ->
val ownedAccount = getProfileUseCase.accountOnCurrentNetwork(claimsPerAddress.key) ?: error("No account found")
val withdrawingNfts = claimsPerAddress.value.distinctBy { it.resourceAddress }.map { resourceClaim ->
val resourceAddress = resourceClaim.resourceAddress
val nftResource =
resources.find { it.resourceAddress == resourceAddress } as? Resource.NonFungibleResource
?: error("No resource found")
val validatorClaim = validatorClaims.find { it.claimNftAddress.addressString() == resourceAddress }
?: error("No validator claim found")
val validator =
involvedValidators.find { validatorClaim.validatorAddress.addressString() == it.address } ?: error("No validator found")
val items = validatorClaims.filter { it.claimNftAddress.addressString() == resourceAddress }.map { claim ->
claim.claimNftIds.map { localId ->
val localIdString = nonFungibleLocalIdAsStr(localId)
val claimAmount = stakeClaimsNfts.find {
resourceAddress == it.collectionAddress && localIdString == nonFungibleLocalIdAsStr(it.localId.toRetId())
}?.claimAmountXrd
Resource.NonFungibleResource.Item(
collectionAddress = resourceAddress,
localId = Resource.NonFungibleResource.Item.ID.from(localId)
) to (claimAmount ?: claim.xrdAmount.asStr().toBigDecimal())
val withdrawingNfts =
claimsPerAddress.value.filterIsInstance<ResourceIndicator.NonFungible>().groupBy { it.resourceAddress.addressString() }
.map { resourceClaim ->
val resourceAddress = resourceClaim.key
val nftResource =
resources.find { it.resourceAddress == resourceAddress } as? Resource.NonFungibleResource
?: error("No resource found")
val validatorAddress = nftResource.validatorAddress ?: error("No validator address")
val validator = involvedValidators.find { validatorAddress == it.address } ?: error("No validator found")
val items = resourceClaim.value.map { resourceIndicator ->
resourceIndicator.localIds.map { localId ->
val claimAmount = stakeClaimsNfts.find {
resourceAddress == it.collectionAddress && localId == nonFungibleLocalIdAsStr(it.localId.toRetId())
}?.claimAmountXrd ?: BigDecimal.ZERO
Resource.NonFungibleResource.Item(
collectionAddress = resourceAddress,
localId = Resource.NonFungibleResource.Item.ID.from(localId)
) to claimAmount
}
}.flatten()
Transferable.Withdrawing(
transferable = TransferableAsset.NonFungible.StakeClaimAssets(
claim = StakeClaim(
nonFungibleResource = nftResource.copy(items = items.map { it.first }),
validator = validator
),
xrdWorthPerNftItem = items.associate {
it.first.localId.displayable to it.second
}
)
)
}
}.flatten()
Transferable.Withdrawing(
transferable = TransferableAsset.NonFungible.StakeClaimAssets(
claim = StakeClaim(
nonFungibleResource = nftResource.copy(items = items.map { it.first }),
validator = validator
),
xrdWorthPerNftItem = items.associate {
it.first.localId.displayable to it.second
}
)
)
}
AccountWithTransferableResources.Owned(
account = ownedAccount,
resources = withdrawingNfts
Expand All @@ -106,10 +109,12 @@ class ValidatorClaimProcessor @Inject constructor(
resources: List<Resource>
) = executionSummary.accountDeposits.map { entry ->
val ownedAccount = getProfileUseCase.accountOnCurrentNetwork(entry.key) ?: error("No account found")
val defaultDepositGuarantees = getProfileUseCase.invoke().first().appPreferences.transaction.defaultDepositGuarantee
val xrdResource = resources.find {
it.resourceAddress == XrdResource.address(NetworkId.from(ownedAccount.networkID))
} as? Resource.FungibleResource ?: error("No resource found")

val guaranteeType = entry.value.firstOrNull()?.guaranteeType(defaultDepositGuarantees)
?: GuaranteeType.Guaranteed
val amount = entry.value.sumOf { it.amount }
AccountWithTransferableResources.Owned(
account = ownedAccount,
Expand All @@ -119,7 +124,8 @@ class ValidatorClaimProcessor @Inject constructor(
amount = amount,
resource = xrdResource.copy(ownedAmount = amount),
isNewlyCreated = false
)
),
guaranteeType = guaranteeType
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ 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 kotlinx.coroutines.flow.first
import rdx.works.profile.derivation.model.NetworkId
import rdx.works.profile.domain.GetProfileUseCase
import rdx.works.profile.domain.accountOnCurrentNetwork
Expand Down Expand Up @@ -51,6 +52,7 @@ class ValidatorStakeProcessor @Inject constructor(
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 lsuResource = resources.find {
Expand All @@ -60,12 +62,14 @@ class ValidatorStakeProcessor @Inject constructor(
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)
Transferable.Depositing(
transferable = TransferableAsset.Fungible.LSUAsset(
amount = amount,
lsu = LiquidStakeUnit(lsuResource.copy(ownedAmount = amount), validator),
xrdWorth = stakes.sumOf { it.xrdAmount.asStr().toBigDecimal() },
)
),
guaranteeType = guaranteeType
)
}
AccountWithTransferableResources.Owned(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ 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 kotlinx.coroutines.flow.first
import rdx.works.profile.domain.GetProfileUseCase
import rdx.works.profile.domain.accountOnCurrentNetwork
import rdx.works.profile.domain.currentNetwork
Expand Down Expand Up @@ -47,6 +48,7 @@ class ValidatorUnstakeProcessor @Inject constructor(
involvedValidators: List<ValidatorDetail>
) = 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 nftResource =
Expand All @@ -68,6 +70,7 @@ class ValidatorUnstakeProcessor @Inject constructor(
localId = Resource.NonFungibleResource.Item.ID.from(localId)
) to xrdWorth
}
val guaranteeType = claimedResource.guaranteeType(defaultDepositGuarantees)
Transferable.Depositing(
transferable = TransferableAsset.NonFungible.StakeClaimAssets(
claim = StakeClaim(
Expand All @@ -78,7 +81,8 @@ class ValidatorUnstakeProcessor @Inject constructor(
it.first.localId.displayable to it.second
},
isNewlyCreated = true
)
),
guaranteeType = guaranteeType
)
}
AccountWithTransferableResources.Owned(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,9 @@ internal class TransactionReviewViewModelTest : StateViewModelTest<TransactionRe
resolveAssetsFromAddressUseCase = resolveAssetsFromAddressUseCase
),
poolContributionProcessor = PoolContributionProcessor(
getResourcesUseCase = getResourcesUseCase,
getPoolDetailsUseCase = getPoolDetailsUseCase,
getProfileUseCase = getProfileUseCase,
resolveAssetsFromAddressUseCase = resolveAssetsFromAddressUseCase
),
accountDepositSettingsProcessor = AccountDepositSettingsProcessor(
getProfileUseCase = getProfileUseCase,
Expand Down

0 comments on commit c44540b

Please sign in to comment.