mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-07-22 18:25:50 +00:00
Fix Pending Signatures: query the bridge's own hardcoded multisig, not the generic multisig-account feature
The previous commit reused MultisigPendingOperationsService, but that service only syncs accounts formally registered as a MultisigMetaAccount in this wallet - it silently returns an empty NoOpSyncer for every other account type. The bridge's 5 signatories (Serok etc.) use their own regular wallets to approve calls against the bridge's separate, hardcoded multisig (BridgeMultisigConstants) - they never "are" that multisig account, so the feature was structurally guaranteed to show nothing, not just empty by coincidence. Fix queries the bridge's own multisig accounts directly, mirroring the exact pattern this screen's existing renewal-signing code already uses (BridgeMultisigRuntimeApi's runtime-metadata accessors, a local mirror of feature-account-impl's off-chain call-data indexer client - same module-boundary reasoning already documented in that file) rather than the generic, differently-scoped service: - BridgeMultisigInteractor.getPendingApprovals(): enumerates Multisig.Multisigs entries for both bridge multisig accounts via QueryableStorageEntry2.keys()/entries() (a general runtime-storage capability, not multisig-feature-specific), fetches real call content from the same off-chain indexer the generic feature uses (also general-purpose, keyed by address+hashes - never required the account to be a MultisigMetaAccount either), and filters to calls this signatory hasn't approved yet. - BridgeMultisigInteractor.submitApproval(): reuses composeBridgeMultisigAsMulti generalized to an arbitrary call+ timepoint (previously only used for the renewal call) - refuses to proceed if the call content is unknown, never blind-signs. - BridgeViewModel formats the real parsed transfer (amount+destination) via AssetSourceRegistry.tryParseTransfer, already used elsewhere for the exact same "decode a transfer call" job - no fabricated preview data. Reverts the feature-multisig:operations cross-feature dependency and FeatureApi wiring added in the previous commit - no longer needed since everything now lives in this screen's own existing self-contained domain layer, matching how the rest of BridgeMultisigInteractor already works (and already explains, in its own doc comments, why a synthetic MultisigMetaAccount was deliberately avoided for exactly this reason).
This commit is contained in:
@@ -108,8 +108,6 @@ import io.novafoundation.nova.feature_ledger_impl.presentation.account.addChain.
|
|||||||
import io.novafoundation.nova.feature_ledger_impl.presentation.account.common.selectLedger.SelectLedgerPayload
|
import io.novafoundation.nova.feature_ledger_impl.presentation.account.common.selectLedger.SelectLedgerPayload
|
||||||
import io.novafoundation.nova.feature_multisig_operations.presentation.created.MultisigCreatedBottomSheet
|
import io.novafoundation.nova.feature_multisig_operations.presentation.created.MultisigCreatedBottomSheet
|
||||||
import io.novafoundation.nova.feature_multisig_operations.presentation.created.MultisigCreatedPayload
|
import io.novafoundation.nova.feature_multisig_operations.presentation.created.MultisigCreatedPayload
|
||||||
import io.novafoundation.nova.feature_multisig_operations.presentation.details.general.MultisigOperationDetailsFragment
|
|
||||||
import io.novafoundation.nova.feature_multisig_operations.presentation.details.general.MultisigOperationDetailsPayload
|
|
||||||
import io.novafoundation.nova.feature_onboarding_impl.OnboardingRouter
|
import io.novafoundation.nova.feature_onboarding_impl.OnboardingRouter
|
||||||
import io.novafoundation.nova.feature_onboarding_impl.presentation.welcome.WelcomeFragment
|
import io.novafoundation.nova.feature_onboarding_impl.presentation.welcome.WelcomeFragment
|
||||||
import io.novafoundation.nova.feature_swap_api.presentation.model.SwapSettingsPayload
|
import io.novafoundation.nova.feature_swap_api.presentation.model.SwapSettingsPayload
|
||||||
@@ -985,12 +983,6 @@ class Navigator(
|
|||||||
.navigateInFirstAttachedContext()
|
.navigateInFirstAttachedContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openMultisigOperationDetails(payload: MultisigOperationDetailsPayload) {
|
|
||||||
navigationBuilder().action(R.id.action_multisigOperationDetailsFragment)
|
|
||||||
.setArgs(MultisigOperationDetailsFragment.createPayload(payload))
|
|
||||||
.navigateInFirstAttachedContext()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun openMainWithFinishMultisigTransaction(accountWasSwitched: Boolean) {
|
override fun openMainWithFinishMultisigTransaction(accountWasSwitched: Boolean) {
|
||||||
val payload = MultisigCreatedBottomSheet.createPayload(MultisigCreatedPayload(accountWasSwitched))
|
val payload = MultisigCreatedBottomSheet.createPayload(MultisigCreatedPayload(accountWasSwitched))
|
||||||
openSplitScreenWithInstantAction(R.id.action_open_multisigCreatedDialog, nestedActionExtras = payload)
|
openSplitScreenWithInstantAction(R.id.action_open_multisigCreatedDialog, nestedActionExtras = payload)
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ dependencies {
|
|||||||
implementation project(':feature-deep-linking')
|
implementation project(':feature-deep-linking')
|
||||||
implementation project(':feature-ahm-api')
|
implementation project(':feature-ahm-api')
|
||||||
implementation project(':feature-gift-api')
|
implementation project(':feature-gift-api')
|
||||||
implementation project(':feature-multisig:operations')
|
|
||||||
|
|
||||||
implementation kotlinDep
|
implementation kotlinDep
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -53,7 +53,6 @@ import io.novafoundation.nova.feature_crowdloan_api.di.CrowdloanFeatureApi
|
|||||||
import io.novafoundation.nova.feature_currency_api.di.CurrencyFeatureApi
|
import io.novafoundation.nova.feature_currency_api.di.CurrencyFeatureApi
|
||||||
import io.novafoundation.nova.feature_deep_linking.di.DeepLinkingFeatureApi
|
import io.novafoundation.nova.feature_deep_linking.di.DeepLinkingFeatureApi
|
||||||
import io.novafoundation.nova.feature_gift_api.di.GiftFeatureApi
|
import io.novafoundation.nova.feature_gift_api.di.GiftFeatureApi
|
||||||
import io.novafoundation.nova.feature_multisig_operations.di.MultisigOperationsFeatureApi
|
|
||||||
import io.novafoundation.nova.feature_nft_api.NftFeatureApi
|
import io.novafoundation.nova.feature_nft_api.NftFeatureApi
|
||||||
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
|
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
|
||||||
import io.novafoundation.nova.feature_swap_api.di.SwapFeatureApi
|
import io.novafoundation.nova.feature_swap_api.di.SwapFeatureApi
|
||||||
@@ -176,8 +175,7 @@ interface AssetsFeatureComponent : AssetsFeatureApi {
|
|||||||
BannersFeatureApi::class,
|
BannersFeatureApi::class,
|
||||||
DeepLinkingFeatureApi::class,
|
DeepLinkingFeatureApi::class,
|
||||||
ChainMigrationFeatureApi::class,
|
ChainMigrationFeatureApi::class,
|
||||||
GiftFeatureApi::class,
|
GiftFeatureApi::class
|
||||||
MultisigOperationsFeatureApi::class
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
interface AssetsFeatureDependenciesComponent : AssetsFeatureDependencies
|
interface AssetsFeatureDependenciesComponent : AssetsFeatureDependencies
|
||||||
|
|||||||
+3
-3
@@ -5,6 +5,7 @@ import coil.ImageLoader
|
|||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import io.novafoundation.nova.common.address.AddressIconGenerator
|
import io.novafoundation.nova.common.address.AddressIconGenerator
|
||||||
import io.novafoundation.nova.common.address.format.EthereumAddressFormat
|
import io.novafoundation.nova.common.address.format.EthereumAddressFormat
|
||||||
|
import io.novafoundation.nova.common.data.config.GlobalConfigDataSource
|
||||||
import io.novafoundation.nova.common.data.memory.ComputationalCache
|
import io.novafoundation.nova.common.data.memory.ComputationalCache
|
||||||
import io.novafoundation.nova.common.data.network.AppLinksProvider
|
import io.novafoundation.nova.common.data.network.AppLinksProvider
|
||||||
import io.novafoundation.nova.common.data.network.HttpExceptionHandler
|
import io.novafoundation.nova.common.data.network.HttpExceptionHandler
|
||||||
@@ -99,7 +100,6 @@ import io.novafoundation.nova.feature_account_api.data.fee.capability.CustomFeeC
|
|||||||
import io.novafoundation.nova.feature_ahm_api.domain.ChainMigrationInfoUseCase
|
import io.novafoundation.nova.feature_ahm_api.domain.ChainMigrationInfoUseCase
|
||||||
import io.novafoundation.nova.feature_gift_api.domain.GiftsAccountSupportedUseCase
|
import io.novafoundation.nova.feature_gift_api.domain.GiftsAccountSupportedUseCase
|
||||||
import io.novafoundation.nova.feature_gift_api.domain.AvailableGiftAssetsUseCase
|
import io.novafoundation.nova.feature_gift_api.domain.AvailableGiftAssetsUseCase
|
||||||
import io.novafoundation.nova.feature_multisig_operations.presentation.callFormatting.MultisigCallFormatter
|
|
||||||
import io.novafoundation.nova.feature_wallet_api.domain.SendUseCase
|
import io.novafoundation.nova.feature_wallet_api.domain.SendUseCase
|
||||||
import io.novafoundation.nova.feature_wallet_api.presentation.mixin.amountChooser.AmountChooserMixin
|
import io.novafoundation.nova.feature_wallet_api.presentation.mixin.amountChooser.AmountChooserMixin
|
||||||
import io.novafoundation.nova.feature_wallet_api.presentation.mixin.fee.FeeLoaderMixin
|
import io.novafoundation.nova.feature_wallet_api.presentation.mixin.fee.FeeLoaderMixin
|
||||||
@@ -241,8 +241,6 @@ interface AssetsFeatureDependencies {
|
|||||||
|
|
||||||
val multisigPendingOperationsService: MultisigPendingOperationsService
|
val multisigPendingOperationsService: MultisigPendingOperationsService
|
||||||
|
|
||||||
val multisigCallFormatter: MultisigCallFormatter
|
|
||||||
|
|
||||||
val automaticInteractionGate: AutomaticInteractionGate
|
val automaticInteractionGate: AutomaticInteractionGate
|
||||||
|
|
||||||
val linkBuilderFactory: LinkBuilderFactory
|
val linkBuilderFactory: LinkBuilderFactory
|
||||||
@@ -319,6 +317,8 @@ interface AssetsFeatureDependencies {
|
|||||||
|
|
||||||
fun chainRegistry(): ChainRegistry
|
fun chainRegistry(): ChainRegistry
|
||||||
|
|
||||||
|
fun globalConfigDataSource(): GlobalConfigDataSource
|
||||||
|
|
||||||
@Named(REMOTE_STORAGE_SOURCE)
|
@Named(REMOTE_STORAGE_SOURCE)
|
||||||
fun remoteStorageSource(): StorageDataSource
|
fun remoteStorageSource(): StorageDataSource
|
||||||
|
|
||||||
|
|||||||
-2
@@ -15,7 +15,6 @@ import io.novafoundation.nova.feature_crowdloan_api.di.CrowdloanFeatureApi
|
|||||||
import io.novafoundation.nova.feature_currency_api.di.CurrencyFeatureApi
|
import io.novafoundation.nova.feature_currency_api.di.CurrencyFeatureApi
|
||||||
import io.novafoundation.nova.feature_deep_linking.di.DeepLinkingFeatureApi
|
import io.novafoundation.nova.feature_deep_linking.di.DeepLinkingFeatureApi
|
||||||
import io.novafoundation.nova.feature_gift_api.di.GiftFeatureApi
|
import io.novafoundation.nova.feature_gift_api.di.GiftFeatureApi
|
||||||
import io.novafoundation.nova.feature_multisig_operations.di.MultisigOperationsFeatureApi
|
|
||||||
import io.novafoundation.nova.feature_nft_api.NftFeatureApi
|
import io.novafoundation.nova.feature_nft_api.NftFeatureApi
|
||||||
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
|
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
|
||||||
import io.novafoundation.nova.feature_swap_api.di.SwapFeatureApi
|
import io.novafoundation.nova.feature_swap_api.di.SwapFeatureApi
|
||||||
@@ -52,7 +51,6 @@ class AssetsFeatureHolder @Inject constructor(
|
|||||||
.deepLinkingFeatureApi(getFeature(DeepLinkingFeatureApi::class.java))
|
.deepLinkingFeatureApi(getFeature(DeepLinkingFeatureApi::class.java))
|
||||||
.chainMigrationFeatureApi(getFeature(ChainMigrationFeatureApi::class.java))
|
.chainMigrationFeatureApi(getFeature(ChainMigrationFeatureApi::class.java))
|
||||||
.giftFeatureApi(getFeature(GiftFeatureApi::class.java))
|
.giftFeatureApi(getFeature(GiftFeatureApi::class.java))
|
||||||
.multisigOperationsFeatureApi(getFeature(MultisigOperationsFeatureApi::class.java))
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return DaggerAssetsFeatureComponent.factory()
|
return DaggerAssetsFeatureComponent.factory()
|
||||||
|
|||||||
+138
@@ -1,8 +1,10 @@
|
|||||||
package io.novafoundation.nova.feature_assets.domain.bridge.multisig
|
package io.novafoundation.nova.feature_assets.domain.bridge.multisig
|
||||||
|
|
||||||
import io.novafoundation.nova.common.address.AccountIdKey
|
import io.novafoundation.nova.common.address.AccountIdKey
|
||||||
|
import io.novafoundation.nova.common.address.fromHexOrNull
|
||||||
import io.novafoundation.nova.common.address.intoKey
|
import io.novafoundation.nova.common.address.intoKey
|
||||||
import io.novafoundation.nova.common.address.toHexWithPrefix
|
import io.novafoundation.nova.common.address.toHexWithPrefix
|
||||||
|
import io.novafoundation.nova.common.data.config.GlobalConfigDataSource
|
||||||
import io.novafoundation.nova.common.data.network.runtime.binding.WeightV2
|
import io.novafoundation.nova.common.data.network.runtime.binding.WeightV2
|
||||||
import io.novafoundation.nova.common.di.scope.FeatureScope
|
import io.novafoundation.nova.common.di.scope.FeatureScope
|
||||||
import io.novafoundation.nova.common.utils.callHash
|
import io.novafoundation.nova.common.utils.callHash
|
||||||
@@ -10,7 +12,10 @@ import io.novafoundation.nova.feature_account_api.data.ethereum.transaction.Tran
|
|||||||
import io.novafoundation.nova.feature_account_api.data.extrinsic.ExtrinsicService
|
import io.novafoundation.nova.feature_account_api.data.extrinsic.ExtrinsicService
|
||||||
import io.novafoundation.nova.feature_account_api.data.extrinsic.execution.ExtrinsicExecutionResult
|
import io.novafoundation.nova.feature_account_api.data.extrinsic.execution.ExtrinsicExecutionResult
|
||||||
import io.novafoundation.nova.feature_account_api.data.extrinsic.execution.requireOk
|
import io.novafoundation.nova.feature_account_api.data.extrinsic.execution.requireOk
|
||||||
|
import io.novafoundation.nova.feature_account_api.data.multisig.model.MultisigTimePoint
|
||||||
import io.novafoundation.nova.feature_account_api.domain.interfaces.SelectedAccountUseCase
|
import io.novafoundation.nova.feature_account_api.domain.interfaces.SelectedAccountUseCase
|
||||||
|
import io.novafoundation.nova.feature_account_api.domain.model.MetaAccount
|
||||||
|
import io.novafoundation.nova.feature_account_api.domain.multisig.CallHash
|
||||||
import io.novafoundation.nova.feature_account_api.domain.multisig.intoCallHash
|
import io.novafoundation.nova.feature_account_api.domain.multisig.intoCallHash
|
||||||
import io.novafoundation.nova.runtime.di.REMOTE_STORAGE_SOURCE
|
import io.novafoundation.nova.runtime.di.REMOTE_STORAGE_SOURCE
|
||||||
import io.novafoundation.nova.runtime.ext.ChainGeneses
|
import io.novafoundation.nova.runtime.ext.ChainGeneses
|
||||||
@@ -18,6 +23,8 @@ import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
|||||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||||
import io.novafoundation.nova.runtime.multiNetwork.getRuntime
|
import io.novafoundation.nova.runtime.multiNetwork.getRuntime
|
||||||
import io.novafoundation.nova.runtime.storage.source.StorageDataSource
|
import io.novafoundation.nova.runtime.storage.source.StorageDataSource
|
||||||
|
import io.novasama.substrate_sdk_android.runtime.definitions.types.fromHexOrNull
|
||||||
|
import io.novasama.substrate_sdk_android.runtime.definitions.types.generics.GenericCall
|
||||||
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
|
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -31,6 +38,19 @@ data class BridgeSignerState(
|
|||||||
val approvalsSoFar: Int,
|
val approvalsSoFar: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** A pending Multisig.as_multi call against one of the bridge's own multisig accounts (a real
|
||||||
|
* user swap someone else submitted - NOT this wallet's own renewal signature, see
|
||||||
|
* BridgeSignerState for that), that this signatory hasn't approved yet. [call] is null when the
|
||||||
|
* off-chain indexer hasn't got the call content for this hash yet (or lookup failed) - callers
|
||||||
|
* must treat that as "cannot show/sign this one", never fall back to hash-only approval. */
|
||||||
|
data class PendingBridgeApproval(
|
||||||
|
val chain: Chain,
|
||||||
|
val callHash: CallHash,
|
||||||
|
val timePoint: MultisigTimePoint,
|
||||||
|
val approvalsCount: Int,
|
||||||
|
val call: GenericCall.Instance?,
|
||||||
|
)
|
||||||
|
|
||||||
interface BridgeMultisigInteractor {
|
interface BridgeMultisigInteractor {
|
||||||
|
|
||||||
/** Null if the currently selected wallet isn't one of the 5 known bridge signatories - the
|
/** Null if the currently selected wallet isn't one of the 5 known bridge signatories - the
|
||||||
@@ -66,6 +86,19 @@ interface BridgeMultisigInteractor {
|
|||||||
/** Same as getWusdtRemainingAllowance but for the automation key's real USDT approval on
|
/** Same as getWusdtRemainingAllowance but for the automation key's real USDT approval on
|
||||||
* Polkadot Asset Hub - gates wUSDT->USDT withdrawal auto-pay. */
|
* Polkadot Asset Hub - gates wUSDT->USDT withdrawal auto-pay. */
|
||||||
suspend fun getPolkadotUsdtRemainingAllowance(): BigInteger
|
suspend fun getPolkadotUsdtRemainingAllowance(): BigInteger
|
||||||
|
|
||||||
|
/** Every pending swap-approval call (on either chain) against the bridge's own multisig
|
||||||
|
* accounts that this signatory hasn't approved yet - empty if the selected wallet isn't one
|
||||||
|
* of the 5 known signatories. Unlike the app's generic multisig-operations feature (which
|
||||||
|
* only tracks accounts formally added as a `MultisigMetaAccount`), this queries the bridge's
|
||||||
|
* hardcoded multisig addresses directly, since individual signatories use their own regular
|
||||||
|
* wallet to sign here - they never "are" the multisig account itself. */
|
||||||
|
suspend fun getPendingApprovals(): List<PendingBridgeApproval>
|
||||||
|
|
||||||
|
/** Approves (contributes this wallet's signature to) an existing pending call. Refuses to
|
||||||
|
* proceed if [PendingBridgeApproval.call] is null - approving a call whose content this
|
||||||
|
* device can't verify would be blind-signing, never acceptable for a multisig approval. */
|
||||||
|
suspend fun submitApproval(approval: PendingBridgeApproval): Result<ExtrinsicExecutionResult>
|
||||||
}
|
}
|
||||||
|
|
||||||
@FeatureScope
|
@FeatureScope
|
||||||
@@ -74,6 +107,8 @@ class RealBridgeMultisigInteractor @Inject constructor(
|
|||||||
private val selectedAccountUseCase: SelectedAccountUseCase,
|
private val selectedAccountUseCase: SelectedAccountUseCase,
|
||||||
@Named(REMOTE_STORAGE_SOURCE) private val storageDataSource: StorageDataSource,
|
@Named(REMOTE_STORAGE_SOURCE) private val storageDataSource: StorageDataSource,
|
||||||
private val extrinsicService: ExtrinsicService,
|
private val extrinsicService: ExtrinsicService,
|
||||||
|
private val bridgeMultisigOperationsApi: BridgeMultisigOperationsApi,
|
||||||
|
private val globalConfigDataSource: GlobalConfigDataSource,
|
||||||
) : BridgeMultisigInteractor {
|
) : BridgeMultisigInteractor {
|
||||||
|
|
||||||
override suspend fun getSignerState(): BridgeSignerState? {
|
override suspend fun getSignerState(): BridgeSignerState? {
|
||||||
@@ -132,6 +167,109 @@ class RealBridgeMultisigInteractor @Inject constructor(
|
|||||||
return queryRemainingAllowance(chain, BridgeMultisigConstants.POLKADOT_USDT_ASSET_ID, BridgeMultisigConstants.AUTOMATION_KEY_ADDRESS_POLKADOT)
|
return queryRemainingAllowance(chain, BridgeMultisigConstants.POLKADOT_USDT_ASSET_ID, BridgeMultisigConstants.AUTOMATION_KEY_ADDRESS_POLKADOT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getPendingApprovals(): List<PendingBridgeApproval> {
|
||||||
|
val metaAccount = selectedAccountUseCase.getSelectedMetaAccount()
|
||||||
|
|
||||||
|
return listOf(
|
||||||
|
ChainGeneses.PEZKUWI_ASSET_HUB to BridgeMultisigConstants.MULTISIG_ADDRESS,
|
||||||
|
ChainGeneses.POLKADOT_ASSET_HUB to BridgeMultisigConstants.MULTISIG_ADDRESS_POLKADOT,
|
||||||
|
).flatMap { (chainGenesis, multisigAddress) ->
|
||||||
|
runCatching { getPendingApprovalsFor(chainGenesis, multisigAddress, metaAccount) }.getOrElse { emptyList() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getPendingApprovalsFor(
|
||||||
|
chainGenesis: String,
|
||||||
|
multisigAddress: String,
|
||||||
|
metaAccount: MetaAccount,
|
||||||
|
): List<PendingBridgeApproval> {
|
||||||
|
val chain = chainRegistry.getChain(chainGenesis)
|
||||||
|
val myAccountId = metaAccount.accountIdIn(chain)?.intoKey() ?: return emptyList()
|
||||||
|
|
||||||
|
// Only the 5 known bridge signatories can ever have anything to approve here - same gate
|
||||||
|
// as getSignerStateFor, matching how the rest of this screen already scopes itself.
|
||||||
|
val isKnownSignatory = BridgeMultisigConstants.SIGNATORIES.any { it.address.toAccountId().intoKey() == myAccountId }
|
||||||
|
if (!isKnownSignatory) return emptyList()
|
||||||
|
|
||||||
|
val multisigAccountId = multisigAddress.toAccountId().intoKey()
|
||||||
|
|
||||||
|
val keys = storageDataSource.query(chain.id) {
|
||||||
|
runtime.metadata.bridgeMultisig().multisigs.keys(multisigAccountId)
|
||||||
|
}
|
||||||
|
if (keys.isEmpty()) return emptyList()
|
||||||
|
|
||||||
|
val entries = storageDataSource.query(chain.id) {
|
||||||
|
runtime.metadata.bridgeMultisig().multisigs.entries(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
val notYetApprovedByMe = entries.filterNot { (_, onChainMultisig) -> myAccountId in onChainMultisig.approvals }
|
||||||
|
if (notYetApprovedByMe.isEmpty()) return emptyList()
|
||||||
|
|
||||||
|
val callHashes = notYetApprovedByMe.keys.map { it.second }
|
||||||
|
val callDataByHash = fetchCallData(chain, multisigAccountId, callHashes)
|
||||||
|
|
||||||
|
return notYetApprovedByMe.map { (key, onChainMultisig) ->
|
||||||
|
val callHash = key.second
|
||||||
|
PendingBridgeApproval(
|
||||||
|
chain = chain,
|
||||||
|
callHash = callHash,
|
||||||
|
timePoint = onChainMultisig.timePoint,
|
||||||
|
approvalsCount = onChainMultisig.approvals.size,
|
||||||
|
call = callDataByHash[callHash],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun fetchCallData(
|
||||||
|
chain: Chain,
|
||||||
|
multisigAccountId: AccountIdKey,
|
||||||
|
callHashes: List<CallHash>,
|
||||||
|
): Map<CallHash, GenericCall.Instance?> {
|
||||||
|
return runCatching {
|
||||||
|
val globalConfig = globalConfigDataSource.getGlobalConfig()
|
||||||
|
val request = BridgeOffChainCallDataRequest(multisigAccountId, callHashes, chain.id)
|
||||||
|
val response = bridgeMultisigOperationsApi.getCallDatas(globalConfig.multisigsApiUrl, request)
|
||||||
|
val runtime = chainRegistry.getRuntime(chain.id)
|
||||||
|
|
||||||
|
response.data.multisigOperations.nodes.mapNotNull { node ->
|
||||||
|
val hash = CallHash.fromHexOrNull(node.callHash) ?: return@mapNotNull null
|
||||||
|
val call = node.callData?.let { GenericCall.fromHexOrNull(runtime, it) }
|
||||||
|
hash to call
|
||||||
|
}.toMap()
|
||||||
|
}.getOrElse { emptyMap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun submitApproval(approval: PendingBridgeApproval): Result<ExtrinsicExecutionResult> = runCatching {
|
||||||
|
val call = requireNotNull(approval.call) {
|
||||||
|
"Cannot approve a call whose content is unknown - refusing to blind-sign"
|
||||||
|
}
|
||||||
|
|
||||||
|
val metaAccount = selectedAccountUseCase.getSelectedMetaAccount()
|
||||||
|
val myAccountId = requireNotNull(metaAccount.accountIdIn(approval.chain)?.intoKey()) {
|
||||||
|
"Selected account has no address on ${approval.chain.name}"
|
||||||
|
}
|
||||||
|
|
||||||
|
val otherSignatories = BridgeMultisigConstants.SIGNATORIES
|
||||||
|
.map { it.address.toAccountId().intoKey() }
|
||||||
|
.filter { it != myAccountId }
|
||||||
|
.sortedBy { it.toHexWithPrefix() }
|
||||||
|
|
||||||
|
extrinsicService.submitExtrinsicAndAwaitExecution(
|
||||||
|
chain = approval.chain,
|
||||||
|
origin = TransactionOrigin.WalletWithId(metaAccount.id)
|
||||||
|
) {
|
||||||
|
val multisigCall = runtime.composeBridgeMultisigAsMulti(
|
||||||
|
threshold = BridgeMultisigConstants.THRESHOLD,
|
||||||
|
otherSignatories = otherSignatories,
|
||||||
|
maybeTimePoint = approval.timePoint,
|
||||||
|
call = call,
|
||||||
|
maxWeight = WeightV2(BigInteger.valueOf(1_000_000_000L), BigInteger.valueOf(200_000L)),
|
||||||
|
)
|
||||||
|
|
||||||
|
call(multisigCall)
|
||||||
|
}.getOrThrow().requireOk()
|
||||||
|
}
|
||||||
|
|
||||||
/** Shared by both legs - the only differences between the wUSDT (Pezkuwi) and USDT (Polkadot)
|
/** Shared by both legs - the only differences between the wUSDT (Pezkuwi) and USDT (Polkadot)
|
||||||
* renewal flows are which chain/asset/automation-key-address/threshold to use, the actual
|
* renewal flows are which chain/asset/automation-key-address/threshold to use, the actual
|
||||||
* on-chain call shape (Assets.approve_transfer wrapped in Multisig.as_multi) is identical. */
|
* on-chain call shape (Assets.approve_transfer wrapped in Multisig.as_multi) is identical. */
|
||||||
|
|||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
package io.novafoundation.nova.feature_assets.domain.bridge.multisig
|
||||||
|
|
||||||
|
import io.novafoundation.nova.common.address.AccountIdKey
|
||||||
|
import io.novafoundation.nova.common.address.toHexWithPrefix
|
||||||
|
import io.novafoundation.nova.common.data.network.subquery.SubQueryFilters
|
||||||
|
import io.novafoundation.nova.common.data.network.subquery.SubQueryNodes
|
||||||
|
import io.novafoundation.nova.common.data.network.subquery.SubQueryResponse
|
||||||
|
import io.novafoundation.nova.common.utils.HexString
|
||||||
|
import io.novafoundation.nova.feature_account_api.domain.multisig.CallHash
|
||||||
|
import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId
|
||||||
|
import io.novasama.substrate_sdk_android.extensions.requireHexPrefix
|
||||||
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.POST
|
||||||
|
import retrofit2.http.Url
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal, Bridge-screen-local mirror of feature-account-impl's `FindMultisigsApi.getCallDatas`/
|
||||||
|
* `OffChainPendingMultisigInfoRequest`/`GetPedingMultisigOperationsResponse` - same module-
|
||||||
|
* boundary reason as BridgeMultisigRuntimeApi.kt (feature-assets can't depend on
|
||||||
|
* feature-account-impl). Only `getCallDatas` is mirrored - `findMultisigs` isn't needed here
|
||||||
|
* since the bridge's multisig account is already known (BridgeMultisigConstants), not discovered.
|
||||||
|
*/
|
||||||
|
interface BridgeMultisigOperationsApi {
|
||||||
|
|
||||||
|
@POST
|
||||||
|
suspend fun getCallDatas(
|
||||||
|
@Url url: String,
|
||||||
|
@Body body: BridgeOffChainCallDataRequest
|
||||||
|
): SubQueryResponse<BridgeCallDataResponse>
|
||||||
|
}
|
||||||
|
|
||||||
|
class BridgeOffChainCallDataRequest(
|
||||||
|
accountIdKey: AccountIdKey,
|
||||||
|
callHashes: Collection<CallHash>,
|
||||||
|
chainId: ChainId
|
||||||
|
) : SubQueryFilters {
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private val callHashesHex = callHashes.map { it.toHexWithPrefix() }
|
||||||
|
|
||||||
|
val query = """
|
||||||
|
query {
|
||||||
|
multisigOperations(filter: {
|
||||||
|
${"accountId" equalTo accountIdKey.toHexWithPrefix() }
|
||||||
|
${"status" equalToEnum "pending"}
|
||||||
|
${"callHash" presentIn callHashesHex}
|
||||||
|
${"chainId" equalTo chainId.requireHexPrefix()}
|
||||||
|
}) {
|
||||||
|
nodes {
|
||||||
|
callHash
|
||||||
|
callData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
class BridgeCallDataResponse(val multisigOperations: SubQueryNodes<BridgeOperationRemote>) {
|
||||||
|
|
||||||
|
class BridgeOperationRemote(val callHash: HexString, val callData: HexString?)
|
||||||
|
}
|
||||||
-3
@@ -11,7 +11,6 @@ import io.novafoundation.nova.feature_assets.presentation.tokens.add.enterInfo.A
|
|||||||
import io.novafoundation.nova.feature_assets.presentation.tokens.manage.chain.ManageChainTokensPayload
|
import io.novafoundation.nova.feature_assets.presentation.tokens.manage.chain.ManageChainTokensPayload
|
||||||
import io.novafoundation.nova.feature_assets.presentation.trade.webInterface.TradeWebPayload
|
import io.novafoundation.nova.feature_assets.presentation.trade.webInterface.TradeWebPayload
|
||||||
import io.novafoundation.nova.feature_assets.presentation.transaction.filter.TransactionHistoryFilterPayload
|
import io.novafoundation.nova.feature_assets.presentation.transaction.filter.TransactionHistoryFilterPayload
|
||||||
import io.novafoundation.nova.feature_multisig_operations.presentation.details.general.MultisigOperationDetailsPayload
|
|
||||||
import io.novafoundation.nova.feature_swap_api.presentation.model.SwapSettingsPayload
|
import io.novafoundation.nova.feature_swap_api.presentation.model.SwapSettingsPayload
|
||||||
import io.novafoundation.nova.feature_wallet_api.presentation.model.AssetPayload
|
import io.novafoundation.nova.feature_wallet_api.presentation.model.AssetPayload
|
||||||
|
|
||||||
@@ -121,8 +120,6 @@ interface AssetsRouter {
|
|||||||
|
|
||||||
fun openPendingMultisigOperations()
|
fun openPendingMultisigOperations()
|
||||||
|
|
||||||
fun openMultisigOperationDetails(payload: MultisigOperationDetailsPayload)
|
|
||||||
|
|
||||||
fun openAssetDetailsFromDeepLink(payload: AssetPayload)
|
fun openAssetDetailsFromDeepLink(payload: AssetPayload)
|
||||||
|
|
||||||
fun openGifts()
|
fun openGifts()
|
||||||
|
|||||||
+2
-6
@@ -222,12 +222,8 @@ class BridgeFragment : BaseFragment<BridgeViewModel, FragmentBridgeBinding>() {
|
|||||||
val rowBinding = ItemPendingSignatureRowBinding.inflate(container.inflater(), container, false)
|
val rowBinding = ItemPendingSignatureRowBinding.inflate(container.inflater(), container, false)
|
||||||
|
|
||||||
rowBinding.pendingSignatureChainIcon.loadChainIcon(model.chain.icon, imageLoader)
|
rowBinding.pendingSignatureChainIcon.loadChainIcon(model.chain.icon, imageLoader)
|
||||||
// primaryValue (e.g. "100K USDT") is the amount - the whole point of this row - so it
|
rowBinding.pendingSignatureTitle.text = model.amountText
|
||||||
// takes the prominent title line; the call's own title/action name ("Transfer") isn't
|
rowBinding.pendingSignatureSubtitle.text = "${model.destinationText} • ${model.chain.name} • ${model.progress}"
|
||||||
// shown at all here, only the destination (subtitle) is, to keep the row to two lines.
|
|
||||||
rowBinding.pendingSignatureTitle.text = model.primaryValue ?: model.title
|
|
||||||
rowBinding.pendingSignatureSubtitle.text = listOfNotNull(model.subtitle, model.chain.name, model.progress)
|
|
||||||
.joinToString(separator = " • ")
|
|
||||||
rowBinding.pendingSignatureSignButton.setOnClickListener { viewModel.pendingSignatureSignClicked(model) }
|
rowBinding.pendingSignatureSignButton.setOnClickListener { viewModel.pendingSignatureSignClicked(model) }
|
||||||
|
|
||||||
container.addView(rowBinding.root)
|
container.addView(rowBinding.root)
|
||||||
|
|||||||
+39
-30
@@ -10,35 +10,28 @@ import io.novafoundation.nova.common.utils.formatting.format
|
|||||||
import io.novafoundation.nova.common.utils.images.Icon
|
import io.novafoundation.nova.common.utils.images.Icon
|
||||||
import io.novafoundation.nova.common.view.ButtonState
|
import io.novafoundation.nova.common.view.ButtonState
|
||||||
import io.novafoundation.nova.feature_account_api.data.mappers.mapChainToUi
|
import io.novafoundation.nova.feature_account_api.data.mappers.mapChainToUi
|
||||||
import io.novafoundation.nova.feature_account_api.data.multisig.MultisigPendingOperationsService
|
|
||||||
import io.novafoundation.nova.feature_account_api.data.multisig.model.MultisigAction
|
|
||||||
import io.novafoundation.nova.feature_account_api.data.multisig.model.PendingMultisigOperation
|
|
||||||
import io.novafoundation.nova.feature_account_api.data.multisig.model.userAction
|
|
||||||
import io.novafoundation.nova.feature_account_api.domain.interfaces.SelectedAccountUseCase
|
|
||||||
import io.novafoundation.nova.feature_account_api.domain.model.MetaAccount
|
|
||||||
import io.novafoundation.nova.feature_account_api.domain.model.requireAccountIdKeyIn
|
|
||||||
import io.novafoundation.nova.feature_account_api.presenatation.chain.getAssetIconOrFallback
|
import io.novafoundation.nova.feature_account_api.presenatation.chain.getAssetIconOrFallback
|
||||||
import io.novafoundation.nova.feature_assets.R
|
import io.novafoundation.nova.feature_assets.R
|
||||||
import io.novafoundation.nova.feature_assets.domain.WalletInteractor
|
import io.novafoundation.nova.feature_assets.domain.WalletInteractor
|
||||||
import io.novafoundation.nova.feature_assets.domain.bridge.multisig.BridgeMultisigConstants
|
import io.novafoundation.nova.feature_assets.domain.bridge.multisig.BridgeMultisigConstants
|
||||||
import io.novafoundation.nova.feature_assets.domain.bridge.multisig.BridgeMultisigInteractor
|
import io.novafoundation.nova.feature_assets.domain.bridge.multisig.BridgeMultisigInteractor
|
||||||
import io.novafoundation.nova.feature_assets.domain.bridge.multisig.BridgeSignerState
|
import io.novafoundation.nova.feature_assets.domain.bridge.multisig.BridgeSignerState
|
||||||
|
import io.novafoundation.nova.feature_assets.domain.bridge.multisig.PendingBridgeApproval
|
||||||
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
|
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
|
||||||
import io.novafoundation.nova.feature_assets.presentation.bridge.execution.BridgeExecutionPayload
|
import io.novafoundation.nova.feature_assets.presentation.bridge.execution.BridgeExecutionPayload
|
||||||
import io.novafoundation.nova.feature_multisig_operations.presentation.callFormatting.MultisigCallFormatter
|
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.assets.AssetSourceRegistry
|
||||||
import io.novafoundation.nova.feature_multisig_operations.presentation.common.MultisigOperationPayload
|
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.assets.tranfers.tryParseTransfer
|
||||||
import io.novafoundation.nova.feature_multisig_operations.presentation.common.fromOperationId
|
import io.novafoundation.nova.feature_wallet_api.domain.model.amountFromPlanks
|
||||||
import io.novafoundation.nova.feature_multisig_operations.presentation.details.general.MultisigOperationDetailsPayload
|
|
||||||
import io.novafoundation.nova.runtime.ext.ChainGeneses
|
import io.novafoundation.nova.runtime.ext.ChainGeneses
|
||||||
import io.novafoundation.nova.runtime.ext.addressOf
|
import io.novafoundation.nova.runtime.ext.addressOf
|
||||||
import io.novafoundation.nova.runtime.ext.displayNameWithAssetStandard
|
import io.novafoundation.nova.runtime.ext.displayNameWithAssetStandard
|
||||||
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
||||||
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
|
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
|
import java.text.NumberFormat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DOT<->HEZ used to be a second pair here, retired 2026-07 in favor of the multisig-custodied
|
* DOT<->HEZ used to be a second pair here, retired 2026-07 in favor of the multisig-custodied
|
||||||
@@ -62,9 +55,7 @@ class BridgeViewModel(
|
|||||||
private val assetIconProvider: AssetIconProvider,
|
private val assetIconProvider: AssetIconProvider,
|
||||||
private val walletInteractor: WalletInteractor,
|
private val walletInteractor: WalletInteractor,
|
||||||
private val bridgeMultisigInteractor: BridgeMultisigInteractor,
|
private val bridgeMultisigInteractor: BridgeMultisigInteractor,
|
||||||
private val multisigPendingOperationsService: MultisigPendingOperationsService,
|
private val assetSourceRegistry: AssetSourceRegistry,
|
||||||
private val multisigCallFormatter: MultisigCallFormatter,
|
|
||||||
private val selectedAccountUseCase: SelectedAccountUseCase,
|
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -483,32 +474,50 @@ class BridgeViewModel(
|
|||||||
|
|
||||||
fun refreshPendingSignatures() {
|
fun refreshPendingSignatures() {
|
||||||
launch {
|
launch {
|
||||||
val account = selectedAccountUseCase.getSelectedMetaAccount()
|
val models = bridgeMultisigInteractor.getPendingApprovals()
|
||||||
|
.mapNotNull { it.toPendingSignatureUiOrNull() }
|
||||||
val models = multisigPendingOperationsService.pendingOperations().first()
|
|
||||||
.filter { it.userAction() is MultisigAction.CanApprove }
|
|
||||||
.map { it.toPendingSignatureUi(account) }
|
|
||||||
|
|
||||||
_pendingSignatures.postValue(models)
|
_pendingSignatures.postValue(models)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var pendingApprovalSubmission: Job? = null
|
||||||
|
|
||||||
fun pendingSignatureSignClicked(model: PendingSignatureModel) {
|
fun pendingSignatureSignClicked(model: PendingSignatureModel) {
|
||||||
val operationPayload = MultisigOperationPayload.fromOperationId(model.id)
|
if (pendingApprovalSubmission?.isActive == true) return
|
||||||
router.openMultisigOperationDetails(MultisigOperationDetailsPayload(operationPayload))
|
|
||||||
|
pendingApprovalSubmission = launch {
|
||||||
|
bridgeMultisigInteractor.submitApproval(model.approval)
|
||||||
|
.onFailure { showError(it.message ?: resourceManager.getString(R.string.bridge_sign_error)) }
|
||||||
|
|
||||||
|
refreshPendingSignatures()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun PendingMultisigOperation.toPendingSignatureUi(selectedAccount: MetaAccount): PendingSignatureModel {
|
/** Null whenever the call's actual content couldn't be recovered or isn't a plain transfer
|
||||||
val initialOrigin = selectedAccount.requireAccountIdKeyIn(chain)
|
* this screen knows how to preview - never shown/approvable in that case (see
|
||||||
val formattedCall = multisigCallFormatter.formatPreview(call, initialOrigin, chain)
|
* BridgeMultisigInteractor.submitApproval's own refusal to blind-sign for why this isn't
|
||||||
|
* just a display-only gap: an unparseable row would have no safe "Sign" action anyway). */
|
||||||
|
private suspend fun PendingBridgeApproval.toPendingSignatureUiOrNull(): PendingSignatureModel? {
|
||||||
|
val call = call ?: return null
|
||||||
|
|
||||||
|
val assetId = if (chain.id == ChainGeneses.POLKADOT_ASSET_HUB) {
|
||||||
|
BridgeMultisigConstants.POLKADOT_USDT_ASSET_ID
|
||||||
|
} else {
|
||||||
|
BridgeMultisigConstants.WUSDT_ASSET_ID
|
||||||
|
}
|
||||||
|
val asset = chain.assetsById[assetId] ?: return null
|
||||||
|
val parsed = assetSourceRegistry.sourceFor(asset).transfers.tryParseTransfer(call, chain) ?: return null
|
||||||
|
|
||||||
|
val decimalAmount = asset.amountFromPlanks(parsed.amount.amount)
|
||||||
|
val amountText = "${NumberFormat.getNumberInstance().format(decimalAmount)} ${asset.symbol.value}"
|
||||||
|
|
||||||
return PendingSignatureModel(
|
return PendingSignatureModel(
|
||||||
id = operationId,
|
approval = this,
|
||||||
chain = mapChainToUi(chain),
|
chain = mapChainToUi(chain),
|
||||||
title = formattedCall.title,
|
amountText = amountText,
|
||||||
subtitle = formattedCall.subtitle,
|
destinationText = chain.addressOf(parsed.destination),
|
||||||
primaryValue = formattedCall.primaryValue,
|
progress = resourceManager.getString(R.string.multisig_operations_progress, approvalsCount.format(), BridgeMultisigConstants.THRESHOLD.format())
|
||||||
progress = resourceManager.getString(R.string.multisig_operations_progress, approvals.size.format(), threshold.format())
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-5
@@ -1,13 +1,12 @@
|
|||||||
package io.novafoundation.nova.feature_assets.presentation.bridge
|
package io.novafoundation.nova.feature_assets.presentation.bridge
|
||||||
|
|
||||||
import io.novafoundation.nova.feature_account_api.data.multisig.model.PendingMultisigOperationId
|
|
||||||
import io.novafoundation.nova.feature_account_api.presenatation.chain.ChainUi
|
import io.novafoundation.nova.feature_account_api.presenatation.chain.ChainUi
|
||||||
|
import io.novafoundation.nova.feature_assets.domain.bridge.multisig.PendingBridgeApproval
|
||||||
|
|
||||||
data class PendingSignatureModel(
|
data class PendingSignatureModel(
|
||||||
val id: PendingMultisigOperationId,
|
val approval: PendingBridgeApproval,
|
||||||
val chain: ChainUi,
|
val chain: ChainUi,
|
||||||
val title: String,
|
val amountText: String,
|
||||||
val subtitle: String?,
|
val destinationText: String,
|
||||||
val primaryValue: CharSequence?,
|
|
||||||
val progress: String,
|
val progress: String,
|
||||||
)
|
)
|
||||||
|
|||||||
+22
-10
@@ -6,19 +6,21 @@ import androidx.lifecycle.ViewModelProvider
|
|||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.multibindings.IntoMap
|
import dagger.multibindings.IntoMap
|
||||||
|
import io.novafoundation.nova.common.data.config.GlobalConfigDataSource
|
||||||
|
import io.novafoundation.nova.common.data.network.NetworkApiCreator
|
||||||
import io.novafoundation.nova.common.di.viewmodel.ViewModelKey
|
import io.novafoundation.nova.common.di.viewmodel.ViewModelKey
|
||||||
import io.novafoundation.nova.common.di.viewmodel.ViewModelModule
|
import io.novafoundation.nova.common.di.viewmodel.ViewModelModule
|
||||||
import io.novafoundation.nova.common.presentation.AssetIconProvider
|
import io.novafoundation.nova.common.presentation.AssetIconProvider
|
||||||
import io.novafoundation.nova.common.resources.ResourceManager
|
import io.novafoundation.nova.common.resources.ResourceManager
|
||||||
import io.novafoundation.nova.feature_account_api.data.extrinsic.ExtrinsicService
|
import io.novafoundation.nova.feature_account_api.data.extrinsic.ExtrinsicService
|
||||||
import io.novafoundation.nova.feature_account_api.data.multisig.MultisigPendingOperationsService
|
|
||||||
import io.novafoundation.nova.feature_account_api.domain.interfaces.SelectedAccountUseCase
|
import io.novafoundation.nova.feature_account_api.domain.interfaces.SelectedAccountUseCase
|
||||||
import io.novafoundation.nova.feature_assets.domain.WalletInteractor
|
import io.novafoundation.nova.feature_assets.domain.WalletInteractor
|
||||||
import io.novafoundation.nova.feature_assets.domain.bridge.multisig.BridgeMultisigInteractor
|
import io.novafoundation.nova.feature_assets.domain.bridge.multisig.BridgeMultisigInteractor
|
||||||
|
import io.novafoundation.nova.feature_assets.domain.bridge.multisig.BridgeMultisigOperationsApi
|
||||||
import io.novafoundation.nova.feature_assets.domain.bridge.multisig.RealBridgeMultisigInteractor
|
import io.novafoundation.nova.feature_assets.domain.bridge.multisig.RealBridgeMultisigInteractor
|
||||||
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
|
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
|
||||||
import io.novafoundation.nova.feature_assets.presentation.bridge.BridgeViewModel
|
import io.novafoundation.nova.feature_assets.presentation.bridge.BridgeViewModel
|
||||||
import io.novafoundation.nova.feature_multisig_operations.presentation.callFormatting.MultisigCallFormatter
|
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.assets.AssetSourceRegistry
|
||||||
import io.novafoundation.nova.runtime.di.REMOTE_STORAGE_SOURCE
|
import io.novafoundation.nova.runtime.di.REMOTE_STORAGE_SOURCE
|
||||||
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
||||||
import io.novafoundation.nova.runtime.storage.source.StorageDataSource
|
import io.novafoundation.nova.runtime.storage.source.StorageDataSource
|
||||||
@@ -27,14 +29,28 @@ import javax.inject.Named
|
|||||||
@Module(includes = [ViewModelModule::class])
|
@Module(includes = [ViewModelModule::class])
|
||||||
class BridgeModule {
|
class BridgeModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideBridgeMultisigOperationsApi(apiCreator: NetworkApiCreator): BridgeMultisigOperationsApi {
|
||||||
|
return apiCreator.create(BridgeMultisigOperationsApi::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
fun provideBridgeMultisigInteractor(
|
fun provideBridgeMultisigInteractor(
|
||||||
chainRegistry: ChainRegistry,
|
chainRegistry: ChainRegistry,
|
||||||
selectedAccountUseCase: SelectedAccountUseCase,
|
selectedAccountUseCase: SelectedAccountUseCase,
|
||||||
@Named(REMOTE_STORAGE_SOURCE) storageDataSource: StorageDataSource,
|
@Named(REMOTE_STORAGE_SOURCE) storageDataSource: StorageDataSource,
|
||||||
extrinsicService: ExtrinsicService
|
extrinsicService: ExtrinsicService,
|
||||||
|
bridgeMultisigOperationsApi: BridgeMultisigOperationsApi,
|
||||||
|
globalConfigDataSource: GlobalConfigDataSource,
|
||||||
): BridgeMultisigInteractor {
|
): BridgeMultisigInteractor {
|
||||||
return RealBridgeMultisigInteractor(chainRegistry, selectedAccountUseCase, storageDataSource, extrinsicService)
|
return RealBridgeMultisigInteractor(
|
||||||
|
chainRegistry,
|
||||||
|
selectedAccountUseCase,
|
||||||
|
storageDataSource,
|
||||||
|
extrinsicService,
|
||||||
|
bridgeMultisigOperationsApi,
|
||||||
|
globalConfigDataSource,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -47,9 +63,7 @@ class BridgeModule {
|
|||||||
assetIconProvider: AssetIconProvider,
|
assetIconProvider: AssetIconProvider,
|
||||||
walletInteractor: WalletInteractor,
|
walletInteractor: WalletInteractor,
|
||||||
bridgeMultisigInteractor: BridgeMultisigInteractor,
|
bridgeMultisigInteractor: BridgeMultisigInteractor,
|
||||||
multisigPendingOperationsService: MultisigPendingOperationsService,
|
assetSourceRegistry: AssetSourceRegistry,
|
||||||
multisigCallFormatter: MultisigCallFormatter,
|
|
||||||
selectedAccountUseCase: SelectedAccountUseCase,
|
|
||||||
): ViewModel {
|
): ViewModel {
|
||||||
return BridgeViewModel(
|
return BridgeViewModel(
|
||||||
router,
|
router,
|
||||||
@@ -58,9 +72,7 @@ class BridgeModule {
|
|||||||
assetIconProvider,
|
assetIconProvider,
|
||||||
walletInteractor,
|
walletInteractor,
|
||||||
bridgeMultisigInteractor,
|
bridgeMultisigInteractor,
|
||||||
multisigPendingOperationsService,
|
assetSourceRegistry,
|
||||||
multisigCallFormatter,
|
|
||||||
selectedAccountUseCase,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user