Skip to content

feat(keys): add SeedPhraseKey.load() and CryptoProviderKey identifier storage#83

Merged
jaymengxy merged 1 commit into
mainfrom
fix-provider-wallet
Mar 3, 2026
Merged

feat(keys): add SeedPhraseKey.load() and CryptoProviderKey identifier storage#83
jaymengxy merged 1 commit into
mainfrom
fix-provider-wallet

Conversation

@jaymengxy
Copy link
Copy Markdown
Contributor

Overview

This PR introduces two additions to the keys module that decouple key material from the Account object, enabling key recovery even after an account-cache loss.


Changes

1. SeedPhraseKey — static load() factory

// Before: get() is an instance method, requires a live SeedPhraseKey object
// After: static factory callable from anywhere
val key: SeedPhraseKey? = SeedPhraseKey.load(id, password, storage)

Companion.load(id, password, storage) decrypts and reconstructs a SeedPhraseKey directly from a StorageProtocol without needing an existing instance. Returns null on miss or decryption failure (never throws).


2. CryptoProviderKey — companion identifier storage helpers

Three static methods for persisting an opaque provider identifier (e.g. an Android Keystore alias, key prefix, slot number) independently of the account cache:

Method Description
saveProviderIdentifier(id, identifier, password, storage) Encrypt identifier with ChaCha20-Poly1305 and write to storage
getProviderIdentifier(id, password, storage): String? Retrieve and decrypt; returns null on miss or error
hasProviderIdentifier(id, storage): Boolean Lightweight existence check (no decryption)

Typical lifecycle (Android Keystore example):

// At registration — persist the key identifier once:
CryptoProviderKey.saveProviderIdentifier(uid, keystorePrefix, password, akpStorage)

// At wallet creation — reconstruct provider and create wallet:
val prefix   = CryptoProviderKey.getProviderIdentifier(uid, password, akpStorage) ?: return
val provider = AndroidKeystoreCryptoProvider(prefix)   // app-layer concrete type
val wallet   = WalletFactory.createProxyWallet(provider, setOf(Mainnet, Testnet), storage)

The same pattern works for any future CryptoProvider implementation (Ledger, HSM, custom signers, etc.).


3. AndroidKeystoreKey — removed

AndroidKeystoreKey was a KeyProtocol subclass whose only consumed functionality was three static storage helpers. Those helpers are now provided by CryptoProviderKey.companion, making the class redundant. All call sites updated to use CryptoProviderKey.saveProviderIdentifier / getProviderIdentifier / hasProviderIdentifier.


4. settings.gradle — AGP bump

Android Gradle Plugin 8.6.18.8.2.


Test Plan

  • SeedPhraseKey.load() returns a valid reconstructed key after store()
  • SeedPhraseKey.load() returns null for an unknown id
  • CryptoProviderKey.getProviderIdentifier() returns the original string after saveProviderIdentifier()
  • CryptoProviderKey.hasProviderIdentifier() returns false before save, true after
  • Decryption with a wrong password returns null (no exception propagated)
  • No references to the deleted AndroidKeystoreKey remain in any call site

🤖 Generated with Claude Code

… storage

- SeedPhraseKey: add static Companion.load(id, password, storage) factory
  method that decrypts and reconstructs a SeedPhraseKey without requiring
  an existing instance, enabling key recovery independent of Account cache

- CryptoProviderKey: add companion object with three static helpers for
  persisting an opaque provider identifier (e.g. Android Keystore prefix)
  independently of the Account cache:
    saveProviderIdentifier(id, identifier, password, storage)
    getProviderIdentifier(id, password, storage): String?
    hasProviderIdentifier(id, storage): Boolean
  Identifier is encrypted with ChaCha20-Poly1305 via ChaChaPolyCipher.
  Removes AndroidKeystoreKey (superseded by CryptoProviderKey companion).

- settings.gradle: bump Android Gradle Plugin to 8.8.2

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 3, 2026

PR Summary

Enhanced the keys module with static factory methods for key recovery independent of account cache. Added SeedPhraseKey.load() companion method for reconstructing seed phrase keys from storage without existing instances. Introduced CryptoProviderKey companion object with encrypted storage helpers for provider identifiers (e.g., Android Keystore aliases), enabling crypto provider reconstruction after cache loss. Also updated Android Gradle Plugin to version 8.8.2.

Changes

File Summary
Android/wallet/settings.gradle Bumped Android Gradle Plugin from version 8.6.1 to 8.8.2 in the plugin management configuration.
Android/wallet/src/main/java/com/flow/wallet/keys/CryptoProviderKey.kt Enhanced class documentation and added companion object with three static methods: saveProviderIdentifier(), getProviderIdentifier(), and hasProviderIdentifier(). These methods provide encrypted storage for opaque provider identifiers using ChaCha20-Poly1305 encryption, enabling crypto provider reconstruction after account cache loss.
Android/wallet/src/main/java/com/flow/wallet/keys/SeedPhraseKey.kt Added companion object with load() static factory method that reconstructs a SeedPhraseKey from storage without requiring an existing instance. The method decrypts stored key data and returns null on failure instead of throwing exceptions, enabling key recovery independent of account cache.

autogenerated by presubmit.ai

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Review Summary

Commits Considered (1)
  • 4e8c186: feat(keys): add SeedPhraseKey.load() and CryptoProviderKey identifier storage

  • SeedPhraseKey: add static Companion.load(id, password, storage) factory
    method that decrypts and reconstructs a SeedPhraseKey without requiring
    an existing instance, enabling key recovery independent of Account cache

  • CryptoProviderKey: add companion object with three static helpers for
    persisting an opaque provider identifier (e.g. Android Keystore prefix)
    independently of the Account cache:
    saveProviderIdentifier(id, identifier, password, storage)
    getProviderIdentifier(id, password, storage): String?
    hasProviderIdentifier(id, storage): Boolean
    Identifier is encrypted with ChaCha20-Poly1305 via ChaChaPolyCipher.
    Removes AndroidKeystoreKey (superseded by CryptoProviderKey companion).

  • settings.gradle: bump Android Gradle Plugin to 8.8.2

Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com

Files Processed (3)
  • Android/wallet/settings.gradle (1 hunk)
  • Android/wallet/src/main/java/com/flow/wallet/keys/CryptoProviderKey.kt (2 hunks)
  • Android/wallet/src/main/java/com/flow/wallet/keys/SeedPhraseKey.kt (1 hunk)
Actionable Comments (0)
Skipped Comments (5)
  • Android/wallet/src/main/java/com/flow/wallet/keys/CryptoProviderKey.kt [138-138]

    security: "Password should be processed through a key derivation function."

  • Android/wallet/src/main/java/com/flow/wallet/keys/CryptoProviderKey.kt [156-156]

    security: "Password should be processed through a key derivation function."

  • Android/wallet/src/main/java/com/flow/wallet/keys/SeedPhraseKey.kt [72-72]

    security: "Password should be processed through a key derivation function."

  • Android/wallet/src/main/java/com/flow/wallet/keys/CryptoProviderKey.kt [157-158]

    maintainability: "Exception handling could benefit from logging for debugging."

  • Android/wallet/src/main/java/com/flow/wallet/keys/SeedPhraseKey.kt [76-77]

    maintainability: "Exception handling could benefit from logging for debugging."

@jaymengxy jaymengxy merged commit b8f3dc2 into main Mar 3, 2026
1 of 2 checks passed
@jaymengxy jaymengxy deleted the fix-provider-wallet branch March 3, 2026 00:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant