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

[ABW-2889] properly compute unstake values. #777

Merged
merged 5 commits into from
Feb 1, 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
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
Loading