Skip to content

Multisig Feature#522

Merged
mitsinsar merged 114 commits intodevfrom
multisig/parent
Jan 30, 2026
Merged

Multisig Feature#522
mitsinsar merged 114 commits intodevfrom
multisig/parent

Conversation

@mitsinsar
Copy link
Collaborator

This is a parent branch of multisig feature. Branches has been merged in the order below;
#9 -> #8 -> #7 -> #6 -> #5 -> #4 -> #0

  • Resolved dev conflicts
  • Changed account type with AccountType class, instead of LocalAccount.Type
  • Removed joint account from swap flow
  • Updated signature endpoints to send array instead of single item

- Add AI-assisted development story document
- Add backend feedback documentation
- Add QA testing guide for joint account flows
- Add code review guide for reviewers
- Add happy paths documentation with limitations
- Add joint account related string resources
- Add multisig icon drawable (ic_multisig.xml)
- Add navigation graph entries for joint account screens
- Add menu resources for joint account actions
- Add JointAccountRepository with API integration
- Add JointSignRequest API use cases
- Add JointAccountInbox API use cases
- Add domain models for joint accounts (LocalAccount.Joint)
- Add DTOs for API communication
- Implement account type detection for joint accounts
- Implement joint account import deep link parser
- Add deep link navigation to joint account screens
- Add unit tests for deep link builders
- Add unit tests for joint account mappers
- Add unit tests for repository implementations
…onse

- Delete AssetInboxRequestMapperImplTest.kt
- Delete AssetInboxRepositoryImplTest.kt
- Delete AssetInboxCacheManagerImplTest.kt

These tests reference AssetInboxRequestResponse which no longer exists.
- Fix duplicate DI bindings for joint account repository
- Add missing interface implementations
- Update transaction signer handling for joint accounts
- Fix import statements and code organization
- Add inbox data models for joint account notifications
- Add sign request transaction list models
- Add participant signature DTOs
- Implement inbox cache manager for joint accounts
- Add inbox item mappers and transformers
- Update AccountIconResource with JOINT type
- Add joint account badge to account list items
- Update account detail screens for joint accounts
- Add joint account icon drawable preview
- Update account type detection in existing flows
- Add TransactionSigner.Joint handling
- Fix duplicate DI bindings for joint account repository
- Add missing interface implementations
- Update transaction signer handling for joint accounts
- Fix import statements and code organization
- Add CreateJointAccountFragment and ViewModel
- Add NameJointAccountFragment for account naming
- Add AddJointAccountFragment for participant selection
- Implement JointAccountTransactionSignHelper
- Add CreateJointAccountUseCase
- Add participant selection UI components
- Handle contact creation for external addresses
- Update AccountAssetsFragment to use onInboxClick
- Update AccountAssetsAccountDetailAdapter to call listener.onInboxClick
- Update AccountDetailQuickActionsView to match Inbox sealed class
- Fix duplicate DI bindings for joint account repository
- Add missing interface implementations
- Update transaction signer handling for joint accounts
- Fix import statements and code organization
- Add CreateJointAccountFragment and ViewModel
- Add NameJointAccountFragment for account naming
- Add AddJointAccountFragment for participant selection
- Implement JointAccountTransactionSignHelper
- Add CreateJointAccountUseCase
- Add participant selection UI components
- Handle contact creation for external addresses
- Add AllAccountsInboxFragment with unified inbox view
- Add sign request notification items to inbox
- Implement JointAccountInvitationDetailFragment
- Add inbox preview mappers for different notification types
- Handle inbox item click navigation
- Add badge indicators for pending requests
This commit adds complete joint account functionality including:

SDK Layer (common-sdk):
- Local data layer: JointEntity, JointDao, JointMapper, JointAccountRepository
- Account core: AccountType.Joint, TransactionSigner.Joint, AddJointAccountUseCase
- Inbox module: InboxRepository, InboxCacheManager, AssetInboxRepository
- JointAccount module: JointAccountRepository, JointSignRequest handling
- DeepLink support for joint account imports
- Tests for all SDK modules

App Layer:
- Joint Account Detail screen with participant management
- Joint Account Creation flow (Add, Create, Name, Edit, SetThreshold screens)
- Joint Account Transaction signing flow with pending signatures UI
- Inbox screen for joint account invitations
- Add Account Intro screen
- Joint Account Info dialog
- Transaction signing helpers for joint accounts
- Resources, navigation, and integration updates

Documentation:
- AI_ASSISTED_DEVELOPMENT_STORY.md
- CODE_REVIEW_GUIDE.md
- JOINT_ACCOUNT_HAPPY_PATHS.md
- QA_JOINT_ACCOUNT_TESTING.md
Add new rule sections based on PR review learnings:
- Resource & Build Rules (theme-aware icons, string placeholders, ndk abiFilters)
- Data Layer Rules (interface+impl pattern, separate files, test updates)
- Cross-Module Architecture (internal data layer, public domain layer, app-sdk boundaries)
Per PR review comments:
- Make ic_wallet_add.xml theme-aware by using @color/text_main instead of hardcoded #F1F1F2
- Use format placeholder in transfer_to string for proper localization support
Per PR #511 review comment:
- Remove ndk config from defaultConfig
- Add arm64-v8a to debug build type for faster builds
- Add all architectures to release build type (no need to clear)
Per PR review comments:
- Split JointEntityMapper interface and impl into separate files
- Split JointMapper interface and impl into separate files
- Make CreateJointAccountDTOMapper internal with interface+impl pattern
- Make JointAccountDTOMapper internal with interface+impl pattern
- Make CreateJointAccountRequest internal
- Make JointAccountResponse internal
- Add DI bindings for new mapper interfaces
- Update tests to use Impl classes
Per PR review: Move canSignTransaction() function from companion
object to interface method with subclass implementations.

- Each AccountType subtype now overrides canSignTransaction()
- Removed companion object with extension function
- Updated all call sites to remove companion import
Per PR review comments:
- Remove GetJointAccount/GetJointAccountUseCase (use repository directly)
- Remove GetJointAccountParticipantCount/GetJointAccountParticipantCountUseCase
- Remove JointAccountDTO and CreateJointAccountDTO
- Remove JointAccountDTOMapper and CreateJointAccountDTOMapper
- Remove related tests
- Update DI module
…link

Resolves merge conflicts and applies data layer visibility rules:
- Restore DTOs (domain models needed by public repository interface)
- Make API service internal
- Make data layer mappers internal
- Make request/response models internal
- Rename JointAccountDTO → JointAccount
- Rename CreateJointAccountDTO → CreateJointAccountInput
- Rename JointSignRequestDTO → JointSignRequest
- Rename ParticipantSignatureDTO → ParticipantSignature
- Rename ProposeJointSignRequestDTO → CreateSignRequestInput
- Rename SearchSignRequestsDTO → SearchSignRequestsInput
- Rename SignRequestTransactionListResponseDTO → AddSignatureInput
- Rename SignRequestWithFullSignatureDTO → SignRequestWithFullSignature

Also rename related mappers to match new domain model names:
- ProposeJointSignRequestDTOMapper → CreateSignRequestInputMapper
- SearchSignRequestsDTOMapper → SearchSignRequestsInputMapper
- SignRequestTransactionListResponseDTOMapper → AddSignatureInputMapper

Update all usages across repository, use cases, and tests.
Mock JointAccountDTOMapper in JointSignRequestMapperTest per PR review.
Use companion object for test constants per PR review.
Create JointAccountTransactionUseCases.kt containing all use case
interfaces (AddJointAccountSignature, GetSignRequestWithSignatures,
ProposeJointSignRequest) per PR review feedback.

Keep implementation classes in their own files.
Per PR review, use cases that just delegate to repository are simplified:
- AddJointAccountSignature: provided directly via DI lambda
- ProposeJointSignRequest: provided directly via DI lambda
- GetSignRequestWithSignatures: mapping logic moved to repository

Changes:
- Add getSignRequestWithSignatures() to JointAccountRepository
- Move mapping logic to JointAccountRepositoryImpl
- Remove searchSignRequests() from public interface (internal only)
- Delete use case implementation classes
- Delete use case tests (logic now covered by repository)
- Update DI module to provide interfaces via lambdas
Follow project rule: "One public class/interface per file"
New rules based on PR review feedback:

Data Layer:
- Data models (Request/Response) must be internal

Domain Layer:
- Domain models do NOT use DTO suffix
- Use descriptive input/output naming (*Input suffix)

UseCase Pattern:
- Avoid unnecessary use case implementations
- Provide simple use cases via DI lambda
- Only create implementation when there's business logic

Unit Test Best Practices:
- Mock ALL dependencies (don't test multiple classes)
- Use companion object for test constants
- Use .copy() for test variations
yasin-ce and others added 21 commits January 26, 2026 20:27
* multisig/07-creation-flow:
  fix: Remove @nAmed annotation referencing removed INJECTION_NAME
- Remove unnecessary @singleton from InboxRepositoryModule
- Use JointAccountValidationException.MIN_PARTICIPANTS constant instead
  of hardcoded value in JointAccountInvitationInboxItemMapper
- Refactor InboxLastOpenedTimeLocalSource to use PersistentCache
…uestInboxItemMapper

- Replace ZonedDateTime.now() calls with TimeProvider for testability
- Use RelativeTimeDifference utility for calculating relative time strings
- Add time formatting string resources for short time display
…oxScreenListener

- Fragment now implements InboxScreenListener directly
- Move isJointAccountImportHandled flag to ViewModel for state persistence
- Add jointAccountInvitationToOpen and jointAccountAddressToOpen events
- Remove unnecessary comments
…iewModel

- Create JointAccountInvitationDetailViewModel for account display names and icons
- Create JointAccountInvitationDetailViewState to hold view state
- Refactor Fragment to use ViewModel instead of LaunchedEffect
- Remove @nAmed annotation for InboxApiRepository injection
- Use InboxScreen overload that accepts StateFlow instead of ViewModel
- Remove unnecessary mock ViewModel construction and reflection
- Significantly reduce preview code complexity
…rovider

- Add DI provider for InboxLastOpenedTimeLocalSource in InboxRepositoryModule
- Remove @Inject from InboxLastOpenedTimeLocalSource constructor
- Delete duplicate unused class in sharedpref package
- Create InboxViewState sealed interface (Loading, Empty, Content, Error)
- Create InboxViewEvent sealed interface for navigation events
- Update InboxViewModel to use StateDelegate and EventDelegate
- Add InboxViewStateMapper for creating view states
- Update InboxFragment to observe viewEvent for navigation
- Update InboxScreen to use InboxViewState
- Keep backward compatible InboxPreview overload for previews
- Use sealed interface ViewState pattern for JointAccountInvitationDetail
- Use TimeProvider instead of ZonedDateTime.now() in mappers
- Optimize AccountsPreviewUseCase to use getJointAccountInboxCountFlow()
- Add SQLite JDBC force resolution in build.gradle.kts
- Update .cursorrules with PR review feedback patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* multisig/08-inbox-pages:
  fix: Address PR review feedback and add coding rules
  refactor: Implement ViewState/ViewEvent pattern for Inbox
  refactor: Fix InboxLastOpenedTimeLocalSource DI with PersistentCacheProvider
  refactor: Simplify InboxScreenPreview by using StateFlow overload
  refactor: Move account display logic to JointAccountInvitationDetailViewModel
  refactor: Move deep link handling flag to ViewModel and implement InboxScreenListener
  refactor: Use TimeProvider and RelativeTimeDifference in SignatureRequestInboxItemMapper
  fix: Address PR #518 review comments
  fix: Remove @nAmed annotation referencing removed INJECTION_NAME

# Conflicts:
#	app/src/main/kotlin/com/algorand/android/modules/inbox/allaccounts/ui/mapper/SignatureRequestInboxItemMapper.kt
- Use sealed interface ViewState pattern for JointAccountInvitationDetail
- Use TimeProvider instead of ZonedDateTime.now() in mappers
- Optimize AccountsPreviewUseCase to use getJointAccountInboxCountFlow()
- Add SQLite JDBC force resolution in build.gradle.kts
- Update .cursorrules with PR review feedback patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…android into multisig/08-inbox-pages

* 'multisig/08-inbox-pages' of github.com:perawallet/pera-android:
  fix: Address PR review feedback and add coding rules
* multisig/08-inbox-pages:
  fix: Address PR review feedback and add coding rules
# Conflicts:
#	.cursorrules
#	app/src/main/kotlin/com/algorand/android/core/transaction/external/ExternalTransactionSignManager.kt
#	app/src/main/kotlin/com/algorand/android/modules/accountcore/ui/usecase/GetAccountDetailSummaryUseCase.kt
#	app/src/main/kotlin/com/algorand/android/modules/accountcore/ui/usecase/GetAccountIconDrawablePreviewByTypeUseCase.kt
#	app/src/main/kotlin/com/algorand/android/modules/accountdetail/accountstatusdetail/ui/decider/AccountStatusDetailPreviewDecider.kt
#	app/src/main/kotlin/com/algorand/android/modules/accountdetail/assets/ui/domain/DefaultAccountDetailAccountsItemProcessor.kt
#	app/src/main/kotlin/com/algorand/android/modules/accounts/domain/usecase/AccountDetailSummaryUseCase.kt
#	app/src/main/kotlin/com/algorand/android/modules/deeplink/ui/DeeplinkHandler.kt
#	app/src/main/kotlin/com/algorand/android/modules/rekey/rekeytoledgeraccount/instruction/ui/decider/RekeyToLedgerAccountPreviewDecider.kt
#	app/src/main/kotlin/com/algorand/android/modules/rekey/rekeytostandardaccount/instruction/ui/decider/RekeyToStandardAccountIntroductionPreviewDecider.kt
#	app/src/main/kotlin/com/algorand/android/modules/walletconnect/domain/usecase/GetWalletConnectTransactionSignerUseCase.kt
#	app/src/main/res/values/strings.xml
#	common-sdk/schemas/com.algorand.wallet.account.local.data.database.AddressDatabase/2.json
#	common-sdk/src/main/kotlin/com/algorand/wallet/account/local/data/database/AddressDatabase.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/account/local/data/database/dao/JointDao.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/account/local/data/database/model/JointEntity.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/account/local/data/mapper/entity/JointEntityMapper.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/account/local/data/mapper/entity/JointEntityMapperImpl.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/account/local/data/mapper/model/JointMapper.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/account/local/data/mapper/model/JointMapperImpl.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/account/local/data/repository/JointAccountRepositoryImpl.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/account/local/di/LocalAccountsModule.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/account/local/domain/repository/JointAccountRepository.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/account/local/domain/usecase/GetLocalAccountsFlowUseCase.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/account/local/domain/usecase/GetLocalAccountsUseCase.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/algosdk/transaction/sdk/SignHdKeyTransaction.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/algosdk/transaction/sdk/SignHdKeyTransactionImpl.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/asset/assetinbox/data/mapper/AssetInboxRequestMapperImpl.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/inbox/domain/model/InboxSearchInput.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/jointaccount/data/repository/JointAccountRepositoryImpl.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/jointaccount/di/JointAccountModule.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/jointaccount/domain/repository/JointAccountRepository.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/jointaccount/transaction/data/mapper/JointSignRequestDTOMapper.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/jointaccount/transaction/data/model/JointSignRequestResponse.kt
#	common-sdk/src/main/kotlin/com/algorand/wallet/jointaccount/transaction/domain/model/JointSignRequest.kt
#	common-sdk/src/test/kotlin/com/algorand/wallet/account/local/data/mapper/entity/JointEntityMapperImplTest.kt
#	common-sdk/src/test/kotlin/com/algorand/wallet/account/local/data/mapper/model/JointMapperImplTest.kt
#	common-sdk/src/test/kotlin/com/algorand/wallet/account/local/data/repository/JointAccountRepositoryImplTest.kt
#	common-sdk/src/test/kotlin/com/algorand/wallet/jointaccount/transaction/data/mapper/JointSignRequestMapperTest.kt
@mitsinsar mitsinsar self-assigned this Jan 30, 2026
@mitsinsar mitsinsar marked this pull request as ready for review January 30, 2026 12:03
@mitsinsar mitsinsar merged commit 3d185a3 into dev Jan 30, 2026
2 checks passed
@mitsinsar mitsinsar deleted the multisig/parent branch January 30, 2026 12:06
@github-actions github-actions bot locked and limited conversation to collaborators Jan 30, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants