Skip to content

Commit

Permalink
Merge pull request #1350 from novasamatech/rc/7.7.4
Browse files Browse the repository at this point in the history
Rc/7.7.4
  • Loading branch information
valentunn committed Jan 31, 2024
2 parents 8e24e18 + 0ad39d7 commit 4b766db
Show file tree
Hide file tree
Showing 56 changed files with 867 additions and 283 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import android.content.Context
import androidx.test.core.app.ApplicationProvider
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.utils.orZero
import io.novafoundation.nova.feature_wallet_api.data.network.crosschain.CrossChainFee
import io.novafoundation.nova.feature_wallet_api.data.network.crosschain.CrossChainFeeModel
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
import io.novafoundation.nova.feature_wallet_api.domain.implementations.transferConfiguration
import io.novafoundation.nova.feature_wallet_api.presentation.formatters.formatPlanks
Expand Down Expand Up @@ -74,22 +74,9 @@ class CrossChainTransfersIntegrationTest : BaseIntegrationTest() {
destinationParaId = parachainInfoRepository.paraId(destinationChain.id)
)!!

val crossChainFee = crossChainWeigher.estimateFee(crossChainTransfer)
val crossChainFeeResult = runCatching { crossChainWeigher.estimateFee(BigInteger.ZERO, crossChainTransfer) }

error(crossChainFee.formatWith(asssetInOrigin))
check(crossChainFeeResult.isSuccess)
}
}

private fun CrossChainFee.formatWith(
transferringAsset: Chain.Asset
): String {
fun BigInteger?.formatAmount() = this?.let { it.formatPlanks(transferringAsset) }

return """
Destination Fee: ${destination?.formatAmount()}
Reserve Fee: ${reserve?.formatAmount()}
Total XCM Fee: ${(reserve.orZero() + destination.orZero()).formatAmount()}
""".trimIndent()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import io.novafoundation.nova.app.root.presentation.deepLinks.common.DeepLinkHan
import io.novafoundation.nova.common.utils.Urls
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
import io.novafoundation.nova.common.utils.sequrity.awaitInteractionAllowed
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
import io.novafoundation.nova.feature_dapp_api.data.repository.DAppMetadataRepository
import io.novafoundation.nova.feature_dapp_impl.DAppRouter
import kotlinx.coroutines.flow.Flow
Expand All @@ -16,7 +15,6 @@ import kotlinx.coroutines.flow.emptyFlow
private const val DAPP_DEEP_LINK_PREFIX = "/open/dapp"

class DAppDeepLinkHandler(
private val accountRepository: AccountRepository,
private val dappRepository: DAppMetadataRepository,
private val dAppRouter: DAppRouter,
private val automaticInteractionGate: AutomaticInteractionGate
Expand All @@ -35,10 +33,14 @@ class DAppDeepLinkHandler(
val url = data.getDappUrl() ?: throw DAppHandlingException.UrlIsInvalid
val normalizedUrl = runCatching { Urls.normalizeUrl(url) }.getOrNull() ?: throw DAppHandlingException.UrlIsInvalid

val dAppMetadata = dappRepository.syncAndGetDapp(normalizedUrl)
if (dAppMetadata == null) throw DAppHandlingException.DomainIsNotMatched(normalizedUrl)
ensureDAppInCatalog(normalizedUrl)

dAppRouter.openDAppBrowser(normalizedUrl)
dAppRouter.openDAppBrowser(url)
}

private suspend fun ensureDAppInCatalog(normalizedUrl: String) {
dappRepository.syncAndGetDapp(normalizedUrl)
?: throw DAppHandlingException.DomainIsNotMatched(normalizedUrl)
}

private fun Uri.getDappUrl(): String? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoSet
import io.novafoundation.nova.app.root.domain.RootInteractor
import io.novafoundation.nova.app.root.presentation.deepLinks.DeepLinkHandler
import io.novafoundation.nova.app.root.presentation.deepLinks.RootDeepLinkHandler
import io.novafoundation.nova.app.root.presentation.deepLinks.handlers.BuyCallbackDeepLinkHandler
import io.novafoundation.nova.app.root.presentation.deepLinks.handlers.DAppDeepLinkHandler
import io.novafoundation.nova.app.root.presentation.deepLinks.DeepLinkHandler
import io.novafoundation.nova.app.root.presentation.deepLinks.handlers.ImportMnemonicDeepLinkHandler
import io.novafoundation.nova.app.root.presentation.deepLinks.handlers.ReferendumDeepLinkHandler
import io.novafoundation.nova.app.root.presentation.deepLinks.RootDeepLinkHandler
import io.novafoundation.nova.app.root.presentation.deepLinks.handlers.StakingDashboardDeepLinkHandler
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
import io.novafoundation.nova.feature_account_api.domain.account.common.EncryptionDefaults
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
import io.novafoundation.nova.feature_dapp_api.data.repository.DAppMetadataRepository
Expand Down Expand Up @@ -54,13 +54,11 @@ class DeepLinkModule {
@Provides
@IntoSet
fun provideDappDeepLinkHandler(
accountRepository: AccountRepository,
dAppMetadataRepository: DAppMetadataRepository,
dAppRouter: DAppRouter,
automaticInteractionGate: AutomaticInteractionGate
): DeepLinkHandler {
return DAppDeepLinkHandler(
accountRepository,
dAppMetadataRepository,
dAppRouter,
automaticInteractionGate
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
buildscript {
ext {
// App version
versionName = '7.7.3'
versionCode = 111
versionName = '7.7.4'
versionCode = 113

applicationId = "io.novafoundation.nova"
releaseApplicationSuffix = "market"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ interface ResourceManager {

fun getDimensionPixelSize(id: Int): Int

fun getFont(@FontRes fontRes: Int): Typeface
fun getFont(@FontRes fontRes: Int): Typeface?
}

fun ResourceManager.formatTimeLeft(elapsedTimeInMillis: Long): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.text.format.DateUtils
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import io.novafoundation.nova.common.R
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.common.utils.daysFromMillis
import io.novafoundation.nova.common.utils.formatting.duration.EstimatedDurationFormatter
import io.novafoundation.nova.common.utils.formatting.baseDurationFormatter
import io.novafoundation.nova.common.utils.formatting.duration.EstimatedDurationFormatter
import io.novafoundation.nova.common.utils.formatting.formatDateTime
import io.novafoundation.nova.common.utils.getDrawableCompat
import io.novafoundation.nova.common.utils.readText
Expand Down Expand Up @@ -109,7 +110,7 @@ class ResourceManagerImpl(
return contextManager.getApplicationContext().resources.getDimensionPixelSize(id)
}

override fun getFont(fontRes: Int): Typeface {
return contextManager.getApplicationContext().resources.getFont(fontRes)
override fun getFont(fontRes: Int): Typeface? {
return ResourcesCompat.getFont(contextManager.getApplicationContext(), fontRes)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.novafoundation.nova.common.utils

import java.math.BigInteger

class BigRational(private val numerator: BigInteger, private val denominator: BigInteger) {

val quotient: BigInteger
get() = numerator / denominator

companion object
}

fun BigRational.Companion.fixedU128(value: BigInteger): BigInteger {
return BigRational(value, BigInteger.TEN.pow(18)).quotient
}
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,14 @@ fun CallRepresentation.toCallInstance(): CallRepresentation.Instance? {
return (this as? CallRepresentation.Instance)
}

fun RuntimeMetadata.moduleOrFallback(name: String, vararg fallbacks: String): Module = modules[name]
?: fallbacks.firstOrNull { modules[it] != null }
?.let { modules[it] } ?: throw NoSuchElementException()

fun Module.storageOrFallback(name: String, vararg fallbacks: String): StorageEntry = storage?.get(name)
?: fallbacks.firstOrNull { storage?.get(it) != null }
?.let { storage?.get(it) } ?: throw NoSuchElementException()

object Modules {
const val VESTING: String = "Vesting"
const val STAKING = "Staking"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
package io.novafoundation.nova.common.utils

import android.annotation.TargetApi
import android.content.Context
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.os.Build
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.SpannedString
import android.text.TextPaint
import android.text.style.CharacterStyle
import android.text.style.ClickableSpan
import android.text.style.ForegroundColorSpan
import android.text.style.ImageSpan
import android.text.style.MetricAffectingSpan
import android.text.style.TypefaceSpan
import android.view.View
import androidx.annotation.FontRes
import androidx.core.content.res.ResourcesCompat
import androidx.core.text.toSpannable
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.common.utils.formatting.spannable.SpannableFormatter
Expand Down Expand Up @@ -64,12 +69,38 @@ fun colorSpan(color: Int) = ForegroundColorSpan(color)

fun fontSpan(resourceManager: ResourceManager, @FontRes fontRes: Int) = fontSpan(resourceManager.getFont(fontRes))

fun fontSpan(context: Context, @FontRes fontRes: Int) = fontSpan(context.resources.getFont(fontRes))
fun fontSpan(context: Context, @FontRes fontRes: Int) = fontSpan(ResourcesCompat.getFont(context, fontRes))

fun fontSpan(typeface: Typeface) = TypefaceSpan(typeface)
fun fontSpan(typeface: Typeface?): CharacterStyle {
return when {
typeface == null -> NoOpSpan()

Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> typefaceSpanCompatV28(typeface)

else -> CustomTypefaceSpan(typeface)
}
}

fun drawableSpan(drawable: Drawable) = ImageSpan(drawable)

fun CharSequence.formatAsSpannable(vararg args: Any): SpannedString {
return SpannableFormatter.format(this, *args)
}

@TargetApi(Build.VERSION_CODES.P)
private fun typefaceSpanCompatV28(typeface: Typeface) =
TypefaceSpan(typeface)

private class CustomTypefaceSpan(private val typeface: Typeface?) : MetricAffectingSpan() {
override fun updateDrawState(paint: TextPaint) {
paint.typeface = typeface
}

override fun updateMeasureState(paint: TextPaint) {
paint.typeface = typeface
}
}

private class NoOpSpan : CharacterStyle() {
override fun updateDrawState(tp: TextPaint?) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ val Fee.requestedAccountPaysFees: Boolean
get() = submissionOrigin.requestedOrigin.contentEquals(submissionOrigin.actualOrigin)

val Fee.amountByRequestedAccount: BigInteger
get() = amount.asAmountByRequestedAccount

context(Fee)
val BigInteger.asAmountByRequestedAccount: BigInteger
get() = if (requestedAccountPaysFees) {
amount
this
} else {
BigInteger.ZERO
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import io.novafoundation.nova.runtime.ethereum.contract.erc20.Erc20Standard
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
import io.novafoundation.nova.runtime.multiNetwork.qr.MultiChainQrSharingFactory
import io.novafoundation.nova.runtime.multiNetwork.runtime.repository.EventsRepository
import io.novafoundation.nova.runtime.repository.ChainStateRepository
import io.novafoundation.nova.runtime.repository.ParachainInfoRepository
import io.novafoundation.nova.runtime.storage.source.StorageDataSource
import io.novafoundation.nova.web3names.domain.networking.Web3NamesInteractor
Expand Down Expand Up @@ -235,4 +236,6 @@ interface AssetsFeatureDependencies {
val swapRateFormatter: SwapRateFormatter

val bottomSheetLauncher: DescriptionBottomSheetLauncher

val chainStateRepository: ChainStateRepository
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
package io.novafoundation.nova.feature_assets.domain.send

import io.novafoundation.nova.common.utils.orZero
import io.novafoundation.nova.feature_account_api.data.extrinsic.SubmissionOrigin
import io.novafoundation.nova.feature_account_api.data.model.Fee
import io.novafoundation.nova.feature_account_api.data.model.SubstrateFee
import io.novafoundation.nova.feature_account_api.data.model.amountByRequestedAccount
import io.novafoundation.nova.feature_account_api.domain.model.requireAccountIdIn
import io.novafoundation.nova.feature_assets.domain.send.model.TransferFeeModel
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.assets.AssetSourceRegistry
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.assets.tranfers.AssetTransfer
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.assets.tranfers.WeightedAssetTransfer
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.assets.tranfers.isCrossChain
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.assets.tranfers.senderAccountId
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.types.Balance
import io.novafoundation.nova.feature_wallet_api.data.network.crosschain.CrossChainFeeModel
import io.novafoundation.nova.feature_wallet_api.data.network.crosschain.CrossChainTransactor
import io.novafoundation.nova.feature_wallet_api.data.network.crosschain.CrossChainTransfersRepository
import io.novafoundation.nova.feature_wallet_api.data.network.crosschain.CrossChainWeigher
import io.novafoundation.nova.feature_wallet_api.domain.implementations.transferConfiguration
import io.novafoundation.nova.feature_wallet_api.domain.interfaces.WalletRepository
import io.novafoundation.nova.feature_wallet_api.domain.model.CrossChainTransfersConfiguration
import io.novafoundation.nova.feature_wallet_api.domain.model.OriginDecimalFee
import io.novafoundation.nova.feature_wallet_api.domain.model.OriginFee
import io.novafoundation.nova.feature_wallet_api.domain.model.RecipientSearchResult
import io.novafoundation.nova.feature_wallet_api.presentation.model.DecimalFee
import io.novafoundation.nova.feature_wallet_api.domain.model.networkFeePart
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId
import io.novafoundation.nova.runtime.repository.ParachainInfoRepository
import jp.co.soramitsu.fearless_utils.runtime.AccountId
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

Expand Down Expand Up @@ -62,47 +68,42 @@ class SendInteractor(
crossChainTransfersRepository.syncConfiguration()
}

suspend fun getOriginFee(transfer: AssetTransfer): Fee = withContext(Dispatchers.Default) {
suspend fun getFee(amount: Balance, transfer: AssetTransfer): TransferFeeModel = withContext(Dispatchers.Default) {
if (transfer.isCrossChain) {
val config = crossChainTransfersRepository.getConfiguration().configurationFor(transfer)!!

crossChainTransactor.estimateOriginFee(config, transfer)
} else {
getAssetTransfers(transfer).calculateFee(transfer)
}
}
val originFee = crossChainTransactor.estimateOriginFee(config, transfer)
val crossChainFeeModel = crossChainWeigher.estimateFee(amount, config)

suspend fun getCrossChainFee(transfer: AssetTransfer): Fee? = if (transfer.isCrossChain) {
val feePlanks = withContext(Dispatchers.Default) {
val config = crossChainTransfersRepository.getConfiguration().configurationFor(transfer)!!
val crossChainFee = crossChainWeigher.estimateFee(config)
val deliveryPartFee = getDeliveryFee(crossChainFeeModel.senderPart, transfer.senderAccountId())
val originFeeWithSenderPart = OriginFee(originFee, deliveryPartFee, transfer.commissionAssetToken.configuration)

crossChainFee.reserve.orZero() + crossChainFee.destination.orZero()
TransferFeeModel(originFeeWithSenderPart, crossChainFeeModel.toSubstrateFee(transfer))
} else {
val originFee = getAssetTransfers(transfer).calculateFee(transfer)
TransferFeeModel(
OriginFee(originFee, null, transfer.commissionAssetToken.configuration),
null
)
}

val submissionOriginId = transfer.sender.requireAccountIdIn(transfer.originChain)
val submissionOrigin = SubmissionOrigin.singleOrigin(submissionOriginId) // cross-chain fee is always paid by requested account id

SubstrateFee(feePlanks, submissionOrigin)
} else {
null
}

suspend fun performTransfer(
transfer: WeightedAssetTransfer,
originFee: DecimalFee,
crossChainFee: DecimalFee?,
originFee: OriginDecimalFee,
crossChainFee: Fee?,
): Result<*> = withContext(Dispatchers.Default) {
if (transfer.isCrossChain) {
val crossChainFeePlanks = crossChainFee!!.networkFee.amountByRequestedAccount
val config = crossChainTransfersRepository.getConfiguration().configurationFor(transfer)!!

crossChainTransactor.performTransfer(config, transfer, crossChainFeePlanks)
crossChainTransactor.performTransfer(config, transfer, crossChainFee!!.amountByRequestedAccount)
} else {
val networkFee = originFee.networkFeePart()

getAssetTransfers(transfer).performTransfer(transfer)
.onSuccess { submission ->
// Insert used fee regardless of who paid it
walletRepository.insertPendingTransfer(submission.hash, transfer, originFee.networkFeeDecimalAmount)
walletRepository.insertPendingTransfer(submission.hash, transfer, networkFee.networkFeeDecimalAmount)
}
}
}
Expand All @@ -123,4 +124,16 @@ class SendInteractor(
destinationChain = transfer.destinationChain,
destinationParaId = parachainInfoRepository.paraId(transfer.destinationChain.id)
)

private fun getDeliveryFee(amount: Balance, accountId: AccountId): Fee {
return SubstrateFee(
amount = amount,
submissionOrigin = SubmissionOrigin.singleOrigin(accountId)
)
}

private fun CrossChainFeeModel.toSubstrateFee(transfer: AssetTransfer) = SubstrateFee(
amount = holdingPart,
submissionOrigin = SubmissionOrigin.singleOrigin(transfer.sender.requireAccountIdIn(transfer.originChain))
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.novafoundation.nova.feature_assets.domain.send.model

import io.novafoundation.nova.feature_account_api.data.model.Fee
import io.novafoundation.nova.feature_wallet_api.domain.model.OriginFee

class TransferFeeModel(
val originFee: OriginFee,
val crossChainFee: Fee?
)

0 comments on commit 4b766db

Please sign in to comment.