mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-07-22 20:45:53 +00:00
Support first-signer case for /open/multisigOperation deep link
The pending-operation model assumed a call already exists on-chain (Multisig.Multisigs entry with a Timepoint) before it could be reviewed or signed. A signatory who is the first to see a brand-new call - reachable via the deep link before anyone has submitted anything - hit "operation not found" and the screen refused to open, even though the deep link already carried the call's data in its callData param (previously decoded only to format Executed/Rejected dialog text, then discarded for the Active case that needed it). - PendingMultisigOperation.timePoint/depositor are now nullable to represent "not yet submitted"; composeMultisigAsMulti's maybeTimePoint parameter already accepted null (used for ordinary first-time multisig-origin submissions via MultisigSigner), so the pallet-call layer needed no changes. - New PendingMultisigOperation.notYetSubmitted(...) factory and MultisigOperationDetailsInteractor.buildNotYetSubmittedOperation() build a synthetic operation from the deep link's callData, hash verified against the URL's callHash rather than trusted blindly. - OperationIsStillPendingValidation now skips its on-chain "still pending" check for a not-yet-submitted operation, since it would otherwise always fail and block the very first submission. - MultisigOperationPayload carries the callData through so both the details screen and the full-details screen (independently reachable via "Call Details", since that button is available whenever call data is known) can construct the synthetic operation.
This commit is contained in:
+53
-2
@@ -4,6 +4,7 @@ import io.novafoundation.nova.common.address.AccountIdKey
|
||||
import io.novafoundation.nova.common.address.toHex
|
||||
import io.novafoundation.nova.common.utils.Identifiable
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.MetaAccount
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.MultisigMetaAccount
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.addressIn
|
||||
import io.novafoundation.nova.feature_account_api.domain.multisig.CallHash
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
@@ -18,9 +19,24 @@ class PendingMultisigOperation(
|
||||
val call: GenericCall.Instance?,
|
||||
val callHash: CallHash,
|
||||
val chain: Chain,
|
||||
val timePoint: MultisigTimePoint,
|
||||
/**
|
||||
* Null means this call has never been submitted on-chain yet (no `Multisig.Multisigs` entry
|
||||
* exists) - the "first signer" case reachable via a `/open/multisigOperation` deep link
|
||||
* before anyone has signed. `composeMultisigAsMulti`'s `maybeTimePoint` parameter already
|
||||
* accepts null for exactly this (see `MultisigSigner.wrapCallsInAsMulti`, which uses the
|
||||
* same null-timepoint-for-first-submission pattern for ordinary multisig-origin calls) -
|
||||
* this model just didn't have a way to represent that state before.
|
||||
*/
|
||||
val timePoint: MultisigTimePoint?,
|
||||
val approvals: List<AccountIdKey>,
|
||||
val depositor: AccountIdKey,
|
||||
/**
|
||||
* Null for the same not-yet-submitted case as [timePoint] - nobody has deposited/proposed
|
||||
* this call yet, so there is no depositor. [userAction] already handles this correctly:
|
||||
* comparing [signatoryAccountId] to a null depositor is simply never true, so a signatory
|
||||
* viewing a not-yet-submitted call is never offered "Reject" (rejecting something that was
|
||||
* never proposed makes no sense - only cancel_as_multi on an *existing* operation does).
|
||||
*/
|
||||
val depositor: AccountIdKey?,
|
||||
val deposit: BigInteger,
|
||||
val signatoryAccountId: AccountIdKey,
|
||||
val signatoryMetaId: Long,
|
||||
@@ -30,6 +46,9 @@ class PendingMultisigOperation(
|
||||
|
||||
val operationId = PendingMultisigOperationId(multisigMetaId, chain.id, callHash.toHex())
|
||||
|
||||
val isSubmittedOnChain: Boolean
|
||||
get() = timePoint != null
|
||||
|
||||
override val identifier: String = operationId.identifier()
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -78,3 +97,35 @@ fun PendingMultisigOperation.Companion.createOperationHash(metaAccount: MetaAcco
|
||||
fun PendingMultisigOperationId.Companion.create(metaAccount: MetaAccount, chain: Chain, callHash: String): PendingMultisigOperationId {
|
||||
return PendingMultisigOperationId(metaAccount.id, chain.id, callHash)
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a synthetic [PendingMultisigOperation] for a call that has never been submitted
|
||||
* on-chain - the deep-link "first signer" case (see `MultisigOperationDetailsDeepLinkHandler`
|
||||
* and `RealMultisigOperationDetailsInteractor.buildNotYetSubmittedOperation`). Unlike
|
||||
* `PendingMultisigOperation.from` (used by the chain-storage-driven syncer), this never touches
|
||||
* chain state - everything it needs (threshold, other signatories) is already known locally
|
||||
* from the already-added [MultisigMetaAccount], and [call] comes from the deep link's `callData`
|
||||
* param, whose hash the caller must already have verified matches the link's `callHash`.
|
||||
*/
|
||||
fun PendingMultisigOperation.Companion.notYetSubmitted(
|
||||
multisigMetaAccount: MultisigMetaAccount,
|
||||
call: GenericCall.Instance,
|
||||
callHash: CallHash,
|
||||
chain: Chain,
|
||||
timestamp: Duration,
|
||||
): PendingMultisigOperation {
|
||||
return PendingMultisigOperation(
|
||||
multisigMetaId = multisigMetaAccount.id,
|
||||
call = call,
|
||||
callHash = callHash,
|
||||
chain = chain,
|
||||
timePoint = null,
|
||||
approvals = emptyList(),
|
||||
depositor = null,
|
||||
deposit = BigInteger.ZERO,
|
||||
signatoryAccountId = multisigMetaAccount.signatoryAccountId,
|
||||
signatoryMetaId = multisigMetaAccount.signatoryMetaId,
|
||||
threshold = multisigMetaAccount.threshold,
|
||||
timestamp = timestamp,
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ fun TableCellView.showWallet(walletModel: WalletModel) {
|
||||
walletModel.icon?.let(::setImage)
|
||||
}
|
||||
|
||||
fun TableCellView.showAccountWithLoading(loadingState: ExtendedLoadingState<AccountModel>) {
|
||||
fun TableCellView.showAccountWithLoading(loadingState: ExtendedLoadingState<AccountModel?>) {
|
||||
showLoadingState(loadingState) {
|
||||
showAccount(it)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user