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

Compute stake/pool preview guarantees state #774

Merged
merged 5 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
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,40 @@ 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> {
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)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this needed here? Can you explain when we have empty contributions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

see second bullet

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if you contribute to pool amounts of resources that do not convert to pool units entirely (for example you have ratio of resources A:B as 1:2, and you contribute 1 unit of A and 2.5 of B (via sandbox), that reminder of 0.5 B is returned by RET as regular resource deposited to the specific account. Without that change, this is not reflected in the "Depositing" section, although you see in "Withdrawn" 2.5 of B being withdrawn.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah I see now thanks

} 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 +86,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
Loading