diff --git a/.DS_Store b/.DS_Store index a2a4a785..dcca6016 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/CHANGELOG.md b/CHANGELOG.md index f12c0fce..cae59d2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,45 +1 @@ # MRT Wallet Changelog - -## Version 4.0.0 - -### Bitcoin (mainnet, testnet): -- **Features:** Comprehensive support for Bitcoin transactions. -- **Highlights:** - - Robust support for P2TR, P2WPKH, P2WSH, P2SH, P2PKH, and multisignature functionalities. - - Enhanced control over transactions, allowing you to effortlessly create, sign, and send transactions from multiple UTXOs and addresses. - -### Bitcoin Cash (mainnet, testnet): -- **Features:** Comprehensive support for Bitcoin Cash transactions. -- **Highlights:** - - P2SH, P2SH32, P2PKH and token-aware addresses. - - Enhanced control over transactions, supporting the creation, signing, minting and sending of Cash tokens. - -### Dogecoin: -- **Features:** Seamless transaction creation and management on the Dogecoin network. -- **Highlights:** Extends capabilities to the playful realm of Dogecoin, ensuring a smooth and secure user experience. - -### Dash: -- **Features:** Exploration of the fast and efficient world of Dash. -- **Highlights:** Easy creation, signing, and sending of transactions with support for various Dash network functionalities. - -### Bitcoin SV: -- **Features:** Effortless navigation of Bitcoin SV transactions. -- **Highlights:** - - User-friendly interface for smooth and secure transaction creation and management. - - Support for handling P2PKH addresses, simplifying the Bitcoin SV experience. - -### Litecoin: -- **Features:** Support for Litecoin transactions. -- **Highlights:** Dive into the silver to Bitcoin's gold with MRT Wallet, managing Litecoin transactions effortlessly with confidence. - -### Ripple: -- **Features:** Unlocking the potential of the Ripple network. -- **Highlights:** Comprehensive support for advanced cryptographic algorithms, NFTs, tokens, multisignature transactions, account settings, trust settings, escrow transactions, regular key settings, and more. - -### Ethereum: -- **Features:** Support for Ethereum transactions. -- **Highlights:** Compatibility with both legacy and EIP-1559 transactions. Import and manage ERC-20 assets effortlessly, and execute ERC-20 token transfers with ease. - -### Tron: -- **Features:** Seamless interaction with the Tron blockchain. -- **Highlights:** Confidence in sending TRX, TRC-20, and TRC-10 tokens. Support for native contracts, including multi-signature transactions. Control over updating account permissions, managing accounts, unstaking (v2), delegating resources, and creating/updating witnesses. \ No newline at end of file diff --git a/README.md b/README.md index ea6f5101..6f6227a6 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,93 @@ # About MRT Wallet -Welcome to MRT Wallet, the open-source wallet designed for the decentralized future of finance. Our mission is to empower users with a secure and versatile solution that supports both Bitcoin mainnet and testnet, as well as networks such as -Ethereum, Tron, Ripple, Dogecoin, Litecoin, and Dash, with a roadmap set to embrace a broad spectrum of cryptocurrencies. +Welcome to MRT Wallet, the open-source wallet designed for the decentralized future of finance. Our mission is to empower users with a secure and versatile solution that supports both Bitcoin mainnet and testnet, as well as networks such as +Ethereum, Tron, Ripple, Dogecoin, Litecoin, Solana, Cardano, Cosmos and Dash, with a roadmap set to embrace a broad spectrum of cryptocurrencies. ## Networks -### Bitcoin (mainnet, testnet): +### Bitcoin (mainnet, testnet) + - **Features:** Comprehensive support for Bitcoin transactions. - **Highlights:** - Robust support for P2TR, P2WPKH, P2WSH, P2SH, P2PKH, and multisignature functionalities. - Enhanced control over transactions, allowing you to effortlessly create, sign, and send transactions from multiple UTXOs and addresses. + - Allows for multiple account transactions. + +### Bitcoin Cash (mainnet, testnet) -### Bitcoin Cash (mainnet, testnet): - **Features:** Comprehensive support for Bitcoin Cash transactions. - **Highlights:** - P2SH, P2SH32, P2PKH and token-aware addresses. - Enhanced control over transactions, supporting the creation, signing, minting and sending of Cash tokens. + - Allows for multiple account transactions. + +### Dogecoin -### Dogecoin: - **Features:** Seamless transaction creation and management on the Dogecoin network. - **Highlights:** Extends capabilities to the playful realm of Dogecoin, ensuring a smooth and secure user experience. +- Allows for multiple account transactions. + +### Dash -### Dash: - **Features:** Exploration of the fast and efficient world of Dash. - **Highlights:** Easy creation, signing, and sending of transactions with support for various Dash network functionalities. +- Allows for multiple account transactions. + +### Bitcoin SV -### Bitcoin SV: - **Features:** Effortless navigation of Bitcoin SV transactions. - **Highlights:** - User-friendly interface for smooth and secure transaction creation and management. - Support for handling P2PKH addresses, simplifying the Bitcoin SV experience. + - Allows for multiple account transactions. + +### Litecoin -### Litecoin: - **Features:** Support for Litecoin transactions. - **Highlights:** Dive into the silver to Bitcoin's gold with MRT Wallet, managing Litecoin transactions effortlessly with confidence. +- Allows for multiple account transactions. + +### Ripple -### Ripple: - **Features:** Unlocking the potential of the Ripple network. - **Highlights:** Comprehensive support for advanced cryptographic algorithms, NFTs, tokens, multisignature transactions, account settings, trust settings, escrow transactions, regular key settings, and more. -### Ethereum: +### Solana (Mainnet, Testnet) + +- **Features:** Seamless support for Solana transactions. +- **Highlights:** + - SPLToken transfer, account creation and token minting. + +### Cardano (Mainnet, Preproad) + +- **Features:** Streamlined support for Cardano transactions. +- **Highlights:** + - Full compatibility with Shelley and Byron addresses and transactions. + - Facilitates minting and transfer of assets. + - Integration for stake certificates. + - Allows for multiple account transactions. + +### Ethereum + - **Features:** Support for Ethereum transactions. - **Highlights:** Compatibility with both legacy and EIP-1559 transactions. Import and manage ERC-20 assets effortlessly, and execute ERC-20 token transfers with ease. -### Tron: +### Tron + - **Features:** Seamless interaction with the Tron blockchain. - **Highlights:** Confidence in sending TRX, TRC-20, and TRC-10 tokens. Support for native contracts, including multi-signature transactions. Control over updating account permissions, managing accounts, unstaking (v2), delegating resources, and creating/updating witnesses. +### Cosmos (Mainnet) + +- **Features:** Seamless support for Cosmos transactions. + +### Thor (Mainnet) + +- **Features:** Seamless support for Thor transactions. + +### Maya (Mainnet) + +- **Features:** Seamless support for Maya transactions. ## Decentralized, Secure, and Open Source @@ -54,7 +95,6 @@ At MRT Wallet, we believe in the power of decentralization. Our commitment to de We take pride in being fully open source. You can explore and audit our code on [GitHub](https://github.com/mrtnetwork/mrtwallet) under the Apache License 2.0. This commitment to transparency means that every line of code is accessible, empowering the community to verify the security and integrity of our wallet. - ## Platform Support MRT Wallet is available on multiple platforms to provide a seamless experience: @@ -63,6 +103,8 @@ MRT Wallet is available on multiple platforms to provide a seamless experience: - **Windows:** Enjoy the convenience of MRT Wallet on your desktop. Our Windows version brings the power of decentralized finance to your fingertips. +- **Macos:** Enjoy the convenience of MRT Wallet on your desktop. Our Mac version brings the power of decentralized finance to your fingertips. + - **Web:** Access your wallet from any web browser with our web platform. Manage your assets with ease, all while enjoying the security and privacy MRT Wallet provides. ## Roadmap: Embracing a Diverse Crypto Landscape @@ -75,59 +117,66 @@ MRT Wallet is not just a wallet; it's a community-driven project. We welcome col ## Build Instructions - Clone the repository and build using Flutter: -**WEB** +- **WEB** - you can view the web version of MRT Wallet at https://mrtnetwork.github.io/mrtwallet/. + you can view the web version of MRT Wallet at . -``` +```shell gh repo clone mrtnetwork/mrtwallet cd mrt_wallet flutter pub get flutter build web --release ``` -**Android** -``` +- **Android** + +```shell gh repo clone mrtnetwork/mrtwallet cd mrt_wallet flutter pub get -flutter build apk +flutter build apk ``` -**Windows** -``` +- **Windows** + +```shell gh repo clone mrtnetwork/mrtwallet cd mrt_wallet flutter pub get -flutter build windows +flutter build windows --release ``` +- **Macos** -## Contributing +```shell +gh repo clone mrtnetwork/mrtwallet +cd mrt_wallet +flutter pub get +flutter build macos --release +``` +## Contributing Contributions are welcome! Please follow these guidelines: - - Fork the repository and create a new branch. - - Make your changes and ensure tests pass. - - Submit a pull request with a detailed description of your changes. -## Feature requests and bugs +- Fork the repository and create a new branch. +- Make your changes and ensure tests pass. +- Submit a pull request with a detailed description of your changes. +## Feature requests and bugs Please file feature requests and bugs in the issue tracker. ## Get Involved - Join us on our mission to redefine the landscape of decentralized finance. Contribute to our open-source project on [GitHub](https://github.com/mrtnetwork/mrtwallet) or connect with our community on [Telegram](https://t.me/blockchain_web3_solidity). Thank you for choosing MRT Wallet as your trusted partner in the world of decentralized finance. - ## Support + 1KMRGUzRFCuR9y73gUnjxfC1Dte8Ua3vcp bc1q92fvc5jm4k8e5wegzmhzdv72gwe43sgfnuspgzfmj7llkd8xhmusgd44qf diff --git a/mrt_wallet/lib/app/constant/page_path.dart b/mrt_wallet/lib/app/constant/page_path.dart index 3243d05b..331d4bdb 100644 --- a/mrt_wallet/lib/app/constant/page_path.dart +++ b/mrt_wallet/lib/app/constant/page_path.dart @@ -27,13 +27,10 @@ class PagePathConst { static const String cardanoTransaction = "/cardano/transaction"; static const String cosmosTransaction = "/cosmos/transaction"; - static const String setupRippleAddress = "/ripple/setup_address"; + // static const String setupRippleAddress = "/ripple/setup_address"; static const String setupBitcoinAddress = "/bitcoin/setup_address"; - static const String setupEthAddress = "/ethereum/setup_address"; - static const String setupTronAddress = "/tron/setup_address"; - static const String setupSolanaAddress = "/solana/setup_address"; + static const String setupGenericAddress = "/networks/setup_address"; static const String setupCardanoAddress = "/cardano/setup_address"; - static const String setupCosmosAddress = "/cosmos/setup_address"; static const String setupBitcoinMultsig = "/bitcoin/setup_multisig_address"; @@ -78,17 +75,12 @@ class PagePathConst { static String setupAddressPage(AppNetworkImpl network) { if (network is AppBitcoinNetwork) return setupBitcoinAddress; - if (network is APPEVMNetwork) return setupEthAddress; - if (network is APPTVMNetwork) return setupTronAddress; - if (network is APPSolanaNetwork) return setupSolanaAddress; if (network is APPCardanoNetwork) return setupCardanoAddress; - if (network is APPCosmosNetwork) return setupCosmosAddress; - return setupRippleAddress; + return setupGenericAddress; } static const String importERC20Token = "ethereum/import_token"; static const String importTRC20Token = "tron/import_token"; static const String importTrc10Token = "tron/import_trc10_token"; - static const String importSPLTokens = "solana/import_spl_tokens"; } diff --git a/mrt_wallet/lib/app/error/exception/wallet_ex.dart b/mrt_wallet/lib/app/error/exception/wallet_ex.dart index 5eebefc4..aec5cf47 100644 --- a/mrt_wallet/lib/app/error/exception/wallet_ex.dart +++ b/mrt_wallet/lib/app/error/exception/wallet_ex.dart @@ -66,6 +66,8 @@ class WalletExceptionConst { WalletException("invalid_balance"); static final WalletException unsuportedFeature = WalletException("unsuported_feature"); + static final WalletException featureUnavailableForMultiSignature = + WalletException("feature__unavailable_for_multi_signature"); static final WalletException condition = WalletException("message"); static final WalletException emptyThrow = WalletException(""); diff --git a/mrt_wallet/lib/app/extention/context.dart b/mrt_wallet/lib/app/extention/context.dart index a476b937..94f4a03e 100644 --- a/mrt_wallet/lib/app/extention/context.dart +++ b/mrt_wallet/lib/app/extention/context.dart @@ -68,6 +68,7 @@ extension QuickContextAccsess on BuildContext { BodyBuilder? bodyBuilder, List appbarActions = const [], bool centerContent = true}) async { + if (!mounted) return null; return await showModalBottomSheet( context: this, constraints: const BoxConstraints(maxWidth: 900), @@ -93,6 +94,7 @@ extension QuickContextAccsess on BuildContext { return await showAdaptiveDialog( context: this, useRootNavigator: true, + barrierDismissible: true, builder: (context) { return DialogView( title: label, @@ -103,17 +105,22 @@ extension QuickContextAccsess on BuildContext { ); } - Future openDialogPage(WidgetContext child, String label, - {List Function(BuildContext)? content}) async { + Future openDialogPage( + String label, { + WidgetContext? child, + List Function(BuildContext)? content, + Widget? fullWidget, + }) async { return await showAdaptiveDialog( context: this, useRootNavigator: true, + barrierDismissible: true, builder: (context) { - return DialogView( - title: label, - content: content?.call(context) ?? const [], - child: child(context), - ); + return fullWidget ?? + DialogView( + title: label, + content: content?.call(context) ?? const [], + child: child?.call(context) ?? WidgetConstant.sizedBox); }, ); } diff --git a/mrt_wallet/lib/app/extention/string.dart b/mrt_wallet/lib/app/extention/string.dart index b06601dc..c6e225b2 100644 --- a/mrt_wallet/lib/app/extention/string.dart +++ b/mrt_wallet/lib/app/extention/string.dart @@ -25,6 +25,7 @@ extension Translate on String { } String get orEmpty => trim().isEmpty ? "value_is_empty".tr : this; + String or(String or) => trim().isEmpty ? or : this; String? get nullOnEmpty => trim().isEmpty ? null : this; String get to3Digits => AppStringUtility.to3Digits(this, separator: ","); } diff --git a/mrt_wallet/lib/app/localization/localization.dart b/mrt_wallet/lib/app/localization/localization.dart index 8432c9f7..db14e36c 100644 --- a/mrt_wallet/lib/app/localization/localization.dart +++ b/mrt_wallet/lib/app/localization/localization.dart @@ -77,7 +77,7 @@ class Localization { "unlock": "Unlock", "derive_network_address": "Deriving ___1__ Addresses", "generate_from_hd_wallet": - "Generating an address based on your HD wallet seed.", + "Creating an address from your HD wallet seed or imported keys.", "generate_from_imported_keys": "Generating an address based on your imported keys", "generate_from_imported_key_desc1": @@ -133,6 +133,7 @@ class Localization { "hardened_index": "Hardened key with an index of ___1__.", "path": "Path", "setup_derivation_path": "Setup Derivation path", + "setup_derivation": "Setup Derivation", "generate_address": "Generate address", "invalid_argruments": "Invalid arguments expected ___1__ but got ___2__", @@ -805,7 +806,7 @@ class Localization { "apply_for_condition": "Apply for condition.", "saved_fulfillment_desc": "Are you certain that the fulfillment and conditions have been securely saved?", - "ripple_key_type": "Ripple key type", + "key_algorithm": "Key algorithm", "invalid_ripple_privatekey_algorithm": "Invalid Ripple private key encryption algorithm.", "ed25519_support_derivation_desc": @@ -1422,8 +1423,7 @@ class Localization { "back_to_the_page": "Back to the page", "label": "Label", "metadatum_label": "Metadatum label", - "enther_valid_un_label": - "Please enter a valid unsigned number for the label.", + "enther_valid_un_label": "Please enter a valid unsigned number.", "label_already_exists": "The label you entered already exists.", "no_account_chosen": "No account has been chosen.", "associated_token_program": "Associated Token Program", @@ -1487,7 +1487,93 @@ class Localization { "The token name must be at least 3 characters long", "create_new_provider": "Create New Provider", "gnesis_hash_desc": - "The genesis hash is the unique identifier for the initial block in a blockchain network, often used for network bootstrapping and verifying network state" + "The genesis hash is the unique identifier for the initial block in a blockchain network, often used for network bootstrapping and verifying network state", + "policy_id": "Policy ID", + "utxos_amount": "UTXOs amount", + "create_a_new_token": "Create a new token", + "ada_asset_name_validator": "Please provide a name for the asset.", + "ada_create_new_token_desc": + "Kindly specify the asset name and total supply for your new token.", + "asset_name": "Asset name", + "name_of_token": "Name of token", + "total_supply_desc": + "Please specify the initial token supply amount.", + "setup_supply": "Setup supply", + "tap_to_input_total_supply": "Tap to input total supply", + "owner_of_token": "The owner of token", + "tap_to_select_owner": "Tap to select accout", + "setup_mint": "Setup mint", + "byron_does_not_support_minting_token": + "The Byron address does not support the initialization of minting tokens.", + "certificates": "Certificates", + "add_certificate_to_transaction": + "Add certificates to the transaction", + "tap_to_add_certificate": "Tap to add certificate", + "deregistration": "Deregistration", + "registration": "Registration", + "delegation": "Delegation", + "certificate_type": "Certificate type", + "add_certificate_to_transaction_desc": + "Please choose the certificate you would like to include in the transaction.", + "certificate": "Certificate", + "stake_address": "Stake address", + "setup_certificate": "Setup certificate", + "stake_registration": "Stake Registration", + "stake_address_validator": + "The account does not possess a stake address", + "stake_deregistration": "Stake Deregistration", + "stake_delegation": "Stake Delegation", + "deposit": "Deposit", + "transaction_deposits_list": "List of required transaction deposits", + "refund_deposit": "Refund deposit", + "unable_to_spend_from_stake_address": + "Unable to spend from a stake address.", + "cannot_send_ada_to_stake_address": + "Sending ADA or assets to a stake address is not allowed.", + "stake_key_derivation": "Stake key derivation", + "ada_base_stake_key_same_error": + "ensure that the base key and stake key are not generated using the same key.", + "feature__unavailable_for_multi_signature": + "The feature is unavailable for multi-signature addresses.", + "select_creation_type": "Select creation type", + "setup_address_derivation_keys_desc": + "In the setup derivation, you can select imported keys if they exist.", + "please_following_steps_to_generate_address": + "Please follow these steps to generate an address.", + "stake_key": "Stake key", + "main_key": "Main key", + "base_key": "Base key", + "switch_between_keys": + "Please switch between keys to view information about each one.", + "retrieve_the_public": + "Please wait while we retrieve the public key of the account.", + "public_keys": "Public keys", + "Invalid_coin_default_path": "Invalid coin default path", + "invalid_hd_wallet_derivation_path": + "Invalid HD wallet derivation path", + "derivation_path": "Derivation path", + "hd_wallet_path_max_indeqxes": + "only supports up to ___1__ HD wallet indexes.", + "hd_wallet_hardened_desc": + "For hardened indices, append ' or h to the end of the index.", + "imported_": "Imported(___1__)", + "hd_path": "HD Path", + "hd_path_key": "HD Path key", + "byron_legacy_hd_path_desc": + "Please set up the HD Path and HD Path key, or disable manual configuration to generate them automatically from the key.", + "invalid_byron_legacy_hd_path_key": + "Invalid byron legacy HD path key", + "byron_legacy_hd_wallet_length_desc": + "Byron Legacy only supports the first two HD wallet indexes, such as m/1/2.", + "byron_legacy_hd_path_key_desc": + "Invalid HD Path key bytes. Please provide the HD path key in hexadecimal format.", + "byron_legacy_hd_path_key_length_desc": + "The HD path key should be 32 bytes in length.", + "byron_legacy_hd_path_key_desc2": + "Please provide the HD path key in hexadecimal format.", + "manually_set_hd_path": "Manually set HD path details.", + "byron_legacy_hd_path_generate_from_master_key_desc": + "Do not toggle on for generating from master key.", } }; } diff --git a/mrt_wallet/lib/app/utility/blockchain_constant/constant.dart b/mrt_wallet/lib/app/utility/blockchain_constant/constant.dart new file mode 100644 index 00000000..a573add7 --- /dev/null +++ b/mrt_wallet/lib/app/utility/blockchain_constant/constant.dart @@ -0,0 +1,4 @@ +class BlockchainConstant { + static const int maxBip32LevelIndex = 5; + static const int maxByronLegacyBip32LevelIndex = 2; +} diff --git a/mrt_wallet/lib/app/utility/blockchin_utils/blockchaain_utils.dart b/mrt_wallet/lib/app/utility/blockchin_utils/blockchaain_utils.dart index a8cd1671..0ae053d9 100644 --- a/mrt_wallet/lib/app/utility/blockchin_utils/blockchaain_utils.dart +++ b/mrt_wallet/lib/app/utility/blockchin_utils/blockchaain_utils.dart @@ -3,33 +3,38 @@ import 'package:blockchain_utils/bip/bip/bip44/base/bip44_base_ex.dart'; import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:blockchain_utils/bip/mnemonic/mnemonic.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; -import 'package:mrt_wallet/app/error/exception/wallet_ex.dart'; -import 'package:mrt_wallet/app/utility/blockchin_utils/ripple/ripple_utils.dart'; -import 'package:mrt_wallet/app/utility/compute/compute.dart'; -import 'package:mrt_wallet/models/wallet_models/address/core/core.dart'; -import 'package:mrt_wallet/models/wallet_models/keys/keys.dart'; -import 'package:mrt_wallet/models/wallet_models/network/network_models.dart'; -import 'package:xrpl_dart/xrpl_dart.dart'; +import 'package:mrt_wallet/app/core.dart'; +import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; class BlockchainUtils { - static Bip32Base privteKeyToBip32( - List privateKeyBytes, EllipticCurveTypes curve, - {bool icarus = false}) { + static Bip32Base privteKeyToBip32(List privateKeyBytes, CryptoCoins coin, + {Bip32KeyNetVersions? keyNetVar}) { try { - switch (curve) { + if (coin.proposal == CustomProposal.cip0019) { + return CardanoByronLegacyBip32.fromPrivateKey( + privateKeyBytes, null, keyNetVar); + } + switch (coin.conf.type) { case EllipticCurveTypes.secp256k1: - return Bip32Slip10Secp256k1.fromPrivateKey(privateKeyBytes); + return Bip32Slip10Secp256k1.fromPrivateKey(privateKeyBytes, + keyNetVer: keyNetVar); case EllipticCurveTypes.ed25519: - return Bip32Slip10Ed25519.fromPrivateKey(privateKeyBytes); + return Bip32Slip10Ed25519.fromPrivateKey(privateKeyBytes, + keyNetVer: keyNetVar); case EllipticCurveTypes.ed25519Kholaw: + final bool icarus = coin.conf.addrParams["is_icarus"] ?? false; if (icarus) { - return CardanoIcarusBip32.fromPrivateKey(privateKeyBytes); + return CardanoIcarusBip32.fromPrivateKey(privateKeyBytes, + keyNetVer: keyNetVar); } - return Bip32KholawEd25519.fromPrivateKey(privateKeyBytes); + return Bip32KholawEd25519.fromPrivateKey(privateKeyBytes, + keyNetVer: keyNetVar); case EllipticCurveTypes.ed25519Blake2b: - return Bip32Slip10Ed25519Blake2b.fromPrivateKey(privateKeyBytes); + return Bip32Slip10Ed25519Blake2b.fromPrivateKey(privateKeyBytes, + keyNetVer: keyNetVar); case EllipticCurveTypes.nist256p1: - return Bip32Slip10Nist256p1.fromPrivateKey(privateKeyBytes); + return Bip32Slip10Nist256p1.fromPrivateKey(privateKeyBytes, + keyNetVer: keyNetVar); default: throw WalletExceptionConst.invalidPrivateKey; } @@ -39,23 +44,32 @@ class BlockchainUtils { } static Bip32Base extendedKeyToBip32( - String extendedKey, EllipticCurveTypes curve, - {bool icarus = false}) { + {required String extendedKey, required CryptoCoins coin}) { try { - switch (curve) { + if (coin.proposal == CustomProposal.cip0019) { + return CardanoByronLegacyBip32.fromExtendedKey( + extendedKey, coin.conf.keyNetVer); + } + switch (coin.conf.type) { case EllipticCurveTypes.secp256k1: - return Bip32Slip10Secp256k1.fromExtendedKey(extendedKey); + return Bip32Slip10Secp256k1.fromExtendedKey( + extendedKey, coin.conf.keyNetVer); case EllipticCurveTypes.ed25519: - return Bip32Slip10Ed25519.fromExtendedKey(extendedKey); + return Bip32Slip10Ed25519.fromExtendedKey( + extendedKey, coin.conf.keyNetVer); case EllipticCurveTypes.ed25519Kholaw: - if (icarus) { - return CardanoIcarusBip32.fromExtendedKey(extendedKey); + if (coin.conf.addrParams["is_icarus"] == true) { + return CardanoIcarusBip32.fromExtendedKey( + extendedKey, coin.conf.keyNetVer); } - return Bip32KholawEd25519.fromExtendedKey(extendedKey); + return Bip32KholawEd25519.fromExtendedKey( + extendedKey, coin.conf.keyNetVer); case EllipticCurveTypes.ed25519Blake2b: - return Bip32Slip10Ed25519Blake2b.fromExtendedKey(extendedKey); + return Bip32Slip10Ed25519Blake2b.fromExtendedKey( + extendedKey, coin.conf.keyNetVer); case EllipticCurveTypes.nist256p1: - return Bip32Slip10Nist256p1.fromExtendedKey(extendedKey); + return Bip32Slip10Nist256p1.fromExtendedKey( + extendedKey, coin.conf.keyNetVer); default: throw WalletExceptionConst.invalidPrivateKey; } @@ -68,7 +82,7 @@ class BlockchainUtils { try { final keyBytes = WifDecoder.decode(wifKey, netVer: coin.conf.wifNetVer!).item1; - return privteKeyToBip32(keyBytes, coin.conf.type); + return privteKeyToBip32(keyBytes, coin); } on WalletException { rethrow; } catch (e) { @@ -95,109 +109,9 @@ class BlockchainUtils { return seed; } - static Bip44Base privateKeyToBip44(String key, CryptoCoins coin) { - final privateKey = BytesUtils.fromHexString(key); - switch (coin.proposal) { - case BipProposal.bip44: - return Bip44.fromPrivateKey(privateKey, coin as Bip44Coins); - case BipProposal.bip49: - return Bip49.fromPrivateKey(privateKey, coin as Bip49Coins); - case BipProposal.bip84: - return Bip84.fromPrivateKey(privateKey, coin as Bip84Coins); - case BipProposal.bip86: - return Bip86.fromPrivateKey(privateKey, coin as Bip86Coins); - case CipProposal.cip1852: - return Cip1852.fromPrivateKey(privateKey, coin as Cip1852Coins); - default: - throw WalletExceptionConst.invalidPrivateKey; - } - } - - static Bip44Base extendedKeyToBip44(String key, CryptoCoins coin) { - switch (coin.proposal) { - case BipProposal.bip44: - return Bip44.fromExtendedKey(key, coin as Bip44Coins); - case BipProposal.bip49: - return Bip49.fromExtendedKey(key, coin as Bip49Coins); - case BipProposal.bip84: - return Bip84.fromExtendedKey(key, coin as Bip84Coins); - case BipProposal.bip86: - return Bip86.fromExtendedKey(key, coin as Bip86Coins); - default: - throw WalletExceptionConst.invalidPrivateKey; - } - } - - static String exportPrivateKey(String privateKey, CryptoCoins coin) { - switch (coin) { - case Bip44Coins.rippleEd25519: - case Bip44Coins.ripple: - case Bip44Coins.rippleTestnet: - case Bip44Coins.rippleTestnetED25519: - final prv = XRPPrivateKey.fromHex(privateKey, - algorithm: XRPKeyAlgorithm.values - .firstWhere((element) => element.curveType == coin.conf.type)); - return prv.toHex(); - default: - return privateKey; - } - } - - static String extendedKeyToPrivateKey(String privateKey, CryptoCoins coin) { - final bip32 = extendedKeyToBip32(privateKey, coin.conf.type); - return BytesUtils.toHexString(bip32.privateKey.raw); - } - - static ExportedPublicKey? exportPublicKey( - List pubkeyBytes, CryptoCoins coin, AppNetworkImpl network) { - final Bip44Base account; - try { - switch (coin.proposal) { - case BipProposal.bip44: - account = Bip44.fromPublicKey(pubkeyBytes, coin as Bip44Coins); - break; - case BipProposal.bip49: - account = Bip49.fromPublicKey(pubkeyBytes, coin as Bip49Coins); - break; - case BipProposal.bip84: - account = Bip84.fromPublicKey(pubkeyBytes, coin as Bip84Coins); - break; - case BipProposal.bip86: - account = Bip86.fromPublicKey(pubkeyBytes, coin as Bip86Coins); - break; - case CipProposal.cip1852: - account = Cip1852.fromPublicKey(pubkeyBytes, coin as Cip1852Coins); - break; - default: - return null; - } - } catch (e) { - return null; - } - String extendedKey = account.publicKey.toExtended; - List comprossed = account.publicKey.compressed; - List? unComprossed = account.publicKey.uncompressed; - String pubKey; - String? unComprossedPubKey; - if (network is AppXRPNetwork) { - pubKey = - RippleUtils.toRipplePublicKey(BytesUtils.toHexString(comprossed)); - } else { - pubKey = BytesUtils.toHexString(comprossed); - } - if (!bytesEqual(unComprossed, comprossed)) { - unComprossedPubKey = BytesUtils.toHexString(unComprossed); - } - return ExportedPublicKey( - extendedKey: extendedKey, - comprossed: pubKey, - uncomprossed: unComprossedPubKey); - } - - static Bip32Base extendedToBip32( - String exKeyStr, { - CryptoCoins? coin, - EllipticCurveTypes? type, + static Bip32Base seedToBip32({ + required List seedBytes, + required CryptoCoins coin, }) { Bip32Base validate(Bip32Base bip32Obj) { int depth = bip32Obj.depth.depth; @@ -219,79 +133,31 @@ class BlockchainUtils { } Bip32Base bip; - final conf = coin?.conf; - switch (conf?.type ?? type) { + final conf = coin.conf; + switch (conf.type) { case EllipticCurveTypes.secp256k1: - bip = Bip32Slip10Secp256k1.fromExtendedKey(exKeyStr, conf?.keyNetVer); + bip = Bip32Slip10Secp256k1.fromSeed(seedBytes, conf.keyNetVer); break; case EllipticCurveTypes.ed25519: - bip = Bip32Slip10Ed25519.fromExtendedKey(exKeyStr, conf?.keyNetVer); + bip = Bip32Slip10Ed25519.fromSeed(seedBytes, conf.keyNetVer); break; case EllipticCurveTypes.ed25519Kholaw: - if (conf?.addrParams["is_icarus"] == true) { - bip = CardanoIcarusBip32.fromExtendedKey(exKeyStr, conf?.keyNetVer); + if (coin.proposal == CustomProposal.cip0019) { + bip = + CardanoByronLegacyBip32.fromSeed(seedBytes, coin.conf.keyNetVer); break; } - bip = Bip32KholawEd25519.fromExtendedKey(exKeyStr, conf?.keyNetVer); - break; - case EllipticCurveTypes.ed25519Blake2b: - bip = Bip32Slip10Ed25519Blake2b.fromExtendedKey( - exKeyStr, conf?.keyNetVer); - break; - case EllipticCurveTypes.nist256p1: - bip = Bip32Slip10Nist256p1.fromExtendedKey(exKeyStr, conf?.keyNetVer); - break; - default: - throw const ArgumentException("invaid type"); - } - return validate(bip); - } - - static Bip32Base seedToBip32( - List seedBytes, { - CryptoCoins? coin, - EllipticCurveTypes? type, - }) { - Bip32Base validate(Bip32Base bip32Obj) { - int depth = bip32Obj.depth.depth; - - if (bip32Obj.isPublicOnly) { - if (depth < Bip44Levels.account.value || - depth > Bip44Levels.addressIndex.value) { - throw Bip44DepthError( - "Depth of the public-only Bip object ($depth) is below account level or beyond address index level"); - } - } else { - if (depth < 0 || depth > Bip44Levels.addressIndex.value) { - throw Bip44DepthError( - "Depth of the Bip object ($depth) is invalid or beyond address index level"); - } - } - - return bip32Obj; - } - - Bip32Base bip; - final conf = coin?.conf; - switch (conf?.type ?? type) { - case EllipticCurveTypes.secp256k1: - bip = Bip32Slip10Secp256k1.fromSeed(seedBytes, conf?.keyNetVer); - break; - case EllipticCurveTypes.ed25519: - bip = Bip32Slip10Ed25519.fromSeed(seedBytes, conf?.keyNetVer); - break; - case EllipticCurveTypes.ed25519Kholaw: - if (conf?.addrParams["is_icarus"] == true) { - bip = CardanoIcarusBip32.fromSeed(seedBytes, conf?.keyNetVer); + if (conf.addrParams["is_icarus"] == true) { + bip = CardanoIcarusBip32.fromSeed(seedBytes, conf.keyNetVer); break; } - bip = Bip32KholawEd25519.fromSeed(seedBytes, conf?.keyNetVer); + bip = Bip32KholawEd25519.fromSeed(seedBytes, conf.keyNetVer); break; case EllipticCurveTypes.ed25519Blake2b: - bip = Bip32Slip10Ed25519Blake2b.fromSeed(seedBytes, conf?.keyNetVer); + bip = Bip32Slip10Ed25519Blake2b.fromSeed(seedBytes, conf.keyNetVer); break; case EllipticCurveTypes.nist256p1: - bip = Bip32Slip10Nist256p1.fromSeed(seedBytes, conf?.keyNetVer); + bip = Bip32Slip10Nist256p1.fromSeed(seedBytes, conf.keyNetVer); break; default: throw const ArgumentException("invaid type"); @@ -300,50 +166,97 @@ class BlockchainUtils { return validate(bip); } + static Bip32AddressIndex generateAccountNextKeyIndex( + {required CryptoCoins coin, + required List addresses, + required SeedGenerationType seedGenerationType}) { + if (coin.proposal == CustomProposal.cip0019) { + return findNextByronLegacyIndex(coin: coin, addresses: addresses); + } + return findNextBip32Index( + coin: coin, + addresses: addresses, + seedGenerationType: seedGenerationType); + } + static Bip32AddressIndex findNextBip32Index( {required CryptoCoins coin, required List addresses, required SeedGenerationType seedGenerationType}) { - final List addressIndex = addresses + final List existsIndexes = addresses .map((e) => e.keyIndex) .whereType() .toList(); final int purposeIndex = coin.proposal.purpose.index; final int coinIndex = Bip32KeyIndex.hardenIndex(coin.conf.coinIdx).index; - final int accountLevel = coin.conf.type == EllipticCurveTypes.ed25519 - ? Bip32KeyIndex.hardenIndex(0).index - : 0; + final def = Bip32PathParser.parse(coin.conf.defPath); + if (def.elems.isEmpty) { + throw WalletException("Invalid_coin_default_path"); + } + + List indexKeyes = List.from([ + def.elems.elementAtOrNull(0)!.index, + def.elems.elementAtOrNull(1)?.index, + def.elems.elementAtOrNull(2)?.index, + ], growable: false); + int? getCorrectIndex(int elemIndex, int elemValidIndex, int index) { + if (elemIndex == elemValidIndex) return index; + return indexKeyes[elemIndex]; + } - for (int i = accountLevel; i < Bip32KeyDataConst.keyIndexMaxVal; i++) { + final validIndex = indexKeyes.lastIndexWhere((element) => element != null); + final startIndex = def.elems.elementAt(validIndex).index; + for (int i = startIndex; i < Bip32KeyDataConst.keyIndexMaxVal; i++) { final newKeyIndex = Bip32AddressIndex( - purpose: purposeIndex, - coin: coinIndex, - accountLevel: accountLevel, - changeLevel: accountLevel, - addressIndex: i, - currencyCoin: coin, - seedGeneration: seedGenerationType); - if (!addressIndex.contains(newKeyIndex)) { + purpose: purposeIndex, + coin: coinIndex, + accountLevel: getCorrectIndex(0, validIndex, i), + changeLevel: getCorrectIndex(1, validIndex, i), + addressIndex: getCorrectIndex(2, validIndex, i), + currencyCoin: coin, + seedGeneration: seedGenerationType, + ); + if (!existsIndexes.contains(newKeyIndex)) { return newKeyIndex; } } throw WalletExceptionConst.tooManyAccounts; } - static ByronLegacyAddressIndex findNextByronLegacyIndex( + static Bip32AddressIndex findNextByronLegacyIndex( {required CryptoCoins coin, required List addresses}) { - final List addressIndex = addresses + final List addressIndex = addresses .map((e) => e.keyIndex) - .whereType() + .whereType() .toList(); for (int i = 0; i < Bip32KeyDataConst.keyIndexMaxVal; i++) { - final newKeyIndex = ByronLegacyAddressIndex( - firstIndex: 0, secondIndex: i, currencyCoin: coin); + final newKeyIndex = Bip32AddressIndex.byronLegacy( + firstIndex: 0, secoundIndex: i, currencyCoin: coin); if (!addressIndex.contains(newKeyIndex)) { return newKeyIndex; } } throw WalletExceptionConst.tooManyAccounts; } + + static String createCustomKeyChecksum(Bip32Base bip32) { + return BytesUtils.toHexString(MD5.hash([ + ...bip32.publicKey.compressed, + ...bip32.chainCode.toBytes() + ]).sublist(0, 8)); + } + + static String validateHdPathKey(String path, {int? maxIndex}) { + try { + final parser = Bip32PathParser.parse(path); + if (maxIndex != null && parser.length() != maxIndex) { + throw WalletException( + "hd_wallet_path_max_indeqxes".tr.replaceOne(maxIndex.toString())); + } + return path; + } catch (e) { + throw WalletException("invalid_hd_wallet_derivation_path"); + } + } } diff --git a/mrt_wallet/lib/app/utility/blockchin_utils/cardano/cardano_utils.dart b/mrt_wallet/lib/app/utility/blockchin_utils/cardano/cardano_utils.dart new file mode 100644 index 00000000..552dbc61 --- /dev/null +++ b/mrt_wallet/lib/app/utility/blockchin_utils/cardano/cardano_utils.dart @@ -0,0 +1,20 @@ +import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:on_chain/ada/src/address/address.dart'; + +class CardanoUtils { + static const int decimal = 6; + static const int bip32StakeChangeLevel = 2; + static const int bip32StakeAddressLevel = 0; + static const int byronAddressHdPathKeyLengthBytes = 32; + static const String hdPathHint = "m/1/2"; + static ADARewardAddress? extractRewardAddress(ADAAddress addr) { + switch (addr.addressType) { + case ADAAddressType.base: + return (addr as ADABaseAddress).stakeAddress(); + case ADAAddressType.reward: + return addr as ADARewardAddress; + default: + return null; + } + } +} diff --git a/mrt_wallet/lib/app/utility/blockchin_utils/coins/coins.dart b/mrt_wallet/lib/app/utility/blockchin_utils/coins/coins.dart new file mode 100644 index 00000000..18790b22 --- /dev/null +++ b/mrt_wallet/lib/app/utility/blockchin_utils/coins/coins.dart @@ -0,0 +1,2 @@ +export 'custom_currency_coins.dart'; +export 'custom_currency_conf.dart'; diff --git a/mrt_wallet/lib/app/utility/blockchin_utils/coins/custom_currency_coins.dart b/mrt_wallet/lib/app/utility/blockchin_utils/coins/custom_currency_coins.dart new file mode 100644 index 00000000..9f9f2650 --- /dev/null +++ b/mrt_wallet/lib/app/utility/blockchin_utils/coins/custom_currency_coins.dart @@ -0,0 +1,79 @@ +import 'package:blockchain_utils/bip/bip/bip32/bip32_key_data.dart'; +import 'package:blockchain_utils/bip/bip/conf/bip_coin_conf.dart'; +import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; +import 'package:blockchain_utils/exception/exception.dart'; + +import 'custom_currency_conf.dart'; + +class CustomCoins extends CryptoCoins { + CustomCoins._(this.name, this.conf); + final String name; + @override + String get coinName => name; + + @override + final CoinConfig conf; + + @override + CryptoProposal get proposal => CustomProposal.cip0019; + + @override + CryptoCoins get value => this; + + static final CustomCoins byronLegacy = + CustomCoins._("Byron legacy", CustomCurrencyConf.byronLegacy); + static final CustomCoins byronLegacyTestnet = CustomCoins._( + "Byron legacy testnet", CustomCurrencyConf.byronLegacyTestnet); + static final List values = [byronLegacy, byronLegacyTestnet]; + + static CryptoCoins? getCoin(String name, CryptoProposal proposal) { + switch (proposal) { + case CustomProposal.cip0019: + return CustomCoins.fromName(name); + default: + return CryptoCoins.getCoin(name, proposal); + } + } + + static CustomCoins? fromName(String name) { + try { + return values.firstWhere((element) => element.name == name); + } on StateError { + return null; + } + } +} + +class CustomProposal implements CryptoProposal { + static const CustomProposal cip0019 = CustomProposal._('custom'); + + const CustomProposal._(this.name); + final String name; + + @override + String get specName => name; + @override + CustomProposal get value => this; + + static const List values = [cip0019]; + + @override + Bip32KeyIndex get purpose => Bip32KeyIndex(0); + + static CryptoProposal fromName(String name) { + try { + return values.firstWhere( + (element) => element.specName == name, + orElse: () => + BipProposal.values.firstWhere((element) => element.name == name), + ); + } on StateError { + return CipProposal.values.firstWhere( + (element) => element.name == name, + orElse: () => throw MessageException( + "Unable to locate a proposal with the given name.", + details: {"Name": name}), + ); + } + } +} diff --git a/mrt_wallet/lib/app/utility/blockchin_utils/coins/custom_currency_conf.dart b/mrt_wallet/lib/app/utility/blockchin_utils/coins/custom_currency_conf.dart new file mode 100644 index 00000000..0469dbdb --- /dev/null +++ b/mrt_wallet/lib/app/utility/blockchin_utils/coins/custom_currency_conf.dart @@ -0,0 +1,29 @@ +import 'package:blockchain_utils/bip/bip/conf/bip_coin_conf.dart'; +import 'package:blockchain_utils/bip/coin_conf/coins_name.dart'; +import 'package:blockchain_utils/bip/slip/slip44/slip44.dart'; +import 'package:blockchain_utils/blockchain_utils.dart'; + +class CustomCurrencyConf { + static CoinConfig byronLegacy = CoinConfig( + coinNames: const CoinNames("Byron legacy", "ADA"), + coinIdx: 0, + isTestnet: false, + defPath: "0/0", + keyNetVer: Bip32Const.kholawKeyNetVersions, + wifNetVer: null, + type: EllipticCurveTypes.ed25519Kholaw, + addressEncoder: ([dynamic kwargs]) => AdaByronLegacyAddrEncoder(), + addrParams: {"chain_code": true}, + ); + static CoinConfig byronLegacyTestnet = CoinConfig( + coinNames: const CoinNames("Byron legacy testnet", "ADA"), + coinIdx: Slip44.testnet, + isTestnet: true, + defPath: "", + keyNetVer: Bip32Const.kholawKeyNetVersions, + wifNetVer: null, + type: EllipticCurveTypes.ed25519Kholaw, + addressEncoder: ([dynamic kwargs]) => AdaByronLegacyAddrEncoder(), + addrParams: {"chain_code": true}, + ); +} diff --git a/mrt_wallet/lib/app/utility/blockchin_utils/ripple/ripple_utils.dart b/mrt_wallet/lib/app/utility/blockchin_utils/ripple/ripple_utils.dart index ff9db513..26169b97 100644 --- a/mrt_wallet/lib/app/utility/blockchin_utils/ripple/ripple_utils.dart +++ b/mrt_wallet/lib/app/utility/blockchin_utils/ripple/ripple_utils.dart @@ -1,9 +1,11 @@ import 'package:blockchain_utils/bip/bip/bip32/base/bip32_base.dart'; +import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:mrt_wallet/app/constant/network_constant/ripple_const.dart'; import 'package:mrt_wallet/app/error/exception/wallet_ex.dart'; import 'package:mrt_wallet/app/utility/bytes_utils/quick_bytes.dart'; import 'package:mrt_wallet/models/wallet_models/address/network_address/xrp/xrp_account.dart'; +import 'package:mrt_wallet/models/wallet_models/network/core/network.dart'; import 'package:xrpl_dart/xrpl_dart.dart'; import '../blockchaain_utils.dart'; @@ -213,11 +215,13 @@ class RippleUtils { } } - static Bip32Base rippleSeedToBip32(String seed) { + static Bip32Base rippleSeedToBip32(String seed, AppNetworkImpl network) { try { final ripplePrivateKey = XRPPrivateKey.fromSeed(seed); return BlockchainUtils.privteKeyToBip32( - ripplePrivateKey.toBytes(), ripplePrivateKey.algorithm.curveType); + ripplePrivateKey.toBytes(), + network.coins.firstWhere((element) => + element.conf.type == ripplePrivateKey.algorithm.curveType)); } on WalletException { rethrow; } catch (e) { @@ -226,15 +230,15 @@ class RippleUtils { } static Bip32Base ripplePrivateKeyToBip32( - String privateKey, EllipticCurveTypes curve) { + String privateKey, CryptoCoins coin) { try { final ripplePrivateKey = XRPPrivateKey.fromBytes( BytesUtils.fromHexString(privateKey), - algorithm: curve == EllipticCurveTypes.ed25519 + algorithm: coin.conf.type == EllipticCurveTypes.ed25519 ? XRPKeyAlgorithm.ed25519 : XRPKeyAlgorithm.secp256k1); - return BlockchainUtils.privteKeyToBip32( - ripplePrivateKey.toBytes(), ripplePrivateKey.algorithm.curveType); + + return BlockchainUtils.privteKeyToBip32(ripplePrivateKey.toBytes(), coin); } on WalletException { rethrow; } catch (e) { @@ -242,15 +246,13 @@ class RippleUtils { } } - static Bip32Base rippleEntropyToBip32( - String entropy, EllipticCurveTypes algorithm) { + static Bip32Base rippleEntropyToBip32(String entropy, CryptoCoins coin) { try { final ripplePrivateKey = XRPPrivateKey.fromEntropy(entropy, - algorithm: algorithm == EllipticCurveTypes.ed25519 + algorithm: coin.conf.type == EllipticCurveTypes.ed25519 ? XRPKeyAlgorithm.ed25519 : XRPKeyAlgorithm.secp256k1); - return BlockchainUtils.privteKeyToBip32( - ripplePrivateKey.toBytes(), ripplePrivateKey.algorithm.curveType); + return BlockchainUtils.privteKeyToBip32(ripplePrivateKey.toBytes(), coin); } on WalletException { rethrow; } catch (e) { diff --git a/mrt_wallet/lib/app/utility/blockchin_utils/utils.dart b/mrt_wallet/lib/app/utility/blockchin_utils/utils.dart index 5afa6425..89af62f0 100644 --- a/mrt_wallet/lib/app/utility/blockchin_utils/utils.dart +++ b/mrt_wallet/lib/app/utility/blockchin_utils/utils.dart @@ -7,3 +7,5 @@ export 'blockchain_addr_utils.dart'; export 'bitcoin/bitcoin.dart'; export 'bitcoin_cash/bitcoin_cash_utils.dart'; export 'solana/solana.dart'; +export 'cardano/cardano_utils.dart'; +export 'coins/coins.dart'; diff --git a/mrt_wallet/lib/app/utility/string_utility.dart b/mrt_wallet/lib/app/utility/string_utility.dart index c9cc9aaa..df38fc27 100644 --- a/mrt_wallet/lib/app/utility/string_utility.dart +++ b/mrt_wallet/lib/app/utility/string_utility.dart @@ -158,7 +158,7 @@ class AppStringUtility { static bool isValidIPv4WithPort(String ipv4) { // Regular expression to match IPv4 address with optional port final RegExp ipv4WithPortRegex = RegExp(r'^(\d{1,3}\.){3}\d{1,3}:\d+$'); - + // Check if the input matches the pattern return ipv4WithPortRegex.hasMatch(ipv4); } diff --git a/mrt_wallet/lib/app/utility/utility.dart b/mrt_wallet/lib/app/utility/utility.dart index e5d76755..3365ff4c 100644 --- a/mrt_wallet/lib/app/utility/utility.dart +++ b/mrt_wallet/lib/app/utility/utility.dart @@ -13,3 +13,4 @@ export 'numeric_utils/numeric_utils.dart'; export 'lifecycle_listener/core.dart'; export 'file/file.dart'; export 'bytes_utils/quick_bytes.dart'; +export 'blockchain_constant/constant.dart'; diff --git a/mrt_wallet/lib/future/pages/start_page/account_page.dart b/mrt_wallet/lib/future/pages/start_page/account_page.dart index 991b8629..ff529113 100644 --- a/mrt_wallet/lib/future/pages/start_page/account_page.dart +++ b/mrt_wallet/lib/future/pages/start_page/account_page.dart @@ -4,6 +4,7 @@ import 'package:mrt_wallet/future/pages/start_page/home.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/wallet_global_pages.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/bitcoin_cash_pages/account_page.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/bitcoin_pages/account_page.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/account_page.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/ethereum_pages/ethereum_account_page_view.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/ripple_pages/account_page.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/solana_pages/account_page.dart'; @@ -308,6 +309,8 @@ class _AccountPageView extends StatelessWidget { return ETHAccountPageView(chainAccount: chainAccount); case APPTVMNetwork: return TronAccountPageView(chainAccount: chainAccount); + case APPCardanoNetwork: + return CardanoAccountPage(chainAccount: chainAccount); default: return const SizedBox(); } diff --git a/mrt_wallet/lib/future/pages/start_page/home_screen.dart b/mrt_wallet/lib/future/pages/start_page/home_screen.dart index 604e1ada..0dbc82be 100644 --- a/mrt_wallet/lib/future/pages/start_page/home_screen.dart +++ b/mrt_wallet/lib/future/pages/start_page/home_screen.dart @@ -196,15 +196,14 @@ class _BottomAppBar extends StatelessWidget { IconButton( tooltip: "switch_network".tr, onPressed: () async { - await showAdaptiveDialog( - context: context, - useRootNavigator: false, - builder: (context) { - return SwitchNetworkView( - selectedNetwork: model.network, - ); - }, - ).then( + context + .openDialogPage( + "switch_network".tr, + fullWidget: SwitchNetworkView( + selectedNetwork: model.network, + ), + ) + .then( (value) { if (value == null) return; if (value.isNegative) { diff --git a/mrt_wallet/lib/future/pages/wallet_pages/account_pages/show_public_key.dart b/mrt_wallet/lib/future/pages/wallet_pages/account_pages/show_public_key.dart index 7ed2b8dd..e42c3b6d 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/account_pages/show_public_key.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/account_pages/show_public_key.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; import 'package:mrt_wallet/app/core.dart'; +import 'package:mrt_wallet/future/pages/start_page/home.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/wallet_pages.dart'; import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; +import 'package:mrt_wallet/main.dart'; +import 'package:mrt_wallet/models/wallet_models/address/network_address/cardano/cardano.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; class AccountPublicKeyView extends StatelessWidget { @@ -27,77 +30,163 @@ class _BipAccountPublicKey extends StatefulWidget { } class __BipAccountPublicKeyState extends State<_BipAccountPublicKey> { - ExportedPublicKey? publicKey; + final List pubKeys = []; + bool get hasMultipleKey => pubKeys.length > 1; + late AccessPubliKeyResponse publicKey; + final GlobalKey progressKey = GlobalKey(); + bool inited = false; + void initPubKey() async { + if (inited) return; + inited = true; + final wallet = context.watch(StateIdsConst.main); + final result = await wallet.getAccountPubKys(account: widget.account); + if (result.hasResult) { + pubKeys.addAll(result.result); + progressKey.success(); + publicKey = pubKeys.first; + } else { + progressKey.errorText("cannot_export_public_key".tr, backToIdle: false); + } + } + + late final ICardanoAddress? adaLegacyAddress = isAdaLegacy(); + + ICardanoAddress? isAdaLegacy() { + if (widget.account is ICardanoAddress) { + if ((widget.account as ICardanoAddress).addressDetails.isLegacy) { + return widget.account as ICardanoAddress; + } + } + return null; + } - void initPubKey() { - publicKey = BlockchainUtils.exportPublicKey( - widget.account.publicKey, widget.account.coin, widget.network); + void onChangeKey(AccessPubliKeyResponse? changeKey) { + if (publicKey == changeKey || changeKey == null) return; + publicKey = changeKey; + setState(() {}); } @override - void initState() { - super.initState(); + void didChangeDependencies() { + super.didChangeDependencies(); initPubKey(); } @override Widget build(BuildContext context) { - if (publicKey == null) { - return Column( + return PageProgress( + key: progressKey, + initialStatus: StreamWidgetStatus.progress, + backToIdle: AppGlobalConst.oneSecoundDuration, + initialWidget: ProgressWithTextView(text: "retrieve_the_public".tr), + child: () => Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - WidgetConstant.errorIconLarge, - WidgetConstant.height8, - Text("cannot_export_public_key".tr) + PageTitleSubtitle( + title: "export_public_key".tr, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [Text("export_public_key_desc1".tr)], + )), + if (hasMultipleKey) ...[ + Text("public_keys".tr, style: context.textTheme.titleMedium), + Text("switch_between_keys".tr), + WidgetConstant.height8, + AppDropDownBottom( + onChanged: onChangeKey, + items: {for (final i in pubKeys) i: Text(i.keyName.tr)}, + label: "key_name".tr, + value: publicKey, + ), + WidgetConstant.height20, + ], + if (!hasMultipleKey) ...[ + Text("address_details".tr, style: context.textTheme.titleMedium), + WidgetConstant.height8, + ContainerWithBorder( + child: CopyTextWithBarcode( + dataToCopy: widget.account.address.toAddress, + widget: AddressDetailsView(address: widget.account), + barcodeTitle: "address_sharing".tr), + ), + WidgetConstant.height20 + ], + _HDPathDetails(byronLegacy: adaLegacyAddress), + AnimatedSwitcher( + duration: AppGlobalConst.animationDuraion, + child: Column( + key: UniqueKey(), + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("extended_public_key".tr, + style: context.textTheme.titleMedium), + WidgetConstant.height8, + ContainerWithBorder( + child: CopyTextWithBarcode( + dataToCopy: publicKey.extendedKey, + barcodeTitle: "extended_public_key".tr, + widget: SelectableText(publicKey.extendedKey), + )), + WidgetConstant.height20, + Text("comperessed_public_key".tr, + style: context.textTheme.titleMedium), + WidgetConstant.height8, + ContainerWithBorder( + child: CopyTextWithBarcode( + barcodeTitle: "comperessed_public_key".tr, + dataToCopy: publicKey.comprossed, + widget: SelectableText(publicKey.comprossed), + )), + if (publicKey.uncomprossed != null) ...[ + WidgetConstant.height20, + Text("uncomperessed_public_key".tr, + style: context.textTheme.titleMedium), + WidgetConstant.height8, + ContainerWithBorder( + child: CopyTextWithBarcode( + dataToCopy: publicKey.uncomprossed!, + barcodeTitle: "uncomperessed_public_key".tr, + widget: SelectableText(publicKey.uncomprossed!), + )), + ], + ], + ), + ) ], - ); - } + ), + ); + } +} + +class _HDPathDetails extends StatelessWidget { + const _HDPathDetails({this.byronLegacy}); + final ICardanoAddress? byronLegacy; + + @override + Widget build(BuildContext context) { + if (byronLegacy == null) return WidgetConstant.sizedBox; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - PageTitleSubtitle( - title: "export_public_key".tr, - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [Text("export_public_key_desc1".tr)], - )), - Text("address_details".tr, style: context.textTheme.titleMedium), + Text("hd_path".tr, style: context.textTheme.titleMedium), WidgetConstant.height8, ContainerWithBorder( - child: CopyTextWithBarcode( - dataToCopy: widget.account.address.toAddress, - widget: AddressDetailsView(address: widget.account), - barcodeTitle: "address_sharing".tr), + onRemove: () {}, + onRemoveIcon: + CopyTextIcon(dataToCopy: byronLegacy!.addressDetails.hdPath!), + child: + Text(byronLegacy!.addressDetails.hdPath!.or("non_derivation".tr)), ), WidgetConstant.height20, - Text("extended_public_key".tr, style: context.textTheme.titleMedium), + Text("hd_path_key".tr, style: context.textTheme.titleMedium), WidgetConstant.height8, ContainerWithBorder( - child: CopyTextWithBarcode( - dataToCopy: publicKey!.extendedKey, - barcodeTitle: "extended_public_key".tr, - widget: SelectableText(publicKey!.extendedKey), - )), - WidgetConstant.height20, - Text("comperessed_public_key".tr, style: context.textTheme.titleMedium), - WidgetConstant.height8, - ContainerWithBorder( - child: CopyTextWithBarcode( - barcodeTitle: "comperessed_public_key".tr, - dataToCopy: publicKey!.comprossed, - widget: SelectableText(publicKey!.comprossed), - )), - if (publicKey!.uncomprossed != null) ...[ - WidgetConstant.height20, - Text("uncomperessed_public_key".tr, - style: context.textTheme.titleMedium), - WidgetConstant.height8, - ContainerWithBorder( - child: CopyTextWithBarcode( - dataToCopy: publicKey!.uncomprossed!, - barcodeTitle: "uncomperessed_public_key".tr, - widget: SelectableText(publicKey!.uncomprossed!), - )), - ] + onRemove: () {}, + onRemoveIcon: CopyTextIcon( + dataToCopy: byronLegacy!.addressDetails.hdPathKeyHex!), + child: Text(byronLegacy!.addressDetails.hdPathKeyHex!), + ), + WidgetConstant.height20 ], ); } diff --git a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation/address_derivation_view.dart b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation/address_derivation_view.dart new file mode 100644 index 00000000..3f5fda24 --- /dev/null +++ b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation/address_derivation_view.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:mrt_wallet/app/core.dart'; +import 'package:mrt_wallet/future/pages/start_page/home.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/address_derivation/generic.dart'; +import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; +import 'package:mrt_wallet/main.dart'; +import 'controller.dart'; + +class NetworkGenericAddressDerivationView extends StatelessWidget { + const NetworkGenericAddressDerivationView({super.key}); + + @override + Widget build(BuildContext context) { + final wallet = context.watch(StateIdsConst.main); + return MrtViewBuilder( + controller: () => AddressDerivationController( + chainAccount: wallet.chain, network: wallet.network, wallet: wallet), + builder: (controller) => Scaffold( + appBar: AppBar( + title: Text("setup_address".tr), + ), + body: PageProgress( + key: controller.pageProgressKey, + backToIdle: AppGlobalConst.oneSecoundDuration, + initialStatus: PageProgressStatus.idle, + child: () => UnfocusableChild( + child: Center( + child: CustomScrollView( + shrinkWrap: true, + slivers: [ + SliverToBoxAdapter( + child: ConstraintsBoxView( + padding: WidgetConstant.paddingHorizontal20, + child: AnimatedSwitcher( + duration: AppGlobalConst.animationDuraion, + child: Form( + key: controller.form, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + key: const ValueKey(true), + children: [ + PageTitleSubtitle( + title: "setup_network_address".tr.replaceOne( + controller.network.coinParam.token.name), + body: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text("disable_standard_derivation".tr), + WidgetConstant.height8, + Text("setup_address_derivation_keys_desc" + .tr), + WidgetConstant.height8, + Text( + "please_following_steps_to_generate_address" + .tr), + ], + )), + SetupGenericAddressView(controller: controller) + ], + ), + )), + )) + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation/controller.dart b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation/controller.dart new file mode 100644 index 00000000..7ac76fc0 --- /dev/null +++ b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation/controller.dart @@ -0,0 +1,103 @@ +import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; +import 'package:flutter/material.dart'; +import 'package:mrt_wallet/app/core.dart'; +import 'package:mrt_wallet/future/pages/start_page/home.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/address_derivation/setup_address_derivation_mode.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/address_details.dart'; +import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; +import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; + +typedef OnGenerateDerivation = Future Function(); + +enum AddressDerivationMode { + hdWallet, + importedKey; + + bool get isCustomKey => this == AddressDerivationMode.importedKey; +} + +typedef OnSelectDerivation = void Function( + AddressDerivationMode mode, EncryptedCustomKey? selectedKey); + +class AddressDerivationController extends StateController { + AddressDerivationController({ + required this.wallet, + required this.network, + required this.chainAccount, + }); + final WalletProvider wallet; + final GlobalKey visibleContinue = + GlobalKey(debugLabel: "visibleGenerateAddress"); + final AppNetworkImpl network; + + bool showCustomKes = false; + bool showSetupPage = false; + + final GlobalKey pageProgressKey = + GlobalKey(debugLabel: "SetupEthereumAddressView"); + final GlobalKey form = GlobalKey(); + final GlobalKey visibleGenerateAddress = + GlobalKey(debugLabel: "visibleContinue"); + final GlobalKey visibleXAddressDetails = + GlobalKey(debugLabel: "visibleContinue"); + + final AppChain chainAccount; + NetworkAccountCore get networkAccounts => chainAccount.account; + List get coins => network.coins; + CryptoCoins get coin => coins.first; + + Bip32AddressIndex? customKeyIndex; + bool get derivationStandard => customKeyIndex == null; + + bool inited = false; + + void onChangeDerivation(OnGenerateDerivation onGenerateDerivation) async { + if (derivationStandard) { + customKeyIndex = await onGenerateDerivation(); + } else { + customKeyIndex = null; + } + notify(); + } + + AddressDerivationIndex get nextDerivation { + return chainAccount.account.nextDerive(coin); + } + + Future getCoin(BuildContext context) async { + if (!(form.currentState?.validate() ?? true)) return null; + return await context.openSliverBottomSheet( + "setup_derivation".tr, + child: SetupDerivationModeView(coin: coin, chainAccout: chainAccount)); + } + + void generateAddress(NewAccountParams newAccount) async { + if (!(form.currentState?.validate() ?? false)) return; + pageProgressKey.progressText("generating_new_addr".tr); + final result = await MethodCaller.call(() async { + final result = await wallet.deriveNewAccount(newAccount); + result.rethrowIfError(); + return result.result; + }); + if (result.hasError) { + pageProgressKey.errorText(result.error!.tr); + } else { + pageProgressKey.success( + backToIdle: false, + progressWidget: SuccessWithButtomView( + buttomText: "generate_new_address".tr, + buttomWidget: ContainerWithBorder( + margin: WidgetConstant.paddingVertical8, + child: AddressDetailsView(address: result.result)), + onPressed: () { + pageProgressKey.backToIdle(); + }, + text: "address_added_success".tr, + )); + } + notify(); + } + + @override + String get repositoryId => "address_derivation"; +} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation/generic.dart b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation/generic.dart new file mode 100644 index 00000000..73a030e7 --- /dev/null +++ b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation/generic.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:mrt_wallet/app/core.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/address_derivation/controller.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/network/ripple_pages/setup_address.dart'; +import 'package:mrt_wallet/future/widgets/button.dart'; +import 'package:mrt_wallet/future/widgets/widget_constant.dart'; +import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; + +class SetupGenericAddressView extends StatelessWidget { + const SetupGenericAddressView({super.key, required this.controller}); + final AddressDerivationController controller; + + @override + Widget build(BuildContext context) { + switch (controller.network.runtimeType) { + case AppXRPNetwork: + return SetupRippleAddressView( + controller: controller, + ); + default: + return _GenericNetworkAddressGenerationView(controller: controller); + } + } +} + +class _GenericNetworkAddressGenerationView extends StatelessWidget { + const _GenericNetworkAddressGenerationView({required this.controller}); + final AddressDerivationController controller; + static NewAccountParams getnerateAccoutParams( + Bip32AddressIndex keyIndex, AppNetworkImpl network) { + switch (network.runtimeType) { + case APPEVMNetwork: + return EthereumNewAddressParam(deriveIndex: keyIndex); + case APPSolanaNetwork: + return SolanaNewAddressParam(deriveIndex: keyIndex); + case APPCosmosNetwork: + return CosmosNewAddressParams(deriveIndex: keyIndex); + case APPTVMNetwork: + return TronNewAddressParam(deriveIndex: keyIndex); + default: + throw UnimplementedError(); + } + } + + static void generateAddress( + BuildContext context, AddressDerivationController controller) async { + final keyIndex = await controller.getCoin(context); + if (keyIndex == null) return; + final newAccountParam = getnerateAccoutParams(keyIndex, controller.network); + controller.generateAddress(newAccountParam); + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FixedElevatedButton( + padding: WidgetConstant.paddingVertical20, + onPressed: () { + generateAddress(context, controller); + }, + child: Text("generate_address".tr), + ), + ], + ); + } +} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation/setup_address_derivation_mode.dart b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation/setup_address_derivation_mode.dart new file mode 100644 index 00000000..d91576a0 --- /dev/null +++ b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation/setup_address_derivation_mode.dart @@ -0,0 +1,211 @@ +import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; +import 'package:flutter/material.dart'; +import 'package:mrt_wallet/app/core.dart'; +import 'package:mrt_wallet/future/pages/start_page/controller/wallet_provider.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/bip32_derivation.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/byron_legacy_derivation.dart'; +import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; +import 'package:mrt_wallet/main.dart'; +import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; + +typedef _OnGenerateDerivation = Future Function(); + +class SetupDerivationModeView extends StatefulWidget { + final CryptoCoins coin; + final AppChain chainAccout; + final AddressDerivationIndex? defaultDerivation; + final Widget? title; + const SetupDerivationModeView( + {super.key, + required this.coin, + required this.chainAccout, + this.defaultDerivation, + this.title}); + + @override + State createState() => + _SetupDerivationModeView2State(); +} + +class _SetupDerivationModeView2State extends State + with SafeState { + EncryptedCustomKey? selectedCustomKey; + AppNetworkImpl get network => chainAccount.network; + AppChain get chainAccount => widget.chainAccout; + late final bool useByronLegacyDeriavation = + widget.coin.proposal == CustomProposal.cip0019; + + AddressDerivationIndex derivationkey(CryptoCoins coin) { + if (selectedCustomKey != null) { + return (customKeyIndex ?? Bip32AddressIndex(currencyCoin: coin)) + .copyWith(importedKeyId: selectedCustomKey!.id); + } + return customKeyIndex ?? nextDerivation; + } + + AddressDerivationIndex get nextDerivation { + return widget.defaultDerivation ?? + chainAccount.account.nextDerive(widget.coin); + } + + void onChangeCustomKey(EncryptedCustomKey? newSelected) { + selectedCustomKey = newSelected; + setState(() {}); + } + + List customKeys = []; + bool _inited = false; + void _setupIAccount() { + if (!_inited) { + _inited = true; + final model = context.watch(StateIdsConst.main); + customKeys = model.getCustomKeysForCoin(widget.coin); + } + } + + bool get derivationStandard => customKeyIndex == null; + Bip32AddressIndex? customKeyIndex; + + void onChangeDerivation(_OnGenerateDerivation onGenerateDerivation) async { + if (derivationStandard) { + customKeyIndex = await onGenerateDerivation(); + } else { + customKeyIndex = null; + } + setState(() {}); + } + + @override + void didChangeDependencies() { + _setupIAccount(); + super.didChangeDependencies(); + } + + void onSubmit() { + final key = derivationkey(widget.coin); + context.pop(key); + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + widget.title ?? + PageTitleSubtitle( + title: "derive_network_address" + .tr + .replaceOne(network.coinParam.token.name), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [Text("disable_standard_derivation".tr)], + )), + Text( + "derivation_path".tr, + style: context.textTheme.titleMedium, + ), + WidgetConstant.height8, + ContainerWithBorder( + onRemove: () { + onChangeDerivation( + () async { + if (useByronLegacyDeriavation) { + return context.openSliverBottomSheet( + "key_derivation".tr, + child: ByronLegacyKeyDerivationView( + coin: widget.coin, + curve: widget.coin.conf.type, + )); + } + return context.openSliverBottomSheet( + "key_derivation".tr, + child: Bip32KeyDerivationView( + coin: widget.coin, + curve: widget.coin.conf.type, + network: network, + defaultPath: nextDerivation.hdPath, + )); + }, + ); + }, + onRemoveIcon: derivationStandard + ? const Icon(Icons.edit) + : const Icon(Icons.remove_circle), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + selectedCustomKey == null + ? Text( + derivationStandard + ? "standard_derivation".tr + : "custom_derivation".tr, + style: context.textTheme.labelLarge, + ) + : Text( + customKeyIndex == null + ? "non_derivation".tr + : "custom_derivation".tr, + style: context.textTheme.labelLarge, + ), + selectedCustomKey == null + ? Text( + customKeyIndex?.toString() ?? nextDerivation.toString(), + ) + : Text( + customKeyIndex?.toString() ?? + "import_key_derivation_desc2".tr, + ) + ], + )), + WidgetConstant.height20, + Text( + "select_creation_type".tr, + style: context.textTheme.titleMedium, + ), + Text("generate_from_hd_wallet".tr), + WidgetConstant.height8, + RadioListTile( + value: null, + groupValue: selectedCustomKey, + onChanged: onChangeCustomKey, + title: Text("hd_wallet".tr), + subtitle: Text("generate_from_hd_wallet".tr), + ), + Column( + children: List.generate(customKeys.length, (index) { + final key = customKeys[index]; + return RadioListTile( + value: key, + groupValue: selectedCustomKey, + onChanged: onChangeCustomKey, + title: OneLineTextWidget(key.networkPubKey(network)), + subtitle: RichText( + text: + TextSpan(style: context.textTheme.bodyMedium, children: [ + if (key.name != null) ...[ + TextSpan(text: key.name), + TextSpan( + text: " (${key.created.toDateAndTime()}) ", + style: context.textTheme.bodySmall) + ] else + TextSpan( + text: "imported_at".tr.replaceOne(key.created.toString()), + style: context.textTheme.bodyMedium), + ])), + ); + }), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FixedElevatedButton( + padding: WidgetConstant.paddingVertical20, + onPressed: onSubmit, + child: Text("generate_address".tr), + ) + ], + ) + ], + ); + } +} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation_type.dart b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation_type.dart deleted file mode 100644 index d1c1bd06..00000000 --- a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_derivation_type.dart +++ /dev/null @@ -1,193 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:mrt_wallet/app/core.dart'; -import 'package:mrt_wallet/future/pages/start_page/home.dart'; -import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; -import 'package:mrt_wallet/main.dart'; -import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; - -enum AddressDerivationMode { hdWallet, importedKey } - -typedef OnSelectDerivation = void Function( - AddressDerivationMode mode, EncryptedCustomKey? selectedKey); - -class SetupAddressDerivation extends StatefulWidget { - const SetupAddressDerivation(this.onSelectDerivation, {super.key}); - final OnSelectDerivation onSelectDerivation; - @override - State createState() => - _SetupAddressDerivationViewState(); -} - -class _SetupAddressDerivationViewState extends State - with SafeState { - String? _error; - final GlobalKey visibleContinue = - GlobalKey(debugLabel: "visibleGenerateAddress"); - late final AppNetworkImpl network; - AddressDerivationMode? selectedDerivationMode; - EncryptedCustomKey? selectedCustomKey; - bool showCustomKes = false; - bool showSetupPage = false; - bool inAddressPage = false; - - void onChangeCustomKey(EncryptedCustomKey? newSelected) { - selectedCustomKey = newSelected; - if (selectedCustomKey != null && showCustomKes) { - showSetupPage = true; - } else { - showSetupPage = false; - } - setState(() {}); - } - - late final Map derivationModes = { - AddressDerivationMode.hdWallet: Text("hd_wallet".tr), - AddressDerivationMode.importedKey: Text("imported_key".tr) - }; - - void onChangeDerivationMode(AddressDerivationMode? mode) async { - if (mode == selectedDerivationMode) return; - selectedDerivationMode = mode ?? selectedDerivationMode; - _error = null; - showCustomKes = false; - showSetupPage = false; - selectedCustomKey = null; - - try { - if (selectedDerivationMode == AddressDerivationMode.importedKey) { - if (customKeys.isEmpty) { - _error = "empty_custom_key_desc".tr; - } else { - showCustomKes = true; - selectedCustomKey = customKeys.first; - showSetupPage = true; - } - } else { - showSetupPage = true; - } - setState(() {}); - } finally { - if (showSetupPage) { - ensureKeyVisible(key: visibleContinue); - } - } - } - - List customKeys = []; - bool _inited = false; - void _setupIAccount() { - if (!_inited) { - _inited = true; - final model = context.watch(StateIdsConst.main); - customKeys = model.getNetworkImportedKeys(); - network = model.chain.network; - } - } - - @override - void didChangeDependencies() { - _setupIAccount(); - super.didChangeDependencies(); - } - - void onSubmitDerivation() { - widget.onSelectDerivation(selectedDerivationMode!, selectedCustomKey); - } - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("select_derivation_type".tr, style: context.textTheme.titleMedium), - Text("select_derivation_desc".tr), - WidgetConstant.height8, - AppDropDownBottom( - items: derivationModes, - onChanged: onChangeDerivationMode, - label: "derivation_type".tr, - value: selectedDerivationMode, - error: _error, - ), - WidgetConstant.height20, - AnimatedSize( - duration: AppGlobalConst.animationDuraion, - child: showCustomKes - ? _SelectCustomKeys( - existsKeys: customKeys, - selectedKey: selectedCustomKey, - onChangeCustomKey: onChangeCustomKey, - network: network, - ) - : const SizedBox(), - ), - AnimatedSwitcher( - duration: AppGlobalConst.animationDuraion, - child: showSetupPage - ? Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - FixedElevatedButton( - padding: WidgetConstant.paddingVertical20, - onPressed: onSubmitDerivation, - key: visibleContinue, - child: Text("continue".tr), - ) - ]) - : const SizedBox(), - ) - ], - ); - } -} - -typedef _OnChangeCustomKey = void Function(EncryptedCustomKey?); - -class _SelectCustomKeys extends StatelessWidget { - const _SelectCustomKeys( - {required this.existsKeys, - required this.selectedKey, - required this.onChangeCustomKey, - required this.network}); - final List existsKeys; - final EncryptedCustomKey? selectedKey; - final _OnChangeCustomKey onChangeCustomKey; - final AppNetworkImpl network; - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("choose_public_key".tr, style: context.textTheme.titleMedium), - Text("generate_from_imported_keys".tr), - Text("select_imported_key_desc".tr), - WidgetConstant.height8, - Column( - children: List.generate(existsKeys.length, (index) { - return RadioListTile( - value: existsKeys[index], - groupValue: selectedKey, - onChanged: onChangeCustomKey, - title: - OneLineTextWidget(existsKeys[index].networkPubKey(network)), - subtitle: RichText( - text: - TextSpan(style: context.textTheme.bodyMedium, children: [ - if (existsKeys[index].name != null) ...[ - TextSpan(text: existsKeys[index].name), - TextSpan( - text: " (${existsKeys[index].created.toDateAndTime()}) ", - style: context.textTheme.bodySmall) - ] else - TextSpan( - text: "imported_at" - .tr - .replaceOne(existsKeys[index].created.toString()), - style: context.textTheme.bodyMedium), - ])), - ); - }), - ), - ], - ); - } -} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_details.dart b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_details.dart index a7bf1152..a8353aeb 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_details.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/address_details.dart @@ -15,7 +15,6 @@ class AddressDetailsView extends StatelessWidget { }); final CryptoAccountAddress address; - final bool showBalance; final Color? color; @@ -47,7 +46,7 @@ class AddressDetailsView extends StatelessWidget { context.textTheme.labelLarge?.copyWith(color: color)), OneLineTextWidget(address.address.toAddress, style: context.textTheme.bodyMedium?.copyWith(color: color)), - OneLineTextWidget(address.keyIndex.path.tr, + OneLineTextWidget(address.keyIndex.toString(), style: context.textTheme.bodyMedium?.copyWith(color: color)), if (showBalance) CoinPriceView( diff --git a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/bip32_derivation.dart b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/bip32_derivation.dart index a1bfa95d..683b94b6 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/bip32_derivation.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/bip32_derivation.dart @@ -7,9 +7,17 @@ import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; class Bip32KeyDerivationView extends StatefulWidget { const Bip32KeyDerivationView( - {super.key, required this.coin, required this.curve}); + {super.key, + required this.coin, + required this.curve, + required this.network, + required this.defaultPath, + this.seedGeneration = SeedGenerationType.bip39}); final CryptoCoins coin; final EllipticCurveTypes curve; + final SeedGenerationType seedGeneration; + final AppNetworkImpl network; + final String? defaultPath; @override State createState() => _Bip32KeyDerivationViewState(); @@ -17,110 +25,56 @@ class Bip32KeyDerivationView extends StatefulWidget { class _Bip32KeyDerivationViewState extends State { final GlobalKey form = - GlobalKey(debugLabel: "_AddressTypePathSetupState"); - final Map> levelStateKeys = { - Bip44Levels.purpose: GlobalKey( - debugLabel: "_AddressTypePathSetupState_1"), - Bip44Levels.coin: GlobalKey( - debugLabel: "_AddressTypePathSetupState_2"), - Bip44Levels.account: GlobalKey( - debugLabel: "_AddressTypePathSetupState_3"), - Bip44Levels.change: GlobalKey( - debugLabel: "_AddressTypePathSetupState_4"), - Bip44Levels.addressIndex: GlobalKey( - debugLabel: "_AddressTypePathSetupState_5"), - }; - + GlobalKey(debugLabel: "_Bip32KeyDerivationViewState_form"); + final GlobalKey pathTextFieldKey = + GlobalKey( + debugLabel: "_Bip32KeyDerivationViewState_pathTextFieldKey"); late final bool isSupportNoneHardend; - late final int minIndex; - - String? validate(String? v, Bip44Levels level) { - if (levels[level] == null) { - return "bip32_key_index_validate".tr; - } - return null; - } - - final Map levels = { - Bip44Levels.purpose: null, - Bip44Levels.coin: null, - Bip44Levels.account: null, - Bip44Levels.change: null, - Bip44Levels.addressIndex: null, - }; - void onChangedValue(String? v, Bip44Levels level) { - if (v == null) return; - try { - final index = Bip44LevelsDetails.fromIntIndex(int.parse(v), level); - if (!index.isHardened && !isSupportNoneHardend) return; - levels[level] = Bip44LevelsDetails.fromIntIndex(int.parse(v), level); - } on Exception { - levels[level] = null; - } finally { - path = calculatePath(); - setState(() {}); - } - } - - String? helperText(Bip44Levels level) { - if (levels[level]?.isHardened ?? false) { - return "hardened_index" - .tr - .replaceOne(levels[level]!.unHardendValue.toString()); - } - return null; - } - - Color? hardenedColor(Bip44Levels level) { - return (levels[level]?.isHardened ?? false) - ? context.theme.iconTheme.color - : null; - } - - bool isHardened(Bip44Levels level) { - return (levels[level]?.isHardened ?? false); - } void onSubmit() { if (!(form.currentState?.validate() ?? false)) return; - final keyIndex = Bip32AddressIndex.fromBip44KeyIndexDetais( - indexes: levels.values.toList().cast(), - currencyCoin: widget.coin, - seedGeneration: SeedGenerationType.bip39); + final keyIndex = Bip32AddressIndex.fromPath( + path: path, + currencyCoin: widget.coin, + seedGeneration: widget.seedGeneration, + ); context.pop(keyIndex); } - void onTapHardened(Bip44Levels level) { - if (levels[level]?.isHardened ?? true) return; - stateKey(level) - .currentState - ?.changeIndex(Bip32KeyIndex.hardenIndex(levels[level]!.index).index); - } + late String path = widget.defaultPath ?? ""; - GlobalKey stateKey(Bip44Levels level) { - return levelStateKeys[level]!; + @override + void initState() { + super.initState(); + isSupportNoneHardend = widget.curve != EllipticCurveTypes.ed25519; } - String path = ""; + void onChangePath(String v) { + path = v; + } - String calculatePath() { - String p = "m"; - for (final i in levels.values) { - if (i == null) { - p += "/***"; - } else { - p += "/${i.path}"; + String? validator(String? v) { + if (path.trim().isEmpty) return null; + try { + final parse = Bip32PathParser.parse(path); + if (parse.elems.isEmpty) return null; + if (!isSupportNoneHardend && + parse.elems.any((element) => !element.isHardened)) { + return "ed25519_support_derivation_desc".tr; + } + if (parse.elems.length > BlockchainConstant.maxBip32LevelIndex) { + throw WalletException("hd_wallet_path_max_indeqxes" + .tr + .replaceOne(BlockchainConstant.maxBip32LevelIndex.toString())); } + } catch (e) { + return "invalid_hd_wallet_derivation_path".tr; } - return p; + return null; } - @override - void initState() { - super.initState(); - isSupportNoneHardend = widget.curve != EllipticCurveTypes.ed25519; - minIndex = - isSupportNoneHardend ? 0 : Bip32KeyDataConst.hardenKeyIndexMinValue; + void onPaste(String v) { + pathTextFieldKey.currentState?.updateText(v); } @override @@ -144,106 +98,17 @@ class _Bip32KeyDerivationViewState extends State { ]) ], )), - PageTitleSubtitle( - title: "choose_index_each_level".tr, - body: Text("bip32_level_desc".tr)), - Text("path".tr, style: context.textTheme.titleMedium), + Text("derivation_path".tr, style: context.textTheme.titleMedium), + Text("hd_wallet_hardened_desc".tr), WidgetConstant.height8, - ContainerWithBorder( - child: Text( - path, - style: context.textTheme.bodyLarge, - )), - WidgetConstant.height20, - NumberTextField( - label: "p_level".tr, - max: Bip32KeyDataConst.keyIndexMaxVal, - defaultValue: widget.coin.proposal.purpose.index, - helperText: helperText(Bip44Levels.purpose), - suffixIcon: _HardenIconView( - isHarden: (level) => isHardened(level), - level: Bip44Levels.purpose, - onTap: (level) => onTapHardened(level), - ), - key: stateKey(Bip44Levels.purpose), - min: minIndex, - onChange: (v) { - onChangedValue(v, Bip44Levels.purpose); - }, - validator: (v) => validate(v, Bip44Levels.purpose), - ), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: NumberTextField( - label: "c_level".tr, - max: Bip32KeyDataConst.keyIndexMaxVal, - helperText: helperText(Bip44Levels.coin), - key: stateKey(Bip44Levels.coin), - defaultValue: - Bip32KeyIndex.hardenIndex(widget.coin.conf.coinIdx).index, - suffixIcon: _HardenIconView( - isHarden: (level) => isHardened(level), - level: Bip44Levels.coin, - onTap: (level) => onTapHardened(level), - ), - min: minIndex, - onChange: (v) { - onChangedValue(v, Bip44Levels.coin); - }, - validator: (v) => validate(v, Bip44Levels.coin), - ), - ), - ], - ), - NumberTextField( - label: "a_level".tr, - max: Bip32KeyDataConst.keyIndexMaxVal, - helperText: helperText(Bip44Levels.account), - key: stateKey(Bip44Levels.account), - min: minIndex, - onChange: (v) { - onChangedValue(v, Bip44Levels.account); - }, - validator: (v) => validate(v, Bip44Levels.account), - suffixIcon: _HardenIconView( - isHarden: (level) => isHardened(level), - level: Bip44Levels.account, - onTap: (level) => onTapHardened(level), - ), - ), - NumberTextField( - label: "change_level".tr, - max: Bip32KeyDataConst.keyIndexMaxVal, - helperText: helperText(Bip44Levels.change), - key: stateKey(Bip44Levels.change), - suffixIcon: _HardenIconView( - isHarden: (level) => isHardened(level), - level: Bip44Levels.change, - onTap: (level) => onTapHardened(level), - ), - min: minIndex, - onChange: (v) { - onChangedValue(v, Bip44Levels.change); - }, - validator: (v) => validate(v, Bip44Levels.change), - ), - NumberTextField( - label: "address_index".tr, - max: Bip32KeyDataConst.keyIndexMaxVal, - helperText: helperText(Bip44Levels.addressIndex), - key: stateKey(Bip44Levels.addressIndex), - suffixIcon: _HardenIconView( - isHarden: (level) => isHardened(level), - level: Bip44Levels.addressIndex, - onTap: (level) => onTapHardened(level), - ), - min: minIndex, - onChange: (v) { - onChangedValue(v, Bip44Levels.addressIndex); - }, - validator: (v) => validate(v, Bip44Levels.addressIndex), + AppTextField( + onChanged: onChangePath, + initialValue: path, + suffixIcon: PasteTextIcon(onPaste: onPaste), + validator: validator, + key: pathTextFieldKey, + label: "derivation_path".tr, + hint: "derivation_path".tr, ), Row( mainAxisAlignment: MainAxisAlignment.center, @@ -260,33 +125,3 @@ class _Bip32KeyDerivationViewState extends State { ); } } - -typedef _IsHarden = bool Function(Bip44Levels); -typedef _OnTapLevel = void Function(Bip44Levels); - -class _HardenIconView extends StatelessWidget { - const _HardenIconView( - {required this.level, required this.isHarden, required this.onTap}); - final Bip44Levels level; - final _IsHarden isHarden; - final _OnTapLevel onTap; - @override - Widget build(BuildContext context) { - return InkWell( - borderRadius: WidgetConstant.border8, - onTap: () => onTap(level), - child: IgnorePointer( - ignoring: true, - child: Padding( - padding: WidgetConstant.padding5, - child: Column( - children: [ - Text("harden".tr, style: context.textTheme.bodySmall), - Checkbox(value: isHarden(level), onChanged: (v) {}) - ], - ), - ), - ), - ); - } -} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/byron_legacy_derivation.dart b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/byron_legacy_derivation.dart index 5798357b..7c710102 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/byron_legacy_derivation.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/byron_legacy_derivation.dart @@ -76,10 +76,11 @@ class _ByronLegacyKeyDerivationViewState void onSubmit() { if (!(form.currentState?.validate() ?? false)) return; - final keyIndex = ByronLegacyAddressIndex( - firstIndex: levels[Bip44Levels.change]!.index, - secondIndex: levels[Bip44Levels.addressIndex]!.index, - currencyCoin: widget.coin); + final keyIndex = Bip32AddressIndex.byronLegacy( + firstIndex: levels[Bip44Levels.change]!.index, + secoundIndex: levels[Bip44Levels.addressIndex]!.index, + currencyCoin: widget.coin, + ); context.pop(keyIndex); } diff --git a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/receipt_address_view.dart b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/receipt_address_view.dart index 7e4ef675..2c7c6b58 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/receipt_address_view.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/receipt_address_view.dart @@ -12,13 +12,15 @@ class ReceiptAddressView extends StatelessWidget { super.key, this.subtitle, this.validate, - this.onEditIcon}); + this.onEditIcon, + this.onTapWhenOnRemove = true}); final ReceiptAddress? address; final DynamicVoid? onTap; final String? title; final String? subtitle; final bool? validate; final Icon? onEditIcon; + final bool onTapWhenOnRemove; @override Widget build(BuildContext context) { return Column( @@ -33,6 +35,7 @@ class ReceiptAddressView extends StatelessWidget { ContainerWithBorder( validate: validate ?? (address != null), onRemove: onTap, + onTapWhenOnRemove: onTapWhenOnRemove, onRemoveIcon: address == null ? const Icon(Icons.add) : onEditIcon ?? const Icon(Icons.edit), @@ -61,7 +64,7 @@ class ReceiptAddressDetailsView extends StatelessWidget { Text(address.contact!.name, style: context.textTheme.labelSmall?.copyWith(color: color)) else if (address.isAccount) - Text(address.account!.keyIndex.path.tr, + Text(address.account!.keyIndex.toString(), style: context.textTheme.labelSmall?.copyWith(color: color)), OneLineTextWidget( address.view, diff --git a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/select_account_or_contact.dart b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/select_account_or_contact.dart index e27874bf..92544774 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/select_account_or_contact.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/select_account_or_contact.dart @@ -16,10 +16,12 @@ class SelectRecipientAccountView extends StatefulWidget { {super.key, required this.account, required this.scrollController, - this.subtitle}); + this.subtitle, + this.multipleSelect = true}); final NetworkAccountCore account; final ScrollController scrollController; final Widget? subtitle; + final bool multipleSelect; @override State createState() => diff --git a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/wallet_global_pages.dart b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/wallet_global_pages.dart index 069bca25..24d1ad2e 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/wallet_global_pages.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/wallet_global_pages.dart @@ -7,7 +7,6 @@ export 'switch_network.dart'; export 'wallet_signing_password.dart'; export 'select_provider.dart'; export 'add_to_contact_list.dart'; -export 'address_derivation_type.dart'; export 'bip32_derivation.dart'; export 'receipt_address_view.dart'; export 'select_account_or_contact.dart'; diff --git a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/wallet_signing_password.dart b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/wallet_signing_password.dart index 714eb830..3806cb17 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/global_pages/wallet_signing_password.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/global_pages/wallet_signing_password.dart @@ -186,7 +186,7 @@ class _WalletSigningPasswordState extends State return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _KeyIndexDetails(keyIndex: keyIndex), + _HDWalletDerivationDetails(keyIndex: keyIndex), if (!isLastIndex) Divider( color: context.colors.onPrimaryContainer) @@ -227,40 +227,6 @@ class _WalletSigningPasswordState extends State } } -class _KeyIndexDetails extends StatelessWidget { - const _KeyIndexDetails({required this.keyIndex}); - final AddressDerivationIndex keyIndex; - @override - Widget build(BuildContext context) { - switch (keyIndex.runtimeType) { - case ImportedAddressIndex: - return _ImportedKeyDerivationDetails( - keyIndex: keyIndex as ImportedAddressIndex); - default: - return _HDWalletDerivationDetails(keyIndex: keyIndex); - } - } -} - -class _ImportedKeyDerivationDetails extends StatelessWidget { - const _ImportedKeyDerivationDetails({required this.keyIndex}); - final ImportedAddressIndex keyIndex; - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("imported".tr, style: context.textTheme.labelLarge), - Text(keyIndex.accountId), - if (keyIndex.bip32KeyIndex != null) ...[ - Text(keyIndex.bip32KeyIndex!.toString(), - style: context.textTheme.bodySmall) - ] - ], - ); - } -} - class _HDWalletDerivationDetails extends StatelessWidget { const _HDWalletDerivationDetails({required this.keyIndex}); final AddressDerivationIndex keyIndex; diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/bitcoin_pages/setup_address.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/bitcoin_pages/setup_address.dart index c3c9d288..b5a1d566 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/bitcoin_pages/setup_address.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/bitcoin_pages/setup_address.dart @@ -1,14 +1,13 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; -import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:flutter/material.dart'; import 'package:mrt_wallet/app/core.dart'; import 'package:mrt_wallet/future/pages/start_page/home.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/address_derivation/setup_address_derivation_mode.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/wallet_global_pages.dart'; import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; import 'package:mrt_wallet/main.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; - import 'package:mrt_wallet/types/typedef.dart'; class SetupBitcoinAddressView extends StatefulWidget { @@ -25,39 +24,11 @@ class _SetupBitcoinAddressViewState extends State GlobalKey(debugLabel: "SetupBitcoinAddressView"); late final AppChain chainAccount; final GlobalKey visibleGenerateAddress = - GlobalKey(debugLabel: "visibleContinue"); - - AddressDerivationMode? selectedDerivationMode; - EncryptedCustomKey? selectedCustomKey; - bool inAddressPage = false; - - void goToAddressPage( - AddressDerivationMode derivationMode, EncryptedCustomKey? customKey) { - if (derivationMode == AddressDerivationMode.importedKey && - customKey == null) return; - selectedDerivationMode = derivationMode; - selectedCustomKey = customKey; - inAddressPage = true; - setState(() {}); - ensureKeyVisible(key: visibleGenerateAddress); - } - - void _onBackButton() { - if (pageProgressKey.isSuccess) return; - if (inAddressPage) { - selectedDerivationMode = null; - selectedCustomKey = null; - inAddressPage = false; - p2shType = _defaultP2sh(); - customKeyIndex = null; - } - setState(() {}); - } + GlobalKey(debugLabel: "_SetupBitcoinAddressViewState_visibleContinue"); AppBitcoinNetwork get network => chainAccount.network as AppBitcoinNetwork; List get coins => network.coins; bool inited = false; - Bip32AddressIndex? customKeyIndex; late final List p2shTypes; late final List p2pkhTypes; late final List supportAddressTypes; @@ -114,23 +85,11 @@ class _SetupBitcoinAddressViewState extends State selected = value; p2shType = _defaultP2sh(); p2pkhType = P2pkhAddressType.p2pkh; - customKeyIndex = null; setState(() {}); } - bool get derivationStandard => customKeyIndex == null; late P2shAddressType p2shType; P2pkhAddressType p2pkhType = P2pkhAddressType.p2pkh; - void onChangeDerivation(bool? val, DynamicVoid onFalse) { - if (val == null) return; - if (derivationStandard) { - onFalse(); - return; - } else { - customKeyIndex = null; - setState(() {}); - } - } void onChageSegwit(P2shAddressType? val) { if (!p2shTypes.contains(val)) return; @@ -144,34 +103,7 @@ class _SetupBitcoinAddressViewState extends State setState(() {}); } - void setupKeyIndex(Bip32AddressIndex? newKeyIndex) { - customKeyIndex = newKeyIndex; - setState(() {}); - } - - @override - void didChangeDependencies() { - _setupIAccount(); - super.didChangeDependencies(); - } - - AddressDerivationIndex? derivationkey(CryptoCoins coin) { - if (selectedCustomKey != null) { - return ImportedAddressIndex( - accountId: selectedCustomKey!.id, - bip32KeyIndex: customKeyIndex, - currencyCoin: coin); - } - return customKeyIndex; - } - - AddressDerivationIndex get standardDerivation { - final coin = findCoin(); - return chainAccount.account.nextDrive(coin); - } - void generateAddress() async { - pageProgressKey.progressText("generating_new_addr".tr); final wallet = context.watch(StateIdsConst.main); final coin = findCoin(); BitcoinAddressType selectedType; @@ -182,15 +114,22 @@ class _SetupBitcoinAddressViewState extends State } else { selectedType = selected.first; } - final keyIndex = - derivationkey(coin) ?? chainAccount.account.nextDrive(coin); + + final keyIndex = await context.openSliverBottomSheet( + "setup_derivation".tr, + child: SetupDerivationModeView( + coin: coin, + chainAccout: chainAccount, + )); + if (keyIndex == null) return; + pageProgressKey.progressText("generating_new_addr".tr); NewAccountParams newAccount; if (network is AppBitcoinCashNetwork) { newAccount = BitcoinCashNewAddressParams( - coin: coin, deriveIndex: keyIndex, bitcoinAddressType: selectedType); + deriveIndex: keyIndex, bitcoinAddressType: selectedType); } else { newAccount = BitcoinNewAddressParams( - coin: coin, deriveIndex: keyIndex, bitcoinAddressType: selectedType); + deriveIndex: keyIndex, bitcoinAddressType: selectedType); } final result = await wallet.deriveNewAccount(newAccount); @@ -215,95 +154,66 @@ class _SetupBitcoinAddressViewState extends State setState(() {}); } + @override + void didChangeDependencies() { + _setupIAccount(); + super.didChangeDependencies(); + } + @override Widget build(BuildContext context) { - return PopScope( - canPop: !inAddressPage || pageProgressKey.isSuccess, - onPopInvoked: (didPop) { - if (!didPop) { - _onBackButton(); - } - }, - child: Scaffold( - appBar: AppBar( - title: Text("setup_address".tr), - ), - body: PageProgress( - key: pageProgressKey, - backToIdle: AppGlobalConst.oneSecoundDuration, - initialStatus: PageProgressStatus.idle, - child: () => UnfocusableChild( - child: Center( - child: CustomScrollView( - shrinkWrap: true, - slivers: [ - SliverToBoxAdapter( - child: ConstraintsBoxView( - padding: WidgetConstant.paddingHorizontal20, - child: AnimatedSwitcher( - duration: AppGlobalConst.animationDuraion, - child: inAddressPage - ? Column( - key: const ValueKey(true), - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _DriveFromHdWallet( - customKeyIndex: customKeyIndex, - onVisibleGenerateAddress: - visibleGenerateAddress, - nextStandartIndex: standardDerivation, - onChangeDeravation: (p0) { - onChangeDerivation( - p0, - () { - context - .openSliverBottomSheet< - Bip32AddressIndex>( - "key_derivation".tr, - child: Bip32KeyDerivationView( - coin: findCoin(), - curve: EllipticCurveTypes - .secp256k1)) - .then(setupKeyIndex); - }, - ); - }, - derivationStandard: derivationStandard, - typesToSelect: typesToSelect, - generateAddress: generateAddress, - p2shTypes: p2shTypes, - p2pkhTypes: p2pkhTypes, - onChageSegwit: onChageSegwit, - onChangeSelected: onChangeSelected, - selected: selected, - selectedP2shType: p2shType, - customKey: selectedCustomKey, - onChangeP2pkh: onChangeP2pkh, - selectedP2pkhType: p2pkhType, - ), - ], - ) - : Column( - children: [ - WidgetConstant.height20, - PageTitleSubtitle( - title: "derive_network_address" - .tr - .replaceOne(network.coinParam.token.name), - body: LargeTextView([ - "bip44_desc".tr, - "bip49_desc".tr, - if (isSupportSegwit) "bip84_desc".tr, - if (isSupportP2tr) "bip86_desc".tr - ]), - ), - SetupAddressDerivation(goToAddressPage) - ], - ), - ), - )) - ], - ), + return Scaffold( + appBar: AppBar( + title: Text("setup_address".tr), + ), + body: PageProgress( + key: pageProgressKey, + backToIdle: AppGlobalConst.oneSecoundDuration, + initialStatus: PageProgressStatus.idle, + child: () => UnfocusableChild( + child: Center( + child: CustomScrollView( + shrinkWrap: true, + slivers: [ + SliverToBoxAdapter( + child: ConstraintsBoxView( + padding: WidgetConstant.paddingHorizontal20, + child: Column( + key: const ValueKey(true), + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PageTitleSubtitle( + title: "setup_network_address" + .tr + .replaceOne(network.coinParam.token.name), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("disable_standard_derivation".tr), + WidgetConstant.height8, + Text("setup_address_derivation_keys_desc".tr), + WidgetConstant.height8, + Text("please_following_steps_to_generate_address" + .tr), + ], + )), + _DriveFromHdWallet( + onVisibleGenerateAddress: visibleGenerateAddress, + typesToSelect: typesToSelect, + generateAddress: generateAddress, + p2shTypes: p2shTypes, + p2pkhTypes: p2pkhTypes, + onChageSegwit: onChageSegwit, + onChangeSelected: onChangeSelected, + selected: selected, + selectedP2shType: p2shType, + onChangeP2pkh: onChangeP2pkh, + selectedP2pkhType: p2pkhType, + ), + ], + ), + )) + ], ), ), ), @@ -315,35 +225,27 @@ class _SetupBitcoinAddressViewState extends State class _DriveFromHdWallet extends StatelessWidget { const _DriveFromHdWallet( {required this.selected, - required this.customKeyIndex, - required this.onChangeDeravation, required this.generateAddress, - required this.derivationStandard, required this.p2shTypes, required this.onChageSegwit, required this.onChangeSelected, required this.selectedP2shType, required this.onVisibleGenerateAddress, required this.typesToSelect, - required this.nextStandartIndex, + // required this.nextStandartIndex, required this.p2pkhTypes, required this.onChangeP2pkh, - required this.selectedP2pkhType, - this.customKey}); - final AddressDerivationIndex nextStandartIndex; - final AddressDerivationIndex? customKeyIndex; + required this.selectedP2pkhType}); + final Set selected; final _OnChangeP2shType onChageSegwit; final DynamicVoid generateAddress; final VoidSetT onChangeSelected; - final bool derivationStandard; final List p2shTypes; final List p2pkhTypes; final P2shAddressType selectedP2shType; final P2pkhAddressType selectedP2pkhType; final _OnChangeP2pkhTypes onChangeP2pkh; - final NullBoolVoid onChangeDeravation; - final EncryptedCustomKey? customKey; final GlobalKey onVisibleGenerateAddress; final Map typesToSelect; @@ -356,67 +258,34 @@ class _DriveFromHdWallet extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - PageTitleSubtitle( - title: "choose_bitcoin_address_type".tr, - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("choose_bitcoin_address_type_desc".tr), - WidgetConstant.height8, - if (supportP2trOrSegwit) Text("bitcoin_type_recomended".tr), - WidgetConstant.height8, - if (customKey != null) ...[ - Text("generate_from_imported_keys".tr), - WidgetConstant.height8, - Text("generate_from_imported_key_desc1".tr) - ] else ...[ - Text("generate_from_hd_wallet".tr), - ] - ], - )), - AppSwitchListTile( - value: derivationStandard, - onChanged: onChangeDeravation, - title: customKey == null - ? Text(derivationStandard - ? "standard_derivation".tr - : "custom_derivation".tr) - : Text(customKeyIndex == null - ? "non_derivation".tr - : "custom_derivation".tr), - subtitle: customKey == null - ? Text(customKeyIndex?.path ?? nextStandartIndex.path) - : Text(customKeyIndex?.path ?? "import_key_derivation_desc2".tr), - ), + Text("choose_bitcoin_address_type".tr, + style: context.textTheme.titleMedium), + Text("bitcoin_type_recomended".tr), WidgetConstant.height8, - SizedBox( - width: context.mediaQuery.size.width, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - AppSegmentedButton( - items: typesToSelect, - onChangeSelected: onChangeSelected, - selected: selected), - WidgetConstant.height20, - _AddressTypeOPtion( - select: selected.first, - deriveStandard: derivationStandard, - p2shTypes: p2shTypes, - selectedP2shType: selectedP2shType, - onChangeP2shSegwit: onChageSegwit, - p2pkhTypes: p2pkhTypes, - onChangeP2pkhAddress: onChangeP2pkh, - selectP2pkhType: selectedP2pkhType, - ), - FixedElevatedButton( - padding: WidgetConstant.paddingVertical20, - onPressed: generateAddress, - key: onVisibleGenerateAddress, - child: Text("generate_address".tr), - ) - ], - ), + AppSegmentedButton( + items: typesToSelect, + onChangeSelected: onChangeSelected, + selected: selected), + WidgetConstant.height20, + _AddressTypeOPtion( + select: selected.first, + p2shTypes: p2shTypes, + selectedP2shType: selectedP2shType, + onChangeP2shSegwit: onChageSegwit, + p2pkhTypes: p2pkhTypes, + onChangeP2pkhAddress: onChangeP2pkh, + selectP2pkhType: selectedP2pkhType, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FixedElevatedButton( + padding: WidgetConstant.paddingVertical20, + onPressed: generateAddress, + key: onVisibleGenerateAddress, + child: Text("setup_derivation".tr), + ), + ], ) ], ); @@ -430,7 +299,6 @@ class _AddressTypeOPtion extends StatelessWidget { const _AddressTypeOPtion( {required this.select, required this.p2pkhTypes, - required this.deriveStandard, required this.selectedP2shType, required this.onChangeP2shSegwit, required this.p2shTypes, @@ -439,7 +307,6 @@ class _AddressTypeOPtion extends StatelessWidget { final List p2shTypes; final List p2pkhTypes; final BitcoinAddressType select; - final bool deriveStandard; final P2shAddressType selectedP2shType; final P2pkhAddressType selectP2pkhType; final _OnChangeP2shType onChangeP2shSegwit; diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/account_page.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/account_page.dart new file mode 100644 index 00000000..96419b69 --- /dev/null +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/account_page.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:mrt_wallet/app/core.dart'; +import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; +import 'package:mrt_wallet/models/wallet_models/address/network_address/cardano/cardano.dart'; +import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; +import 'package:on_chain/on_chain.dart'; + +class CardanoAccountPage extends StatelessWidget { + const CardanoAccountPage({required this.chainAccount, super.key}); + final AppChain chainAccount; + @override + Widget build(BuildContext context) { + return TabBarView(children: [ + _CardanoAccountPage( + chainAccount: chainAccount.account.address as ICardanoAddress), + ]); + } +} + +class _CardanoAccountPage extends StatelessWidget { + const _CardanoAccountPage({required this.chainAccount}); + final ICardanoAddress chainAccount; + + @override + Widget build(BuildContext context) { + return Column( + children: [_ShowRewardAddress(chainAccount: chainAccount)], + ); + } +} + +class _ShowRewardAddress extends StatelessWidget { + const _ShowRewardAddress({required this.chainAccount}); + final ICardanoAddress chainAccount; + @override + Widget build(BuildContext context) { + final ADARewardAddress? rewardAddress = chainAccount.rewardAddress; + if (rewardAddress == null) return WidgetConstant.sizedBox; + + return ContainerWithBorder( + onRemove: () {}, + onTapWhenOnRemove: false, + onRemoveWidget: CopyTextIcon(dataToCopy: rewardAddress.address), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(rewardAddress.addressType.name, + style: context.textTheme.labelLarge), + Text( + rewardAddress.address, + maxLines: 1, + ), + ], + ), + ); + } +} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/setup_address_page.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/setup_address_page.dart index 52b3992e..c944338f 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/setup_address_page.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/setup_address_page.dart @@ -3,17 +3,25 @@ import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:flutter/material.dart'; import 'package:mrt_wallet/app/core.dart'; import 'package:mrt_wallet/future/pages/start_page/home.dart'; -import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/byron_legacy_derivation.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/address_derivation/setup_address_derivation_mode.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/wallet_global_pages.dart'; import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; import 'package:mrt_wallet/main.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; import 'package:mrt_wallet/types/typedef.dart'; -typedef _OnChangeDerivation = void Function(bool? val, DynamicVoid onFalse); -typedef _SetupKeyIndex = void Function(AddressDerivationIndex? newKeyIndex); typedef _OnChangeShellyAddrType = void Function(ADAAddressType? addrType); +enum _Bip32DerivationMode { + customDerivation("custom_derivation"), + standardDerivation("standard_derivation"); + + const _Bip32DerivationMode(this.viewName); + final String viewName; + bool get isStandard => this == _Bip32DerivationMode.standardDerivation; + bool get isCustom => this == _Bip32DerivationMode.customDerivation; +} + enum _AdaEra { shelly("shelly"), byron("byron"); @@ -21,6 +29,48 @@ enum _AdaEra { final String name; const _AdaEra(this.name); bool get isShelly => this == _AdaEra.shelly; + CryptoCoins getRelatedIcarusCardanoCoin(APPCardanoNetwork network) { + if (isShelly) { + if (network.coinParam.mainnet) { + return Cip1852Coins.cardanoIcarus; + } else { + return Cip1852Coins.cardanoIcarusTestnet; + } + } else { + if (network.coinParam.mainnet) { + return Bip44Coins.cardanoByronIcarus; + } else { + return Bip44Coins.cardanoByronIcarusTestnet; + } + } + } + + CryptoCoins getRelatedLedgerCardanoCoin(APPCardanoNetwork network) { + if (isShelly) { + if (network.coinParam.mainnet) { + return Cip1852Coins.cardanoLedger; + } else { + return Cip1852Coins.cardanoLedgerTestnet; + } + } else { + if (network.coinParam.mainnet) { + return Bip44Coins.cardanoByronLedger; + } else { + return Bip44Coins.cardanoByronLedgerTestnet; + } + } + } + + CryptoCoins getRelatedByronLegacy(APPCardanoNetwork network) { + if (isShelly) { + throw WalletException.invalidArgruments(["byron", "shelly"]); + } + if (network.coinParam.mainnet) { + return CustomCoins.byronLegacy; + } else { + return CustomCoins.byronLegacyTestnet; + } + } } enum _CardanoMasterKeyGenerationType { @@ -40,7 +90,6 @@ enum _GenerateAddressPage { generateAddress } -/// add testnet coin class SetupCardanoAddressView extends StatefulWidget { const SetupCardanoAddressView({super.key}); @@ -53,32 +102,81 @@ class _SetupCardanoAddressViewState extends State with SafeState { final GlobalKey pageProgressKey = GlobalKey(debugLabel: "SetupEthereumAddressView"); - final GlobalKey form = GlobalKey(); - late final AppChain chainAccount; - AddressDerivationIndex get standardDerivation { - return chainAccount.account.nextDrive(coin, - masterKeyGeneration: - keyGenerationType == _CardanoMasterKeyGenerationType.byronLegacy - ? SeedGenerationType.byronLegacySeed - : SeedGenerationType.bip39, - seedGeneration: seedGenerationType); - } - - NetworkAccountCore get networkAccounts => chainAccount.account; final GlobalKey visibleGenerateAddress = GlobalKey(debugLabel: "visibleContinue"); final GlobalKey visibleXAddressDetails = GlobalKey(debugLabel: "visibleContinue"); - AddressDerivationMode? selectedDerivationMode; - EncryptedCustomKey? selectedCustomKey; + final GlobalKey form = GlobalKey(); + final GlobalKey hdPathKeyKey = + GlobalKey(debugLabel: "hdPathKey"); + final GlobalKey hdPathKey = + GlobalKey(debugLabel: "hdPath"); + + late final AppChain chainAccount; + NetworkAccountCore get networkAccounts => chainAccount.account; + APPCardanoNetwork get network => networkAccounts.network as APPCardanoNetwork; + List get coins => network.coins; + late CryptoCoins coin = coins.first; + + bool inited = false; + _AdaEra era = _AdaEra.shelly; _GenerateAddressPage page = _GenerateAddressPage.seedGeneration; - bool byronLegacy = false; - void changeByronLegacy(bool? change) { - byronLegacy = !byronLegacy; + _CardanoMasterKeyGenerationType keyGenerationType = + _CardanoMasterKeyGenerationType.icarus; + SeedGenerationType seedGenerationType = SeedGenerationType.icarus; + + ADAAddressType addrType = ADAAddressType.base; + + String? manuallyHdPath; + String? manuallyHdPathKey; + bool manuallySetLegacyHdPathKey = false; + + void onChangeManuallySetHdPathKey() { + manuallySetLegacyHdPathKey = !manuallySetLegacyHdPathKey; + if (!manuallySetLegacyHdPathKey) { + manuallyHdPath = null; + manuallyHdPathKey = null; + } setState(() {}); } + void onChageHdPath(String path) { + manuallyHdPath = path; + } + + void onChangeHdPathKey(String v) { + manuallyHdPathKey = v; + } + + String? onValidateHdPath(String? v) { + try { + final parse = Bip32PathParser.parse(manuallyHdPath!); + if (parse.elems.length != + BlockchainConstant.maxByronLegacyBip32LevelIndex) { + return "byron_legacy_hd_wallet_length_desc".tr; + } + } catch (e) { + return "invalid_byron_legacy_hd_path_key".tr; + } + return null; + } + + String? onValidateHdPathKey(String? v) { + try { + final inBytes = BytesUtils.tryFromHexString(manuallyHdPathKey); + if (inBytes == null) { + return "byron_legacy_hd_path_key_desc".tr; + } + if (inBytes.length != CardanoUtils.byronAddressHdPathKeyLengthBytes) { + return "byron_legacy_hd_path_key_length_desc".tr; + } + } catch (e) { + return "byron_legacy_hd_path_key_desc".tr; + } + return null; + } + void onChangeEra(_AdaEra? e) { era = e ?? era; setState(() {}); @@ -100,59 +198,40 @@ class _SetupCardanoAddressViewState extends State setState(() {}); } - _CardanoMasterKeyGenerationType keyGenerationType = - _CardanoMasterKeyGenerationType.icarus; - void onChangeMasterKeyGeneration(_CardanoMasterKeyGenerationType? e) { - keyGenerationType = e ?? keyGenerationType; + bool get showLegacy => + !era.isShelly && + (seedGenerationType == SeedGenerationType.byronLegacySeed || + seedGenerationType == SeedGenerationType.none); + void onChangeMasterKeyGeneration( + _CardanoMasterKeyGenerationType? masterKeyGeneration) { + if (masterKeyGeneration == null) return; + if (era.isShelly && masterKeyGeneration.isLegacy) return; + keyGenerationType = masterKeyGeneration; setState(() {}); } - SeedGenerationType seedGenerationType = SeedGenerationType.icarus; - void onChangeSeedGeneration(SeedGenerationType? e) { - if (e == SeedGenerationType.bip39) return; - seedGenerationType = e ?? seedGenerationType; + void onChangeSeedGeneration(SeedGenerationType? seedType) { + if (seedType == SeedGenerationType.bip39) return; + seedGenerationType = seedType ?? seedGenerationType; setState(() {}); } void onContinueFromMasterkeyGeneration() { - page = _GenerateAddressPage.generateAddress; switch (keyGenerationType) { case _CardanoMasterKeyGenerationType.icarus: - if (era.isShelly) { - if (network.coinParam.mainnet) { - coin = Cip1852Coins.cardanoIcarus; - } else { - coin = Cip1852Coins.cardanoIcarusTestnet; - } - } else { - if (network.coinParam.mainnet) { - coin = Bip44Coins.cardanoByronIcarus; - } else { - coin = Bip44Coins.cardanoByronIcarusTestnet; - } - } + coin = era.getRelatedIcarusCardanoCoin(network); + break; + case _CardanoMasterKeyGenerationType.byronLegacy: + coin = era.getRelatedByronLegacy(network); break; default: - if (era.isShelly) { - if (network.coinParam.mainnet) { - coin = Cip1852Coins.cardanoLedger; - } else { - coin = Cip1852Coins.cardanoLedgerTestnet; - } - } else { - if (network.coinParam.mainnet) { - coin = Bip44Coins.cardanoByronLedger; - } else { - coin = Bip44Coins.cardanoByronLedgerTestnet; - } - } + coin = era.getRelatedLedgerCardanoCoin(network); break; } + page = _GenerateAddressPage.generateAddress; setState(() {}); } - ADAAddressType addrType = ADAAddressType.base; - void onChangeShellyddrType(ADAAddressType? type) { if (type == null) return; if (era == _AdaEra.byron) return; @@ -161,109 +240,98 @@ class _SetupCardanoAddressViewState extends State setState(() {}); } - bool inAddressPage = false; - - void goToAddressPage( - AddressDerivationMode derivationMode, EncryptedCustomKey? customKey) { - if (derivationMode == AddressDerivationMode.importedKey && - customKey == null) return; - selectedDerivationMode = derivationMode; - selectedCustomKey = customKey; - inAddressPage = true; - setState(() {}); - ensureKeyVisible(key: visibleGenerateAddress); - } - - void _onBackButton() { - if (pageProgressKey.isSuccess) return; - if (inAddressPage) { - selectedDerivationMode = null; - selectedCustomKey = null; - inAddressPage = false; - customKeyIndex = null; - page = _GenerateAddressPage.seedGeneration; - keyGenerationType = _CardanoMasterKeyGenerationType.icarus; - addrType = ADAAddressType.base; - era = _AdaEra.shelly; - } - setState(() {}); - } - - APPCardanoNetwork get network => networkAccounts.network as APPCardanoNetwork; - List get coins => network.coins; - late CryptoCoins coin = coins.firstWhere((element) => - element.conf.type == - (selectedCustomKey?.type ?? EllipticCurveTypes.ed25519Kholaw)); - bool inited = false; - AddressDerivationIndex? customKeyIndex; - void _setupIAccount() { if (inited) return; final model = context.watch(StateIdsConst.main); chainAccount = model.chain; } - bool get derivationStandard => customKeyIndex == null; - void onChangeDerivation(bool? val, DynamicVoid onFalse) { - if (val == null) return; - if (derivationStandard) { - onFalse(); - return; - } else { - customKeyIndex = null; - setState(() {}); - } - } - - void setupKeyIndex(AddressDerivationIndex? newKeyIndex) { - customKeyIndex = newKeyIndex; - setState(() {}); - } - - @override - void didChangeDependencies() { - _setupIAccount(); - super.didChangeDependencies(); - } - - AddressDerivationIndex? derivationkey(CryptoCoins coin) { - if (selectedCustomKey != null) { - return ImportedAddressIndex( - accountId: selectedCustomKey!.id, - bip32KeyIndex: customKeyIndex, - currencyCoin: coin); - } - return customKeyIndex; - } - void generateAddress() async { if (!(form.currentState?.validate() ?? false)) return; - pageProgressKey.progressText("generating_new_addr".tr); - final model = context.watch(StateIdsConst.main); - final keyIndex = derivationkey(coin) ?? - chainAccount.account.nextDrive(coin, - seedGeneration: seedGenerationType, - masterKeyGeneration: - keyGenerationType == _CardanoMasterKeyGenerationType.byronLegacy - ? SeedGenerationType.byronLegacySeed - : SeedGenerationType.bip39); - - final newAccount = CardanoNewAddressParams( - coin: coin, + final result = await MethodCaller.call(() async { + pageProgressKey.progressText("generating_new_addr".tr); + final model = context.watch(StateIdsConst.main); + Bip32AddressIndex? keyIndex = await context + .openSliverBottomSheet("setup_derivation".tr, + child: SetupDerivationModeView( + coin: coin, + chainAccout: chainAccount, + )); + if (keyIndex == null) { + return null; + } + keyIndex = keyIndex.copyWith(keyName: "base_key"); + Bip32AddressIndex? stakeDerivation; + if (addrType == ADAAddressType.base) { + final defaultStakeKey = keyIndex.copyWith( + changeLevel: CardanoUtils.bip32StakeChangeLevel, + addressIndex: CardanoUtils.bip32StakeAddressLevel); + // ignore: use_build_context_synchronously + stakeDerivation = await context + .openSliverBottomSheet("setup_derivation".tr, + child: SetupDerivationModeView( + coin: coin, + chainAccout: chainAccount, + defaultDerivation: defaultStakeKey, + title: PageTitleSubtitle( + title: "derive_network_address" + .tr + .replaceOne(network.coinParam.token.name), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("disable_standard_derivation".tr), + WidgetConstant.height8, + Text("stake_key_derivation".tr), + WidgetConstant.height8, + Text("ada_base_stake_key_same_error".tr) + ], + )), + )); + if (stakeDerivation == null) return null; + stakeDerivation = stakeDerivation.copyWith(keyName: "stake_key"); + } + if (addrType == ADAAddressType.base && keyIndex == stakeDerivation) { + throw WalletException("ada_base_stake_key_same_error"); + } + String? hdPath; + List? hdPathKey; + if (keyGenerationType.isLegacy) { + if (manuallySetLegacyHdPathKey) { + hdPath = BlockchainUtils.validateHdPathKey(manuallyHdPath!, + maxIndex: BlockchainConstant.maxByronLegacyBip32LevelIndex); + hdPathKey = BytesUtils.tryFromHexString(manuallyHdPathKey!); + } else { + if (keyIndex.hdPath == null) { + hdPath = ""; + hdPathKey = List.filled(32, 0); + } + } + } + + final newAccount = CardanoNewAddressParams( deriveIndex: keyIndex, addressType: addrType, - byronLegacy: era.isShelly ? null : byronLegacy); + rewardKeyIndex: stakeDerivation, + customHdPath: hdPath, + customHdPathKey: hdPathKey, + ); + final result = await model.deriveNewAccount(newAccount); + if (result.hasError) throw result.exception!; + return result.result; + }); - final result = await model.deriveNewAccount(newAccount); if (result.hasError) { pageProgressKey.errorText(result.error!.tr); + } else if (result.result == null) { + pageProgressKey.backToIdle(); } else { pageProgressKey.success( backToIdle: false, progressWidget: SuccessWithButtomView( buttomWidget: ContainerWithBorder( margin: WidgetConstant.paddingVertical8, - child: AddressDetailsView(address: result.result)), + child: AddressDetailsView(address: result.result!)), buttomText: "generate_new_address".tr, onPressed: () { if (mounted) { @@ -275,29 +343,22 @@ class _SetupCardanoAddressViewState extends State setState(() {}); } - String? byronLegacyDerivationValidator(String? v, int index) { - final int? parse = int.tryParse(v ?? ""); - if (parse == null) return "invalid_derivation_index".tr; - if (parse.isNegative || parse > Bip32KeyDataConst.keyIndexMaxVal) { - return "invalid_derivation_index".tr; - } - return null; + @override + void didChangeDependencies() { + _setupIAccount(); + super.didChangeDependencies(); } @override Widget build(BuildContext context) { return PopScope( - canPop: !inAddressPage || pageProgressKey.isSuccess, - onPopInvoked: (didPop) { - if (!didPop) { - _onBackButton(); - } - }, + canPop: true, + onPopInvoked: (didPop) {}, child: Scaffold( appBar: AppBar(title: Text("setup_address".tr)), body: PageProgress( key: pageProgressKey, - backToIdle: AppGlobalConst.oneSecoundDuration, + backToIdle: AppGlobalConst.twoSecoundDuration, initialStatus: PageProgressStatus.idle, child: () => UnfocusableChild( child: Center( @@ -307,85 +368,81 @@ class _SetupCardanoAddressViewState extends State SliverToBoxAdapter( child: ConstraintsBoxView( padding: WidgetConstant.paddingHorizontal20, - child: AnimatedSwitcher( - duration: AppGlobalConst.animationDuraion, - child: inAddressPage - ? Form( - key: form, - child: Column( + child: Form( + key: form, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + key: const ValueKey(true), + children: [ + PageTitleSubtitle( + title: "setup_network_address" + .tr + .replaceOne(network.coinParam.token.name), + body: Column( crossAxisAlignment: CrossAxisAlignment.start, - key: const ValueKey(true), children: [ - AnimatedSwitcher( - duration: AppGlobalConst.animationDuraion, - child: page == - _GenerateAddressPage.seedGeneration - ? _SelectSeedGenerationType( - seedGenerationType: - seedGenerationType, - onChageSeedGeneration: - onChangeSeedGeneration, - onContinue: - onContinueFromSeedGeneration) - : page == _GenerateAddressPage.era - ? _SelectEra( - era: era, - onChangeEra: onChangeEra, - onContinue: onContinueFromEra) - : page == - _GenerateAddressPage - .masterKeyGeneration - ? _SelectMasterKeyGeneration( - keyGenerationType: - keyGenerationType, - era: era, - onChangeKeyGeneration: - onChangeMasterKeyGeneration, - onContinue: - onContinueFromMasterkeyGeneration, - seedGeneration: - seedGenerationType, - ) - : _GenerateAddress( - network: network, - addrType: addrType, - generateAddress: - generateAddress, - coin: coin, - derivationStandard: - derivationStandard, - era: era, - onChangeDerivation: - onChangeDerivation, - onChangeShellyddrType: - onChangeShellyddrType, - setupKeyIndex: - setupKeyIndex, - standardDerivation: - standardDerivation, - customKeyIndex: - customKeyIndex, - selectedCustomKey: - selectedCustomKey, - masterKeyGenerationType: - keyGenerationType), - ), + Text("disable_standard_derivation".tr), + WidgetConstant.height8, + Text("setup_address_derivation_keys_desc".tr), + WidgetConstant.height8, + Text( + "please_following_steps_to_generate_address" + .tr), ], - ), - ) - : Column( - children: [ - WidgetConstant.height20, - PageTitleSubtitle( - title: "derive_network_address" - .tr - .replaceOne(network.coinParam.token.name), - body: LargeTextView( - ["bip44_derivation_desc".tr]), - ), - SetupAddressDerivation(goToAddressPage) - ], - ), + )), + AnimatedSwitcher( + duration: AppGlobalConst.animationDuraion, + child: page == _GenerateAddressPage.seedGeneration + ? _SelectSeedGenerationType( + seedGenerationType: seedGenerationType, + onChageSeedGeneration: + onChangeSeedGeneration, + onContinue: onContinueFromSeedGeneration) + : page == _GenerateAddressPage.era + ? _SelectEra( + era: era, + onChangeEra: onChangeEra, + onContinue: onContinueFromEra) + : page == + _GenerateAddressPage + .masterKeyGeneration + ? _SelectMasterKeyGeneration( + keyGenerationType: + keyGenerationType, + era: era, + onChangeKeyGeneration: + onChangeMasterKeyGeneration, + onContinue: + onContinueFromMasterkeyGeneration, + seedGeneration: seedGenerationType, + ) + : _GenerateAddress( + network: network, + addrType: addrType, + generateAddress: generateAddress, + coin: coin, + era: era, + hdPathKeyKey: hdPathKeyKey, + keyGeneratorType: keyGenerationType, + hdpathKey: hdPathKey, + onChangedHdPath: onChageHdPath, + onChangedHdPathKey: + onChangeHdPathKey, + validatorHdPath: onValidateHdPath, + validatorHdPathKey: + onValidateHdPathKey, + onChangeShellyddrType: + onChangeShellyddrType, + hdPath: manuallyHdPath, + hdPathKey: manuallyHdPathKey, + manuallySetHdPathKey: + manuallySetLegacyHdPathKey, + onChangeManuallySetHdPathKey: + onChangeManuallySetHdPathKey, + ), + ), + ], + ), ), )) ], @@ -410,10 +467,12 @@ class _SelectEra extends StatelessWidget { @override Widget build(BuildContext context) { return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ PageTitleSubtitle( title: "cardano_era".tr, body: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("select_era_for_generate_addr".tr), WidgetConstant.height8, @@ -433,7 +492,13 @@ class _SelectEra extends StatelessWidget { onChanged: onChangeEra, ), WidgetConstant.height20, - ElevatedButton(onPressed: onContinue, child: Text("continue".tr)) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FixedElevatedButton( + onPressed: onContinue, child: Text("continue".tr)), + ], + ) ], ); } @@ -454,14 +519,20 @@ class _SelectMasterKeyGeneration extends StatelessWidget { final DynamicVoid onContinue; final _AdaEra era; final SeedGenerationType seedGeneration; + bool get showLegacy => + !era.isShelly && + (seedGeneration == SeedGenerationType.byronLegacySeed || + seedGeneration == SeedGenerationType.none); @override Widget build(BuildContext context) { return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ PageTitleSubtitle( title: "master_key_generation".tr, body: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("cardano_bip32_master_key".tr), WidgetConstant.height8, @@ -480,8 +551,7 @@ class _SelectMasterKeyGeneration extends StatelessWidget { title: Text("icarus".tr), onChanged: onChangeKeyGeneration, ), - if (!era.isShelly && - seedGeneration == SeedGenerationType.byronLegacySeed) + if (showLegacy) AppRadioListTile( groupValue: keyGenerationType, value: _CardanoMasterKeyGenerationType.byronLegacy, @@ -489,7 +559,13 @@ class _SelectMasterKeyGeneration extends StatelessWidget { onChanged: onChangeKeyGeneration, ), WidgetConstant.height20, - ElevatedButton(onPressed: onContinue, child: Text("continue".tr)) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FixedElevatedButton( + onPressed: onContinue, child: Text("continue".tr)), + ], + ) ], ); } @@ -509,6 +585,7 @@ class _SelectSeedGenerationType extends StatelessWidget { @override Widget build(BuildContext context) { return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ PageTitleSubtitle( title: "seed_generation".tr, @@ -530,132 +607,113 @@ class _SelectSeedGenerationType extends StatelessWidget { onChanged: onChageSeedGeneration, ), WidgetConstant.height20, - ElevatedButton(onPressed: onContinue, child: Text("continue".tr)) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FixedElevatedButton( + onPressed: onContinue, child: Text("continue".tr)), + ], + ) ], ); } } class _GenerateAddress extends StatelessWidget { - const _GenerateAddress({ - required this.network, - required this.addrType, - required this.generateAddress, - required this.coin, - required this.derivationStandard, - required this.era, - required this.onChangeDerivation, - required this.onChangeShellyddrType, - required this.setupKeyIndex, - required this.standardDerivation, - required this.masterKeyGenerationType, - this.selectedCustomKey, - this.customKeyIndex, - }); + const _GenerateAddress( + {required this.network, + required this.addrType, + required this.generateAddress, + required this.coin, + required this.era, + required this.onChangeShellyddrType, + required this.hdpathKey, + required this.hdPathKeyKey, + required this.onChangedHdPath, + required this.validatorHdPath, + required this.onChangedHdPathKey, + required this.validatorHdPathKey, + required this.keyGeneratorType, + required this.hdPath, + required this.hdPathKey, + required this.manuallySetHdPathKey, + required this.onChangeManuallySetHdPathKey}); final APPCardanoNetwork network; - final EncryptedCustomKey? selectedCustomKey; - final AddressDerivationIndex? customKeyIndex; - final bool derivationStandard; final _AdaEra era; final ADAAddressType addrType; - final AddressDerivationIndex standardDerivation; final CryptoCoins coin; final _OnChangeShellyAddrType onChangeShellyddrType; final DynamicVoid generateAddress; - final _OnChangeDerivation onChangeDerivation; - final _SetupKeyIndex setupKeyIndex; - final _CardanoMasterKeyGenerationType masterKeyGenerationType; + final GlobalKey hdpathKey; + final GlobalKey hdPathKeyKey; + final StringVoid onChangedHdPath; + final NullStringString validatorHdPath; + final StringVoid onChangedHdPathKey; + final NullStringString validatorHdPathKey; + final _CardanoMasterKeyGenerationType keyGeneratorType; + final String? hdPath; + final String? hdPathKey; + final bool manuallySetHdPathKey; + final DynamicVoid onChangeManuallySetHdPathKey; + bool get isBaseAddress => addrType == ADAAddressType.base; @override Widget build(BuildContext context) { return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - PageTitleSubtitle( - title: "derive_network_address" - .tr - .replaceOne(network.coinParam.token.name), - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("disable_standard_derivation".tr), - WidgetConstant.height8, - if (selectedCustomKey != null) - Column( - crossAxisAlignment: CrossAxisAlignment.start, + if (era.isShelly) ...[ + WidgetConstant.height20, + Text("shelley_address_format".tr, + style: context.textTheme.titleMedium), + WidgetConstant.height8, + AppDropDownBottom( + items: { + ADAAddressType.base: Text("base".tr), + ADAAddressType.reward: Text("reward".tr), + ADAAddressType.enterprise: Text("enterprise".tr), + }, + value: addrType, + onChanged: onChangeShellyddrType, + label: "shelley_address_format".tr), + WidgetConstant.height20, + ] else if (keyGeneratorType.isLegacy) ...[ + AppSwitchListTile( + value: manuallySetHdPathKey, + title: Text("manually_set_hd_path".tr), + subtitle: + Text("byron_legacy_hd_path_generate_from_master_key_desc".tr), + onChanged: (p0) => onChangeManuallySetHdPathKey(), + ), + AnimatedSize( + duration: AppGlobalConst.animationDuraion, + alignment: Alignment.topCenter, + child: manuallySetHdPathKey + ? Column( children: [ - Text("generate_from_imported_keys".tr), WidgetConstant.height8, - Text("generate_from_imported_key_desc1".tr) + AppTextField( + label: "hd_path".tr, + key: hdpathKey, + onChanged: onChangedHdPath, + validator: validatorHdPath, + hint: CardanoUtils.hdPathHint, + initialValue: hdPath, + ), + AppTextField( + label: "hd_path_key".tr, + key: hdPathKeyKey, + onChanged: onChangedHdPathKey, + validator: validatorHdPathKey, + helperText: "byron_legacy_hd_path_key_desc2".tr, + hint: "0x950163...", + initialValue: hdPathKey, + ), ], ) - else - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("generate_from_hd_wallet".tr), - ], - ) - ], - )), - if (era.isShelly) - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - WidgetConstant.height20, - Text("shelley_address_format".tr, - style: context.textTheme.titleMedium), - WidgetConstant.height8, - AppDropDownBottom( - items: { - ADAAddressType.base: Text("base".tr), - ADAAddressType.reward: Text("reward".tr), - ADAAddressType.enterprise: Text("enterprise".tr), - }, - value: addrType, - onChanged: onChangeShellyddrType, - label: "shelley_address_format".tr), - WidgetConstant.height20, - ], - ), - AppSwitchListTile( - value: derivationStandard, - onChanged: (p0) { - onChangeDerivation( - p0, - () { - if (masterKeyGenerationType.isLegacy) { - context - .openSliverBottomSheet( - "key_derivation".tr, - child: ByronLegacyKeyDerivationView( - coin: coin, - curve: coin.conf.type, - )) - .then(setupKeyIndex); - return; - } - context - .openSliverBottomSheet( - "key_derivation".tr, - child: Bip32KeyDerivationView( - coin: coin, - curve: coin.conf.type, - )) - .then(setupKeyIndex); - }, - ); - }, - title: selectedCustomKey == null - ? Text(derivationStandard - ? "standard_derivation".tr - : "custom_derivation".tr) - : Text(customKeyIndex == null - ? "non_derivation".tr - : "custom_derivation".tr), - subtitle: selectedCustomKey == null - ? Text(customKeyIndex?.path ?? standardDerivation.path) - : Text(customKeyIndex?.path ?? "import_key_derivation_desc2".tr), - ), + : WidgetConstant.sizedBox, + ) + ], WidgetConstant.height20, Row( mainAxisAlignment: MainAxisAlignment.center, @@ -663,7 +721,7 @@ class _GenerateAddress extends StatelessWidget { FixedElevatedButton( padding: WidgetConstant.paddingVertical20, onPressed: generateAddress, - child: Text("generate_address".tr), + child: Text("setup_derivation".tr), ), ], ) diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/controller.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/controller.dart index aaccc4c9..2496ca4b 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/controller.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/controller.dart @@ -1,5 +1,7 @@ +import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/certificate_impl.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/fee_impl.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/memo_impl.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/minting_impl.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/transaction.dart'; import 'impl/signer_impl.dart'; @@ -7,7 +9,9 @@ class CardanoTransactionStateController extends CardanoTransactionImpl with CardanoTransactionFeeImpl, CardanoSignerImpl, - CardanoTransactionMemoImpl { + CardanoTransactionMemoImpl, + CardanoMintingImpl, + CardanoCertificateImpl { CardanoTransactionStateController( {required super.walletProvider, required super.chainAccount}); @@ -15,8 +19,8 @@ class CardanoTransactionStateController extends CardanoTransactionImpl String get repositoryId => "cardano"; @override - void sendTransaction() { + void buildAndBroadcastTransaction() { if (!trReady) return; - super.sendTransaction(); + super.buildAndBroadcastTransaction(); } } diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/certificate_impl.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/certificate_impl.dart new file mode 100644 index 00000000..5c5510d0 --- /dev/null +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/certificate_impl.dart @@ -0,0 +1,22 @@ +import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/transaction.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/cardano.dart'; + +mixin CardanoCertificateImpl on CardanoTransactionImpl { + List _certificates = []; + @override + List get certificates => _certificates; + void addCertificate(ADATransactionCertificate? newCertificate) { + if (newCertificate == null) return; + _certificates = [newCertificate, ...certificates]; + notify(); + calculateFee(); + } + + void removeCertificate(ADATransactionCertificate? certificate) { + if (!_certificates.contains(certificate)) return; + _certificates = List.unmodifiable( + _certificates.where((element) => element != certificate)); + notify(); + calculateFee(); + } +} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/fee_impl.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/fee_impl.dart index 962b89b8..5471d89a 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/fee_impl.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/fee_impl.dart @@ -21,7 +21,8 @@ mixin CardanoTransactionFeeImpl on CardanoTransactionImpl { String? _feeError; @override - int get coinsPerUtxoSize => _protocolParams!.coinsPerUtxoSize; + ADAEpochParametersResponse get protocolParams => _protocolParams!; + String? get feeError => _feeError; CardanoFeeRateType get feeRateType => _feeRateType; bool get hasFee => !transactionFee.isZero; @@ -31,14 +32,8 @@ mixin CardanoTransactionFeeImpl on CardanoTransactionImpl { @override Future calculateFee() async { - final builder = ADATransactionBuilder( - outputs: [ - ...receivers.map((e) => e.toOutput()).toList(), - if (remindAmount.largerThanZero) changeADAOutput.toOutput(), - if (changeAssetOutput.hasAssets) changeAssetOutput.toOutput() - ], - utxos: selectedUtxos.map((e) => e.utxo.toUtxoResponse()).toList(), - metadata: transactionMemo); + super.calculateFee(); + final builder = buildTransaction(transactionFee.balance); final size = builder.estimateSize( onChangeAddress: changeADAOutput.address.networkAddress); final calcFee = _protocolParams!.calculateFee(size); diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/minting_impl.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/minting_impl.dart new file mode 100644 index 00000000..e075b6a6 --- /dev/null +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/minting_impl.dart @@ -0,0 +1,32 @@ +import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/transaction.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/cardano.dart'; + +mixin CardanoMintingImpl on CardanoTransactionImpl { + List _mints = []; + @override + List get mints => _mints; + + void setupMint(ADAMintInfo? mint) { + if (mint == null) return; + _mints = List.unmodifiable([mint, ..._mints]); + setTotalAssets(totalAssets + mint.toMultiAsset); + calculateFee(); + } + + void removeMint(ADAMintInfo? mint) { + if (!_mints.contains(mint)) return; + _mints = List.unmodifiable( + _mints.where((element) => element != mint)); + _cleanUpUsedMint(mint!); + } + + void _cleanUpUsedMint(ADAMintInfo mint) { + final mintingAsset = mint.toMultiAsset; + setTotalAssets(totalAssets - mintingAsset); + changeAssetOutput.setAsset(changeAssetOutput.asset - mintingAsset); + for (final i in receivers) { + i.setAsset(i.asset - mintingAsset); + } + calculateFee(); + } +} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/signer_impl.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/signer_impl.dart index 7b090d32..74925c35 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/signer_impl.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/signer_impl.dart @@ -6,48 +6,31 @@ import 'package:on_chain/ada/ada.dart'; import 'transaction.dart'; mixin CardanoSignerImpl on CardanoTransactionImpl { - Future _sendTransaction() async { - TransactionOutput? output; - if (remindAmount.largerThanZero) { - output = changeADAOutput.toOutput(); - output = output.copyWith( - amount: output.amount.copyWith(coin: remindAmount.balance)); + Future _signTransaction() async { + try { + final builder = buildTransaction(transactionFee.balance); + final signers = getTransactionSignerAccounts(); + final signerKeyIndexes = getTransactionSignersKeysIndex(); + final tr = await walletProvider.signCardanoTransaction( + request: CardanoSigningRequest( + addresses: signers, + network: network, + transaction: builder, + signers: signerKeyIndexes)); + if (tr.hasError) { + throw tr.exception!; + } + return tr.result; + } catch (e) { + rethrow; } - TransactionOutput? assetOutput; - if (changeAssetOutput.hasAssets) { - assetOutput = changeAssetOutput.toOutput(); - } - - final builder = ADATransactionBuilder( - outputs: [ - ...receivers.map((e) => e.toOutput()).toList(), - if (output != null) output, - if (assetOutput != null) assetOutput - ], - utxos: selectedUtxos.map((e) => e.utxo.toUtxoResponse()).toList(), - metadata: transactionMemo); - builder.setFee(transactionFee.balance); - final signerAddrs = - selectedUtxos.map((e) => e.utxo.address).toList().toSet(); - final signers = addresses - .where( - (element) => signerAddrs.contains(element.networkAddress.address)) - .toList(); - - final tr = await walletProvider.signCardanoTransaction( - request: CardanoSigningRequest( - addresses: signers, network: network, transaction: builder)); - if (tr.hasError) { - throw tr.exception!; - } - return tr.result; } - void sendTransaction() async { + void buildAndBroadcastTransaction() async { progressKey.progressText( "create_send_transaction".tr.replaceOne(network.coinParam.token.name)); final result = await MethodCaller.call(() async { - final result = await _sendTransaction(); + final result = await _signTransaction(); final ser = result.serialize(); final broadcast = await providers.broadcastTransaction(ser); return broadcast; diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/transaction.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/transaction.dart index dd751d98..d8262484 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/transaction.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/controller/impl/transaction.dart @@ -3,11 +3,8 @@ import 'package:mrt_wallet/app/core.dart'; import 'package:mrt_wallet/future/pages/start_page/controller/wallet_provider.dart'; import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; import 'package:mrt_wallet/models/wallet_models/address/network_address/cardano/cardano.dart'; -import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/account_utxos.dart'; -import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/cardano_output.dart'; -import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/utxo.dart'; -import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/utxo_multi_asset.dart'; -import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/utxo_with_owner.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/utxos.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/cardano.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; import 'package:mrt_wallet/provider/api/api_provider.dart'; import 'package:mrt_wallet/types/typedef.dart'; @@ -16,10 +13,35 @@ import 'package:on_chain/on_chain.dart'; enum CardanoTransactionPages { account, utxo, send } abstract class CardanoTransactionImpl extends StateController { + List get mints; + List get certificates; + List _deposits = []; + List get deposits => _deposits; + bool get hasDeposit => deposits.isNotEmpty; + List _refundDepsit = []; + List get refundDeposit => _refundDepsit; + bool get hasRefundDeposit => refundDeposit.isNotEmpty; + void _findDeposits() { + List depositsValues = []; + List refund = []; + for (final i in certificates) { + if (i.type == ADATransactionCertificateType.registraction) { + depositsValues.add(ADATransactionDeposit.amount( + type: ADACustomFeeTypes.stakeRegistration, + fee: protocolParams.keyDeposit)); + } else if (i.type == ADATransactionCertificateType.deregistration) { + refund.add(ADATransactionDeposit.amount( + type: ADACustomFeeTypes.stakeRegistration, + fee: protocolParams.keyDeposit)); + } + } + _deposits = List.unmodifiable(depositsValues); + _refundDepsit = List.unmodifiable(refund); + } + late final NoneDecimalBalance spendableAmount = NoneDecimalBalance.zero(network.coinParam.decimal); - late final NoneDecimalBalance setupAmount = - NoneDecimalBalance.zero(network.coinParam.decimal); + late final NoneDecimalBalance sumOfSelectedUtxo = NoneDecimalBalance.zero(network.coinParam.decimal); @@ -28,7 +50,8 @@ abstract class CardanoTransactionImpl extends StateController { address: ReceiptAddress( view: address.address.toAddress, type: address.type, - networkAddress: address.networkAddress), + networkAddress: address.networkAddress, + account: address), network: network); CardanoOutputWithBalance get changeADAOutput => _changeADAOutput; NoneDecimalBalance get remindAmount => changeADAOutput.balance; @@ -38,7 +61,8 @@ abstract class CardanoTransactionImpl extends StateController { address: ReceiptAddress( view: address.address.toAddress, type: address.type, - networkAddress: address.networkAddress), + networkAddress: address.networkAddress, + account: address), network: network); CardanoOutputWithBalance get changeAssetOutput => _changeAssetOutput; @@ -56,8 +80,10 @@ abstract class CardanoTransactionImpl extends StateController { GeneralTransactionMetadata? get transactionMemo; NoneDecimalBalance get transactionFee; - int get coinsPerUtxoSize; - Future calculateFee(); + ADAEpochParametersResponse get protocolParams; + Future calculateFee() async { + _findDeposits(); + } CardanoTransactionImpl( {required this.walletProvider, required this.chainAccount}); @@ -98,35 +124,56 @@ abstract class CardanoTransactionImpl extends StateController { final Map _receivers = {}; List get receivers => _receivers.values.toList(); + void setTotalAssets(UtxoMultiAsset? assets) { + _totalAsset = assets ?? UtxoMultiAsset(const {}); + } + void changeOutputAddress(ICardanoAddress? changeAddr) { if (changeAddr == null || changeAddr.address.toAddress == _changeADAOutput.address.view) return; - _changeADAOutput.setAddress(ReceiptAddress( - view: changeAddr.address.toAddress, - type: changeAddr.type, - networkAddress: changeAddr.networkAddress)); + final bool needRecalculationFee = changeAddr.networkAddress.addressType != + _changeADAOutput.address.networkAddress.addressType; + final ReceiptAddress changeReceiptAddr = + ReceiptAddress( + view: changeAddr.address.toAddress, + type: changeAddr.type, + networkAddress: changeAddr.networkAddress, + account: changeAddr); + _changeADAOutput.setAddress(changeReceiptAddr, + coinsPerUtxoSize: protocolParams.coinsPerUtxoSize); + if (needRecalculationFee) { + calculateFee(); + } notify(); } void changeAssetOutputAddress(ICardanoAddress? changeAddr) { if (changeAddr == null || changeAddr.address.toAddress == _changeAssetOutput.address.view) return; - _changeAssetOutput.setAddress(ReceiptAddress( - view: changeAddr.address.toAddress, - type: changeAddr.type, - networkAddress: changeAddr.networkAddress)); + final bool needRecalculationFee = changeAddr.networkAddress.addressType != + _changeAssetOutput.address.networkAddress.addressType; + _changeAssetOutput.setAddress( + ReceiptAddress( + view: changeAddr.address.toAddress, + type: changeAddr.type, + networkAddress: changeAddr.networkAddress, + account: changeAddr), + coinsPerUtxoSize: protocolParams.coinsPerUtxoSize); + if (needRecalculationFee) { + calculateFee(); + } notify(); } void onSetupUtxo() { - _totalAsset = selectedUtxos.fold(UtxoMultiAsset({}), + final totalUtxosAsset = selectedUtxos.fold(UtxoMultiAsset({}), (previousValue, element) => previousValue + element.utxo.multiAsset); - + setTotalAssets(totalUtxosAsset); onCalculateAmount(); if (hasAsset) { changeAssetOutput.updateBalance( - changeAssetOutput.minValue(coinsPerUtxoSize), - coinsPerUtxoSize: coinsPerUtxoSize); + changeAssetOutput.minValue(protocolParams.coinsPerUtxoSize), + coinsPerUtxoSize: protocolParams.coinsPerUtxoSize); onCalculateAmount(); } _page = CardanoTransactionPages.send; @@ -140,7 +187,11 @@ abstract class CardanoTransactionImpl extends StateController { } } - void addSpender(ICardanoAddress address) { + void addSpender(ICardanoAddress address, StringVoid onError) { + if (address.networkAddress.isRewardAddress) { + onError("unable_to_spend_from_stake_address"); + return; + } final r = _addresses.remove(address.networkAddress); if (!r) { _addresses.add(address.networkAddress); @@ -148,15 +199,20 @@ abstract class CardanoTransactionImpl extends StateController { notify(); } + void _updateSelectedUtxosBalance() { + final BigInt totalUtxosBalances = _selectedUtxos.fold( + BigInt.zero, + (previousValue, element) => + previousValue + element.utxoBalance.balance); + sumOfSelectedUtxo.updateBalance(totalUtxosBalances); + } + void addUtxo(CardanoUtxo utxo) { final r = _selectedUtxos.remove(utxo); if (!r) { _selectedUtxos.add(utxo); } - sumOfSelectedUtxo.updateBalance(_selectedUtxos.fold( - BigInt.zero, - (previousValue, element) => - previousValue + element.utxoBalance.balance)); + _updateSelectedUtxosBalance(); notify(); } @@ -170,10 +226,7 @@ abstract class CardanoTransactionImpl extends StateController { for (final i in _accountUtxos.values) { _selectedUtxos.addAll(i.utxos ?? []); } - sumOfSelectedUtxo.updateBalance(_selectedUtxos.fold( - BigInt.zero, - (previousValue, element) => - previousValue + element.utxoBalance.balance)); + _updateSelectedUtxosBalance(); } finally { notify(); } @@ -225,24 +278,24 @@ abstract class CardanoTransactionImpl extends StateController { void setupAccountAmount(String address, BigInt? amount) async { if (amount == null) return; - _receivers[address] - ?.updateBalance(amount, coinsPerUtxoSize: coinsPerUtxoSize); + _receivers[address]?.updateBalance(amount, + coinsPerUtxoSize: protocolParams.coinsPerUtxoSize); bool isMax = amount == remindAmount.balance; onCalculateAmount(); if (isMax) { await calculateFee(); final fixedAmount = amount + remindAmount.balance; - _receivers[address] - ?.updateBalance(fixedAmount, coinsPerUtxoSize: coinsPerUtxoSize); + _receivers[address]?.updateBalance(fixedAmount, + coinsPerUtxoSize: protocolParams.coinsPerUtxoSize); onCalculateAmount(); } notify(); } void changeAssetAdaAmount(BigInt? amount) async { - if (amount == null || !hasAsset) return; + if (amount == null || !hasAsset || !_changeAssetOutput.hasAssets) return; _changeAssetOutput.updateBalance(amount, - coinsPerUtxoSize: coinsPerUtxoSize); + coinsPerUtxoSize: protocolParams.coinsPerUtxoSize); onCalculateAmount(); notify(); } @@ -256,35 +309,45 @@ abstract class CardanoTransactionImpl extends StateController { } bool _isReady() { - final zeroOutput = receivers.any((element) => !element.hasAmount); - return receivers.isNotEmpty && - !zeroOutput && + final hasNotReadyOutput = receivers.any((element) => !element.isReady); + return !hasNotReadyOutput && !remindAmount.isNegative && - !setupAmount.isZero && transactionFee.largerThanZero; } void onCalculateAmount() { + final filledAsset = _receivers.values.fold(UtxoMultiAsset({}), + (previousValue, element) => previousValue + element.asset); + _changeAssetOutput.setAsset(_totalAsset - filledAsset); final totalAmounts = receivers.fold(BigInt.zero, (previousValue, element) => previousValue + element.balance.balance); - setupAmount.updateBalance(totalAmounts + changeAssetOutput.balance.balance); - final remind = sumOfSelectedUtxo.balance - + _findDeposits(); + + final BigInt depositsAmounts = deposits.fold(BigInt.zero, + (previousValue, element) => previousValue + element.fee.balance); + final BigInt refaunds = refundDeposit.fold(BigInt.zero, + (previousValue, element) => previousValue + element.fee.balance); + final remind = sumOfSelectedUtxo.balance + + refaunds - totalAmounts - changeAssetOutput.balance.balance - - transactionFee.balance; + transactionFee.balance - + depositsAmounts; remindAmount.updateBalance(remind); - final filledAsset = _receivers.values.fold(UtxoMultiAsset({}), - (previousValue, element) => previousValue + element.asset); - _changeAssetOutput.setAsset(_totalAsset - filledAsset); + _trReady = _isReady(); notify(); } void onAddRecever( - ReceiptAddress? addr, DynamicVoid onAccountExists) { + ReceiptAddress? addr, StringVoid onAccountExists) { if (addr == null) return; + if (addr.networkAddress.isRewardAddress) { + onAccountExists("cannot_send_ada_to_stake_address"); + return; + } if (_receivers.containsKey(addr.networkAddress.address)) { - onAccountExists(); + onAccountExists("address_already_exist"); return; } else { _receivers[addr.networkAddress.address] = @@ -293,4 +356,74 @@ abstract class CardanoTransactionImpl extends StateController { notify(); calculateFee(); } + + List _getRemindOutputs() { + final List remindOutputs = []; + if (remindAmount.largerThanZero) { + TransactionOutput output = changeADAOutput.toOutput(); + output = output.copyWith( + amount: output.amount.copyWith(coin: remindAmount.balance)); + remindOutputs.add(output); + } + if (changeAssetOutput.hasAssets) { + TransactionOutput assetOutput = changeAssetOutput.toOutput(); + remindOutputs.add(assetOutput); + } + return remindOutputs; + } + + ADATransactionBuilder buildTransaction(BigInt fee) { + final transaction = ADATransactionBuilder( + outputs: [ + ...receivers.map((e) => e.toOutput()).toList(), + ..._getRemindOutputs() + ], + utxos: selectedUtxos.map((e) => e.utxo.toUtxoResponse()).toList(), + metadata: transactionMemo, + certificates: certificates.map((e) => e.certificate).toList(), + deposits: deposits.map((e) => e.toDepositBuilder()).toList(), + refundDeposits: refundDeposit.map((e) => e.toDepositBuilder()).toList(), + mints: mints.map((e) => e.toMintBuilder()).toList()) + ..setFee(fee); + return transaction; + } + + Set _signerAddresses() { + return { + ...selectedUtxos.map((e) => e.utxo.toAdddress), + ...mints.map((e) => e.owner), + ...certificates + .where((element) => element.certificate.signer != null) + .map((e) => e.certificate.signer!) + }; + } + + List getTransactionSignerAccounts() { + final Set signerAddrs = _signerAddresses(); + List signers = []; + for (final i in signerAddrs) { + final signer = addresses.firstWhere((element) { + return element.networkAddress == i || element.rewardAddress == i; + }, orElse: () => throw WalletExceptionConst.accountDoesNotFound); + signers.add(signer); + } + return signers; + } + + List getTransactionSignersKeysIndex() { + final Set signerAddrs = _signerAddresses(); + List signers = []; + for (final i in signerAddrs) { + final signer = addresses.firstWhere( + (element) => + element.networkAddress == i || element.rewardAddress == i, + orElse: () => throw WalletExceptionConst.accountDoesNotFound); + if (signer.rewardAddress == i) { + signers.add(signer.rewardKeyIndex ?? signer.keyIndex); + } else { + signers.add(signer.keyIndex); + } + } + return signers; + } } diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/asset_info.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/asset_info.dart new file mode 100644 index 00000000..47597f6d --- /dev/null +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/asset_info.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:mrt_wallet/app/core.dart'; +import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/utxo_multi_asset.dart'; +import 'package:mrt_wallet/models/wallet_models/network/params/core/token.dart'; + +class CardanoAssetsInfoView extends StatelessWidget { + const CardanoAssetsInfoView({super.key, required this.asset}); + final UtxoMultiAsset asset; + @override + Widget build(BuildContext context) { + return Column( + children: List.generate(asset.assets.length, (index) { + final pl = asset.assets.keys.toList()[index]; + final assets = asset.assets[pl]!; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...List.generate(assets.assets.length, (pos) { + final assetName = assets.assets.keys.toList()[pos]; + final token = Token(name: assetName.name, symbol: assetName.name); + return Padding( + padding: WidgetConstant.padding5, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: RichText( + maxLines: 1, + text: TextSpan( + style: context.textTheme.bodyMedium, + children: [ + TextSpan(text: assetName.name), + const TextSpan(text: " "), + WidgetSpan( + child: ToolTipView( + waitDuration: AppGlobalConst.milliseconds100, + message: "${"policy_id".tr}: ${pl.toHex()}", + child: Text( + "(${pl.toHex().substring(0, 3)}...)", + style: context.textTheme.labelSmall, + ), + ), + ) + ]), + ), + ), + Flexible( + child: CoinPriceView( + token: token, + balance: assets.assets[assetName], + style: context.textTheme.titleMedium, + )) + ], + ), + ); + }), + // const Divider(), + ], + ); + }), + ); + } +} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/build_transaction.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/build_transaction.dart index e21f3e2e..ea65931f 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/build_transaction.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/build_transaction.dart @@ -59,7 +59,7 @@ class SendCardanoTransactionView extends StatelessWidget { ) ], ) - : SelectAccountUtxo(controller: controller), + : _SelectAccountUtxo(controller: controller), ), ), ), @@ -73,15 +73,15 @@ class SendCardanoTransactionView extends StatelessWidget { } } -class SelectAccountUtxo extends StatefulWidget { - const SelectAccountUtxo({super.key, required this.controller}); +class _SelectAccountUtxo extends StatefulWidget { + const _SelectAccountUtxo({required this.controller}); final CardanoTransactionImpl controller; @override - State createState() => _SelectAccountUtxoState(); + State<_SelectAccountUtxo> createState() => _SelectAccountUtxoState(); } -class _SelectAccountUtxoState extends State { +class _SelectAccountUtxoState extends State<_SelectAccountUtxo> { late final List addresses = List.from(widget.controller.addresses) ..sort( @@ -97,9 +97,14 @@ class _SelectAccountUtxoState extends State { if (!showAll) { bool alert = false; for (final i in addresses) { - if (i == currentAccount) continue; + if (i == currentAccount || i.networkAddress.isRewardAddress) continue; if (widget.controller.addressSelected(i)) { - widget.controller.addSpender(i); + widget.controller.addSpender( + i, + (p0) { + context.showAlert(p0.tr); + }, + ); alert = true; } } @@ -159,17 +164,17 @@ class _SelectAccountUtxoState extends State { checkColor: context.colors.secondary, activeColor: context.colors.secondary, value: widget.controller.addressSelected(currentAccount), - onChanged: - currentAccount.address.balance.value.balance > BigInt.zero - ? (v) { - widget.controller.addressSelected(currentAccount); - } - : null), - onRemove: currentAccount.address.balance.value.balance > BigInt.zero - ? () { - widget.controller.addSpender(currentAccount); - } - : null, + onChanged: (v) { + widget.controller.addressSelected(currentAccount); + }), + onRemove: () { + widget.controller.addSpender( + currentAccount, + (p0) { + context.showAlert(p0.tr); + }, + ); + }, child: AddressDetailsView( address: currentAccount, color: context.colors.onSecondary, @@ -192,11 +197,21 @@ class _SelectAccountUtxoState extends State { value: widget.controller .addressSelected(addresses[index]), onChanged: (value) { - widget.controller.addSpender(addresses[index]); + widget.controller.addSpender( + addresses[index], + (p0) { + context.showAlert(p0.tr); + }, + ); }, ), onRemove: () { - widget.controller.addSpender(addresses[index]); + widget.controller.addSpender( + addresses[index], + (p0) { + context.showAlert(p0.tr); + }, + ); }, child: AddressDetailsView(address: addresses[index])); }), diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/mint_token_view.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/mint_token_view.dart new file mode 100644 index 00000000..2a1b2ce5 --- /dev/null +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/mint_token_view.dart @@ -0,0 +1,196 @@ +import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:mrt_wallet/app/core.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/wallet_pages.dart'; +import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; +import 'package:mrt_wallet/models/wallet_models/address/network_address/cardano/cardano.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/mint_info.dart'; + +import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; +import 'package:on_chain/on_chain.dart'; + +class CardanoMintTokenView extends StatefulWidget { + final NetworkAccountCore account; + const CardanoMintTokenView(this.account, {super.key}); + @override + State createState() => _CardanoMintTokenViewState(); +} + +class _CardanoMintTokenViewState extends State + with SafeState { + static BigRational maxSupply = BigRational(maxU64); + final GlobalKey textFieldKey = + GlobalKey(debugLabel: "CardanoMintTokenView"); + final GlobalKey formKey = + GlobalKey(debugLabel: "CardanoMintTokenView"); + ReceiptAddress? owmnerReceiptView; + ICardanoAddress? ownerAccount; + + bool fieldsIsReady = false; + + void checkFildsReady() { + fieldsIsReady = !totalSupply.isZero && + owmnerReceiptView != null && + (assetName.trim().isNotEmpty); + setState(() {}); + } + + bool validateShellyAddress(ADAAddress addr) { + if (addr.addressType == ADAAddressType.byron) { + context.showAlert("byron_does_not_support_minting_token".tr); + return false; + } + return true; + } + + void setPolicyId(ICardanoAddress? addr) { + if (addr == null) return; + if (!validateShellyAddress(addr.networkAddress)) return; + ownerAccount = addr; + owmnerReceiptView = ReceiptAddress( + view: addr.networkAddress.address, + type: addr.type, + networkAddress: addr.networkAddress, + account: addr); + checkFildsReady(); + } + + String assetName = ""; + BigRational totalSupply = BigRational.zero; + + void setSupply(BigRational? supply) { + if (supply == null || + supply.isNegative || + supply.isZero || + supply > maxSupply) return; + totalSupply = supply; + checkFildsReady(); + } + + void onChangeAssetsName(String v) { + assetName = v; + final isReady = !totalSupply.isZero && + owmnerReceiptView != null && + (assetName.trim().isNotEmpty); + if (isReady != fieldsIsReady) { + checkFildsReady(); + } + } + + String? validator(String? v) { + final int length = v?.length ?? 0; + if (length < 1) { + return "ada_asset_name_validator".tr; + } + return null; + } + + void onPaste(String v) { + textFieldKey.currentState?.updateText(v); + } + + ADAMintInfo buildAsset() { + return ADAMintInfo( + owner: ownerAccount!.networkAddress, + pubKeyBytes: ownerAccount!.addressDetails.publicKey, + assets: + Assets({AssetName.fromString(assetName): totalSupply.toBigInt()}), + script: ownerAccount!.addressDetails.toNativeScript()); + } + + void onSubmit() { + if (!(formKey.currentState?.validate() ?? false)) return; + checkFildsReady(); + if (!fieldsIsReady) return; + context.pop(buildAsset()); + } + + @override + Widget build(BuildContext context) { + return Form( + key: formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PageTitleSubtitle( + title: "create_a_new_token".tr, + body: Text("ada_create_new_token_desc".tr)), + WidgetConstant.height20, + ReceiptAddressView( + title: "owner".tr, + subtitle: "owner_of_token".tr, + onTap: () { + context + .openSliverBottomSheet( + "select_account".tr, + child: SwitchOrSelectAccountView( + account: widget.account, + showMultiSig: true, + ), + minExtent: 0.5, + maxExtend: 0.9, + initialExtend: 0.7, + centerContent: false, + ) + .then(setPolicyId); + }, + address: owmnerReceiptView, + ), + WidgetConstant.height20, + Text("asset_name".tr, style: context.textTheme.titleMedium), + Text("name_of_token".tr), + WidgetConstant.height8, + AppTextField( + label: "asset_name".tr, + initialValue: assetName, + validator: validator, + suffixIcon: PasteTextIcon(onPaste: onPaste), + onChanged: onChangeAssetsName, + key: textFieldKey, + ), + WidgetConstant.height20, + Text("total_supply".tr, style: context.textTheme.titleMedium), + Text("total_supply_desc".tr), + WidgetConstant.height8, + ContainerWithBorder( + onRemoveIcon: totalSupply.isZero + ? const Icon(Icons.add) + : const Icon(Icons.edit), + validate: !totalSupply.isZero, + child: Text(totalSupply.isZero + ? "tap_to_input_total_supply".tr + : totalSupply.toDecimal()), + onRemove: () { + context + .openSliverBottomSheet( + "create_a_new_token".tr, + child: NumberWriteView( + allowDecimal: false, + max: _CardanoMintTokenViewState.maxSupply, + min: BigRational.one, + allowSign: false, + title: PageTitleSubtitle( + title: "total_supply".tr, + body: Text("total_supply_desc".tr)), + buttomText: "setup_supply".tr, + label: "create_a_new_token".tr, + ), + ) + .then(setSupply); + }, + ), + WidgetConstant.height20, + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FixedElevatedButton( + onPressed: fieldsIsReady ? onSubmit : null, + child: Text("setup_mint".tr), + ) + ], + ), + ], + ), + ); + } +} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/send_transaction.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/send_transaction.dart index f653f0f9..f48945c9 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/send_transaction.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/send_transaction.dart @@ -1,16 +1,19 @@ import 'package:flutter/material.dart'; import 'package:mrt_wallet/app/core.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/controller/controller.dart'; -import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/pages/cardano_transaction_asset_selector.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/pages/asset_info.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/pages/mint_token_view.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/pages/transaction_asset_selector.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/pages/memo_write_view.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/wallet_pages.dart'; import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; import 'package:mrt_wallet/models/wallet_models/address/network_address/cardano/cardano.dart'; import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/cardano.dart'; -import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/utxo_multi_asset.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; import 'package:on_chain/on_chain.dart'; +import 'transaction_certificate_view.dart'; + class CardanoBuildTransactionView extends StatelessWidget { const CardanoBuildTransactionView({super.key, required this.controller}); final CardanoTransactionStateController controller; @@ -38,6 +41,73 @@ class CardanoBuildTransactionView extends StatelessWidget { ), ), WidgetConstant.height20, + Text("mint".tr, style: context.textTheme.titleMedium), + Text("create_a_new_token".tr), + WidgetConstant.height8, + ...List.generate(controller.mints.length, (index) { + final assets = controller.mints[index].toUtxoAssets; + return Column( + children: List.generate(assets.assets.length, (pos) { + final assetName = assets.assets.keys.toList()[pos]; + final balance = assets.assets[assetName]!; + final toToken = + Token(name: assetName.name, symbol: assetName.name); + return ContainerWithBorder( + onRemove: () => + controller.removeMint(controller.mints[index]), + child: CoinPriceView( + token: toToken, + balance: balance, + style: context.textTheme.titleMedium)); + }), + ); + }), + ContainerWithBorder( + onRemoveIcon: const Icon(Icons.edit), + child: Text("tap_to_create_token".tr), + onRemove: () { + context + .openSliverBottomSheet("mint".tr, + child: CardanoMintTokenView(controller.account)) + .then(controller.setupMint); + }, + ), + WidgetConstant.height20, + Text("certificates".tr, style: context.textTheme.titleMedium), + Text("add_certificate_to_transaction".tr), + WidgetConstant.height8, + ...List.generate(controller.certificates.length, (index) { + final certificate = controller.certificates[index]; + return ContainerWithBorder( + onRemove: () { + controller.removeCertificate(certificate); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // ADD + Text(certificate.type.viewName.tr, + style: context.textTheme.titleMedium), + Divider( + color: context.colors.onPrimaryContainer, + ), + ReceiptAddressDetailsView(address: certificate.rewardAccount) + ], + )); + }), + ContainerWithBorder( + onRemoveIcon: const Icon(Icons.edit), + child: Text("tap_to_add_certificate".tr), + onRemove: () { + context + .openSliverBottomSheet( + "certificate".tr, + child: CardanoTransactionCertificateView(controller.account), + ) + .then(controller.addCertificate); + }, + ), + WidgetConstant.height20, Text("list_of_recipients".tr, style: context.textTheme.titleMedium), Text("amount_for_each_output".tr), WidgetConstant.height8, @@ -57,6 +127,8 @@ class CardanoBuildTransactionView extends StatelessWidget { children: [ ContainerWithBorder( backgroundColor: context.colors.secondary, + validate: !receiver.isRewardAddress, + validateText: "cannot_send_ada_to_stake_address".tr, child: ReceiptAddressDetailsView( address: receiver.address, color: context.colors.onSecondary, @@ -142,11 +214,27 @@ class CardanoBuildTransactionView extends StatelessWidget { ? Text("tap_to_add_assets_for_recipient".tr, style: context.textTheme.bodyMedium ?.copyWith(color: context.colors.onSecondary)) - : Text( - "n_asset".tr.replaceOne( - receiver.asset.totalAssets.toString()), - style: context.textTheme.bodyMedium - ?.copyWith(color: context.colors.onSecondary), + : Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "n_asset".tr.replaceOne( + receiver.asset.totalAssets.toString()), + style: context.textTheme.bodyMedium?.copyWith( + color: context.colors.onSecondary), + ), + IconButton( + onPressed: () { + context.openSliverDialog( + (p0) => CardanoAssetsInfoView( + asset: receiver.asset), + "assets".tr); + }, + icon: Icon( + Icons.remove_red_eye, + color: context.colors.onSecondary, + )) + ], ), ) ], @@ -155,7 +243,6 @@ class CardanoBuildTransactionView extends StatelessWidget { }), ), ContainerWithBorder( - validate: controller.receivers.isNotEmpty, onRemove: () { context .openSliverBottomSheet>( @@ -167,8 +254,8 @@ class CardanoBuildTransactionView extends StatelessWidget { initialExtend: 0.9) .then( (value) { - controller.onAddRecever(value, () { - context.showAlert("address_already_exist".tr); + controller.onAddRecever(value, (s) { + context.showAlert(s.tr); }); }, ); @@ -185,40 +272,34 @@ class CardanoBuildTransactionView extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ ContainerWithBorder( - validate: !controller.remindAmount.isNegative, - onRemoveIcon: Icon(Icons.edit, color: context.colors.onSecondary), - backgroundColor: context.colors.secondary, - validateText: "transaction_Insufficient_balance".tr, - onRemove: () { - context - .openSliverBottomSheet( - "select_account".tr, - child: SwitchOrSelectAccountView( - account: controller.account, - showMultiSig: true, - ), - minExtent: 0.5, - maxExtend: 0.9, - initialExtend: 0.7, - centerContent: false, - ) - .then(controller.changeOutputAddress); - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(controller.changeADAOutput.address.type ?? "", - style: context.textTheme.labelLarge - ?.copyWith(color: context.colors.onSecondary)), - OneLineTextWidget( - controller.changeADAOutput.address.view, - style: context.textTheme.bodyMedium - ?.copyWith(color: context.colors.onSecondary), - ) - ], - ), - ), + // validate: !controller.remindAmount.isNegative, + onRemoveIcon: + Icon(Icons.edit, color: context.colors.onSecondary), + backgroundColor: context.colors.secondary, + validate: !controller.changeADAOutput.isRewardAddress, + validateText: "cannot_send_ada_to_stake_address".tr, + onRemove: () { + context + .openSliverBottomSheet( + "select_account".tr, + child: SwitchOrSelectAccountView( + account: controller.account, + showMultiSig: true, + ), + minExtent: 0.5, + maxExtend: 0.9, + initialExtend: 0.7, + centerContent: false, + ) + .then(controller.changeOutputAddress); + }, + child: ReceiptAddressDetailsView( + address: controller.changeADAOutput.address, + color: context.colors.onSecondary, + )), ContainerWithBorder( + validate: !controller.remindAmount.isNegative, + validateText: "transaction_Insufficient_balance".tr, backgroundColor: context.colors.secondary, child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -242,160 +323,140 @@ class CardanoBuildTransactionView extends StatelessWidget { ], )), if (controller.hasAsset) ...[ - WidgetConstant.height20, - Text("remaining_asset_amount".tr, - style: context.textTheme.titleMedium), - Text("remaining_asset_amount_and_receiver".tr), - WidgetConstant.height8, - ContainerWithBorder( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ContainerWithBorder( - validate: !controller.remindAmount.isNegative, - onRemoveIcon: - Icon(Icons.edit, color: context.colors.onSecondary), - backgroundColor: context.colors.secondary, - validateText: "transaction_Insufficient_balance".tr, - onRemove: () { - context - .openSliverBottomSheet( - "select_account".tr, - child: SwitchOrSelectAccountView( - account: controller.account, - showMultiSig: true, - ), - minExtent: 0.5, - maxExtend: 0.9, - initialExtend: 0.7, - centerContent: false, - ) - .then(controller.changeAssetOutputAddress); - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(controller.changeAssetOutput.address.type ?? "", - style: context.textTheme.labelLarge - ?.copyWith(color: context.colors.onSecondary)), - OneLineTextWidget( - controller.changeAssetOutput.address.view, - style: context.textTheme.bodyMedium - ?.copyWith(color: context.colors.onSecondary), - ) - ], - ), - ), - ContainerWithBorder( - backgroundColor: context.colors.secondary, - validate: controller.changeAssetOutput.hasAmount, - onRemoveIcon: - Icon(Icons.edit, color: context.colors.onSecondary), - onRemove: () { - context - .openSliverBottomSheet( - "setup_output_amount".tr, - child: SetupNetworkAmount( - token: controller.network.coinParam.token, - max: controller.remindAmount.balance + - controller.changeAssetOutput.balance.balance, - min: BigInt.zero, - subtitle: PageTitleSubtitle( - title: "receiver".tr, - body: ContainerWithBorder( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - controller.changeAssetOutput.address - .networkAddress.addressType.name, - style: context.textTheme.labelLarge), - OneLineTextWidget( - controller.changeAssetOutput.address.view) - ], - ))), - ), - ) - .then((amount) { - controller.changeAssetAdaAmount(amount); - }); - }, - child: Column( + AnimatedSize( + duration: AppGlobalConst.animationDuraion, + alignment: Alignment.topCenter, + child: controller.changeAssetOutput.hasAssets + ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - CoinPriceView( - balance: controller.changeAssetOutput.balance, - token: controller.network.coinParam.token, - symbolColor: context.colors.onSecondary, - style: context.textTheme.titleLarge - ?.copyWith(color: context.colors.onSecondary), - ), - if (controller.changeAssetOutput.minAdaRequired) - ErrorTextContainer( - showErrorIcon: false, - error: "amount_must_exceed".tr.replaceOne( - PriceUtils.priceWithCoinName( - controller - .changeAssetOutput.minAdaValue.price, - controller.network.coinParam.token.symbol))) + WidgetConstant.height20, + Text("remaining_asset_amount".tr, + style: context.textTheme.titleMedium), + Text("remaining_asset_amount_and_receiver".tr), + WidgetConstant.height8, + ContainerWithBorder( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ContainerWithBorder( + validate: + !controller.changeAssetOutput.isRewardAddress, + validateText: + "cannot_send_ada_to_stake_address".tr, + onRemoveIcon: Icon(Icons.edit, + color: context.colors.onSecondary), + backgroundColor: context.colors.secondary, + onRemove: () { + context + .openSliverBottomSheet( + "select_account".tr, + child: SwitchOrSelectAccountView( + account: controller.account, + showMultiSig: true, + ), + minExtent: 0.5, + maxExtend: 0.9, + initialExtend: 0.7, + centerContent: false, + ) + .then(controller.changeAssetOutputAddress); + }, + child: ReceiptAddressDetailsView( + address: controller.changeAssetOutput.address, + color: context.colors.onSecondary, + )), + ContainerWithBorder( + backgroundColor: context.colors.secondary, + validate: controller.changeAssetOutput.hasAmount, + onRemoveIcon: Icon(Icons.edit, + color: context.colors.onSecondary), + onRemove: () { + context + .openSliverBottomSheet( + "setup_output_amount".tr, + child: SetupNetworkAmount( + token: controller.network.coinParam.token, + max: controller.remindAmount.balance + + controller + .changeAssetOutput.balance.balance, + min: BigInt.zero, + subtitle: PageTitleSubtitle( + title: "receiver".tr, + body: ContainerWithBorder( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + controller + .changeAssetOutput + .address + .networkAddress + .addressType + .name, + style: context + .textTheme.labelLarge), + OneLineTextWidget(controller + .changeAssetOutput.address.view) + ], + ))), + ), + ) + .then((amount) { + controller.changeAssetAdaAmount(amount); + }); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CoinPriceView( + balance: + controller.changeAssetOutput.balance, + token: controller.network.coinParam.token, + symbolColor: context.colors.onSecondary, + style: context.textTheme.titleLarge + ?.copyWith( + color: context.colors.onSecondary), + ), + if (controller + .changeAssetOutput.minAdaRequired) + ErrorTextContainer( + showErrorIcon: false, + error: "amount_must_exceed" + .tr + .replaceOne( + PriceUtils.priceWithCoinName( + controller.changeAssetOutput + .minAdaValue.price, + controller.network.coinParam + .token.symbol))) + ], + )), + ContainerWithBorder( + backgroundColor: context.colors.secondary, + onRemove: () { + context.openSliverDialog( + (p0) => CardanoAssetsInfoView( + asset: + controller.changeAssetOutput.asset), + "assets".tr); + }, + onRemoveIcon: Icon(Icons.remove_red_eye, + color: context.colors.onSecondary), + child: Text( + "n_asset".tr.replaceOne(controller + .changeAssetOutput.asset.totalAssets + .toString()), + style: context.textTheme.bodyMedium?.copyWith( + color: context.colors.onSecondary), + )), + ], + )) ], - )), - ContainerWithBorder( - backgroundColor: context.colors.secondary, - onRemove: () { - context.openSliverDialog( - (p0) => Column( - children: List.generate( - controller.changeAssetOutput.asset.assets - .length, (index) { - final pl = controller - .changeAssetOutput.asset.assets.keys - .toList()[index]; - final assets = controller - .changeAssetOutput.asset.assets[pl]!; - return Column( - children: [ - Text(pl.toHex(), maxLines: 1), - const Divider(), - ...List.generate(assets.assets.length, - (pos) { - final assetName = - assets.assets.keys.toList()[pos]; - return Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - assetName.name(), - style: context.textTheme.labelLarge, - ), - Flexible( - child: CoinPriceView( - token: Token( - name: assetName.name(), - symbol: assetName.name()), - balance: assets.assets[assetName], - )) - ], - ); - }) - ], - ); - }), - ), - "assets".tr); - }, - onRemoveIcon: - Icon(Icons.help, color: context.colors.onSecondary), - child: Text( - "n_asset".tr.replaceOne(controller - .changeAssetOutput.asset.totalAssets - .toString()), - style: context.textTheme.bodyMedium - ?.copyWith(color: context.colors.onSecondary), - )), - ], - )), + ) + : WidgetConstant.sizedBox, + ), ], WidgetConstant.height20, Text("setup_memo".tr, style: context.textTheme.titleMedium), @@ -450,6 +511,58 @@ class CardanoBuildTransactionView extends StatelessWidget { }, child: Text("tap_to_add_memo".tr, style: context.textTheme.labelLarge)), + + /// deposit + AnimatedSize( + duration: AppGlobalConst.animationDuraion, + alignment: Alignment.topCenter, + child: controller.hasDeposit + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + WidgetConstant.height20, + Text("deposit".tr, style: context.textTheme.titleMedium), + Text("transaction_deposits_list".tr), + WidgetConstant.height8, + ...List.generate(controller.deposits.length, (index) { + final deposit = controller.deposits[index]; + return ContainerWithBorder( + child: CoinPriceView( + token: controller.network.coinParam.token, + balance: deposit.fee, + style: context.textTheme.titleMedium)); + }), + ], + ) + : null, + ), + + /// deposit + AnimatedSize( + duration: AppGlobalConst.animationDuraion, + alignment: Alignment.topCenter, + child: controller.hasRefundDeposit + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + WidgetConstant.height20, + Text("refund_deposit".tr, + style: context.textTheme.titleMedium), + Text("transaction_deposits_list".tr), + WidgetConstant.height8, + ...List.generate(controller.refundDeposit.length, (index) { + final deposit = controller.refundDeposit[index]; + return ContainerWithBorder( + child: CoinPriceView( + token: controller.network.coinParam.token, + balance: deposit.fee, + style: context.textTheme.titleMedium)); + }), + ], + ) + : null, + ), + WidgetConstant.height20, Text("transaction_fee".tr, style: context.textTheme.titleMedium), Text("cost_for_transaction".tr), @@ -495,8 +608,9 @@ class CardanoBuildTransactionView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ FixedElevatedButton( - onPressed: - controller.trReady ? controller.sendTransaction : null, + onPressed: controller.trReady + ? controller.buildAndBroadcastTransaction + : null, child: Text("send_transaction".tr)), ], ) diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/cardano_transaction_asset_selector.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/transaction_asset_selector.dart similarity index 96% rename from mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/cardano_transaction_asset_selector.dart rename to mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/transaction_asset_selector.dart index 33832043..d94c12c0 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/cardano_transaction_asset_selector.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/transaction_asset_selector.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:mrt_wallet/app/core.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/setup_amount.dart'; import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; -import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/cardano_output.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/output.dart'; import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/utxo_asset.dart'; import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/utxo_multi_asset.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; @@ -138,10 +138,13 @@ class _CardanoTransactionAssetSelectorViewState ))), WidgetConstant.height20, Text("choose_asset_you_want_to_transfer".tr, - style: context.textTheme.titleLarge), + style: context.textTheme.titleMedium), + WidgetConstant.height8, ListView.builder( shrinkWrap: true, itemCount: assets.length, + physics: WidgetConstant.noScrollPhysics, + // controller: widget.controller, itemBuilder: (c, index) { final asset = assets[index]; return ContainerWithCheckBoxAndBorder( @@ -188,7 +191,7 @@ class _CardanoTransactionAssetSelectorViewState children: [ Flexible( child: Text( - asset.name.name(), + asset.name.name, maxLines: 1, style: context.textTheme.bodySmall, ), @@ -240,7 +243,7 @@ class _AssetWithPolicyId { final PolicyID policyID; final int decimal; late final Token token = - Token(name: name.name(), symbol: name.name(), decimal: decimal); + Token(name: name.name, symbol: name.name, decimal: decimal); _AssetWithPolicyId( {required this.name, required this.policyID, diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/transaction_certificate_view.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/transaction_certificate_view.dart new file mode 100644 index 00000000..bdffb608 --- /dev/null +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/transaction_certificate_view.dart @@ -0,0 +1,135 @@ +import 'package:flutter/material.dart'; +import 'package:mrt_wallet/app/core.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/receipt_address_view.dart'; +import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/select_account.dart'; +import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; +import 'package:mrt_wallet/models/wallet_models/address/network_address/cardano/cardano.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/cardano.dart'; +import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; +import 'package:on_chain/on_chain.dart'; + +class CardanoTransactionCertificateView extends StatefulWidget { + final NetworkAccountCore account; + const CardanoTransactionCertificateView(this.account, {super.key}); + + @override + State createState() => + _CardanoTransactionCertificateViewState(); +} + +class _CardanoTransactionCertificateViewState + extends State with SafeState { + ADATransactionCertificateType type = + ADATransactionCertificateType.registraction; + + bool get isReady => reward != null && address != null; + + void onChageCertificate(ADATransactionCertificateType? newCertificate) { + type = newCertificate ?? type; + setState(() {}); + } + + ReceiptAddress? reward; + ICardanoAddress? address; + + bool _validateRewardAccount(ICardanoAddress addr) { + if (addr.rewardAddress == null) { + context.showAlert("stake_address_validator".tr); + return false; + } + return true; + } + + void updateRewardAddress(ICardanoAddress? addr) { + if (addr == null) return; + if (_validateRewardAccount(addr)) { + address = addr; + reward = ReceiptAddress( + view: addr.rewardAddress!.address, + type: addr.rewardAddress!.addressType.name, + networkAddress: addr.rewardAddress!, + account: addr); + setState(() {}); + } + } + + ADACertificateBuilder toCertificateBuilder() { + switch (type) { + case ADATransactionCertificateType.deregistration: + return ADACertificateBuilder( + certificate: + StakeDeregistration(reward!.networkAddress.paymentCredential), + signer: reward!.networkAddress); + case ADATransactionCertificateType.registraction: + return ADACertificateBuilder( + certificate: + StakeRegistration(reward!.networkAddress.paymentCredential)); + default: + throw UnimplementedError(); + } + } + + ADATransactionCertificate toAdaTransactionCertificate() { + return ADATransactionCertificate( + certificate: toCertificateBuilder(), + type: type, + rewardAccount: ReceiptAddress( + view: reward!.networkAddress.address, + type: reward!.networkAddress.addressType.name, + networkAddress: reward!.networkAddress, + account: address)); + } + + void onSubmit() { + if (!isReady) return; + context.pop(toAdaTransactionCertificate()); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Text("certificate_type".tr), + Text("add_certificate_to_transaction_desc".tr), + WidgetConstant.height8, + AppDropDownBottom( + items: { + for (final i in ADATransactionCertificateType.values) + i: Text(i.viewName.tr) + }, + label: "certificate_type".tr, + onChanged: onChageCertificate, + ), + WidgetConstant.height20, + ReceiptAddressView( + title: "stake_address".tr, + onTap: () { + context + .openSliverBottomSheet( + "select_account".tr, + child: SwitchOrSelectAccountView( + account: widget.account, + showMultiSig: true, + ), + minExtent: 0.5, + maxExtend: 0.9, + initialExtend: 0.7, + centerContent: false, + ) + .then(updateRewardAddress); + }, + address: reward, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FixedElevatedButton( + padding: WidgetConstant.paddingVertical20, + onPressed: isReady ? onSubmit : null, + child: Text("setup_certificate".tr)) + ], + ) + ], + ); + } +} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/utxo_view.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/utxo_view.dart index 74702142..47c2bc7a 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/utxo_view.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cardano_pages/transaction/pages/utxo_view.dart @@ -11,7 +11,7 @@ class CardanoTransactionUtxoView extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("spendable_amount".tr, style: context.textTheme.titleMedium), + Text("utxos_amount".tr, style: context.textTheme.titleMedium), WidgetConstant.height8, ContainerWithBorder( child: AnimatedSwitcher( diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/setup_address.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/setup_address.dart deleted file mode 100644 index 6deb8fc3..00000000 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/setup_address.dart +++ /dev/null @@ -1,318 +0,0 @@ -import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; -import 'package:blockchain_utils/blockchain_utils.dart'; -import 'package:flutter/material.dart'; -import 'package:mrt_wallet/app/core.dart'; -import 'package:mrt_wallet/future/pages/start_page/home.dart'; -import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/wallet_global_pages.dart'; -import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; -import 'package:mrt_wallet/main.dart'; -import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; - -import 'package:mrt_wallet/types/typedef.dart'; - -typedef _OnChangeDerivation = void Function(bool? val, DynamicVoid onFalse); -typedef _OnSetupKeyIndex = void Function(Bip32AddressIndex? newKeyIndex); - -class SetupCosmosAddressView extends StatefulWidget { - const SetupCosmosAddressView({super.key}); - - @override - State createState() => _SetupCosmosAddressViewState(); -} - -class _SetupCosmosAddressViewState extends State - with SafeState { - final GlobalKey pageProgressKey = - GlobalKey(debugLabel: "SetupEthereumAddressView"); - final GlobalKey form = GlobalKey(); - late final AppChain chainAccount; - NetworkAccountCore get networkAccounts => chainAccount.account; - final GlobalKey visibleGenerateAddress = - GlobalKey(debugLabel: "visibleContinue"); - final GlobalKey visibleXAddressDetails = - GlobalKey(debugLabel: "visibleContinue"); - - AddressDerivationMode? selectedDerivationMode; - EncryptedCustomKey? selectedCustomKey; - bool inAddressPage = false; - void goToAddressPage( - AddressDerivationMode derivationMode, EncryptedCustomKey? customKey) { - if (derivationMode == AddressDerivationMode.importedKey && - customKey == null) return; - selectedDerivationMode = derivationMode; - selectedCustomKey = customKey; - inAddressPage = true; - setState(() {}); - ensureKeyVisible(key: visibleGenerateAddress); - } - - void _onBackButton() { - if (pageProgressKey.isSuccess) return; - try { - if (inAddressPage) { - selectedDerivationMode = null; - selectedCustomKey = null; - inAddressPage = false; - customKeyIndex = null; - } - } finally { - setState(() {}); - } - } - - APPCosmosNetwork get network => networkAccounts.network as APPCosmosNetwork; - List get coins => network.coins; - CryptoCoins get coin => coins.firstWhere((element) => - element.conf.type == - (selectedCustomKey?.type ?? EllipticCurveTypes.secp256k1)); - bool inited = false; - Bip32AddressIndex? customKeyIndex; - - void _setupIAccount() { - if (inited) return; - final model = context.watch(StateIdsConst.main); - chainAccount = model.chain; - } - - bool get derivationStandard => customKeyIndex == null; - void onChangeDerivation(bool? val, DynamicVoid onFalse) { - if (val == null) return; - if (derivationStandard) { - onFalse(); - return; - } else { - customKeyIndex = null; - setState(() {}); - } - } - - void setupKeyIndex(Bip32AddressIndex? newKeyIndex) { - customKeyIndex = newKeyIndex; - setState(() {}); - } - - @override - void didChangeDependencies() { - _setupIAccount(); - super.didChangeDependencies(); - } - - AddressDerivationIndex? derivationkey(CryptoCoins coin) { - if (selectedCustomKey != null) { - return ImportedAddressIndex( - accountId: selectedCustomKey!.id, - bip32KeyIndex: customKeyIndex, - currencyCoin: coin); - } - return customKeyIndex; - } - - AddressDerivationIndex get standardDerivation { - return chainAccount.account.nextDrive(coin); - } - - void generateAddress() async { - if (!(form.currentState?.validate() ?? false)) return; - pageProgressKey.progressText("generating_new_addr".tr); - final model = context.watch(StateIdsConst.main); - - final keyIndex = - derivationkey(coin) ?? chainAccount.account.nextDrive(coin); - final newAccount = - CosmosNewAddressParams(coin: coin, deriveIndex: keyIndex); - - final result = await model.deriveNewAccount(newAccount); - if (result.hasError) { - pageProgressKey.errorText(result.error!.tr); - } else { - pageProgressKey.success( - backToIdle: false, - progressWidget: SuccessWithButtomView( - buttomText: "generate_new_address".tr, - buttomWidget: ContainerWithBorder( - margin: WidgetConstant.paddingVertical8, - child: AddressDetailsView(address: result.result)), - onPressed: () { - if (mounted) { - pageProgressKey.backToIdle(); - } - }, - text: "address_added_success".tr, - )); - } - setState(() {}); - } - - @override - Widget build(BuildContext context) { - return PopScope( - canPop: !inAddressPage || pageProgressKey.isSuccess, - onPopInvoked: (didPop) { - if (!didPop) { - _onBackButton(); - } - }, - child: Scaffold( - appBar: AppBar( - title: Text("setup_address".tr), - ), - body: PageProgress( - key: pageProgressKey, - backToIdle: AppGlobalConst.oneSecoundDuration, - initialStatus: PageProgressStatus.idle, - child: () => UnfocusableChild( - child: Center( - child: CustomScrollView( - shrinkWrap: true, - slivers: [ - SliverToBoxAdapter( - child: ConstraintsBoxView( - padding: WidgetConstant.paddingHorizontal20, - child: AnimatedSwitcher( - duration: AppGlobalConst.animationDuraion, - child: inAddressPage - ? _GenerateAddress( - network: network, - form: form, - derivationStandard: derivationStandard, - onChangeDerivation: onChangeDerivation, - coin: coin, - setupKeyIndex: setupKeyIndex, - standardDerivation: standardDerivation, - generateAddress: generateAddress, - customKeyIndex: customKeyIndex, - selectedCustomKey: selectedCustomKey, - ) - : Column( - children: [ - WidgetConstant.height20, - PageTitleSubtitle( - title: "derive_network_address" - .tr - .replaceOne( - network.coinParam.token.name), - body: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - LargeTextView( - ["bip44_derivation_desc".tr]), - ], - )), - SetupAddressDerivation(goToAddressPage) - ], - ), - ), - )) - ], - ), - ), - ), - ), - ), - ); - } -} - -class _GenerateAddress extends StatelessWidget { - const _GenerateAddress( - {required this.network, - required this.form, - this.selectedCustomKey, - required this.derivationStandard, - required this.onChangeDerivation, - this.customKeyIndex, - required this.coin, - required this.setupKeyIndex, - required this.standardDerivation, - required this.generateAddress}); - final APPCosmosNetwork network; - final GlobalKey form; - final EncryptedCustomKey? selectedCustomKey; - final bool derivationStandard; - final _OnChangeDerivation onChangeDerivation; - final Bip32AddressIndex? customKeyIndex; - final CryptoCoins coin; - final _OnSetupKeyIndex setupKeyIndex; - final AddressDerivationIndex standardDerivation; - final DynamicVoid generateAddress; - @override - Widget build(BuildContext context) { - return Form( - key: form, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - key: const ValueKey(true), - children: [ - PageTitleSubtitle( - title: "derive_network_address" - .tr - .replaceOne(network.coinParam.token.name), - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("disable_standard_derivation".tr), - WidgetConstant.height8, - if (selectedCustomKey != null) - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("generate_from_imported_keys".tr), - WidgetConstant.height8, - Text("generate_from_imported_key_desc1".tr) - ], - ) - else - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("generate_from_hd_wallet".tr), - ], - ) - ], - )), - AppSwitchListTile( - value: derivationStandard, - onChanged: (p0) { - onChangeDerivation( - p0, - () { - context - .openSliverBottomSheet( - "key_derivation".tr, - child: Bip32KeyDerivationView( - coin: coin, - curve: coin.conf.type, - )) - .then(setupKeyIndex); - }, - ); - }, - title: selectedCustomKey == null - ? Text(derivationStandard - ? "standard_derivation".tr - : "custom_derivation".tr) - : Text(customKeyIndex == null - ? "non_derivation".tr - : "custom_derivation".tr), - subtitle: selectedCustomKey == null - ? Text(customKeyIndex?.path ?? standardDerivation.path) - : Text( - customKeyIndex?.path ?? "import_key_derivation_desc2".tr), - ), - WidgetConstant.height20, - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FixedElevatedButton( - padding: WidgetConstant.paddingVertical20, - onPressed: generateAddress, - child: Text("generate_address".tr), - ), - ], - ) - ], - ), - ); - } -} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/transaction/controller/impl/fee.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/transaction/controller/impl/fee.dart index 4067ac60..fb895158 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/transaction/controller/impl/fee.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/transaction/controller/impl/fee.dart @@ -72,6 +72,7 @@ mixin CosmosTransactionFeeImpl on CosmosTransactiomImpl { _feeError = null; _cancelable.cancel(); feeProgressKey.process(); + notify(); try { final result = await MethodCaller.call(() async { return await _simulateTr(); diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/transaction/controller/impl/signer.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/transaction/controller/impl/signer.dart index 38847cbe..68c44900 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/transaction/controller/impl/signer.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/transaction/controller/impl/signer.dart @@ -23,9 +23,11 @@ mixin CosmosSignerImpl on CosmosTransactiomImpl { .where((element) => element != null) .toList() .cast(); + final signers = account.addresses .where((element) => signersAddr.contains(element.address.toAddress)) .toList(); + final signRequest = CosmosSigningRequest( addresses: signers, network: network, digest: signDoc.toBuffer()); final signatures = diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/transaction/fields/transaction.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/transaction/fields/transaction.dart index e80fecc7..8d2ddd47 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/transaction/fields/transaction.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/cosmos/transaction/fields/transaction.dart @@ -79,6 +79,9 @@ class CosmosTransactionFieldsView extends StatelessWidget { validateText: controller.feeError?.tr, validate: controller.feeError == null && controller.hasFee, + onTapError: () { + controller.simulateTr(); + }, onRemove: () { if (controller.isThorChain) { return; @@ -188,6 +191,7 @@ class CosmosTransactionFieldsView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ FixedElevatedButton( + padding: WidgetConstant.paddingVertical20, onPressed: controller.trIsReady ? controller.sendTransaction : null, diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/ethereum_account_page_view.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/ethereum_account_page_view.dart index 70bfcff9..dca323ec 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/ethereum_account_page_view.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/ethereum_account_page_view.dart @@ -63,12 +63,13 @@ class _EthereumTokenView extends StatelessWidget { return ContainerWithBorder( onRemove: () { context.openDialogPage( - (ctx) => TokenDetailsModalView( - token: token, - address: account, - transferPath: PagePathConst.ethereumTransaction, - ), - "token_info".tr); + "token_info".tr, + child: (ctx) => TokenDetailsModalView( + token: token, + address: account, + transferPath: PagePathConst.ethereumTransaction, + ), + ); }, onRemoveWidget: WidgetConstant.sizedBox, child: Row( diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/setup_address.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/setup_address.dart deleted file mode 100644 index f91f19e8..00000000 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/setup_address.dart +++ /dev/null @@ -1,298 +0,0 @@ -import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; -import 'package:blockchain_utils/blockchain_utils.dart'; -import 'package:flutter/material.dart'; -import 'package:mrt_wallet/app/core.dart'; -import 'package:mrt_wallet/future/pages/start_page/home.dart'; -import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/wallet_global_pages.dart'; -import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; -import 'package:mrt_wallet/main.dart'; -import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; - -import 'package:mrt_wallet/types/typedef.dart'; - -class SetupEthereumAddressView extends StatefulWidget { - const SetupEthereumAddressView({super.key}); - - @override - State createState() => - _SetupEthereumAddressViewState(); -} - -class _SetupEthereumAddressViewState extends State - with SafeState { - final GlobalKey pageProgressKey = - GlobalKey(debugLabel: "SetupEthereumAddressView"); - final GlobalKey form = GlobalKey(); - late final AppChain chainAccount; - NetworkAccountCore get networkAccounts => chainAccount.account; - final GlobalKey visibleGenerateAddress = - GlobalKey(debugLabel: "visibleContinue"); - final GlobalKey visibleXAddressDetails = - GlobalKey(debugLabel: "visibleContinue"); - AddressDerivationMode? selectedDerivationMode; - EncryptedCustomKey? selectedCustomKey; - int tag = 0; - - void onChangeTag(String v) { - final newTag = int.tryParse(v); - if (newTag == null || newTag < 0 || newTag > mask32 - 1) return; - tag = newTag; - } - - bool inAddressPage = false; - - void goToAddressPage( - AddressDerivationMode derivationMode, EncryptedCustomKey? customKey) { - if (derivationMode == AddressDerivationMode.importedKey && - customKey == null) return; - selectedDerivationMode = derivationMode; - selectedCustomKey = customKey; - inAddressPage = true; - setState(() {}); - ensureKeyVisible(key: visibleGenerateAddress); - } - - void _onBackButton() { - if (pageProgressKey.isSuccess) return; - if (inAddressPage) { - selectedDerivationMode = null; - selectedCustomKey = null; - inAddressPage = false; - - customKeyIndex = null; - } - setState(() {}); - } - - APPEVMNetwork get network => networkAccounts.network as APPEVMNetwork; - List get coins => network.coins; - CryptoCoins get coin => coins.firstWhere((element) => - element.conf.type == - (selectedCustomKey?.type ?? EllipticCurveTypes.secp256k1)); - bool inited = false; - Bip32AddressIndex? customKeyIndex; - - void _setupIAccount() { - if (inited) return; - final model = context.watch(StateIdsConst.main); - chainAccount = model.chain; - } - - bool get derivationStandard => customKeyIndex == null; - void onChangeDerivation(bool? val, DynamicVoid onFalse) { - if (val == null) return; - if (derivationStandard) { - onFalse(); - return; - } else { - customKeyIndex = null; - setState(() {}); - } - } - - void setupKeyIndex(Bip32AddressIndex? newKeyIndex) { - customKeyIndex = newKeyIndex; - setState(() {}); - } - - @override - void didChangeDependencies() { - _setupIAccount(); - super.didChangeDependencies(); - } - - AddressDerivationIndex? derivationkey(CryptoCoins coin) { - if (selectedCustomKey != null) { - return ImportedAddressIndex( - accountId: selectedCustomKey!.id, - bip32KeyIndex: customKeyIndex, - currencyCoin: coin); - } - return customKeyIndex; - } - - AddressDerivationIndex get standardDerivation { - return chainAccount.account.nextDrive(coin); - } - - void generateAddress() async { - if (!(form.currentState?.validate() ?? false)) return; - pageProgressKey.progressText("generating_new_addr".tr); - final model = context.watch(StateIdsConst.main); - final curve = selectedCustomKey?.type ?? EllipticCurveTypes.secp256k1; - final coin = coins.firstWhere((element) => element.conf.type == curve); - - final keyIndex = - derivationkey(coin) ?? chainAccount.account.nextDrive(coin); - final newAccount = - EthereumNewAddressParam(coin: coin, deriveIndex: keyIndex); - - final result = await model.deriveNewAccount(newAccount); - if (result.hasError) { - pageProgressKey.errorText(result.error!.tr); - } else { - pageProgressKey.success( - backToIdle: false, - progressWidget: SuccessWithButtomView( - buttomText: "generate_new_address".tr, - buttomWidget: ContainerWithBorder( - margin: WidgetConstant.paddingVertical8, - child: AddressDetailsView(address: result.result)), - onPressed: () { - if (mounted) { - pageProgressKey.backToIdle(); - } - }, - text: "address_added_success".tr, - )); - } - setState(() {}); - } - - @override - Widget build(BuildContext context) { - return PopScope( - canPop: !inAddressPage || pageProgressKey.isSuccess, - onPopInvoked: (didPop) { - if (!didPop) { - _onBackButton(); - } - }, - child: Scaffold( - appBar: AppBar( - title: Text("setup_address".tr), - ), - body: PageProgress( - key: pageProgressKey, - backToIdle: AppGlobalConst.oneSecoundDuration, - initialStatus: PageProgressStatus.idle, - child: () => UnfocusableChild( - child: Center( - child: CustomScrollView( - shrinkWrap: true, - slivers: [ - SliverToBoxAdapter( - child: ConstraintsBoxView( - padding: WidgetConstant.paddingHorizontal20, - child: AnimatedSwitcher( - duration: AppGlobalConst.animationDuraion, - child: inAddressPage - ? Form( - key: form, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - key: const ValueKey(true), - children: [ - PageTitleSubtitle( - title: "derive_network_address" - .tr - .replaceOne( - network.coinParam.token.name), - body: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "disable_standard_derivation".tr), - WidgetConstant.height8, - if (selectedCustomKey != null) - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "generate_from_imported_keys" - .tr), - WidgetConstant.height8, - Text( - "generate_from_imported_key_desc1" - .tr) - ], - ) - else - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text("generate_from_hd_wallet" - .tr), - ], - ) - ], - )), - AppSwitchListTile( - value: derivationStandard, - onChanged: (p0) { - onChangeDerivation( - p0, - () { - context - .openSliverBottomSheet< - Bip32AddressIndex>( - "key_derivation".tr, - child: Bip32KeyDerivationView( - coin: coin, - curve: coin.conf.type, - )) - .then(setupKeyIndex); - }, - ); - }, - title: selectedCustomKey == null - ? Text(derivationStandard - ? "standard_derivation".tr - : "custom_derivation".tr) - : Text(customKeyIndex == null - ? "non_derivation".tr - : "custom_derivation".tr), - subtitle: selectedCustomKey == null - ? Text(customKeyIndex?.path ?? - standardDerivation.path) - : Text(customKeyIndex?.path ?? - "import_key_derivation_desc2".tr), - ), - WidgetConstant.height20, - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FixedElevatedButton( - padding: - WidgetConstant.paddingVertical20, - onPressed: generateAddress, - child: Text("generate_address".tr), - ), - ], - ) - ], - ), - ) - : Column( - children: [ - WidgetConstant.height20, - PageTitleSubtitle( - title: "derive_network_address" - .tr - .replaceOne( - network.coinParam.token.name), - body: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - LargeTextView( - ["bip44_derivation_desc".tr]), - ], - )), - SetupAddressDerivation(goToAddressPage) - ], - ), - ), - )) - ], - ), - ), - ), - ), - ), - ); - } -} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/transaction/fields/ethereum_transaction_fields_view.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/transaction/fields/ethereum_transaction_fields_view.dart index 6abcb01b..c2a71502 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/transaction/fields/ethereum_transaction_fields_view.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/transaction/fields/ethereum_transaction_fields_view.dart @@ -54,8 +54,7 @@ class EthereumTransactionFieldsView extends StatelessWidget { onRemoveIcon: const Icon(Icons.edit), child: AddressDetailsView( address: controller.owner, - key: ValueKey( - controller.owner)), + key: ValueKey(controller.owner)), onRemove: () { context .openSliverBottomSheet( diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/transaction/fields/global/gas_fee_view.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/transaction/fields/global/gas_fee_view.dart index ce4ba5f8..0193751a 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/transaction/fields/global/gas_fee_view.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/ethereum_pages/transaction/fields/global/gas_fee_view.dart @@ -42,8 +42,9 @@ class EthereumGasFeeView extends StatelessWidget { child: AnimatedSwitcher( duration: AppGlobalConst.animationDuraion, child: Row( - key: ValueKey( - "${transaction.gasInited}/${transaction.updatingGas}"), + key: UniqueKey(), + // key: ValueKey( + // "${transaction.gasInited}/${transaction.updatingGas}"), children: [ Expanded( child: transaction.gasInited diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/ripple_pages/account_page.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/ripple_pages/account_page.dart index 953695f1..67b1be45 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/ripple_pages/account_page.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/ripple_pages/account_page.dart @@ -223,12 +223,13 @@ class _RippleTokensView extends StatelessWidget { return ContainerWithBorder( onRemove: () { context.openDialogPage( - (ctx) => TokenDetailsModalView( - token: token, - address: account, - transferPath: PagePathConst.rippleTransfer, - ), - "token_info".tr); + "token_info".tr, + child: (ctx) => TokenDetailsModalView( + token: token, + address: account, + transferPath: PagePathConst.rippleTransfer, + ), + ); }, onRemoveWidget: WidgetConstant.sizedBox, backgroundColor: Colors.transparent, diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/ripple_pages/setup_address.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/ripple_pages/setup_address.dart index a384b1ef..8e92cbaa 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/ripple_pages/setup_address.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/ripple_pages/setup_address.dart @@ -1,43 +1,22 @@ -import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:flutter/material.dart'; import 'package:mrt_wallet/app/core.dart'; - -import 'package:mrt_wallet/future/pages/start_page/home.dart'; -import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/wallet_global_pages.dart'; - +import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/address_derivation/controller.dart'; import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; -import 'package:mrt_wallet/main.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; -import 'package:mrt_wallet/types/typedef.dart'; - class SetupRippleAddressView extends StatefulWidget { - const SetupRippleAddressView({super.key}); - + final AddressDerivationController controller; + const SetupRippleAddressView({super.key, required this.controller}); @override State createState() => _SetupRippleAddressViewState(); } class _SetupRippleAddressViewState extends State with SafeState { - final GlobalKey pageProgressKey = - GlobalKey(debugLabel: "SetupBitcoinAddressView"); - final GlobalKey form = GlobalKey(); - late final AppChain chainAccount; - AddressDerivationIndex get standardDerivation { - return chainAccount.account.nextDrive(coin); - } - - NetworkAccountCore get networkAccounts => chainAccount.account; - final GlobalKey visibleGenerateAddress = - GlobalKey(debugLabel: "visibleContinue"); - final GlobalKey visibleXAddressDetails = - GlobalKey(debugLabel: "visibleContinue"); - AddressDerivationMode? selectedDerivationMode; - EncryptedCustomKey? selectedCustomKey; + Set addressTyoe = {XrpAddressType.classic}; + bool get isXAddress => addressTyoe.first == XrpAddressType.xAddress; int tag = 0; - void onChangeTag(String v) { final newTag = int.tryParse(v); if (newTag == null || newTag < 0 || newTag > mask32 - 1) return; @@ -53,296 +32,75 @@ class _SetupRippleAddressViewState extends State return null; } - bool inAddressPage = false; - Set addressTyoe = {XrpAddressType.classic}; void onSelectAddressType(Set selectType) { addressTyoe = selectType; setState(() {}); - if (isXAddress) { - ensureKeyVisible(key: visibleXAddressDetails); - } - } - - bool get isXAddress => addressTyoe.first == XrpAddressType.xAddress; - - void goToAddressPage( - AddressDerivationMode derivationMode, EncryptedCustomKey? customKey) { - if (derivationMode == AddressDerivationMode.importedKey && - customKey == null) return; - selectedDerivationMode = derivationMode; - selectedCustomKey = customKey; - inAddressPage = true; - setState(() {}); - ensureKeyVisible(key: visibleGenerateAddress); - } - - void _onBackButton() { - if (pageProgressKey.isSuccess) return; - if (inAddressPage) { - selectedDerivationMode = null; - selectedCustomKey = null; - inAddressPage = false; - - customKeyIndex = null; - } - setState(() {}); - } - - AppXRPNetwork get network => networkAccounts.network as AppXRPNetwork; - List get coins => network.coins; - CryptoCoins get coin => coins.firstWhere((element) => - element.conf.type == - (selectedCustomKey?.type ?? EllipticCurveTypes.secp256k1)); - bool inited = false; - Bip32AddressIndex? customKeyIndex; - - void _setupIAccount() { - if (inited) return; - final model = context.watch(StateIdsConst.main); - chainAccount = model.chain; - } - - bool get derivationStandard => customKeyIndex == null; - void onChangeDerivation(bool? val, DynamicVoid onFalse) { - if (val == null) return; - if (derivationStandard) { - onFalse(); - return; - } else { - customKeyIndex = null; - setState(() {}); - } - } - - void setupKeyIndex(Bip32AddressIndex? newKeyIndex) { - customKeyIndex = newKeyIndex; - setState(() {}); - } - - @override - void didChangeDependencies() { - _setupIAccount(); - super.didChangeDependencies(); - } - - AddressDerivationIndex? derivationkey(CryptoCoins coin) { - if (selectedCustomKey != null) { - return ImportedAddressIndex( - accountId: selectedCustomKey!.id, - bip32KeyIndex: customKeyIndex, - currencyCoin: coin); - } - return customKeyIndex; } void generateAddress() async { - if (!(form.currentState?.validate() ?? false)) return; - pageProgressKey.progressText("generating_new_addr".tr); - final model = context.watch(StateIdsConst.main); - final curve = selectedCustomKey?.type ?? EllipticCurveTypes.secp256k1; - final coin = coins.firstWhere((element) => element.conf.type == curve); - - final keyIndex = - derivationkey(coin) ?? chainAccount.account.nextDrive(coin); + final keyIndex = await widget.controller.getCoin(context); + if (keyIndex == null) return; final newAccount = RippleNewAddressParam( - coin: coin, deriveIndex: keyIndex, - type: curve, + type: keyIndex.currencyCoin.conf.type, tag: addressTyoe.first == XrpAddressType.classic ? null : tag); - - final result = await model.deriveNewAccount(newAccount); - if (result.hasError) { - pageProgressKey.errorText(result.error!.tr); - } else { - pageProgressKey.success( - backToIdle: false, - progressWidget: SuccessWithButtomView( - buttomText: "generate_new_address".tr, - buttomWidget: ContainerWithBorder( - margin: WidgetConstant.paddingVertical8, - child: AddressDetailsView(address: result.result)), - onPressed: () { - if (mounted) { - pageProgressKey.backToIdle(); - } - }, - text: "address_added_success".tr, - )); - } - setState(() {}); + widget.controller.generateAddress(newAccount); } @override Widget build(BuildContext context) { - return PopScope( - canPop: !inAddressPage || pageProgressKey.isSuccess, - onPopInvoked: (didPop) { - if (!didPop) { - _onBackButton(); - } - }, - child: Scaffold( - appBar: AppBar( - title: Text("setup_address".tr), - ), - body: PageProgress( - key: pageProgressKey, - backToIdle: AppGlobalConst.oneSecoundDuration, - initialStatus: PageProgressStatus.idle, - child: () => UnfocusableChild( - child: Center( - child: CustomScrollView( - shrinkWrap: true, - slivers: [ - SliverToBoxAdapter( - child: ConstraintsBoxView( - padding: WidgetConstant.paddingHorizontal20, - child: AnimatedSwitcher( - duration: AppGlobalConst.animationDuraion, - child: inAddressPage - ? Form( - key: form, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - key: const ValueKey(true), - children: [ - PageTitleSubtitle( - title: "derive_network_address" - .tr - .replaceOne( - network.coinParam.token.name), - body: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - LargeTextView([ - "choose_bitcoin_address_type_desc" - .tr, - "x_address_desc".tr, - "classic_address_desc".tr, - if (selectedCustomKey != null) ...[ - "generate_from_imported_keys".tr, - "generate_from_imported_key_desc1" - .tr - ] else ...[ - "generate_from_hd_wallet".tr - ] - ]), - ], - )), - AppSwitchListTile( - value: derivationStandard, - onChanged: (p0) { - onChangeDerivation( - p0, - () { - context - .openSliverBottomSheet< - Bip32AddressIndex>( - "key_derivation".tr, - child: Bip32KeyDerivationView( - coin: coin, - curve: coin.conf.type, - )) - .then(setupKeyIndex); - }, - ); - }, - title: selectedCustomKey == null - ? Text(derivationStandard - ? "standard_derivation".tr - : "custom_derivation".tr) - : Text(customKeyIndex == null - ? "non_derivation".tr - : "custom_derivation".tr), - subtitle: selectedCustomKey == null - ? Text(customKeyIndex?.path ?? - standardDerivation.path) - : Text(customKeyIndex?.path ?? - "import_key_derivation_desc2".tr), - ), - WidgetConstant.height20, - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: - AppSegmentedButton( - items: { - XrpAddressType.classic: - XrpAddressType.classic.value.tr, - XrpAddressType.xAddress: - XrpAddressType.xAddress.value.tr - }, - selected: addressTyoe, - onChangeSelected: onSelectAddressType, - ), - ), - ], - ), - WidgetConstant.height20, - AnimatedSize( - duration: AppGlobalConst.animationDuraion, - child: isXAddress - ? Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - PageTitleSubtitle( - title: "x_address_desc2".tr, - body: LargeTextView([ - "x_address_desc3".tr - ])), - Text("assigning_tag".tr, - style: context - .textTheme.titleMedium), - Text("enter_tag_desc".tr), - WidgetConstant.height8, - NumberTextField( - label: "tag".tr, - onChange: onChangeTag, - validator: validateTag, - defaultValue: tag, - max: mask32 - 1, - min: 0), - ], - ) - : const SizedBox()), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FixedElevatedButton( - padding: - WidgetConstant.paddingVertical20, - onPressed: generateAddress, - child: Text("generate_address".tr), - ), - ], - ) - ], - ), - ) - : Column( - children: [ - WidgetConstant.height20, - PageTitleSubtitle( - title: "derive_network_address" - .tr - .replaceOne( - network.coinParam.token.name), - body: LargeTextView( - ["bip44_derivation_desc".tr])), - SetupAddressDerivation(goToAddressPage) - ], - ), - ), - )) - ], + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: AppSegmentedButton( + items: { + XrpAddressType.classic: XrpAddressType.classic.value.tr, + XrpAddressType.xAddress: XrpAddressType.xAddress.value.tr + }, + selected: addressTyoe, + onChangeSelected: onSelectAddressType, ), ), - ), + ], ), - ), + WidgetConstant.height20, + AnimatedSize( + duration: AppGlobalConst.animationDuraion, + child: isXAddress + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PageTitleSubtitle( + title: "x_address_desc2".tr, + body: LargeTextView(["x_address_desc3".tr])), + Text("assigning_tag".tr, + style: context.textTheme.titleMedium), + Text("enter_tag_desc".tr), + WidgetConstant.height8, + NumberTextField( + label: "tag".tr, + onChange: onChangeTag, + validator: validateTag, + defaultValue: tag, + max: mask32 - 1, + min: 0), + ], + ) + : const SizedBox()), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FixedElevatedButton( + padding: WidgetConstant.paddingVertical20, + onPressed: generateAddress, + child: Text("generate_address".tr), + ), + ], + ) + ], ); } } diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/account_page.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/account_page.dart index 93f5ae84..b86a3d7b 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/account_page.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/account_page.dart @@ -65,13 +65,12 @@ class _SolanaTokenView extends StatelessWidget { final SolanaSPLToken token = account.tokens[index]; return ContainerWithBorder( onRemove: () { - context.openDialogPage( - (ctx) => TokenDetailsModalView( + context.openDialogPage("token_info".tr, + child: (ctx) => TokenDetailsModalView( token: token, address: account, transferPath: PagePathConst.solanaTransfer, - ), - "token_info".tr); + )); }, onRemoveWidget: WidgetConstant.sizedBox, child: Row( diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/setup_address.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/setup_address.dart deleted file mode 100644 index 429b43e6..00000000 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/setup_address.dart +++ /dev/null @@ -1,291 +0,0 @@ -import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; -import 'package:blockchain_utils/blockchain_utils.dart'; -import 'package:flutter/material.dart'; -import 'package:mrt_wallet/app/core.dart'; - -import 'package:mrt_wallet/future/pages/start_page/home.dart'; -import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/wallet_global_pages.dart'; -import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; -import 'package:mrt_wallet/main.dart'; -import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; - -import 'package:mrt_wallet/types/typedef.dart'; - -/// add testnet coin -class SetupSolanaAddressView extends StatefulWidget { - const SetupSolanaAddressView({super.key}); - - @override - State createState() => _SetupSolanaAddressViewState(); -} - -class _SetupSolanaAddressViewState extends State - with SafeState { - final GlobalKey pageProgressKey = - GlobalKey(debugLabel: "SetupEthereumAddressView"); - final GlobalKey form = GlobalKey(); - late final AppChain chainAccount; - AddressDerivationIndex get standardDerivation { - return chainAccount.account.nextDrive(coin); - } - - NetworkAccountCore get networkAccounts => chainAccount.account; - final GlobalKey visibleGenerateAddress = - GlobalKey(debugLabel: "visibleContinue"); - final GlobalKey visibleXAddressDetails = - GlobalKey(debugLabel: "visibleContinue"); - AddressDerivationMode? selectedDerivationMode; - EncryptedCustomKey? selectedCustomKey; - int tag = 0; - - void onChangeTag(String v) { - final newTag = int.tryParse(v); - if (newTag == null || newTag < 0 || newTag > mask32 - 1) return; - tag = newTag; - } - - bool inAddressPage = false; - - void goToAddressPage( - AddressDerivationMode derivationMode, EncryptedCustomKey? customKey) { - if (derivationMode == AddressDerivationMode.importedKey && - customKey == null) return; - selectedDerivationMode = derivationMode; - selectedCustomKey = customKey; - inAddressPage = true; - setState(() {}); - ensureKeyVisible(key: visibleGenerateAddress); - } - - void _onBackButton() { - if (pageProgressKey.isSuccess) return; - if (inAddressPage) { - selectedDerivationMode = null; - selectedCustomKey = null; - inAddressPage = false; - - customKeyIndex = null; - } - setState(() {}); - } - - APPSolanaNetwork get network => networkAccounts.network as APPSolanaNetwork; - List get coins => network.coins; - CryptoCoins get coin => coins.firstWhere((element) => - element.conf.type == - (selectedCustomKey?.type ?? EllipticCurveTypes.ed25519)); - bool inited = false; - Bip32AddressIndex? customKeyIndex; - - void _setupIAccount() { - if (inited) return; - final model = context.watch(StateIdsConst.main); - chainAccount = model.chain; - } - - bool get derivationStandard => customKeyIndex == null; - void onChangeDerivation(bool? val, DynamicVoid onFalse) { - if (val == null) return; - if (derivationStandard) { - onFalse(); - return; - } else { - customKeyIndex = null; - setState(() {}); - } - } - - void setupKeyIndex(Bip32AddressIndex? newKeyIndex) { - customKeyIndex = newKeyIndex; - setState(() {}); - } - - @override - void didChangeDependencies() { - _setupIAccount(); - super.didChangeDependencies(); - } - - AddressDerivationIndex? derivationkey(CryptoCoins coin) { - if (selectedCustomKey != null) { - return ImportedAddressIndex( - accountId: selectedCustomKey!.id, - bip32KeyIndex: customKeyIndex, - currencyCoin: coin); - } - return customKeyIndex; - } - - void generateAddress() async { - if (!(form.currentState?.validate() ?? false)) return; - pageProgressKey.progressText("generating_new_addr".tr); - final model = context.watch(StateIdsConst.main); - final curve = selectedCustomKey?.type ?? EllipticCurveTypes.ed25519; - final coin = coins.firstWhere((element) => element.conf.type == curve); - - final keyIndex = - derivationkey(coin) ?? chainAccount.account.nextDrive(coin); - final newAccount = SolanaNewAddressParam(coin: coin, deriveIndex: keyIndex); - - final result = await model.deriveNewAccount(newAccount); - if (result.hasError) { - pageProgressKey.errorText(result.error!.tr); - } else { - pageProgressKey.success( - backToIdle: false, - progressWidget: SuccessWithButtomView( - buttomWidget: ContainerWithBorder( - margin: WidgetConstant.paddingVertical8, - child: AddressDetailsView(address: result.result)), - buttomText: "generate_new_address".tr, - onPressed: () { - if (mounted) { - pageProgressKey.backToIdle(); - } - }, - )); - } - setState(() {}); - } - - @override - Widget build(BuildContext context) { - return PopScope( - canPop: !inAddressPage || pageProgressKey.isSuccess, - onPopInvoked: (didPop) { - if (!didPop) { - _onBackButton(); - } - }, - child: Scaffold( - appBar: AppBar( - title: Text("setup_address".tr), - ), - body: PageProgress( - key: pageProgressKey, - backToIdle: AppGlobalConst.oneSecoundDuration, - initialStatus: PageProgressStatus.idle, - child: () => UnfocusableChild( - child: Center( - child: CustomScrollView( - shrinkWrap: true, - slivers: [ - SliverToBoxAdapter( - child: ConstraintsBoxView( - padding: WidgetConstant.paddingHorizontal20, - child: AnimatedSwitcher( - duration: AppGlobalConst.animationDuraion, - child: inAddressPage - ? Form( - key: form, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - key: const ValueKey(true), - children: [ - PageTitleSubtitle( - title: "derive_network_address" - .tr - .replaceOne( - network.coinParam.token.name), - body: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "disable_standard_derivation".tr), - WidgetConstant.height8, - if (selectedCustomKey != null) - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "generate_from_imported_keys" - .tr), - WidgetConstant.height8, - Text( - "generate_from_imported_key_desc1" - .tr) - ], - ) - else - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text("generate_from_hd_wallet" - .tr), - ], - ) - ], - )), - AppSwitchListTile( - value: derivationStandard, - onChanged: (p0) { - onChangeDerivation( - p0, - () { - context - .openSliverBottomSheet< - Bip32AddressIndex>( - "key_derivation".tr, - child: Bip32KeyDerivationView( - coin: coin, - curve: coin.conf.type, - )) - .then(setupKeyIndex); - }, - ); - }, - title: selectedCustomKey == null - ? Text(derivationStandard - ? "standard_derivation".tr - : "custom_derivation".tr) - : Text(customKeyIndex == null - ? "non_derivation".tr - : "custom_derivation".tr), - subtitle: selectedCustomKey == null - ? Text(customKeyIndex?.path ?? - standardDerivation.path) - : Text(customKeyIndex?.path ?? - "import_key_derivation_desc2".tr), - ), - WidgetConstant.height20, - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FixedElevatedButton( - padding: - WidgetConstant.paddingVertical20, - onPressed: generateAddress, - child: Text("generate_address".tr), - ), - ], - ) - ], - ), - ) - : Column( - children: [ - WidgetConstant.height20, - PageTitleSubtitle( - title: "derive_network_address" - .tr - .replaceOne(network.coinParam.token.name), - body: LargeTextView( - ["bip44_derivation_desc".tr]), - ), - SetupAddressDerivation(goToAddressPage) - ], - ), - ), - )) - ], - ), - ), - ), - ), - ), - ); - } -} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/transaction/controller/imp/fee_impl.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/transaction/controller/imp/fee_impl.dart index ded804f4..26ec725f 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/transaction/controller/imp/fee_impl.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/transaction/controller/imp/fee_impl.dart @@ -26,8 +26,7 @@ mixin SolanaTransactionFeeImpl on SolanaTransactionImpl { await validator.validator.instructions(address.networkAddress), recentBlockhash: _blockHash!); return await apiProvider.getFee(transaction); - } catch (e, s) { - WalletLogging.print("has error $e $s"); + } catch (e) { rethrow; } } @@ -45,7 +44,6 @@ mixin SolanaTransactionFeeImpl on SolanaTransactionImpl { return result; }, ); - WalletLogging.print("called $call"); return call; }, canclable: _cancelable, closeOnSuccess: true) .listen( diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/update_provider.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/update_provider.dart index a5a00467..de8352ef 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/update_provider.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/solana_pages/update_provider.dart @@ -110,7 +110,6 @@ class _ImportSolanaProviderState extends State<_ImportSolanaProvider> { } void onAddProvider() async { - WalletLogging.print((providers.isEmpty && defaultProviders.isEmpty)); if ((providers.isEmpty && defaultProviders.isEmpty) || selectedProvider != null) return; if (!(formKey.currentState?.validate() ?? false)) return; diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/tron_pages/setup_address.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/tron_pages/setup_address.dart deleted file mode 100644 index f3fbae33..00000000 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/tron_pages/setup_address.dart +++ /dev/null @@ -1,290 +0,0 @@ -import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; -import 'package:blockchain_utils/blockchain_utils.dart'; -import 'package:flutter/material.dart'; -import 'package:mrt_wallet/app/core.dart'; - -import 'package:mrt_wallet/future/pages/start_page/home.dart'; -import 'package:mrt_wallet/future/pages/wallet_pages/global_pages/wallet_global_pages.dart'; -import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; -import 'package:mrt_wallet/main.dart'; -import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; - -import 'package:mrt_wallet/types/typedef.dart'; - -class SetupTronAddressView extends StatefulWidget { - const SetupTronAddressView({super.key}); - - @override - State createState() => _SetupTronAddressViewState(); -} - -class _SetupTronAddressViewState extends State - with SafeState { - final GlobalKey pageProgressKey = - GlobalKey(debugLabel: "SetupEthereumAddressView"); - final GlobalKey form = GlobalKey(); - late final AppChain chainAccount; - AddressDerivationIndex get standardDerivation { - return chainAccount.account.nextDrive(coin); - } - - NetworkAccountCore get networkAccounts => chainAccount.account; - final GlobalKey visibleGenerateAddress = - GlobalKey(debugLabel: "visibleContinue"); - final GlobalKey visibleXAddressDetails = - GlobalKey(debugLabel: "visibleContinue"); - AddressDerivationMode? selectedDerivationMode; - EncryptedCustomKey? selectedCustomKey; - int tag = 0; - - void onChangeTag(String v) { - final newTag = int.tryParse(v); - if (newTag == null || newTag < 0 || newTag > mask32 - 1) return; - tag = newTag; - } - - bool inAddressPage = false; - - void goToAddressPage( - AddressDerivationMode derivationMode, EncryptedCustomKey? customKey) { - if (derivationMode == AddressDerivationMode.importedKey && - customKey == null) return; - selectedDerivationMode = derivationMode; - selectedCustomKey = customKey; - inAddressPage = true; - setState(() {}); - ensureKeyVisible(key: visibleGenerateAddress); - } - - void _onBackButton() { - if (pageProgressKey.isSuccess) return; - if (inAddressPage) { - selectedDerivationMode = null; - selectedCustomKey = null; - inAddressPage = false; - - customKeyIndex = null; - } - setState(() {}); - } - - APPTVMNetwork get network => networkAccounts.network as APPTVMNetwork; - List get coins => network.coins; - CryptoCoins get coin => coins.firstWhere((element) => - element.conf.type == - (selectedCustomKey?.type ?? EllipticCurveTypes.secp256k1)); - bool inited = false; - Bip32AddressIndex? customKeyIndex; - - void _setupIAccount() { - if (inited) return; - final model = context.watch(StateIdsConst.main); - chainAccount = model.chain; - } - - bool get derivationStandard => customKeyIndex == null; - void onChangeDerivation(bool? val, DynamicVoid onFalse) { - if (val == null) return; - if (derivationStandard) { - onFalse(); - return; - } else { - customKeyIndex = null; - setState(() {}); - } - } - - void setupKeyIndex(Bip32AddressIndex? newKeyIndex) { - customKeyIndex = newKeyIndex; - setState(() {}); - } - - @override - void didChangeDependencies() { - _setupIAccount(); - super.didChangeDependencies(); - } - - AddressDerivationIndex? derivationkey(CryptoCoins coin) { - if (selectedCustomKey != null) { - return ImportedAddressIndex( - accountId: selectedCustomKey!.id, - bip32KeyIndex: customKeyIndex, - currencyCoin: coin); - } - return customKeyIndex; - } - - void generateAddress() async { - if (!(form.currentState?.validate() ?? false)) return; - pageProgressKey.progressText("generating_new_addr".tr); - final model = context.watch(StateIdsConst.main); - final curve = selectedCustomKey?.type ?? EllipticCurveTypes.secp256k1; - final coin = coins.firstWhere((element) => element.conf.type == curve); - - final keyIndex = - derivationkey(coin) ?? chainAccount.account.nextDrive(coin); - final newAccount = TronNewAddressParam(coin: coin, deriveIndex: keyIndex); - - final result = await model.deriveNewAccount(newAccount); - if (result.hasError) { - pageProgressKey.errorText(result.error!.tr); - } else { - pageProgressKey.success( - backToIdle: false, - progressWidget: SuccessWithButtomView( - buttomWidget: ContainerWithBorder( - margin: WidgetConstant.paddingVertical8, - child: AddressDetailsView(address: result.result)), - buttomText: "generate_new_address".tr, - onPressed: () { - if (mounted) { - pageProgressKey.backToIdle(); - } - }, - )); - } - setState(() {}); - } - - @override - Widget build(BuildContext context) { - return PopScope( - canPop: !inAddressPage || pageProgressKey.isSuccess, - onPopInvoked: (didPop) { - if (!didPop) { - _onBackButton(); - } - }, - child: Scaffold( - appBar: AppBar( - title: Text("setup_address".tr), - ), - body: PageProgress( - key: pageProgressKey, - backToIdle: AppGlobalConst.oneSecoundDuration, - initialStatus: PageProgressStatus.idle, - child: () => UnfocusableChild( - child: Center( - child: CustomScrollView( - shrinkWrap: true, - slivers: [ - SliverToBoxAdapter( - child: ConstraintsBoxView( - padding: WidgetConstant.paddingHorizontal20, - child: AnimatedSwitcher( - duration: AppGlobalConst.animationDuraion, - child: inAddressPage - ? Form( - key: form, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - key: const ValueKey(true), - children: [ - PageTitleSubtitle( - title: "derive_network_address" - .tr - .replaceOne( - network.coinParam.token.name), - body: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "disable_standard_derivation".tr), - WidgetConstant.height8, - if (selectedCustomKey != null) - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "generate_from_imported_keys" - .tr), - WidgetConstant.height8, - Text( - "generate_from_imported_key_desc1" - .tr) - ], - ) - else - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text("generate_from_hd_wallet" - .tr), - ], - ) - ], - )), - AppSwitchListTile( - value: derivationStandard, - onChanged: (p0) { - onChangeDerivation( - p0, - () { - context - .openSliverBottomSheet< - Bip32AddressIndex>( - "key_derivation".tr, - child: Bip32KeyDerivationView( - coin: coin, - curve: coin.conf.type, - )) - .then(setupKeyIndex); - }, - ); - }, - title: selectedCustomKey == null - ? Text(derivationStandard - ? "standard_derivation".tr - : "custom_derivation".tr) - : Text(customKeyIndex == null - ? "non_derivation".tr - : "custom_derivation".tr), - subtitle: selectedCustomKey == null - ? Text(customKeyIndex?.path ?? - standardDerivation.path) - : Text(customKeyIndex?.path ?? - "import_key_derivation_desc2".tr), - ), - WidgetConstant.height20, - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FixedElevatedButton( - padding: - WidgetConstant.paddingVertical20, - onPressed: generateAddress, - child: Text("generate_address".tr), - ), - ], - ) - ], - ), - ) - : Column( - children: [ - WidgetConstant.height20, - PageTitleSubtitle( - title: "derive_network_address" - .tr - .replaceOne(network.coinParam.token.name), - body: LargeTextView( - ["bip44_derivation_desc".tr]), - ), - SetupAddressDerivation(goToAddressPage) - ], - ), - ), - )) - ], - ), - ), - ), - ), - ), - ); - } -} diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/tron_pages/transaction/controller/impl/fee_impl.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/tron_pages/transaction/controller/impl/fee_impl.dart index 8163e10f..17b58c21 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/tron_pages/transaction/controller/impl/fee_impl.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/tron_pages/transaction/controller/impl/fee_impl.dart @@ -67,7 +67,7 @@ mixin TronTransactionFeeIMpl on TronTransactionImpl { if (address.multiSigAccount) { final multiSigAccount = address as ITronMultisigAddress; permissionId = multiSigAccount.multiSignatureAccount.permissionID; - signer = multiSigAccount.signers.length; + signer = multiSigAccount.keyDetails.length; } final smartContractAddr = field.smartContractAddress; final raw = _buildFeeTr(permissionId); diff --git a/mrt_wallet/lib/future/pages/wallet_pages/network/tron_pages/tron_account_page_view.dart b/mrt_wallet/lib/future/pages/wallet_pages/network/tron_pages/tron_account_page_view.dart index b08a3051..925a6578 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/network/tron_pages/tron_account_page_view.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/network/tron_pages/tron_account_page_view.dart @@ -71,12 +71,13 @@ class _TronTokenView extends StatelessWidget { return ContainerWithBorder( onRemove: () { context.openDialogPage( - (ctx) => TokenDetailsModalView( - token: token, - address: account, - transferPath: PagePathConst.tronTransfer, - ), - "token_info".tr); + "token_info".tr, + child: (ctx) => TokenDetailsModalView( + token: token, + address: account, + transferPath: PagePathConst.tronTransfer, + ), + ); }, onRemoveWidget: WidgetConstant.sizedBox, child: Row( diff --git a/mrt_wallet/lib/future/pages/wallet_pages/security_pages/export_private_key.dart b/mrt_wallet/lib/future/pages/wallet_pages/security_pages/export_private_key.dart index 9ea135ec..e7740f26 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/security_pages/export_private_key.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/security_pages/export_private_key.dart @@ -1,3 +1,4 @@ +import 'package:blockchain_utils/bip/bip/bip32/base/bip32_base.dart'; import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:flutter/material.dart'; @@ -27,13 +28,13 @@ class AccountPrivteKeyView extends StatelessWidget { } return PasswordCheckerView( - accsess: WalletAccsessType.privateKey, + accsess: WalletAccsessType.extendedKey, account: account, password: password, customKey: customKey, onAccsess: (p0, p1) { return _AccountPrivateKeyView( - privateKey: p0, + keys: p0.whereType().toList(), password: p1, account: account, network: wallet.network, @@ -55,13 +56,13 @@ class AccountPrivteKeyView extends StatelessWidget { class _AccountPrivateKeyView extends StatefulWidget { const _AccountPrivateKeyView({ - required this.privateKey, + required this.keys, required this.password, required this.account, required this.network, required this.customKey, }); - final String privateKey; + final List keys; final String password; final CryptoAccountAddress? account; final EncryptedCustomKey? customKey; @@ -72,271 +73,80 @@ class _AccountPrivateKeyView extends StatefulWidget { class _AccountPrivateKeyViewState extends State<_AccountPrivateKeyView> with SafeState, SecureState { - late Bip44Base account; - late final String privateKeyHex; - String? coinPrivateKey; - String get privateKey => coinPrivateKey ?? privateKeyHex; - late CryptoCoins coin; - final List coins = []; - CryptoCoins? wifCoin; - bool inited = false; - String? keyName; - - late final Map supportedCoins = { - for (final i in coins) - i: RichText( - text: TextSpan(style: context.textTheme.bodyMedium, children: [ - TextSpan(text: i.coinName.camelCase), - TextSpan(text: " (${i.proposal.specName.toUpperCase()}) ") - ])) - }; - late final Map supportedWif = { - for (final i in coins) - if (i.conf.wifNetVer != null) - i: RichText( - text: TextSpan(style: context.textTheme.bodyMedium, children: [ - TextSpan(text: i.coinName.camelCase), - TextSpan(text: " (${i.proposal.specName.toUpperCase()}) ") - ])) - }; - bool get supportWif => supportedWif.isNotEmpty; - String? wif; - - bool supported = false; - void _importCoins() { - for (final i in widget.network.coins) { - if (coins.contains(i)) continue; - coins.add(i); - } - } - - void initPrivateKey() { - try { - if (inited) return; - inited = true; - keyName = widget.customKey?.name; - coin = widget.network.coins.firstWhere((element) { - return element.conf.type == - (widget.account?.coin.conf.type ?? widget.customKey!.type); - }); - - if (widget.account != null) { - coin = widget.account!.coin; - privateKeyHex = widget.privateKey; - account = BlockchainUtils.privateKeyToBip44(privateKeyHex, coin); - } else { - privateKeyHex = - BlockchainUtils.extendedKeyToPrivateKey(widget.privateKey, coin); - account = BlockchainUtils.privateKeyToBip44(privateKeyHex, coin); - } - coins.add(coin); - coinPrivateKey = BlockchainUtils.exportPrivateKey(privateKeyHex, coin); - _importCoins(); - if (supportWif) { - wifCoin = coin.conf.wifNetVer != null ? coin : supportedWif.keys.first; - wif = _toWif(); - } - supported = true; - } catch (e) { - supported = false; - } - } + late AccessPrivateKeyResponse key = widget.keys.first; + bool get hasMultipleKey => widget.keys.length > 1; + Bip32Base get account => key.account; + String get privateKey => key.privateKey; + CryptoCoins get coin => key.coin; + String? get keyName => widget.customKey?.name; + String? get wif => key.wif; + bool _showPrivateKey = false; - String? _toWif() { - if (wifCoin != null) { - return WifEncoder.encode(account.privateKey.raw, - netVer: wifCoin!.conf.wifNetVer!); - } - return null; + void onChangeKey(AccessPrivateKeyResponse? changeKey) { + if (key == changeKey || changeKey == null) return; + key = changeKey; + setState(() {}); } - bool _showPrivateKey = false; void onChangeShowPrivateKey() { _showPrivateKey = !_showPrivateKey; setState(() {}); } final GlobalKey progressKey = GlobalKey(); - @override - void didChangeDependencies() { - initPrivateKey(); - super.didChangeDependencies(); - } - - void onChangeExtendedType(CryptoCoins? v) { - if (v == null) return; - coin = v; - account = BlockchainUtils.privateKeyToBip44(privateKeyHex, coin); - setState(() {}); - } - - void onChangeWif(CryptoCoins? v) { - if (v == null) return; - wifCoin = v; - wif = _toWif(); - setState(() {}); - } @override Widget build(BuildContext context) { - return PageProgress( - key: progressKey, - initialWidget: - ErrorWithTextView(text: "unable_to_accsess_private_key".tr), - initialStatus: - !supported ? PageProgressStatus.error : PageProgressStatus.idle, - backToIdle: AppGlobalConst.oneSecoundDuration, - child: () => ConstraintsBoxView( - padding: WidgetConstant.paddingHorizontal20, - alignment: Alignment.center, - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ + return ConstraintsBoxView( + padding: WidgetConstant.paddingHorizontal20, + alignment: Alignment.center, + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + PageTitleSubtitle( + title: "private_key".tr, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [Text("export_private_key_desc".tr)], + )), + if (hasMultipleKey) ...[ + Text("private_keys".tr, style: context.textTheme.titleMedium), + Text("switch_between_keys".tr), + WidgetConstant.height8, + AppDropDownBottom( + onChanged: onChangeKey, + items: {for (final i in widget.keys) i: Text(i.keyName.tr)}, + label: "key_name".tr, + value: key, + ), WidgetConstant.height20, - PageTitleSubtitle( - title: "private_key".tr, - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [Text("export_private_key_desc".tr)], - )), - if (widget.account != null) ...[ - Text("address_details".tr, - style: context.textTheme.titleMedium), - WidgetConstant.height8, - ContainerWithBorder( - child: CopyTextWithBarcode( - dataToCopy: widget.account!.address.toAddress, - widget: AddressDetailsView(address: widget.account!), - barcodeTitle: "address_sharing".tr), - ), - WidgetConstant.height20, - ], - if (keyName != null) ...[ - Text("key_name".tr, style: context.textTheme.titleMedium), - WidgetConstant.height8, - ContainerWithBorder(child: Text(keyName ?? "")), - WidgetConstant.height20, - ], - Text("private_key".tr, style: context.textTheme.titleMedium), + ], + if (widget.account != null && !hasMultipleKey) ...[ + Text("address_details".tr, style: context.textTheme.titleMedium), WidgetConstant.height8, - Stack( - children: [ - AnimatedSwitcher( - duration: AppGlobalConst.animationDuraion, - child: Container( - foregroundDecoration: _showPrivateKey - ? null - : BoxDecoration( - color: context.colors.secondary, - borderRadius: WidgetConstant.border8, - ), - child: ContainerWithBorder( - child: CopyTextWithBarcode( - secureBarcode: true, - barcodeWidget: ContainerWithBorder( - child: CopyTextIcon( - dataToCopy: privateKey, - widget: - ObscureTextView(privateKey, maxLine: 3))), - underBarcodeWidget: ErrorTextContainer( - margin: WidgetConstant.paddingVertical10, - error: "image_store_alert_keys".tr), - dataToCopy: privateKey, - barcodeTitle: "private_key".tr, - widget: SelectableText(privateKey), - )), - ), - ), - Positioned.fill( - child: AnimatedSwitcher( - duration: AppGlobalConst.animationDuraion, - child: SizedBox( - key: ValueKey(_showPrivateKey), - child: _showPrivateKey - ? WidgetConstant.sizedBox - : FilledButton.icon( - onPressed: onChangeShowPrivateKey, - icon: const Icon(Icons.remove_red_eye), - label: Text("show_private_key".tr)), - ), - ), - ) - ], + ContainerWithBorder( + child: CopyTextWithBarcode( + dataToCopy: widget.account!.address.toAddress, + widget: AddressDetailsView(address: widget.account!), + barcodeTitle: "address_sharing".tr), ), WidgetConstant.height20, - if (supportedCoins.length > 1) ...[ - Text("key_extended_for".tr, - style: context.textTheme.titleMedium), - WidgetConstant.height8, - AppDropDownBottom( - items: supportedCoins, - label: "key_extended_for".tr, - onChanged: onChangeExtendedType, - value: coin, - ), - WidgetConstant.height20 - ], - Text("extended_private_key".tr, - style: context.textTheme.titleMedium), + ], + if (keyName != null) ...[ + Text("key_name".tr, style: context.textTheme.titleMedium), WidgetConstant.height8, - Stack( + ContainerWithBorder(child: Text(keyName ?? "")), + WidgetConstant.height20, + ], + AnimatedSwitcher( + duration: AppGlobalConst.animationDuraion, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + key: UniqueKey(), children: [ - AnimatedSwitcher( - duration: AppGlobalConst.animationDuraion, - child: Container( - foregroundDecoration: _showPrivateKey - ? null - : BoxDecoration( - color: context.colors.secondary, - borderRadius: WidgetConstant.border8, - ), - child: ContainerWithBorder( - child: CopyTextWithBarcode( - secureBarcode: true, - barcodeWidget: ContainerWithBorder( - child: CopyTextIcon( - dataToCopy: account.privateKey.toExtended, - widget: ObscureTextView( - account.privateKey.toExtended, - maxLine: 5))), - underBarcodeWidget: ErrorTextContainer( - margin: WidgetConstant.paddingVertical10, - error: "image_store_alert_keys".tr), - dataToCopy: account.privateKey.toExtended, - barcodeTitle: "extended_private_key".tr, - widget: SelectableText(account.privateKey.toExtended), - )), - ), - ), - Positioned.fill( - child: AnimatedSwitcher( - duration: AppGlobalConst.animationDuraion, - child: SizedBox( - key: ValueKey(_showPrivateKey), - child: _showPrivateKey - ? WidgetConstant.sizedBox - : FilledButton.icon( - onPressed: onChangeShowPrivateKey, - icon: const Icon(Icons.remove_red_eye), - label: Text("show_private_key".tr)), - ), - ), - ) - ], - ), - if (supportWif) ...[ - WidgetConstant.height20, - Text("wif".tr, style: context.textTheme.titleMedium), - WidgetConstant.height8, - AppDropDownBottom( - items: supportedWif, - label: "wif_for".tr, - onChanged: onChangeWif, - value: wifCoin, - ), - if (wif != null) ...[ + Text("private_key".tr, style: context.textTheme.titleMedium), WidgetConstant.height8, Stack( children: [ @@ -354,14 +164,15 @@ class _AccountPrivateKeyViewState extends State<_AccountPrivateKeyView> secureBarcode: true, barcodeWidget: ContainerWithBorder( child: CopyTextIcon( - dataToCopy: wif!, - widget: ObscureTextView(wif!, maxLine: 3))), + dataToCopy: privateKey, + widget: ObscureTextView(privateKey, + maxLine: 3))), underBarcodeWidget: ErrorTextContainer( margin: WidgetConstant.paddingVertical10, error: "image_store_alert_keys".tr), - dataToCopy: wif!, + dataToCopy: privateKey, barcodeTitle: "private_key".tr, - widget: SelectableText(wif!), + widget: SelectableText(privateKey), )), ), ), @@ -381,33 +192,132 @@ class _AccountPrivateKeyViewState extends State<_AccountPrivateKeyView> ) ], ), - ] - ], - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: WidgetConstant.paddingVertical20, - child: FilledButton.icon( - label: Text("create_backup".tr), - onPressed: () { - context.openSliverDialog( - (ctx) => SecureBackupView( - password: widget.password, - data: privateKey, - descriptions: [ - WidgetConstant.height8, - Text("about_web3_defination_desc4".tr), - ], + WidgetConstant.height20, + Text("extended_private_key".tr, + style: context.textTheme.titleMedium), + WidgetConstant.height8, + Stack( + children: [ + AnimatedSwitcher( + duration: AppGlobalConst.animationDuraion, + child: Container( + foregroundDecoration: _showPrivateKey + ? null + : BoxDecoration( + color: context.colors.secondary, + borderRadius: WidgetConstant.border8, + ), + child: ContainerWithBorder( + child: CopyTextWithBarcode( + secureBarcode: true, + barcodeWidget: ContainerWithBorder( + child: CopyTextIcon( + dataToCopy: account.privateKey.toExtended, + widget: ObscureTextView( + account.privateKey.toExtended, + maxLine: 5))), + underBarcodeWidget: ErrorTextContainer( + margin: WidgetConstant.paddingVertical10, + error: "image_store_alert_keys".tr), + dataToCopy: account.privateKey.toExtended, + barcodeTitle: "extended_private_key".tr, + widget: + SelectableText(account.privateKey.toExtended), + )), + ), + ), + Positioned.fill( + child: AnimatedSwitcher( + duration: AppGlobalConst.animationDuraion, + child: SizedBox( + key: ValueKey(_showPrivateKey), + child: _showPrivateKey + ? WidgetConstant.sizedBox + : FilledButton.icon( + onPressed: onChangeShowPrivateKey, + icon: const Icon(Icons.remove_red_eye), + label: Text("show_private_key".tr)), + ), + ), + ) + ], + ), + if (wif != null) ...[ + WidgetConstant.height20, + Text("wif".tr, style: context.textTheme.titleMedium), + WidgetConstant.height8, + Stack( + children: [ + AnimatedSwitcher( + duration: AppGlobalConst.animationDuraion, + child: Container( + foregroundDecoration: _showPrivateKey + ? null + : BoxDecoration( + color: context.colors.secondary, + borderRadius: WidgetConstant.border8, ), - "backup_private_key".tr); - }, - icon: const Icon(Icons.backup)), - ) + child: ContainerWithBorder( + child: CopyTextWithBarcode( + secureBarcode: true, + barcodeWidget: ContainerWithBorder( + child: CopyTextIcon( + dataToCopy: wif!, + widget: + ObscureTextView(wif!, maxLine: 3))), + underBarcodeWidget: ErrorTextContainer( + margin: WidgetConstant.paddingVertical10, + error: "image_store_alert_keys".tr), + dataToCopy: wif!, + barcodeTitle: "private_key".tr, + widget: SelectableText(wif!), + )), + ), + ), + Positioned.fill( + child: AnimatedSwitcher( + duration: AppGlobalConst.animationDuraion, + child: SizedBox( + key: ValueKey(_showPrivateKey), + child: _showPrivateKey + ? WidgetConstant.sizedBox + : FilledButton.icon( + onPressed: onChangeShowPrivateKey, + icon: const Icon(Icons.remove_red_eye), + label: Text("show_private_key".tr)), + ), + ), + ) + ], + ), + ], ], - ) - ], - ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: WidgetConstant.paddingVertical20, + child: FilledButton.icon( + label: Text("create_backup".tr), + onPressed: () { + context.openSliverDialog( + (ctx) => SecureBackupView( + password: widget.password, + data: privateKey, + descriptions: [ + WidgetConstant.height8, + Text("about_web3_defination_desc4".tr), + ], + ), + "backup_private_key".tr); + }, + icon: const Icon(Icons.backup)), + ) + ], + ) + ], ), ), ); diff --git a/mrt_wallet/lib/future/pages/wallet_pages/security_pages/export_seed.dart b/mrt_wallet/lib/future/pages/wallet_pages/security_pages/export_seed.dart index bc659bac..dd84301b 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/security_pages/export_seed.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/security_pages/export_seed.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:mrt_wallet/app/core.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/wallet_pages.dart'; import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; +import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; class ExportSeedView extends StatelessWidget { const ExportSeedView({super.key}); @@ -11,7 +12,8 @@ class ExportSeedView extends StatelessWidget { return PasswordCheckerView( accsess: WalletAccsessType.seed, onAccsess: (p0, p1) { - return _ExportSeedView(mnemonic: p0, password: p1); + return _ExportSeedView( + mnemonic: p0.first as AccessMnemonicResponse, password: p1); }, title: "export_mnemonic".tr, subtitle: PageTitleSubtitle( @@ -22,7 +24,7 @@ class ExportSeedView extends StatelessWidget { class _ExportSeedView extends StatefulWidget { const _ExportSeedView({required this.mnemonic, required this.password}); - final String mnemonic; + final AccessMnemonicResponse mnemonic; final String password; @override @@ -34,7 +36,7 @@ class _ExportSeedViewState extends State<_ExportSeedView> final GlobalKey form = GlobalKey(debugLabel: "ExportSeedView"); final GlobalKey progressKey = GlobalKey(); - late final Mnemonic _mnemonic = Mnemonic.fromString(widget.mnemonic); + late final Mnemonic _mnemonic = widget.mnemonic.mnemonic; bool _showMnemonic = false; void onChangeShowMnemonic() { diff --git a/mrt_wallet/lib/future/pages/wallet_pages/security_pages/import_account.dart b/mrt_wallet/lib/future/pages/wallet_pages/security_pages/import_account.dart index 7b71a077..9580ffa3 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/security_pages/import_account.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/security_pages/import_account.dart @@ -1,4 +1,5 @@ import 'package:blockchain_utils/bip/bip/bip32/base/bip32_base.dart'; +import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:flutter/material.dart'; import 'package:mrt_wallet/app/core.dart'; @@ -22,6 +23,16 @@ enum _PrivateKeyTypes { bool get forRipple => this == _PrivateKeyTypes.rippleEntropy || this == _PrivateKeyTypes.rippleSeed; + bool get isExtendedKey => this == _PrivateKeyTypes.extendKey; + CustomKeyType toCustomKeyType() { + if (this == _PrivateKeyTypes.extendKey) return CustomKeyType.extendedKey; + return CustomKeyType.privateKey; + } + + String toKey(Bip32Base key) { + if (isExtendedKey) return key.privateKey.toExtended; + return key.privateKey.toHex(); + } } class ImportAccountView extends StatelessWidget { @@ -57,24 +68,33 @@ class _ImportAccountState extends State<_ImportAccount> with SafeState { final GlobalKey form = GlobalKey(debugLabel: "_ImportAccountState_2"); late Map<_PrivateKeyTypes, Widget> keyTypes = _buildKeyTypes(); + + bool get inRipple => widget.network is AppXRPNetwork; + + List get coins => widget.network.coins; + late CryptoCoins coin = coins.first; + bool get needSelectCoins => selected.isExtendedKey && coins.length > 1; + + _PrivateKeyTypes selected = _PrivateKeyTypes.privateKey; + Bip32Base? _account; + bool get isBackup => selected == _PrivateKeyTypes.backup; + String? keyName; + String _backup = ""; + String _password = ""; + String _key = ""; + SecretWalletEncoding encoding = SecretWalletEncoding.json; + void setKeyName(String? name) { setState(() { keyName = name; }); } - bool get inRipple => widget.network is AppXRPNetwork; - late EllipticCurveTypes curve = widget.network.keyTypes.first; - List get curves => widget.network.keyTypes; - late final bool needSelectCurve = curves.length > 1; - Map<_PrivateKeyTypes, Widget> _buildKeyTypes() { Map<_PrivateKeyTypes, Widget> types = {}; for (final i in _PrivateKeyTypes.values) { - if (i == _PrivateKeyTypes.wif && - !widget.network.coins - .any((element) => element.conf.wifNetVer != null)) continue; + if (i == _PrivateKeyTypes.wif && coin.conf.wifNetVer == null) continue; if (i.forRipple) { if (!inRipple) continue; @@ -84,22 +104,18 @@ class _ImportAccountState extends State<_ImportAccount> with SafeState { return types; } - _PrivateKeyTypes selected = _PrivateKeyTypes.privateKey; - - Bip32Base? _account; - - bool get isBackup => selected == _PrivateKeyTypes.backup; - void onSelect(_PrivateKeyTypes? s) { selected = s ?? selected; _account = null; _error = null; - // showRippleKeyAlgorithm = _showRippleKeyAlgorithm(); setState(() {}); } - void onSelectRippleKeyAlgorithm(EllipticCurveTypes? alg) { - curve = alg ?? curve; + void onChangeKeyAlogrithm(CryptoCoins? mewCoin) { + if (mewCoin == null) return; + coin = mewCoin; + keyTypes = _buildKeyTypes(); + selected = _PrivateKeyTypes.privateKey; setState(() {}); } @@ -110,33 +126,34 @@ class _ImportAccountState extends State<_ImportAccount> with SafeState { String? _error; Future _getKey(WalletProvider model) async { - final coin = widget.network.coins.first; - - switch (selected) { - case _PrivateKeyTypes.extendKey: - return BlockchainUtils.extendedKeyToBip32(_key, coin.conf.type); - case _PrivateKeyTypes.privateKey: - if (inRipple) { - return RippleUtils.ripplePrivateKeyToBip32(_key, curve); - } - return BlockchainUtils.privteKeyToBip32( - BytesUtils.fromHexString(_key), coin.conf.type); - case _PrivateKeyTypes.wif: - return BlockchainUtils.wifToBip32(_key, coin); - case _PrivateKeyTypes.backup: - final backupResult = BytesUtils.toHexString( - await model.restoreBackup(_password, _backup, encoding)); - if (inRipple) { - return RippleUtils.ripplePrivateKeyToBip32(backupResult, curve); - } - return BlockchainUtils.privteKeyToBip32( - BytesUtils.fromHexString(backupResult), coin.conf.type); - case _PrivateKeyTypes.rippleSeed: - return RippleUtils.rippleSeedToBip32(_key); - case _PrivateKeyTypes.rippleEntropy: - return RippleUtils.rippleEntropyToBip32(_key, curve); - default: - throw UnimplementedError(); + try { + switch (selected) { + case _PrivateKeyTypes.extendKey: + return BlockchainUtils.extendedKeyToBip32( + extendedKey: _key, coin: coin); + case _PrivateKeyTypes.privateKey: + if (inRipple) { + return RippleUtils.ripplePrivateKeyToBip32(_key, coin); + } + return BlockchainUtils.privteKeyToBip32( + BytesUtils.fromHexString(_key), coin); + case _PrivateKeyTypes.wif: + return BlockchainUtils.wifToBip32(_key, coin); + case _PrivateKeyTypes.backup: + final backupResult = BytesUtils.toHexString( + await model.restoreBackup(_password, _backup, encoding)); + if (inRipple) { + return RippleUtils.ripplePrivateKeyToBip32(backupResult, coin); + } + return BlockchainUtils.privteKeyToBip32( + BytesUtils.fromHexString(backupResult), coin); + case _PrivateKeyTypes.rippleSeed: + return RippleUtils.rippleSeedToBip32(_key, widget.network); + case _PrivateKeyTypes.rippleEntropy: + return RippleUtils.rippleEntropyToBip32(_key, coin); + } + } catch (e) { + rethrow; } } @@ -147,17 +164,17 @@ class _ImportAccountState extends State<_ImportAccount> with SafeState { final createKey = await MethodCaller.call(() async { _account = await _getKey(model); - if (_account == null) { _error = "private_key_invalid".tr; throw WalletExceptionConst.invalidPrivateKey; } final customKey = WalletCustomKeys( - checksum: _account!.publicKey.fingerPrint.toHex(), - extendedPrivateKey: _account!.privateKey.toExtended, - type: _account!.curveType, + checksum: BlockchainUtils.createCustomKeyChecksum(_account!), + extendedPrivateKey: selected.toKey(_account!), + coin: coin, publicKey: _account!.publicKey.toHex(), - name: keyName); + name: keyName, + keyType: selected.toCustomKeyType()); return customKey; }); if (createKey.hasError) { @@ -181,8 +198,6 @@ class _ImportAccountState extends State<_ImportAccount> with SafeState { return null; } - String _key = ""; - void onChangeKey(String key) { _key = key; if (_error != null) { @@ -191,15 +206,12 @@ class _ImportAccountState extends State<_ImportAccount> with SafeState { } } - SecretWalletEncoding encoding = SecretWalletEncoding.json; - void onChangeEncoding(SecretWalletEncoding? updateEncoding) { encoding = updateEncoding ?? encoding; setState(() {}); } - String _password = ""; void onChangePassword(String password) { _password = password; } @@ -227,7 +239,6 @@ class _ImportAccountState extends State<_ImportAccount> with SafeState { return "bcakup_validator".tr; } - String _backup = ""; void onChange(String v) { _backup = v; if (_error != null) { @@ -262,6 +273,20 @@ class _ImportAccountState extends State<_ImportAccount> with SafeState { Text("import_account_desc1".tr), ], )), + if (needSelectCoins) ...[ + Text("key_algorithm".tr, + style: context.textTheme.titleMedium), + Text("inidicate_type_of_key".tr), + WidgetConstant.height8, + AppDropDownBottom( + items: { + for (final i in coins) i: Text(i.coinName.camelCase) + }, + value: coin, + label: "key_algorithm".tr, + onChanged: onChangeKeyAlogrithm), + WidgetConstant.height20, + ], Text("key_type".tr, style: context.textTheme.titleMedium), Text("inidicate_type_of_key".tr), WidgetConstant.height8, @@ -271,20 +296,6 @@ class _ImportAccountState extends State<_ImportAccount> with SafeState { label: "key_type".tr, onChanged: onSelect), WidgetConstant.height20, - if (needSelectCurve) ...[ - Text("ripple_key_type".tr, - style: context.textTheme.titleMedium), - Text("inidicate_type_of_key".tr), - WidgetConstant.height8, - AppDropDownBottom( - items: { - for (final i in curves) i: Text(i.name.camelCase) - }, - value: curve, - label: "ripple_key_type".tr, - onChanged: onSelectRippleKeyAlgorithm), - WidgetConstant.height20, - ], Text(selected.value, style: context.textTheme.titleMedium), WidgetConstant.height8, if (isBackup) ...[ diff --git a/mrt_wallet/lib/future/pages/wallet_pages/security_pages/password_checker.dart b/mrt_wallet/lib/future/pages/wallet_pages/security_pages/password_checker.dart index a87c31d0..a9df0bb9 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/security_pages/password_checker.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/security_pages/password_checker.dart @@ -5,9 +5,20 @@ import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; import 'package:mrt_wallet/main.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; -enum WalletAccsessType { privateKey, seed, verify } +enum WalletAccsessType { + privateKey, + seed, + verify, + extendedKey; -typedef FuncWidgetStringPaagePrgoressKey = Widget Function(String, String); + bool get isAccsessKey => + this == WalletAccsessType.privateKey || + this == WalletAccsessType.extendedKey; + bool get isExtendedKey => this == WalletAccsessType.extendedKey; +} + +typedef FuncWidgetStringPaagePrgoressKey = Widget Function( + List, String); class PasswordCheckerView extends StatefulWidget { const PasswordCheckerView( @@ -41,7 +52,7 @@ class _PasswordCheckerViewState extends State GlobalKey(debugLabel: "AppTextFieldState"); String _password = ""; - String? credentials; + List? credentials; String? error; String? psaswordValidator(String? v) { if (AppStringUtility.isStrongPassword(v)) return null; diff --git a/mrt_wallet/lib/future/pages/wallet_pages/security_pages/secure_backup.dart b/mrt_wallet/lib/future/pages/wallet_pages/security_pages/secure_backup.dart index 12640f49..64cdd421 100644 --- a/mrt_wallet/lib/future/pages/wallet_pages/security_pages/secure_backup.dart +++ b/mrt_wallet/lib/future/pages/wallet_pages/security_pages/secure_backup.dart @@ -64,7 +64,6 @@ class _SecureBackupViewState extends State with SafeState { setState(() {}); } buttomState.process(); - WalletLogging.print("come here?!"); final result = await MethodCaller.call(() async { final name = "credentials_${DateTime.now().toFileName()}.txt"; final toFile = await CrossFileWriter.writeString(backup!, name); @@ -75,7 +74,6 @@ class _SecureBackupViewState extends State with SafeState { mimeType: FileMimeTypes.textPlain, ); }); - WalletLogging.print("result $result"); if (result.hasError || !result.result) { buttomState.error(); _shareError = result.error?.tr; diff --git a/mrt_wallet/lib/future/widgets/container_with_border.dart b/mrt_wallet/lib/future/widgets/container_with_border.dart index f1b039de..ef466f7f 100644 --- a/mrt_wallet/lib/future/widgets/container_with_border.dart +++ b/mrt_wallet/lib/future/widgets/container_with_border.dart @@ -18,7 +18,8 @@ class ContainerWithBorder extends StatelessWidget { this.validateText, this.shadow = false, this.onTapWhenOnRemove = true, - this.iconAlginment = CrossAxisAlignment.center}); + this.iconAlginment = CrossAxisAlignment.center, + this.onTapError}); final Widget child; final EdgeInsets padding; final EdgeInsets margin; @@ -32,6 +33,7 @@ class ContainerWithBorder extends StatelessWidget { final bool shadow; final bool onTapWhenOnRemove; final CrossAxisAlignment iconAlginment; + final DynamicVoid? onTapError; @override Widget build(BuildContext context) { return Column( @@ -83,9 +85,11 @@ class ContainerWithBorder extends StatelessWidget { ), if (!validate && validateText != null) ErrorTextContainer( - error: validateText ?? "", - margin: WidgetConstant.padding5, - padding: WidgetConstant.padding5) + error: validateText ?? "", + margin: WidgetConstant.padding5, + padding: WidgetConstant.padding5, + oTapError: onTapError, + ) ], ), ), diff --git a/mrt_wallet/lib/future/widgets/dialog_view.dart b/mrt_wallet/lib/future/widgets/dialog_view.dart index 671638ed..27f6775e 100644 --- a/mrt_wallet/lib/future/widgets/dialog_view.dart +++ b/mrt_wallet/lib/future/widgets/dialog_view.dart @@ -36,10 +36,7 @@ class DialogView extends StatelessWidget { leading: WidgetConstant.sizedBox, leadingWidth: 0, pinned: true, - actions: [ - ...content, - const CloseButton(), - ], + actions: [...content, const CloseButton()], ), SliverToBoxAdapter( child: ConstraintsBoxView( diff --git a/mrt_wallet/lib/future/widgets/error_text_container.dart b/mrt_wallet/lib/future/widgets/error_text_container.dart index c34f4468..0c9e243c 100644 --- a/mrt_wallet/lib/future/widgets/error_text_container.dart +++ b/mrt_wallet/lib/future/widgets/error_text_container.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:mrt_wallet/app/core.dart'; import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; +import 'package:mrt_wallet/types/typedef.dart'; class ErrorTextContainer extends StatelessWidget { const ErrorTextContainer( @@ -10,12 +11,14 @@ class ErrorTextContainer extends StatelessWidget { this.padding = WidgetConstant.padding10, this.margin = WidgetConstant.padding5, this.verticalMargin = EdgeInsets.zero, - this.showErrorIcon = true}); + this.showErrorIcon = true, + this.oTapError}); final EdgeInsets margin; final EdgeInsets padding; final String? error; final EdgeInsets verticalMargin; final bool showErrorIcon; + final DynamicVoid? oTapError; @override Widget build(BuildContext context) { @@ -26,7 +29,11 @@ class ErrorTextContainer extends StatelessWidget { : Padding( padding: verticalMargin, child: ContainerWithBorder( - onRemove: showErrorIcon ? () {} : null, + onRemove: showErrorIcon + ? () { + oTapError?.call(); + } + : null, margin: margin, padding: padding, onRemoveIcon: const Icon(Icons.error), diff --git a/mrt_wallet/lib/future/widgets/list_tile.dart b/mrt_wallet/lib/future/widgets/list_tile.dart index 7604d6fd..5aaaa8c6 100644 --- a/mrt_wallet/lib/future/widgets/list_tile.dart +++ b/mrt_wallet/lib/future/widgets/list_tile.dart @@ -148,7 +148,7 @@ class AppSwitchListTile extends StatelessWidget { style: context.textTheme.bodyMedium!, maxLines: 2, overflow: TextOverflow.ellipsis, - child: subtitle!, + child: subtitle!, ), ); } diff --git a/mrt_wallet/lib/main.dart b/mrt_wallet/lib/main.dart index 8e2ee0a9..6ad5c65b 100644 --- a/mrt_wallet/lib/main.dart +++ b/mrt_wallet/lib/main.dart @@ -9,25 +9,20 @@ import 'package:mrt_wallet/future/pages/wallet_pages/network/bitcoin_cash_pages/ import 'package:mrt_wallet/future/pages/wallet_pages/network/bitcoin_pages/update_provider/import_electrum_provider.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/setup_address_page.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/cardano_pages/transaction/pages/build_transaction.dart'; -import 'package:mrt_wallet/future/pages/wallet_pages/network/cosmos/setup_address.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/cosmos/transaction/fields/transfer.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/ethereum_pages/import_network/edit_network.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/ethereum_pages/import_network/import_network.dart'; -import 'package:mrt_wallet/future/pages/wallet_pages/network/ethereum_pages/setup_address.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/ethereum_pages/token/import_tokens.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/ethereum_pages/transaction/fields/ethereum_transfer_field_view.dart'; -import 'package:mrt_wallet/future/pages/wallet_pages/network/ripple_pages/setup_address.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/ripple_pages/setup_multi_sig_address.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/ripple_pages/token/import_nfts.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/ripple_pages/token/import_token.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/ripple_pages/transaction/fields/ripple_tranaction_fields_view.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/ripple_pages/transaction/fields/transfer.dart'; -import 'package:mrt_wallet/future/pages/wallet_pages/network/solana_pages/setup_address.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/solana_pages/spl_token/account_spl_tokens_view.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/solana_pages/transaction/fields/transaction.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/solana_pages/transaction/fields/transfer.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/solana_pages/update_provider.dart'; -import 'package:mrt_wallet/future/pages/wallet_pages/network/tron_pages/setup_address.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/tron_pages/setup_multisig_address.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/tron_pages/token/import_trc10_tokens.dart'; import 'package:mrt_wallet/future/pages/wallet_pages/network/tron_pages/transaction/fields/tron_transaction_fields.dart'; @@ -41,6 +36,7 @@ import 'package:mrt_wallet/models/app/app_seting.dart'; import 'package:mrt_wallet/models/app/material.dart'; import 'future/pages/wallet_pages/account_pages/account_pages.dart'; +import 'future/pages/wallet_pages/global_pages/address_derivation/address_derivation_view.dart'; import 'future/pages/wallet_pages/network/bitcoin_pages/bitcoin.dart'; class MyHttpOverrides extends HttpOverrides { @@ -200,12 +196,9 @@ class PageRouter { return const BackupWalletView(); case PagePathConst.manageImportedKey: return const ManageImportedKeysView(); - case PagePathConst.setupRippleAddress: - return const SetupRippleAddressView(); - case PagePathConst.setupEthAddress: - return const SetupEthereumAddressView(); - case PagePathConst.setupTronAddress: - return const SetupTronAddressView(); + + case PagePathConst.setupGenericAddress: + return const NetworkGenericAddressDerivationView(); case PagePathConst.rippleTransfer: return const RippleTransferTransactionView(); case PagePathConst.ethereumTransaction: @@ -236,16 +229,12 @@ class PageRouter { return const EditEVMNetwork(); case PagePathConst.updateElectrumProviders: return const ImportElectrumProviderView(); - case PagePathConst.setupSolanaAddress: - return const SetupSolanaAddressView(); case PagePathConst.importSPLTokens: return const SolanaImportSPLTokensView(); case PagePathConst.setupCardanoAddress: return const SetupCardanoAddressView(); case PagePathConst.cardanoTransaction: return const SendCardanoTransactionView(); - case PagePathConst.setupCosmosAddress: - return const SetupCosmosAddressView(); case PagePathConst.cosmosTransaction: return const CosmosTransferTransactionView(); case PagePathConst.solanaTransaction: diff --git a/mrt_wallet/lib/models/wallet_models/account/bip32_network_account.dart b/mrt_wallet/lib/models/wallet_models/account/bip32_network_account.dart index ed5bdde3..b5c8fbbf 100644 --- a/mrt_wallet/lib/models/wallet_models/account/bip32_network_account.dart +++ b/mrt_wallet/lib/models/wallet_models/account/bip32_network_account.dart @@ -80,14 +80,9 @@ class Bip32NetworkAccount implements NetworkAccountCore { List> get contacts => _contacts; @override - AddressDerivationIndex nextDrive(CryptoCoins coin, - {SeedGenerationType masterKeyGeneration = SeedGenerationType.bip39, - SeedGenerationType seedGeneration = SeedGenerationType.bip39}) { - if (masterKeyGeneration == SeedGenerationType.byronLegacySeed) { - return BlockchainUtils.findNextByronLegacyIndex( - coin: coin, addresses: addresses); - } - return BlockchainUtils.findNextBip32Index( + Bip32AddressIndex nextDerive(CryptoCoins coin, + {SeedGenerationType seedGeneration = SeedGenerationType.bip39}) { + return BlockchainUtils.generateAccountNextKeyIndex( coin: coin, addresses: addresses, seedGenerationType: seedGeneration); } @@ -117,9 +112,11 @@ class Bip32NetworkAccount implements NetworkAccountCore { } else { throw WalletExceptionConst.invalidAccountDetails; } + if (addresses.contains(newAddress)) { throw WalletExceptionConst.addressAlreadyExist; } + _addresses = List.unmodifiable([newAddress, ..._addresses]); return newAddress as CryptoAccountAddress; } diff --git a/mrt_wallet/lib/models/wallet_models/account/core/account.dart b/mrt_wallet/lib/models/wallet_models/account/core/account.dart index ef01f2f3..b939a779 100644 --- a/mrt_wallet/lib/models/wallet_models/account/core/account.dart +++ b/mrt_wallet/lib/models/wallet_models/account/core/account.dart @@ -15,9 +15,8 @@ abstract class NetworkAccountCore with CborSerializable { ContactCore? getContact(String address); ReceiptAddress? getReceiptAddress(String address); abstract final CryptoAccountAddress address; - AddressDerivationIndex nextDrive(CryptoCoins coin, - {SeedGenerationType masterKeyGeneration = SeedGenerationType.bip39, - SeedGenerationType seedGeneration = SeedGenerationType.bip39}); + Bip32AddressIndex nextDerive(CryptoCoins coin, + {SeedGenerationType seedGeneration = SeedGenerationType.bip39}); void removeAccount(CryptoAccountAddress address); bool get haveAddress; CryptoAccountAddress addNewAddress( diff --git a/mrt_wallet/lib/models/wallet_models/address/core/address.dart b/mrt_wallet/lib/models/wallet_models/address/core/address.dart index fffe8e3d..7b505d05 100644 --- a/mrt_wallet/lib/models/wallet_models/address/core/address.dart +++ b/mrt_wallet/lib/models/wallet_models/address/core/address.dart @@ -11,6 +11,7 @@ abstract class CryptoAccountAddress with CborSerializable { abstract final CryptoCoins coin; abstract final NetworkAddressDetailsCore address; abstract final AddressDerivationIndex keyIndex; + abstract final List keyIndexes; abstract final List> tokens; abstract final List nfts; abstract final int network; @@ -19,7 +20,6 @@ abstract class CryptoAccountAddress with CborSerializable { abstract final String? accountName; abstract final String orginalAddress; void setAccountName(String? name); - List get signers; bool get multiSigAccount; String accountToString(); void addNFT(NFTCore newNft); diff --git a/mrt_wallet/lib/models/wallet_models/address/core/core.dart b/mrt_wallet/lib/models/wallet_models/address/core/core.dart index 3a9d2eb0..bbf34e96 100644 --- a/mrt_wallet/lib/models/wallet_models/address/core/core.dart +++ b/mrt_wallet/lib/models/wallet_models/address/core/core.dart @@ -2,7 +2,5 @@ export 'bip/bip32_address_core.dart'; export 'bip/bip44_level_details.dart'; export 'derivation/address_derivation.dart'; export 'derivation/bip32_address_index.dart'; -export 'derivation/imported_address_index.dart'; export 'derivation/multi_sig_address_index.dart'; export 'address.dart'; -export 'derivation/byron_legacy_address_index.dart'; diff --git a/mrt_wallet/lib/models/wallet_models/address/core/derivation/address_derivation.dart b/mrt_wallet/lib/models/wallet_models/address/core/derivation/address_derivation.dart index df496017..3aa27509 100644 --- a/mrt_wallet/lib/models/wallet_models/address/core/derivation/address_derivation.dart +++ b/mrt_wallet/lib/models/wallet_models/address/core/derivation/address_derivation.dart @@ -7,12 +7,23 @@ import 'package:mrt_wallet/models/serializable/serializable.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; import 'package:mrt_wallet/provider/wallet/constant/constant.dart'; +enum AddressDerivationType { + bip32, + multisig; + + bool get isMultiSig => this == AddressDerivationType.multisig; +} + abstract class AddressDerivationIndex with CborSerializable, Equatable { - String get path; - EllipticCurveTypes? get curve; - CryptoCoins? get currencyCoin; - const AddressDerivationIndex(); + String? get hdPath; + // String get hdPath; + CryptoCoins get currencyCoin; + AddressDerivationType get derivationType; + bool get isImportedKey; + String get name; + // bool get is; + const AddressDerivationIndex(); static AddressDerivationIndex fromCborBytesOrObject( {List? bytes, CborObject? obj}) { final cbor = (obj ?? CborObject.fromCbor(bytes!)) as CborTagValue; @@ -21,12 +32,6 @@ abstract class AddressDerivationIndex with CborSerializable, Equatable { } else if (bytesEqual( cbor.tags, WalletModelCborTagsConst.multiSigAccountKeyIndex)) { return const MultiSigAddressIndex(); - } else if (bytesEqual( - cbor.tags, WalletModelCborTagsConst.importedAccountKeyIndex)) { - return ImportedAddressIndex.fromCborBytesOrObject(obj: cbor); - } else if (bytesEqual( - cbor.tags, WalletModelCborTagsConst.byronLegacyKeyIndex)) { - return ByronLegacyAddressIndex.fromCborBytesOrObject(obj: cbor); } else { throw WalletExceptionConst.invalidAccountDetails; } @@ -35,7 +40,5 @@ abstract class AddressDerivationIndex with CborSerializable, Equatable { T derive(T derivator, {Bip44Levels maxLevel = Bip44Levels.addressIndex}); - String storageKey({Bip44Levels maxLevel = Bip44Levels.addressIndex}); - SeedGenerationType get seedGeneration; } diff --git a/mrt_wallet/lib/models/wallet_models/address/core/derivation/bip32_address_index.dart b/mrt_wallet/lib/models/wallet_models/address/core/derivation/bip32_address_index.dart index 96667e00..ac7125d4 100644 --- a/mrt_wallet/lib/models/wallet_models/address/core/derivation/bip32_address_index.dart +++ b/mrt_wallet/lib/models/wallet_models/address/core/derivation/bip32_address_index.dart @@ -8,147 +8,149 @@ import 'package:mrt_wallet/provider/wallet/constant/constant.dart'; class Bip32AddressIndex extends AddressDerivationIndex with Equatable, CborSerializable { - final int purpose; - final int coin; - final int accountLevel; - final int changeLevel; - final int addressIndex; - + final int? purpose; + final int? coin; + final int? accountLevel; + final int? changeLevel; + final int? addressIndex; + final String? importedKeyId; + final String? keyName; @override - final String path; - + bool get isImportedKey => importedKeyId != null; @override - final EllipticCurveTypes? curve; + final String? hdPath; + @override final SeedGenerationType seedGeneration; @override - final CryptoCoins? currencyCoin; + final CryptoCoins currencyCoin; - const Bip32AddressIndex._( - {required this.purpose, - required this.coin, - required this.accountLevel, - required this.changeLevel, - required this.addressIndex, - required this.curve, - required this.currencyCoin, - required this.seedGeneration, - required this.path}); + Bip32AddressIndex._({ + required this.purpose, + required this.coin, + required this.accountLevel, + required this.changeLevel, + required this.addressIndex, + required this.currencyCoin, + required this.seedGeneration, + this.importedKeyId, + this.keyName, + }) : hdPath = _toPath( + [purpose, coin, accountLevel, changeLevel, addressIndex], + importedKeyId: importedKeyId); factory Bip32AddressIndex.fromCborBytesOrObject( {List? bytes, CborObject? obj}) { - try { - final CborListValue cbor = CborSerializable.decodeCborTags( - bytes, obj, WalletModelCborTagsConst.accoutKeyIndex); - final int purposeLevel = cbor.value[0].value; - final int coinLevel = cbor.value[1].value; - final int accountLevel = cbor.value[2].value; - final int changeLevel = cbor.value[3].value; - final int addressIndex = cbor.value[4].value; - final String? curve = cbor.elementAt(5); - final String? proposal = cbor.elementAt(6); - final String? coinName = cbor.elementAt(7); - final CryptoCoins? coin = proposal != null && coinName != null - ? CryptoCoins.getCoin(coinName, CryptoProposal.fromName(proposal)) - : null; - final String? seedGeneration = cbor.elementAt(8); - return Bip32AddressIndex._( - accountLevel: accountLevel, - addressIndex: addressIndex, - changeLevel: changeLevel, - purpose: purposeLevel, - coin: coinLevel, - path: _toPath([ - purposeLevel, - coinLevel, - accountLevel, - changeLevel, - addressIndex - ]), - curve: curve == null - ? coin == null - ? EllipticCurveTypes.secp256k1 - : null - : EllipticCurveTypes.fromName(curve), - currencyCoin: coin, - seedGeneration: seedGeneration == null - ? SeedGenerationType.bip39 - : SeedGenerationType.fromName(seedGeneration)); - } catch (e) { - throw WalletExceptionConst.invalidAccountDetails; - } + final CborListValue cbor = CborSerializable.decodeCborTags( + bytes, obj, WalletModelCborTagsConst.accoutKeyIndex); + final String? seedGeneration = cbor.elementAt(7); + return Bip32AddressIndex._( + accountLevel: cbor.elementAt(2), + addressIndex: cbor.elementAt(4), + changeLevel: cbor.elementAt(3), + purpose: cbor.elementAt(0), + coin: cbor.elementAt(1), + currencyCoin: CustomCoins.getCoin( + cbor.elementAt(6), CustomProposal.fromName(cbor.elementAt(5)))!, + seedGeneration: seedGeneration == null + ? SeedGenerationType.bip39 + : SeedGenerationType.fromName(seedGeneration), + importedKeyId: cbor.elementAt(8), + keyName: cbor.elementAt(9)); + } + factory Bip32AddressIndex.byronLegacy( + {required int firstIndex, + required int secoundIndex, + required CryptoCoins currencyCoin, + String? keyName}) { + return Bip32AddressIndex._( + purpose: firstIndex, + coin: secoundIndex, + accountLevel: null, + changeLevel: null, + addressIndex: null, + currencyCoin: currencyCoin, + seedGeneration: SeedGenerationType.byronLegacySeed, + keyName: keyName); } - factory Bip32AddressIndex( - {required int purpose, - required int coin, - required int accountLevel, - required int changeLevel, - required int addressIndex, + {int? purpose, + int? coin, + int? accountLevel, + int? changeLevel, + int? addressIndex, required CryptoCoins currencyCoin, - required SeedGenerationType seedGeneration}) { + SeedGenerationType seedGeneration = SeedGenerationType.bip39, + String? keyName}) { return Bip32AddressIndex._( purpose: purpose, coin: coin, - path: _toPath([purpose, coin, accountLevel, changeLevel, addressIndex]), - curve: null, accountLevel: accountLevel, changeLevel: changeLevel, addressIndex: addressIndex, currencyCoin: currencyCoin, - seedGeneration: seedGeneration); + seedGeneration: seedGeneration, + keyName: keyName); + } + + Bip32AddressIndex copyWith( + {int? purpose, + int? coin, + int? accountLevel, + int? changeLevel, + int? addressIndex, + String? path, + SeedGenerationType? seedGeneration, + CryptoCoins? currencyCoin, + String? importedKeyId, + String? keyName}) { + return Bip32AddressIndex._( + purpose: purpose ?? this.purpose, + coin: coin ?? this.coin, + accountLevel: accountLevel ?? this.accountLevel, + changeLevel: changeLevel ?? this.changeLevel, + addressIndex: addressIndex ?? this.addressIndex, + seedGeneration: seedGeneration ?? this.seedGeneration, + currencyCoin: currencyCoin ?? this.currencyCoin, + importedKeyId: importedKeyId ?? this.importedKeyId, + keyName: keyName ?? this.keyName); } - factory Bip32AddressIndex.fromBip44KeyIndexDetais( - {required List indexes, + factory Bip32AddressIndex.fromPath( + {required String path, required CryptoCoins currencyCoin, required SeedGenerationType seedGeneration}) { - final int purpose = indexes - .firstWhere((element) => element.level == Bip44Levels.purpose) - .index; - final int coin = indexes - .firstWhere((element) => element.level == Bip44Levels.coin) - .index; - final int accountLevel = indexes - .firstWhere((element) => element.level == Bip44Levels.account) - .index; - final int changeLevel = indexes - .firstWhere((element) => element.level == Bip44Levels.change) - .index; - final int addressIndex = indexes - .firstWhere((element) => element.level == Bip44Levels.addressIndex) - .index; + final indexes = Bip32PathParser.parse(path).elems; + if (indexes.length > 5) { + throw WalletException("hd_wallet_path_max_indeqxes" + .tr + .replaceOne(BlockchainConstant.maxBip32LevelIndex.toString())); + } return Bip32AddressIndex( - purpose: purpose, - coin: coin, - accountLevel: accountLevel, - changeLevel: changeLevel, - addressIndex: addressIndex, + purpose: indexes.elementAtOrNull(0)?.index, + coin: indexes.elementAtOrNull(1)?.index, + accountLevel: indexes.elementAtOrNull(2)?.index, + changeLevel: indexes.elementAtOrNull(3)?.index, + addressIndex: indexes.elementAtOrNull(4)?.index, currencyCoin: currencyCoin, - seedGeneration: seedGeneration); + seedGeneration: seedGeneration, + keyName: null); } @override CborTagValue toCbor() { return CborTagValue( CborListValue.dynamicLength([ - CborIntValue(purpose), - CborIntValue(coin), - CborIntValue(accountLevel), - CborIntValue(changeLevel), - CborIntValue(addressIndex), - if (curve == null) - const CborNullValue() - else - CborStringValue(curve!.name), - if (currencyCoin != null) ...[ - CborStringValue(currencyCoin!.proposal.specName), - CborStringValue(currencyCoin!.coinName), - ] else ...[ - const CborNullValue(), - const CborNullValue() - ], - seedGeneration.name + purpose, + coin, + accountLevel, + changeLevel, + addressIndex, + CborStringValue(currencyCoin.proposal.specName), + CborStringValue(currencyCoin.coinName), + seedGeneration.name, + importedKeyId, + keyName ]), WalletModelCborTagsConst.accoutKeyIndex); } @@ -160,52 +162,69 @@ class Bip32AddressIndex extends AddressDerivationIndex accountLevel, changeLevel, addressIndex, - currencyCoin?.conf.type ?? curve, - seedGeneration.name + currencyCoin.conf.type, + seedGeneration.name, + importedKeyId ]; - static String _toPath(List indexses) { - String path = "m/"; - for (final i in indexses) { - final toBip32KeyIndex = Bip32KeyIndex(i); - if (toBip32KeyIndex.isHardened) { - path += "${toBip32KeyIndex.unharden().index}'/"; + static String? _toPath(List indexses, {String? importedKeyId}) { + if (indexses.isEmpty) return null; + final bipIndexes = indexses + .where((element) => element != null) + .map((e) => Bip32KeyIndex(e!)) + .toList(); + if (bipIndexes.isEmpty) return null; + String pathStr = "${Bip32PathConst.masterChar}/"; + for (final elem in bipIndexes) { + if (!elem.isHardened) { + pathStr += "${elem.toInt()}/"; } else { - path += "${toBip32KeyIndex.index}/"; + pathStr += "${elem.unharden().toInt()}'/"; } } - path = path.substring(0, path.length - 1); - return path; + return pathStr.substring(0, pathStr.length - 1); + } + + Bip32Base _derive(Bip32Base key, + {Bip44Levels maxLevel = Bip44Levels.addressIndex}) { + if (maxLevel == Bip44Levels.master || indexes.isEmpty) return key; + List bip32KeyIndexes = List.unmodifiable(indexes); + final maxIndex = maxLevel.value; + if (bip32KeyIndexes.length > maxIndex) { + bip32KeyIndexes = List.unmodifiable(bip32KeyIndexes.sublist(0, maxIndex)); + } + Bip32Base deriveToIndex = key; + for (final i in bip32KeyIndexes) { + deriveToIndex = deriveToIndex.childKey(i); + } + return deriveToIndex; } @override T derive(T derivator, {Bip44Levels maxLevel = Bip44Levels.addressIndex}) { - Bip32Base deriveToIndex = derivator; - for (int i = 0; i < maxLevel.value; i++) { - deriveToIndex = deriveToIndex.childKey(indexes.elementAt(i)); - } - return deriveToIndex as T; + return _derive(derivator, maxLevel: maxLevel) as T; } - List get indexes => [ - Bip32KeyIndex(purpose), - Bip32KeyIndex(coin), - Bip32KeyIndex(accountLevel), - Bip32KeyIndex(changeLevel), - Bip32KeyIndex(addressIndex) - ]; + List get indexes => + [purpose, coin, accountLevel, changeLevel, addressIndex] + .where((element) => element != null) + .map((e) => Bip32KeyIndex(e!)) + .toList(); @override - String storageKey({Bip44Levels maxLevel = Bip44Levels.addressIndex}) => - BytesUtils.toHexString(MD5.hash([ - ...toCbor().encode(), - ...seedGeneration.name.codeUnits, - ...maxLevel.name.codeUnits - ])); + String toString() { + if (importedKeyId != null) { + return "imported_".tr.replaceOne(hdPath ?? "non_derivation".tr); + } + return hdPath ?? "non_derivation".tr; + } @override - String toString() { - return path; + AddressDerivationType get derivationType { + return AddressDerivationType.bip32; } + + @override + String get name => keyName ?? "main_key"; } diff --git a/mrt_wallet/lib/models/wallet_models/address/core/derivation/byron_legacy_address_index.dart b/mrt_wallet/lib/models/wallet_models/address/core/derivation/byron_legacy_address_index.dart deleted file mode 100644 index eb92b370..00000000 --- a/mrt_wallet/lib/models/wallet_models/address/core/derivation/byron_legacy_address_index.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:blockchain_utils/bip/bip/bip32/base/bip32_base.dart'; -import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; -import 'package:blockchain_utils/blockchain_utils.dart'; -import 'package:mrt_wallet/app/core.dart'; -import 'package:mrt_wallet/models/serializable/serializable.dart'; -import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; -import 'package:mrt_wallet/provider/wallet/constant/constant.dart'; - -class ByronLegacyAddressIndex extends AddressDerivationIndex - with Equatable, CborSerializable { - final int firstIndex; - final int secondIndex; - - @override - final CryptoCoins? currencyCoin; - @override - final EllipticCurveTypes? curve = null; - @override - SeedGenerationType get seedGeneration => SeedGenerationType.byronLegacySeed; - - const ByronLegacyAddressIndex( - {required this.firstIndex, - required this.secondIndex, - required this.currencyCoin}); - - factory ByronLegacyAddressIndex.fromCborBytesOrObject( - {List? bytes, CborObject? obj}) { - try { - final CborListValue cbor = CborSerializable.decodeCborTags( - bytes, obj, WalletModelCborTagsConst.byronLegacyKeyIndex); - final int firstIndex = cbor.elementAt(0); - final int secondIndex = cbor.elementAt(1); - final String proposal = cbor.elementAt(2); - final String coinName = cbor.elementAt(3); - final CryptoCoins? coin = - CryptoCoins.getCoin(coinName, CryptoProposal.fromName(proposal)); - return ByronLegacyAddressIndex( - firstIndex: firstIndex, secondIndex: secondIndex, currencyCoin: coin); - } catch (e) { - throw WalletExceptionConst.invalidAccountDetails; - } - } - - @override - CborTagValue toCbor() { - return CborTagValue( - CborListValue.dynamicLength([ - CborIntValue(firstIndex), - CborIntValue(secondIndex), - CborStringValue(currencyCoin!.proposal.specName), - CborStringValue(currencyCoin!.coinName), - ]), - WalletModelCborTagsConst.byronLegacyKeyIndex); - } - - @override - List get variabels => - [firstIndex, secondIndex, currencyCoin?.conf.type ?? curve]; - - @override - String get path { - return 'm/$firstIndex\'/$secondIndex\''; - } - - @override - T derive(T derivator, - {Bip44Levels maxLevel = Bip44Levels.addressIndex}) { - return derivator.derivePath(path) as T; - } - - @override - String storageKey({Bip44Levels maxLevel = Bip44Levels.addressIndex}) => - BytesUtils.toHexString(MD5.hash([ - ...toCbor().encode(), - ...seedGeneration.name.codeUnits, - ...maxLevel.name.codeUnits - ])); - - @override - String toString() { - return path; - } -} diff --git a/mrt_wallet/lib/models/wallet_models/address/core/derivation/imported_address_index.dart b/mrt_wallet/lib/models/wallet_models/address/core/derivation/imported_address_index.dart deleted file mode 100644 index ef0cb513..00000000 --- a/mrt_wallet/lib/models/wallet_models/address/core/derivation/imported_address_index.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:blockchain_utils/bip/bip/bip32/base/bip32_base.dart'; -import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; -import 'package:blockchain_utils/blockchain_utils.dart'; -import 'package:mrt_wallet/app/error/exception/wallet_ex.dart'; -import 'package:mrt_wallet/app/euqatable/equatable.dart'; -import 'package:mrt_wallet/app/extention/cbor.dart'; -import 'package:mrt_wallet/models/serializable/serializable.dart'; -import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; -import 'package:mrt_wallet/provider/wallet/constant/constant.dart'; - -class ImportedAddressIndex with Equatable implements AddressDerivationIndex { - const ImportedAddressIndex({ - required this.accountId, - required CryptoCoins this.currencyCoin, - this.bip32KeyIndex, - }); - const ImportedAddressIndex._({ - required this.accountId, - required this.currencyCoin, - this.bip32KeyIndex, - }); - factory ImportedAddressIndex.fromCborBytesOrObject( - {List? bytes, CborObject? obj}) { - try { - final CborListValue cbor = CborSerializable.decodeCborTags( - bytes, obj, WalletModelCborTagsConst.importedAccountKeyIndex); - Bip32AddressIndex? keyIndex; - - if (cbor.value[0] is! CborNullValue) { - keyIndex = Bip32AddressIndex.fromCborBytesOrObject(obj: cbor.value[0]); - } - final String accountId = cbor.value[1].value; - CryptoCoins? coin; - final String? proposalName = cbor.elementAt(2); - if (proposalName != null) { - final CryptoProposal proposal = CryptoProposal.fromName(proposalName); - coin = CryptoCoins.getCoin(cbor.elementAt(3), proposal)!; - } - return ImportedAddressIndex._( - accountId: accountId, bip32KeyIndex: keyIndex, currencyCoin: coin); - } catch (e) { - throw WalletExceptionConst.invalidAccountDetails; - } - } - final String accountId; - final AddressDerivationIndex? bip32KeyIndex; - - @override - final EllipticCurveTypes? curve = null; - @override - final CryptoCoins? currencyCoin; - - @override - CborTagValue toCbor() { - return CborTagValue( - CborListValue.fixedLength([ - bip32KeyIndex?.toCbor() ?? const CborNullValue(), - accountId, - currencyCoin?.proposal ?? const CborNullValue(), - currencyCoin?.coinName ?? const CborNullValue() - ]), - WalletModelCborTagsConst.importedAccountKeyIndex); - } - - @override - String get path => - "imported${bip32KeyIndex == null ? '' : ' ( ${bip32KeyIndex!.path} )'}"; - - @override - List get variabels => [accountId, bip32KeyIndex]; - - @override - T derive(T derivator, - {Bip44Levels maxLevel = Bip44Levels.addressIndex}) { - if (bip32KeyIndex != null) { - return bip32KeyIndex!.derive(derivator); - } - - return derivator; - } - - @override - String storageKey({Bip44Levels maxLevel = Bip44Levels.addressIndex}) => - BytesUtils.toHexString(MD5.hash([ - ...toCbor().encode(), - ...seedGeneration.name.codeUnits, - ...maxLevel.name.codeUnits - ])); - - @override - SeedGenerationType get seedGeneration => - bip32KeyIndex?.seedGeneration ?? SeedGenerationType.none; -} diff --git a/mrt_wallet/lib/models/wallet_models/address/core/derivation/multi_sig_address_index.dart b/mrt_wallet/lib/models/wallet_models/address/core/derivation/multi_sig_address_index.dart index d3e0636c..0bcebea3 100644 --- a/mrt_wallet/lib/models/wallet_models/address/core/derivation/multi_sig_address_index.dart +++ b/mrt_wallet/lib/models/wallet_models/address/core/derivation/multi_sig_address_index.dart @@ -1,18 +1,19 @@ import 'package:blockchain_utils/bip/bip/bip32/base/bip32_base.dart'; import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; -import 'package:mrt_wallet/app/error/exception/wallet_ex.dart'; +import 'package:mrt_wallet/app/core.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; import 'package:mrt_wallet/provider/wallet/constant/constant.dart'; class MultiSigAddressIndex implements AddressDerivationIndex { @override - String get path => "multi_signature"; - const MultiSigAddressIndex(); + final String? hdPath = null; + final String? keyName; + const MultiSigAddressIndex({this.keyName}); @override CborTagValue toCbor() { - return CborTagValue(CborListValue.fixedLength([]), + return CborTagValue(CborListValue.fixedLength([keyName]), WalletModelCborTagsConst.multiSigAccountKeyIndex); } @@ -25,21 +26,25 @@ class MultiSigAddressIndex implements AddressDerivationIndex { @override List get variabels => []; - @override - EllipticCurveTypes get curve => - throw WalletExceptionConst.inaccessibleKeyAlgorithm; - @override CryptoCoins get currencyCoin => throw WalletExceptionConst.inaccessibleKeyAlgorithm; @override - String storageKey( - {SeedGenerationType seedGenerationType = SeedGenerationType.bip39, - Bip44Levels maxLevel = Bip44Levels.addressIndex}) => + SeedGenerationType get seedGeneration => throw WalletExceptionConst.unsuportedFeature; @override - SeedGenerationType get seedGeneration => - throw WalletExceptionConst.unsuportedFeature; + AddressDerivationType get derivationType => AddressDerivationType.multisig; + + @override + bool get isImportedKey => false; + + @override + String get name => "multi_signature".tr; + + @override + String toString() { + return name; + } } diff --git a/mrt_wallet/lib/models/wallet_models/address/network_address/bitcoin/bitcoin_account.dart b/mrt_wallet/lib/models/wallet_models/address/network_address/bitcoin/bitcoin_account.dart index cd8519f1..609c3969 100644 --- a/mrt_wallet/lib/models/wallet_models/address/network_address/bitcoin/bitcoin_account.dart +++ b/mrt_wallet/lib/models/wallet_models/address/network_address/bitcoin/bitcoin_account.dart @@ -133,7 +133,6 @@ class IBitcoinAddress @override List get variabels => [addressType, keyIndex, network]; - @override List get signers => [BytesUtils.toHexString(publicKey)]; @override @@ -185,6 +184,9 @@ class IBitcoinAddress @override void updateToken(TokenCore token, Token updatedToken) {} + + @override + List get keyIndexes => [keyIndex]; } class IBitcoinMultiSigAddress extends IBitcoinAddress diff --git a/mrt_wallet/lib/models/wallet_models/address/network_address/bitcoin/multisig_address_details.dart b/mrt_wallet/lib/models/wallet_models/address/network_address/bitcoin/multisig_address_details.dart index f543842a..896dfa8a 100644 --- a/mrt_wallet/lib/models/wallet_models/address/network_address/bitcoin/multisig_address_details.dart +++ b/mrt_wallet/lib/models/wallet_models/address/network_address/bitcoin/multisig_address_details.dart @@ -47,7 +47,7 @@ class BitcoinMultiSigSignerDetais int get weight => _wieght; final AddressDerivationIndex keyIndex; - String get path => keyIndex.path; + String get path => keyIndex.toString(); @override CborTagValue toCbor() { diff --git a/mrt_wallet/lib/models/wallet_models/address/network_address/cardano/cardano.dart b/mrt_wallet/lib/models/wallet_models/address/network_address/cardano/cardano.dart index b54b6ed9..91602b90 100644 --- a/mrt_wallet/lib/models/wallet_models/address/network_address/cardano/cardano.dart +++ b/mrt_wallet/lib/models/wallet_models/address/network_address/cardano/cardano.dart @@ -4,12 +4,12 @@ import 'package:mrt_wallet/app/core.dart'; import 'package:mrt_wallet/models/serializable/serializable.dart'; import 'package:mrt_wallet/models/wallet_models/address/address.dart'; import 'package:mrt_wallet/models/wallet_models/currency_balance/balance.dart'; -import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/cardano_address_details.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/address_details.dart'; import 'package:mrt_wallet/models/wallet_models/network/network_models.dart'; import 'package:mrt_wallet/models/wallet_models/nfts/core/nft_core.dart'; import 'package:mrt_wallet/models/wallet_models/token/core/core.dart'; import 'package:mrt_wallet/provider/wallet/constant/constant.dart'; -import 'package:on_chain/ada/src/address/address.dart'; +import 'package:on_chain/on_chain.dart'; class ICardanoAddress with Equatable @@ -24,11 +24,14 @@ class ICardanoAddress required List tokens, required List nfts, required this.addressDetails, + AddressDerivationIndex? rewardKeyIndex, String? accountName}) : publicKey = List.unmodifiable(publicKey), _tokens = List.unmodifiable(tokens), _nfts = List.unmodifiable(nfts), - _accountName = accountName; + _accountName = accountName, + _rewardKeyIndex = rewardKeyIndex, + rewardAddress = CardanoUtils.extractRewardAddress(networkAddress); factory ICardanoAddress.newAccount( {required CardanoNewAddressParams accountParams, @@ -39,16 +42,16 @@ class ICardanoAddress address: cardanoAddr.address, balance: NoneDecimalBalance.zero(network.coinParam.decimal)); return ICardanoAddress._( - coin: accountParams.coin, - publicKey: publicKey, - address: addressDetauls, - keyIndex: accountParams.deriveIndex, - networkAddress: cardanoAddr, - network: network.value, - tokens: const [], - nfts: const [], - addressDetails: accountParams.addressDetails!, - ); + coin: accountParams.coin, + publicKey: publicKey, + address: addressDetauls, + keyIndex: accountParams.deriveIndex, + networkAddress: cardanoAddr, + network: network.value, + tokens: const [], + nfts: const [], + addressDetails: accountParams.addressDetails!, + rewardKeyIndex: accountParams.rewardKeyIndex); } factory ICardanoAddress.fromCbsorHex(String hex, AppNetworkImpl network) { return ICardanoAddress.fromCborBytesOrObject(network, @@ -60,8 +63,8 @@ class ICardanoAddress final CborListValue cbor = CborSerializable.decodeCborTags( null, toCborTag, WalletModelCborTagsConst.cardanoAccount); - final CryptoProposal proposal = CryptoProposal.fromName(cbor.elementAt(0)); - final CryptoCoins coin = CryptoCoins.getCoin(cbor.elementAt(1), proposal)!; + final CryptoProposal proposal = CustomProposal.fromName(cbor.elementAt(0)); + final CryptoCoins coin = CustomCoins.getCoin(cbor.elementAt(1), proposal)!; final keyIndex = AddressDerivationIndex.fromCborBytesOrObject(obj: cbor.getCborTag(2)); final List publicKey = cbor.elementAt(3); @@ -85,6 +88,13 @@ class ICardanoAddress } final String? accountName = cbor.elementAt(10); + final CborTagValue? rewardIndexCbor = cbor.getCborTag(11); + final rewardIndex = rewardIndexCbor == null + ? null + : AddressDerivationIndex.fromCborBytesOrObject(obj: rewardIndexCbor); + if (adaAddress.addressType == ADAAddressType.base && rewardIndex == null) { + throw WalletExceptionConst.invalidAccountDetails; + } return ICardanoAddress._( coin: coin, publicKey: publicKey, @@ -95,7 +105,8 @@ class ICardanoAddress addressDetails: addrDetails, tokens: [], nfts: [], - accountName: accountName); + accountName: accountName, + rewardKeyIndex: rewardIndex); } @override @@ -118,9 +129,6 @@ class ICardanoAddress @override final List publicKey; - @override - List get signers => [BytesUtils.toHexString(publicKey)]; - @override CborTagValue toCbor() { return CborTagValue( @@ -135,7 +143,8 @@ class ICardanoAddress addressDetails.toCbor(), CborListValue.fixedLength(_tokens.map((e) => e.toCbor()).toList()), CborListValue.fixedLength(_nfts.map((e) => e.toCbor()).toList()), - accountName ?? const CborNullValue() + accountName ?? const CborNullValue(), + rewardKeyIndex?.toCbor() ?? const CborNullValue() ]), WalletModelCborTagsConst.cardanoAccount); } @@ -150,6 +159,14 @@ class ICardanoAddress @override final ADAAddress networkAddress; + final ADARewardAddress? rewardAddress; + + final AddressDerivationIndex? _rewardKeyIndex; + + AddressDerivationIndex? get rewardKeyIndex => _rewardKeyIndex; + + bool get isBaseAddress => networkAddress.addressType == ADAAddressType.base; + @override String? get type => networkAddress.addressType.name; @@ -194,4 +211,8 @@ class ICardanoAddress @override String get orginalAddress => networkAddress.address; + + @override + List get keyIndexes => + [keyIndex, if (rewardKeyIndex != null) rewardKeyIndex!]; } diff --git a/mrt_wallet/lib/models/wallet_models/address/network_address/cosmos/cosmos.dart b/mrt_wallet/lib/models/wallet_models/address/network_address/cosmos/cosmos.dart index 01e08048..a36d6f31 100644 --- a/mrt_wallet/lib/models/wallet_models/address/network_address/cosmos/cosmos.dart +++ b/mrt_wallet/lib/models/wallet_models/address/network_address/cosmos/cosmos.dart @@ -113,9 +113,6 @@ class ICosmosAddress final String hrp; - @override - List get signers => [BytesUtils.toHexString(publicKey)]; - @override CborTagValue toCbor() { return CborTagValue( @@ -193,4 +190,6 @@ class ICosmosAddress @override String get orginalAddress => networkAddress.address; + @override + List get keyIndexes => [keyIndex]; } diff --git a/mrt_wallet/lib/models/wallet_models/address/network_address/ethereum/ethereum_account.dart b/mrt_wallet/lib/models/wallet_models/address/network_address/ethereum/ethereum_account.dart index b74c90d6..7fa1673f 100644 --- a/mrt_wallet/lib/models/wallet_models/address/network_address/ethereum/ethereum_account.dart +++ b/mrt_wallet/lib/models/wallet_models/address/network_address/ethereum/ethereum_account.dart @@ -116,9 +116,6 @@ class IEthAddress @override final List publicKey; - @override - List get signers => [BytesUtils.toHexString(publicKey)]; - @override CborTagValue toCbor() { return CborTagValue( @@ -210,4 +207,7 @@ class IEthAddress @override String get orginalAddress => networkAddress.address; + + @override + List get keyIndexes => [keyIndex]; } diff --git a/mrt_wallet/lib/models/wallet_models/address/network_address/solana/solana_address.dart b/mrt_wallet/lib/models/wallet_models/address/network_address/solana/solana_address.dart index ec12dab4..722c7797 100644 --- a/mrt_wallet/lib/models/wallet_models/address/network_address/solana/solana_address.dart +++ b/mrt_wallet/lib/models/wallet_models/address/network_address/solana/solana_address.dart @@ -115,9 +115,6 @@ class ISolanaAddress @override final List publicKey; - @override - List get signers => [BytesUtils.toHexString(publicKey)]; - @override CborTagValue toCbor() { return CborTagValue( @@ -215,4 +212,7 @@ class ISolanaAddress AssociatedTokenAccountProgramUtils.associatedTokenAccount( mint: mint, owner: networkAddress, tokenProgramId: tokenProgramId) .address; + + @override + List get keyIndexes => [keyIndex]; } diff --git a/mrt_wallet/lib/models/wallet_models/address/network_address/tron/multi_sig_address_details.dart b/mrt_wallet/lib/models/wallet_models/address/network_address/tron/multi_sig_address_details.dart index 03bf3d7f..bb6de531 100644 --- a/mrt_wallet/lib/models/wallet_models/address/network_address/tron/multi_sig_address_details.dart +++ b/mrt_wallet/lib/models/wallet_models/address/network_address/tron/multi_sig_address_details.dart @@ -34,7 +34,7 @@ class TronMultiSigSignerDetais with Equatable, CborSerializable { final BigInt weight; final AddressDerivationIndex keyIndex; - String get path => keyIndex.path; + String get path => keyIndex.toString(); @override CborTagValue toCbor() { diff --git a/mrt_wallet/lib/models/wallet_models/address/network_address/tron/tron_account.dart b/mrt_wallet/lib/models/wallet_models/address/network_address/tron/tron_account.dart index bf41631c..225cadb7 100644 --- a/mrt_wallet/lib/models/wallet_models/address/network_address/tron/tron_account.dart +++ b/mrt_wallet/lib/models/wallet_models/address/network_address/tron/tron_account.dart @@ -133,9 +133,6 @@ class ITronAddress @override final List publicKey; - @override - List get signers => [BytesUtils.toHexString(publicKey)]; - @override CborTagValue toCbor() { return CborTagValue( @@ -275,6 +272,9 @@ class ITronAddress @override String get orginalAddress => networkAddress.toAddress(); + + @override + List get keyIndexes => [keyIndex]; } class ITronMultisigAddress extends ITronAddress @@ -373,9 +373,6 @@ class ITronMultisigAddress extends ITronAddress @override List get publicKey => throw UnimplementedError(); - @override - List get signers => - multiSignatureAccount.signers.map((e) => e.publicKey).toList(); @override List get variabels { return [keyIndex, network, multiSignatureAccount]; diff --git a/mrt_wallet/lib/models/wallet_models/address/network_address/xrp/mutlisig_address_details.dart b/mrt_wallet/lib/models/wallet_models/address/network_address/xrp/mutlisig_address_details.dart index 748cbcde..ab650803 100644 --- a/mrt_wallet/lib/models/wallet_models/address/network_address/xrp/mutlisig_address_details.dart +++ b/mrt_wallet/lib/models/wallet_models/address/network_address/xrp/mutlisig_address_details.dart @@ -34,7 +34,7 @@ class RippleMultiSigSignerDetais with Equatable, CborSerializable { final int weight; final AddressDerivationIndex keyIndex; - String get path => keyIndex.path; + String get path => keyIndex.toString(); @override CborTagValue toCbor() { diff --git a/mrt_wallet/lib/models/wallet_models/address/network_address/xrp/xrp_account.dart b/mrt_wallet/lib/models/wallet_models/address/network_address/xrp/xrp_account.dart index 8045b47e..47967014 100644 --- a/mrt_wallet/lib/models/wallet_models/address/network_address/xrp/xrp_account.dart +++ b/mrt_wallet/lib/models/wallet_models/address/network_address/xrp/xrp_account.dart @@ -155,9 +155,6 @@ class IXRPAddress @override final List publicKey; - @override - List get signers => [BytesUtils.toHexString(publicKey)]; - @override CborTagValue toCbor() { return CborTagValue( @@ -267,6 +264,9 @@ class IXRPAddress @override String get orginalAddress => networkAddress.address; + + @override + List get keyIndexes => [keyIndex]; } class IXRPMultisigAddress extends IXRPAddress @@ -369,9 +369,6 @@ class IXRPMultisigAddress extends IXRPAddress @override EllipticCurveTypes get curveType => throw UnimplementedError(); - @override - List get signers => - multiSignatureAccount.signers.map((e) => e.publicKey).toList(); @override List get variabels { return [tag, keyIndex, network, multiSignatureAccount]; diff --git a/mrt_wallet/lib/models/wallet_models/address/new_address_params/bitcoin/address_param.dart b/mrt_wallet/lib/models/wallet_models/address/new_address_params/bitcoin/address_param.dart index c75fdf16..6b059250 100644 --- a/mrt_wallet/lib/models/wallet_models/address/new_address_params/bitcoin/address_param.dart +++ b/mrt_wallet/lib/models/wallet_models/address/new_address_params/bitcoin/address_param.dart @@ -1,37 +1,37 @@ -import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:bitcoin_base/bitcoin_base.dart' show BitcoinAddressType; +import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; -class BitcoinNewAddressParams implements NewAccountParams { - const BitcoinNewAddressParams({ - required this.coin, - required this.deriveIndex, - required this.bitcoinAddressType, - }); +class BitcoinNewAddressParams + implements NewAccountParams { @override bool get isMultiSig => false; @override - final CryptoCoins coin; - @override final AddressDerivationIndex deriveIndex; final BitcoinAddressType bitcoinAddressType; + @override + CryptoCoins get coin => deriveIndex.currencyCoin; + + BitcoinNewAddressParams({ + required this.deriveIndex, + required this.bitcoinAddressType, + }); } class BitcoinMultiSigNewAddressParams implements BitcoinNewAddressParams { const BitcoinMultiSigNewAddressParams({ - required this.coin, required this.bitcoinAddressType, required this.multiSignatureAddress, + required this.coin, }) : deriveIndex = const MultiSigAddressIndex(); - @override - final CryptoCoins coin; @override final BitcoinAddressType bitcoinAddressType; final BitcoinMultiSignatureAddress multiSignatureAddress; - @override final AddressDerivationIndex deriveIndex; @override bool get isMultiSig => true; + @override + final CryptoCoins coin; } diff --git a/mrt_wallet/lib/models/wallet_models/address/new_address_params/bitcoin_cash/address_param.dart b/mrt_wallet/lib/models/wallet_models/address/new_address_params/bitcoin_cash/address_param.dart index 98d49208..9ece5267 100644 --- a/mrt_wallet/lib/models/wallet_models/address/new_address_params/bitcoin_cash/address_param.dart +++ b/mrt_wallet/lib/models/wallet_models/address/new_address_params/bitcoin_cash/address_param.dart @@ -1,35 +1,31 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; -import 'package:mrt_wallet/models/wallet_models/address/core/derivation/address_derivation.dart'; -import 'package:mrt_wallet/models/wallet_models/address/core/derivation/multi_sig_address_index.dart'; -import 'package:mrt_wallet/models/wallet_models/address/network_address/bitcoin/multisig_address_details.dart'; -import 'package:mrt_wallet/models/wallet_models/address/new_address_params/core.dart'; +import 'package:mrt_wallet/models/wallet_models/address/address.dart'; -class BitcoinCashNewAddressParams implements NewAccountParams { - const BitcoinCashNewAddressParams({ - required this.coin, +class BitcoinCashNewAddressParams + implements NewAccountParams { + BitcoinCashNewAddressParams({ required this.deriveIndex, required this.bitcoinAddressType, }); @override bool get isMultiSig => false; - @override - final CryptoCoins coin; + @override final AddressDerivationIndex deriveIndex; final BitcoinAddressType bitcoinAddressType; + @override + CryptoCoins get coin => deriveIndex.currencyCoin; } class BitcoinCashMultiSigNewAddressParams implements BitcoinCashNewAddressParams { - const BitcoinCashMultiSigNewAddressParams({ - required this.coin, - required this.bitcoinAddressType, - required this.multiSignatureAddress, - }) : deriveIndex = const MultiSigAddressIndex(); + const BitcoinCashMultiSigNewAddressParams( + {required this.bitcoinAddressType, + required this.multiSignatureAddress, + required this.coin}) + : deriveIndex = const MultiSigAddressIndex(); - @override - final CryptoCoins coin; @override final BitcoinAddressType bitcoinAddressType; final BitcoinMultiSignatureAddress multiSignatureAddress; @@ -38,4 +34,7 @@ class BitcoinCashMultiSigNewAddressParams final AddressDerivationIndex deriveIndex; @override bool get isMultiSig => true; + + @override + final CryptoCoins coin; } diff --git a/mrt_wallet/lib/models/wallet_models/address/new_address_params/cardano/cardano_params.dart b/mrt_wallet/lib/models/wallet_models/address/new_address_params/cardano/cardano_params.dart index 61a34855..971ebe41 100644 --- a/mrt_wallet/lib/models/wallet_models/address/new_address_params/cardano/cardano_params.dart +++ b/mrt_wallet/lib/models/wallet_models/address/new_address_params/cardano/cardano_params.dart @@ -2,43 +2,54 @@ import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:mrt_wallet/models/wallet_models/address/core/derivation/address_derivation.dart'; import 'package:mrt_wallet/models/wallet_models/address/new_address_params/new_address_params.dart'; -import 'package:mrt_wallet/models/wallet_models/keys/master_key.dart'; -import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/cardano_address_details.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/address_details.dart'; import 'package:on_chain/on_chain.dart'; -class CardanoNewAddressParams implements NewAccountParams { - const CardanoNewAddressParams( - {required this.coin, - required this.addressType, - required this.deriveIndex, - this.addressDetails, - this.byronLegacy}); - CardanoNewAddressParams copyWith( - {CryptoCoins? coin, - ADAAddressType? addressType, - AddressDerivationIndex? deriveIndex, - CardanoAddrDetails? addressDetails, - SeedGenerationType? seedGeneration}) { - return CardanoNewAddressParams( - coin: coin ?? this.coin, - addressType: addressType ?? this.addressType, - deriveIndex: deriveIndex ?? this.deriveIndex, - addressDetails: addressDetails ?? this.addressDetails); - } - - @override - final CryptoCoins coin; +class CardanoNewAddressParams + implements NewAccountParams { final ADAAddressType addressType; @override final AddressDerivationIndex deriveIndex; - final bool? byronLegacy; + final AddressDerivationIndex? rewardKeyIndex; final CardanoAddrDetails? addressDetails; - ADAAddress toAddress() { - return addressDetails!.toAddress(coin); - } + final String? customHdPath; + final List? customHdPathKey; bool get needStakeKey => addressType == ADAAddressType.base; - @override bool get isMultiSig => false; + @override + CryptoCoins get coin => deriveIndex.currencyCoin; + + CardanoNewAddressParams( + {required this.addressType, + required this.deriveIndex, + required this.rewardKeyIndex, + this.addressDetails, + this.customHdPath, + List? customHdPathKey}) + : customHdPathKey = + BytesUtils.tryToBytes(customHdPathKey, unmodifiable: true); + + ADAAddress toAddress() { + return addressDetails!.toAddress(deriveIndex.currencyCoin); + } + + CardanoNewAddressParams copyWith({ + ADAAddressType? addressType, + AddressDerivationIndex? deriveIndex, + CardanoAddrDetails? addressDetails, + AddressDerivationIndex? rewardKeyIndex, + List? publicKey, + String? customHdPath, + List? customHdPathKey, + }) { + return CardanoNewAddressParams( + addressType: addressType ?? this.addressType, + deriveIndex: deriveIndex ?? this.deriveIndex, + addressDetails: addressDetails ?? this.addressDetails, + rewardKeyIndex: rewardKeyIndex ?? this.rewardKeyIndex, + customHdPath: customHdPath, + customHdPathKey: customHdPathKey); + } } diff --git a/mrt_wallet/lib/models/wallet_models/address/new_address_params/core.dart b/mrt_wallet/lib/models/wallet_models/address/new_address_params/core.dart index 16e364b9..493edfcd 100644 --- a/mrt_wallet/lib/models/wallet_models/address/new_address_params/core.dart +++ b/mrt_wallet/lib/models/wallet_models/address/new_address_params/core.dart @@ -1,8 +1,11 @@ import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; -abstract class NewAccountParams { - abstract final CryptoCoins coin; +abstract class NewAccountParams> { + NewAccountParams(); + + CryptoCoins get coin => deriveIndex.currencyCoin; abstract final AddressDerivationIndex deriveIndex; + bool get isMultiSig; } diff --git a/mrt_wallet/lib/models/wallet_models/address/new_address_params/cosmos/cosmos_params.dart b/mrt_wallet/lib/models/wallet_models/address/new_address_params/cosmos/cosmos_params.dart index e11e3c35..ff1373f0 100644 --- a/mrt_wallet/lib/models/wallet_models/address/new_address_params/cosmos/cosmos_params.dart +++ b/mrt_wallet/lib/models/wallet_models/address/new_address_params/cosmos/cosmos_params.dart @@ -4,22 +4,23 @@ import 'package:cosmos_sdk/cosmos_sdk.dart'; import 'package:mrt_wallet/models/wallet_models/address/core/derivation/address_derivation.dart'; import 'package:mrt_wallet/models/wallet_models/address/new_address_params/new_address_params.dart'; -class CosmosNewAddressParams implements NewAccountParams { - const CosmosNewAddressParams({required this.coin, required this.deriveIndex}); - CosmosNewAddressParams copyWith( - {CryptoCoins? coin, AddressDerivationIndex? deriveIndex}) { - return CosmosNewAddressParams( - coin: coin ?? this.coin, deriveIndex: deriveIndex ?? this.deriveIndex); - } - +class CosmosNewAddressParams + implements NewAccountParams { @override - final CryptoCoins coin; + bool get isMultiSig => false; + @override + CryptoCoins get coin => deriveIndex.currencyCoin; + @override final AddressDerivationIndex deriveIndex; + CosmosNewAddressParams({ + required this.deriveIndex, + }); + CosmosBaseAddress toAddress( {required List publicKey, required String hrp}) { - if (coin.conf.type == EllipticCurveTypes.nist256p1) { + if (deriveIndex.currencyCoin.conf.type == EllipticCurveTypes.nist256p1) { return CosmosBaseAddress.fromBytes( CosmosAddrUtils.secp256r1PubKeyToAddress(publicKey), hrp: hrp); @@ -28,7 +29,4 @@ class CosmosNewAddressParams implements NewAccountParams { CosmosAddrUtils.secp256k1PubKeyToAddress(publicKey), hrp: hrp); } - - @override - bool get isMultiSig => false; } diff --git a/mrt_wallet/lib/models/wallet_models/address/new_address_params/ehtereum/address_param.dart b/mrt_wallet/lib/models/wallet_models/address/new_address_params/ehtereum/address_param.dart index 43693fc3..c9fcd650 100644 --- a/mrt_wallet/lib/models/wallet_models/address/new_address_params/ehtereum/address_param.dart +++ b/mrt_wallet/lib/models/wallet_models/address/new_address_params/ehtereum/address_param.dart @@ -1,14 +1,18 @@ import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; -class EthereumNewAddressParam implements NewAccountParams { - const EthereumNewAddressParam( - {required this.coin, required this.deriveIndex}); +class EthereumNewAddressParam + implements NewAccountParams { @override bool get isMultiSig => false; - @override - final CryptoCoins coin; @override final AddressDerivationIndex deriveIndex; + @override + CryptoCoins get coin => deriveIndex.currencyCoin; + + EthereumNewAddressParam({ + required this.deriveIndex, + List? publicKey, + }); } diff --git a/mrt_wallet/lib/models/wallet_models/address/new_address_params/ripple/address_param.dart b/mrt_wallet/lib/models/wallet_models/address/new_address_params/ripple/address_param.dart index 01112a20..573a3d1f 100644 --- a/mrt_wallet/lib/models/wallet_models/address/new_address_params/ripple/address_param.dart +++ b/mrt_wallet/lib/models/wallet_models/address/new_address_params/ripple/address_param.dart @@ -6,36 +6,37 @@ import 'package:mrt_wallet/models/wallet_models/address/network_address/xrp/mutl import 'package:mrt_wallet/models/wallet_models/address/new_address_params/core.dart'; import 'package:xrpl_dart/xrpl_dart.dart'; -class RippleNewAddressParam implements NewAccountParams { - const RippleNewAddressParam( - {required this.coin, - required this.deriveIndex, - this.tag, - required this.type}); +class RippleNewAddressParam implements NewAccountParams { @override bool get isMultiSig => false; - @override - final CryptoCoins coin; + final EllipticCurveTypes type; @override final AddressDerivationIndex deriveIndex; final int? tag; + @override + CryptoCoins get coin => deriveIndex.currencyCoin; + + RippleNewAddressParam({ + required this.deriveIndex, + this.tag, + required this.type, + }); } class RippleMultisigNewAddressParam implements RippleNewAddressParam { const RippleMultisigNewAddressParam( - {required this.coin, - required this.multiSigAccount, + {required this.multiSigAccount, required this.masterAddress, + required this.coin, this.tag, this.deriveIndex = const MultiSigAddressIndex()}); @override bool get isMultiSig => true; final XRPAddress masterAddress; - @override - final CryptoCoins coin; + @override final AddressDerivationIndex deriveIndex; @@ -46,4 +47,7 @@ class RippleMultisigNewAddressParam implements RippleNewAddressParam { @override EllipticCurveTypes get type => throw UnimplementedError(); + + @override + final CryptoCoins coin; } diff --git a/mrt_wallet/lib/models/wallet_models/address/new_address_params/solana/address_param.dart b/mrt_wallet/lib/models/wallet_models/address/new_address_params/solana/address_param.dart index 770d083f..51ed7b87 100644 --- a/mrt_wallet/lib/models/wallet_models/address/new_address_params/solana/address_param.dart +++ b/mrt_wallet/lib/models/wallet_models/address/new_address_params/solana/address_param.dart @@ -1,13 +1,12 @@ import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; -class SolanaNewAddressParam implements NewAccountParams { - const SolanaNewAddressParam({required this.coin, required this.deriveIndex}); +class SolanaNewAddressParam implements NewAccountParams { + SolanaNewAddressParam({required this.deriveIndex}); @override bool get isMultiSig => false; - @override - final CryptoCoins coin; - @override final AddressDerivationIndex deriveIndex; + @override + CryptoCoins get coin => deriveIndex.currencyCoin; } diff --git a/mrt_wallet/lib/models/wallet_models/address/new_address_params/tron/address_param.dart b/mrt_wallet/lib/models/wallet_models/address/new_address_params/tron/address_param.dart index e1120dd2..9d104e13 100644 --- a/mrt_wallet/lib/models/wallet_models/address/new_address_params/tron/address_param.dart +++ b/mrt_wallet/lib/models/wallet_models/address/new_address_params/tron/address_param.dart @@ -2,31 +2,33 @@ import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; import 'package:on_chain/on_chain.dart'; -class TronNewAddressParam implements NewAccountParams { - const TronNewAddressParam({required this.coin, required this.deriveIndex}); +class TronNewAddressParam implements NewAccountParams { + TronNewAddressParam({required this.deriveIndex, List? publicKey}); + @override bool get isMultiSig => false; - @override - final CryptoCoins coin; @override final AddressDerivationIndex deriveIndex; + @override + CryptoCoins get coin => deriveIndex.currencyCoin; } class TronMultisigNewAddressParam implements TronNewAddressParam { const TronMultisigNewAddressParam( - {required this.coin, - required this.multiSigAccount, + {required this.multiSigAccount, required this.masterAddress, + required this.coin, this.deriveIndex = const MultiSigAddressIndex()}); @override bool get isMultiSig => true; final TronAddress masterAddress; - @override - final CryptoCoins coin; + @override final AddressDerivationIndex deriveIndex; final TronMultiSignatureAddress multiSigAccount; + @override + final CryptoCoins coin; } diff --git a/mrt_wallet/lib/models/wallet_models/chain/defauilt_node_providers.dart b/mrt_wallet/lib/models/wallet_models/chain/defauilt_node_providers.dart index 5495f862..cdb7397e 100644 --- a/mrt_wallet/lib/models/wallet_models/chain/defauilt_node_providers.dart +++ b/mrt_wallet/lib/models/wallet_models/chain/defauilt_node_providers.dart @@ -1,5 +1,4 @@ import 'package:mrt_native_support/platform_interface.dart'; -import 'package:mrt_wallet/app/core.dart'; import 'package:mrt_wallet/models/wallet_models/network/core/network.dart'; import 'package:mrt_wallet/provider/api/api_provider.dart'; @@ -359,7 +358,6 @@ class DefaultNodeProviders { } static String? getGnesisHash(AppNetworkImpl network) { - WalletLogging.print("come ${network.value}"); return gnesisHash[network.value]; } } diff --git a/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_ada_legacy_private_key.dart b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_ada_legacy_private_key.dart new file mode 100644 index 00000000..5fd54930 --- /dev/null +++ b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_ada_legacy_private_key.dart @@ -0,0 +1,32 @@ +import 'package:blockchain_utils/bip/bip/bip32/base/bip32_base.dart'; +import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; +import 'package:blockchain_utils/bip/wif/wif.dart'; +import 'package:mrt_wallet/models/wallet_models/keys/access_key_response/access_key_response.dart'; + +class AccessADALegacyPrivateKeyResponse implements AccessKeyResponse { + final Bip32Base account; + final CryptoCoins coin; + final String privateKey; + final String extendedKey; + final String? wif; + final String keyName; + const AccessADALegacyPrivateKeyResponse._(this.account, this.privateKey, + this.extendedKey, this.coin, this.wif, this.keyName); + factory AccessADALegacyPrivateKeyResponse.fromBip32( + {required Bip32Base account, + required CryptoCoins coin, + required String keyName}) { + final wifKey = coin.conf.wifNetVer != null + ? WifEncoder.encode(account.privateKey.raw, + netVer: coin.conf.wifNetVer!) + : null; + + return AccessADALegacyPrivateKeyResponse._( + account, + account.privateKey.toHex(), + account.privateKey.toExtended, + coin, + wifKey, + keyName); + } +} diff --git a/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_fake_response.dart b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_fake_response.dart new file mode 100644 index 00000000..77f606a5 --- /dev/null +++ b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_fake_response.dart @@ -0,0 +1,3 @@ +import 'access_key_response.dart'; + +class AccessFakeResponse implements AccessKeyResponse {} diff --git a/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_key_response.dart b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_key_response.dart new file mode 100644 index 00000000..49b470a7 --- /dev/null +++ b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_key_response.dart @@ -0,0 +1 @@ +abstract class AccessKeyResponse {} diff --git a/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_private_key_response.dart b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_private_key_response.dart new file mode 100644 index 00000000..5e2aab66 --- /dev/null +++ b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_private_key_response.dart @@ -0,0 +1,26 @@ +import 'package:blockchain_utils/bip/bip/bip32/base/bip32_base.dart'; +import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; +import 'package:blockchain_utils/bip/wif/wif.dart'; +import 'package:mrt_wallet/models/wallet_models/keys/access_key_response/access_key_response.dart'; + +class AccessPrivateKeyResponse implements AccessKeyResponse { + final Bip32Base account; + final CryptoCoins coin; + final String privateKey; + final String extendedKey; + final String? wif; + final String keyName; + const AccessPrivateKeyResponse._(this.account, this.privateKey, + this.extendedKey, this.coin, this.wif, this.keyName); + factory AccessPrivateKeyResponse.fromBip32( + {required Bip32Base account, + required CryptoCoins coin, + required String keyName}) { + final wifKey = coin.conf.wifNetVer != null + ? WifEncoder.encode(account.privateKey.raw, + netVer: coin.conf.wifNetVer!) + : null; + return AccessPrivateKeyResponse._(account, account.privateKey.toHex(), + account.privateKey.toExtended, coin, wifKey, keyName); + } +} diff --git a/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_public_key_response.dart b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_public_key_response.dart new file mode 100644 index 00000000..913c126c --- /dev/null +++ b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_public_key_response.dart @@ -0,0 +1,22 @@ +import 'package:blockchain_utils/bip/bip/bip32/base/bip32_base.dart'; +import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; +import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:mrt_wallet/models/wallet_models/keys/access_key_response/access_key_response.dart'; + +class AccessPubliKeyResponse implements AccessKeyResponse { + final String extendedKey; + final String comprossed; + final String? uncomprossed; + final String keyName; + const AccessPubliKeyResponse._( + this.extendedKey, this.comprossed, this.uncomprossed, this.keyName); + factory AccessPubliKeyResponse.fromBip32( + {required Bip32Base account, + required CryptoCoins coin, + required String keyName}) { + final comperesed = BytesUtils.toHexString(account.publicKey.compressed); + final uncompresed = BytesUtils.toHexString(account.publicKey.uncompressed); + return AccessPubliKeyResponse._(account.publicKey.toExtended, comperesed, + uncompresed == comperesed ? null : uncompresed, keyName); + } +} diff --git a/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_seed_response.dart b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_seed_response.dart new file mode 100644 index 00000000..b7dc0ec2 --- /dev/null +++ b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/access_seed_response.dart @@ -0,0 +1,8 @@ +import 'package:blockchain_utils/bip/mnemonic/mnemonic.dart'; + +import 'access_key_response.dart'; + +class AccessMnemonicResponse implements AccessKeyResponse { + final Mnemonic mnemonic; + const AccessMnemonicResponse(this.mnemonic); +} diff --git a/mrt_wallet/lib/models/wallet_models/keys/access_key_response/key_respones.dart b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/key_respones.dart new file mode 100644 index 00000000..45cc85e2 --- /dev/null +++ b/mrt_wallet/lib/models/wallet_models/keys/access_key_response/key_respones.dart @@ -0,0 +1,5 @@ +export 'access_key_response.dart'; +export 'access_private_key_response.dart'; +export 'access_seed_response.dart'; +export 'access_fake_response.dart'; +export 'access_public_key_response.dart'; diff --git a/mrt_wallet/lib/models/wallet_models/keys/crypto_keys/imported_key.dart b/mrt_wallet/lib/models/wallet_models/keys/crypto_keys/imported_key.dart new file mode 100644 index 00000000..ce553ee4 --- /dev/null +++ b/mrt_wallet/lib/models/wallet_models/keys/crypto_keys/imported_key.dart @@ -0,0 +1,99 @@ +import 'package:blockchain_utils/bip/bip/bip32/base/bip32_base.dart'; +import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; +import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:mrt_wallet/app/core.dart'; +import 'package:mrt_wallet/models/serializable/serializable.dart'; +import 'package:mrt_wallet/models/wallet_models/address/core/derivation/bip32_address_index.dart'; +import 'package:mrt_wallet/provider/wallet/constant/constant.dart'; + +enum CustomKeyType { + privateKey, + extendedKey; + + static CustomKeyType fromName(String? name) { + return values.firstWhere( + (e) => e.name == name, + orElse: () => throw const MessageException("Invalid CustomKeyType."), + ); + } + + bool get isPrivateKey => this == CustomKeyType.privateKey; +} + +class WalletCustomKeys with CborSerializable, Equatable { + WalletCustomKeys( + {required this.checksum, + required this.extendedPrivateKey, + required this.coin, + required this.publicKey, + required this.name, + DateTime? created, + required this.keyType}) + : created = created ?? DateTime.now(); + final String checksum; + final String extendedPrivateKey; + final String publicKey; + final String? name; + final DateTime created; + final CryptoCoins coin; + final CustomKeyType keyType; + factory WalletCustomKeys.fromCborBytesOrObject( + {List? bytes, CborObject? obj}) { + try { + final CborListValue cbor = CborSerializable.decodeCborTags( + bytes, obj, WalletModelCborTagsConst.walletCustomKey); + + final CryptoProposal proposal = + CustomProposal.fromName(cbor.elementAt(3)); + final CryptoCoins coin = + CustomCoins.getCoin(cbor.elementAt(4), proposal)!; + return WalletCustomKeys( + checksum: cbor.elementAt(0), + extendedPrivateKey: cbor.elementAt(1), + publicKey: cbor.elementAt(2), + coin: coin, + created: cbor.elementAt(5), + name: cbor.elementAt(6), + keyType: CustomKeyType.fromName(cbor.elementAt(7))); + } catch (e) { + throw WalletExceptionConst.invalidMnemonic; + } + } + + @override + CborTagValue toCbor() { + return CborTagValue( + CborListValue.fixedLength([ + checksum, + extendedPrivateKey, + publicKey, + coin.proposal.specName, + coin.coinName, + CborEpochIntValue(created), + name, + keyType.name + ]), + WalletModelCborTagsConst.walletCustomKey); + } + + @override + List get variabels => + [checksum, extendedPrivateKey, coin.coinName, publicKey]; + + Bip32Base _toBip32KeyKey(Bip32AddressIndex? key) { + if (keyType.isPrivateKey) { + return BlockchainUtils.privteKeyToBip32( + BytesUtils.fromHexString(extendedPrivateKey), + key?.currencyCoin ?? coin); + } + return BlockchainUtils.extendedKeyToBip32( + extendedKey: extendedPrivateKey, coin: key?.currencyCoin ?? coin); + } + + Bip32Base toKey(Bip32AddressIndex? key, + {Bip44Levels maxLevel = Bip44Levels.addressIndex}) { + final toKey = _toBip32KeyKey(key); + if (key == null) return toKey; + return key.derive(toKey, maxLevel: maxLevel); + } +} diff --git a/mrt_wallet/lib/models/wallet_models/keys/master_key.dart b/mrt_wallet/lib/models/wallet_models/keys/crypto_keys/master_key.dart similarity index 75% rename from mrt_wallet/lib/models/wallet_models/keys/master_key.dart rename to mrt_wallet/lib/models/wallet_models/keys/crypto_keys/master_key.dart index 570e2ad9..6c36c0ef 100644 --- a/mrt_wallet/lib/models/wallet_models/keys/master_key.dart +++ b/mrt_wallet/lib/models/wallet_models/keys/crypto_keys/master_key.dart @@ -3,11 +3,11 @@ import 'package:blockchain_utils/bip/mnemonic/mnemonic.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:mrt_wallet/app/core.dart'; import 'package:mrt_wallet/models/serializable/serializable.dart'; +import 'package:mrt_wallet/models/wallet_models/address/core/derivation/address_derivation.dart'; +import 'package:mrt_wallet/models/wallet_models/address/core/derivation/bip32_address_index.dart'; +import 'package:mrt_wallet/models/wallet_models/keys/keys.dart'; import 'package:mrt_wallet/provider/wallet/constant/constant.dart'; -import 'derived_key.dart'; -import 'setting.dart'; - enum SeedGenerationType { bip39("Bip39"), byronLegacySeed("ByronLegacySeed"), @@ -173,66 +173,22 @@ class WalletMasterKeys with CborSerializable { return null; } } -} - -class WalletCustomKeys with CborSerializable, Equatable { - WalletCustomKeys( - {required this.checksum, - required this.extendedPrivateKey, - required this.type, - required this.publicKey, - required this.name, - DateTime? created}) - : created = created ?? DateTime.now(); - final String checksum; - final String extendedPrivateKey; - final String publicKey; - final String? name; - final DateTime created; - final EllipticCurveTypes type; - factory WalletCustomKeys.fromCborBytesOrObject( - {List? bytes, CborObject? obj}) { - try { - final CborListValue cbor = CborSerializable.decodeCborTags( - bytes, obj, WalletModelCborTagsConst.walletCustomKey); - final String checksum = cbor.elementAt(0); - final String extendedPrivateKey = cbor.elementAt(1); - final String publicKey = cbor.elementAt(2); - final String curveName = cbor.elementAt(3); - final curve = EllipticCurveTypes.fromName(curveName); - final DateTime created = cbor.elementAt(4); - final String? name = cbor.elementAt(5); - return WalletCustomKeys( - checksum: checksum, - extendedPrivateKey: extendedPrivateKey, - type: curve, - publicKey: publicKey, - created: created, - name: name); - } catch (e) { - throw WalletExceptionConst.invalidMnemonic; + Bip32Base toKey(AddressDerivationIndex key, + {Bip44Levels maxLevel = Bip44Levels.addressIndex}) { + if (key is! Bip32AddressIndex) { + throw WalletExceptionConst.multiSigDerivationNotSuported; } - } - - @override - CborTagValue toCbor() { - return CborTagValue( - CborListValue.fixedLength([ - checksum, - extendedPrivateKey, - publicKey, - type.name, - CborEpochIntValue(created), - name - ]), - WalletModelCborTagsConst.walletCustomKey); - } - - @override - List get variabels => [checksum, extendedPrivateKey, type, publicKey]; - - Bip32Base toBip32() { - return BlockchainUtils.extendedKeyToBip32(extendedPrivateKey, type); + if (key.isImportedKey) { + final customKey = getKeyById(key.importedKeyId!); + if (customKey == null) { + throw WalletExceptionConst.privateKeyIsNotAvailable; + } + return customKey.toKey(key, maxLevel: maxLevel); + } + final seedBytes = getSeed(type: key.seedGeneration); + final bip32Key = BlockchainUtils.seedToBip32( + seedBytes: seedBytes, coin: key.currencyCoin); + return key.derive(bip32Key, maxLevel: maxLevel); } } diff --git a/mrt_wallet/lib/models/wallet_models/keys/encrypted_master_key.dart b/mrt_wallet/lib/models/wallet_models/keys/encrypted_master_key.dart index f5780139..8565217d 100644 --- a/mrt_wallet/lib/models/wallet_models/keys/encrypted_master_key.dart +++ b/mrt_wallet/lib/models/wallet_models/keys/encrypted_master_key.dart @@ -1,10 +1,10 @@ +import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:mrt_wallet/app/core.dart'; import 'package:mrt_wallet/models/wallet_models/keys/encrypted_derived_key.dart'; +import 'package:mrt_wallet/models/wallet_models/keys/keys.dart'; import 'package:mrt_wallet/models/wallet_models/network/core/network.dart'; -import 'setting.dart'; - class EncryptedMasterKey { EncryptedMasterKey( {required this.checksum, @@ -37,18 +37,20 @@ class EncryptedMasterKey { class EncryptedCustomKey with Equatable { final String publicKey; final String id; - final EllipticCurveTypes type; + final CryptoCoins coin; final DateTime created; final String? name; + final CustomKeyType keyType; const EncryptedCustomKey( {required this.publicKey, - required this.type, + required this.coin, required this.id, required this.created, - required this.name}); + required this.name, + required this.keyType}); @override - List get variabels => [publicKey, id, type]; + List get variabels => [publicKey, id, coin, keyType.name]; String networkPubKey(AppNetworkImpl network) { if (network is AppXRPNetwork) { diff --git a/mrt_wallet/lib/models/wallet_models/keys/exported_pubkes.dart b/mrt_wallet/lib/models/wallet_models/keys/exported_pubkes.dart deleted file mode 100644 index 15961a6f..00000000 --- a/mrt_wallet/lib/models/wallet_models/keys/exported_pubkes.dart +++ /dev/null @@ -1,7 +0,0 @@ -class ExportedPublicKey { - const ExportedPublicKey( - {required this.extendedKey, required this.comprossed, this.uncomprossed}); - final String extendedKey; - final String comprossed; - final String? uncomprossed; -} diff --git a/mrt_wallet/lib/models/wallet_models/keys/keys.dart b/mrt_wallet/lib/models/wallet_models/keys/keys.dart index 4e852532..5241d004 100644 --- a/mrt_wallet/lib/models/wallet_models/keys/keys.dart +++ b/mrt_wallet/lib/models/wallet_models/keys/keys.dart @@ -1,7 +1,8 @@ export 'encrypted_master_key.dart'; -export 'master_key.dart'; +export 'crypto_keys/master_key.dart'; export 'selected_mnemonic.dart'; export 'setting.dart'; export 'wallet_backup.dart'; -export 'exported_pubkes.dart'; export 'derived_key.dart'; +export 'crypto_keys/imported_key.dart'; +export 'access_key_response/key_respones.dart'; diff --git a/mrt_wallet/lib/models/wallet_models/network/core/network.dart b/mrt_wallet/lib/models/wallet_models/network/core/network.dart index bc8c6caa..7c07b223 100644 --- a/mrt_wallet/lib/models/wallet_models/network/core/network.dart +++ b/mrt_wallet/lib/models/wallet_models/network/core/network.dart @@ -227,7 +227,7 @@ class AppXRPNetwork extends AppNetworkImpl { if (isMainnet) { return [Bip44Coins.ripple, Bip44Coins.rippleEd25519]; } - return [Bip44Coins.rippleTestnet, Bip44Coins.rippleEd25519]; + return [Bip44Coins.rippleTestnet, Bip44Coins.rippleTestnetED25519]; } @override @@ -296,9 +296,9 @@ class APPTVMNetwork extends AppNetworkImpl { @override List get coins { if (coinParam.mainnet) { - return [Bip44Coins.ethereum]; + return [Bip44Coins.tron]; } - return [Bip44Coins.ethereumTestnet]; + return [Bip44Coins.tronTestnet]; } @override @@ -385,11 +385,13 @@ class APPCardanoNetwork extends AppNetworkImpl { Bip44Coins.cardanoByronIcarus, Cip1852Coins.cardanoIcarus, Cip1852Coins.cardanoLedger, + CustomCoins.byronLegacy ] else ...[ Bip44Coins.cardanoByronIcarusTestnet, Bip44Coins.cardanoByronLedgerTestnet, Cip1852Coins.cardanoIcarusTestnet, Cip1852Coins.cardanoLedgerTestnet, + CustomCoins.byronLegacyTestnet ] ]; } diff --git a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/ada_custom_fee.dart b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/ada_custom_fee.dart new file mode 100644 index 00000000..88947a51 --- /dev/null +++ b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/ada_custom_fee.dart @@ -0,0 +1,31 @@ +import 'package:mrt_wallet/app/core.dart'; +import 'package:mrt_wallet/models/wallet_models/currency_balance/balance.dart'; +import 'package:mrt_wallet/models/wallet_models/network/core/network.dart'; +import 'package:on_chain/ada/src/builder/builder/deposit.dart'; + +enum ADACustomFeeTypes { + stakeRegistration("stake_registration"); + + final String viewName; + const ADACustomFeeTypes(this.viewName); +} + +class ADATransactionDeposit { + final ADACustomFeeTypes type; + final NoneDecimalBalance fee; + ADADepositBuilder toDepositBuilder() { + return ADADepositBuilder(deposit: fee.balance); + } + + const ADATransactionDeposit({required this.type, required this.fee}); + factory ADATransactionDeposit.amount( + {required ADACustomFeeTypes type, + required BigInt fee, + APPCardanoNetwork? network}) { + return ADATransactionDeposit( + type: type, + fee: NoneDecimalBalance( + fee, network?.coinParam.decimal ?? CardanoUtils.decimal, + imutable: true)); + } +} diff --git a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/cardano_address_details.dart b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/address_details.dart similarity index 87% rename from mrt_wallet/lib/models/wallet_models/network/custom/cardano/cardano_address_details.dart rename to mrt_wallet/lib/models/wallet_models/network/custom/cardano/address_details.dart index e7f4c672..b935cf17 100644 --- a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/cardano_address_details.dart +++ b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/address_details.dart @@ -2,9 +2,10 @@ import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:mrt_wallet/app/core.dart'; import 'package:mrt_wallet/models/serializable/serializable.dart'; -import 'package:mrt_wallet/models/wallet_models/keys/master_key.dart'; +import 'package:mrt_wallet/models/wallet_models/keys/crypto_keys/master_key.dart'; import 'package:mrt_wallet/provider/wallet/constant/constant.dart'; import 'package:on_chain/ada/src/address/address.dart'; +import 'package:on_chain/ada/src/models/ada_models.dart'; class CardanoAddrDetails with CborSerializable { final List publicKey; @@ -12,8 +13,20 @@ class CardanoAddrDetails with CborSerializable { final List? chainCode; final List? hdPathKey; final String? hdPath; + late final String? hdPathKeyHex = BytesUtils.tryToHexString(hdPathKey); final ADAAddressType addressType; - final SeedGenerationType seedGeneration; + bool get isLegacy => hdPath != null; + + PolicyID policyId() { + final keyHash = Ed25519KeyHash.fromPubkey(publicKey); + final mintScript = NativeScriptScriptPubkey(keyHash); + return PolicyID(mintScript.toHash().data); + } + + NativeScript toNativeScript() { + final keyHash = Ed25519KeyHash.fromPubkey(publicKey); + return NativeScriptScriptPubkey(keyHash); + } factory CardanoAddrDetails.fromCborBytesOrObject( {List? bytes, CborObject? obj}) { @@ -25,13 +38,11 @@ class CardanoAddrDetails with CborSerializable { stakePubkey: cbor.elementAt(2), chainCode: cbor.elementAt(3), hdPathKey: cbor.elementAt(4), - hdPath: cbor.elementAt(5), - seedGeneration: SeedGenerationType.fromName(cbor.elementAt(6))); + hdPath: cbor.elementAt(5)); } CardanoAddrDetails._({ required List publicKey, required this.addressType, - required this.seedGeneration, List? stakePubkey, List? chainCode, List? hdPathKey, @@ -61,8 +72,7 @@ class CardanoAddrDetails with CborSerializable { return CardanoAddrDetails._( publicKey: publicKey, addressType: addressType, - stakePubkey: stakePubkey, - seedGeneration: seedGeneration); + stakePubkey: stakePubkey); } factory CardanoAddrDetails.byron( {required List publicKey, @@ -80,8 +90,7 @@ class CardanoAddrDetails with CborSerializable { addressType: ADAAddressType.byron, hdPathKey: hdPathKey, chainCode: chainCode, - hdPath: hdPath, - seedGeneration: seedGeneration); + hdPath: hdPath); } ADAAddress toAddress(CryptoCoins coin) { @@ -127,7 +136,6 @@ class CardanoAddrDetails with CborSerializable { ? const CborNullValue() : CborBytesValue(hdPathKey!), hdPath == null ? const CborNullValue() : CborStringValue(hdPath!), - CborStringValue(seedGeneration.name) ]), WalletModelCborTagsConst.cardanoAccountDetails); } diff --git a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/cardano.dart b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/cardano.dart index 3c3e1c24..6d4212e2 100644 --- a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/cardano.dart +++ b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/cardano.dart @@ -1,4 +1,9 @@ -export 'cardano_address_details.dart'; -export 'cardano_output.dart'; +export 'address_details.dart'; +export 'output.dart'; export 'utxo_with_owner.dart'; export 'utxo.dart'; +export 'mint_info.dart'; +export 'utxo_multi_asset.dart'; +export 'utxo_asset.dart'; +export 'certificate.dart'; +export 'ada_custom_fee.dart'; diff --git a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/certificate.dart b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/certificate.dart new file mode 100644 index 00000000..6bd0e50d --- /dev/null +++ b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/certificate.dart @@ -0,0 +1,21 @@ +import 'package:mrt_wallet/models/wallet_models/address/address.dart'; +import 'package:on_chain/on_chain.dart'; + +enum ADATransactionCertificateType { + deregistration("stake_deregistration"), + registraction("stake_registration"), + delegation("stake_delegation"); + + final String viewName; + const ADATransactionCertificateType(this.viewName); +} + +class ADATransactionCertificate { + final ADACertificateBuilder certificate; + final ADATransactionCertificateType type; + final ReceiptAddress rewardAccount; + const ADATransactionCertificate( + {required this.certificate, + required this.type, + required this.rewardAccount}); +} diff --git a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/mint_info.dart b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/mint_info.dart new file mode 100644 index 00000000..13827dc5 --- /dev/null +++ b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/mint_info.dart @@ -0,0 +1,40 @@ +import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:mrt_wallet/models/wallet_models/currency_balance/currency_balance.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/cardano.dart'; +import 'package:on_chain/ada/ada.dart'; + +class ADAMintInfo { + final Assets assets; + final NativeScript script; + final ADAAddress owner; + final List pubKeyBytes; + PolicyID toPolicyId() { + return PolicyID(script.toHash().data); + } + + ADAMintInfo( + {required this.assets, + required this.script, + required this.owner, + required List pubKeyBytes}) + : pubKeyBytes = BytesUtils.toBytes(pubKeyBytes, unmodifiable: true); + + late final UtxoAssets toUtxoAssets = _toUtxoAssets(); + late final UtxoMultiAsset toMultiAsset = _toMultiAsset(); + UtxoAssets _toUtxoAssets() { + final Map multiAssets = {}; + for (final i in assets.assets.entries) { + multiAssets[i.key] = NoneDecimalBalance(i.value, 0); + } + return UtxoAssets(multiAssets); + } + + UtxoMultiAsset _toMultiAsset() { + return UtxoMultiAsset({toPolicyId(): _toUtxoAssets()}); + } + + ADAMinsBuilder toMintBuilder() { + return ADAMinsBuilder( + pubKeyBytes: pubKeyBytes, mintingAssets: assets, owner: owner); + } +} diff --git a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/cardano_output.dart b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/output.dart similarity index 61% rename from mrt_wallet/lib/models/wallet_models/network/custom/cardano/cardano_output.dart rename to mrt_wallet/lib/models/wallet_models/network/custom/cardano/output.dart index 0c9b7fe5..ad24f5d8 100644 --- a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/cardano_output.dart +++ b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/output.dart @@ -6,7 +6,7 @@ import 'package:on_chain/on_chain.dart'; import 'utxo_multi_asset.dart'; class CardanoOutputWithBalance { - CardanoOutputWithBalance( + CardanoOutputWithBalance._( {required ReceiptAddress address, required APPCardanoNetwork network, UtxoMultiAsset? asset}) @@ -14,6 +14,14 @@ class CardanoOutputWithBalance { _asset = asset ?? UtxoMultiAsset({}), _address = address, minAdaValue = NoneDecimalBalance.zero(network.coinParam.decimal); + factory CardanoOutputWithBalance( + {required ReceiptAddress address, + required APPCardanoNetwork network}) { + final output = + CardanoOutputWithBalance._(address: address, network: network); + output._checkOutput(); + return output; + } final NoneDecimalBalance balance; final NoneDecimalBalance minAdaValue; @@ -23,16 +31,36 @@ class CardanoOutputWithBalance { UtxoMultiAsset get asset => _asset; bool get hasAssets => asset.assets.isNotEmpty; bool get hasAmount => !balance.isZero; - bool get isValid => balance.balance >= minAdaValue.balance; + bool get addressEraSupported => + address.networkAddress.addressType != ADAAddressType.byron; + bool get isRewardAddress => + _address.networkAddress.addressType == ADAAddressType.reward; + bool _isReady = false; + bool get isReady => _isReady; + bool _minAdaRequired = false; + bool get minAdaRequired => _minAdaRequired; - bool get minAdaRequired => hasAmount && !isValid; + void _checkOutput() { + _minAdaRequired = hasAmount && balance.balance < minAdaValue.balance; + _isReady = hasAmount && !isRewardAddress && !minAdaRequired; + } void setAsset(UtxoMultiAsset? updateAsset) { _asset = updateAsset ?? UtxoMultiAsset.empty; + if (!hasAssets) { + updateBalance(BigInt.zero); + return; + } + _checkOutput(); } - void setAddress(ReceiptAddress? updateAddress) { + void setAddress(ReceiptAddress? updateAddress, + {int? coinsPerUtxoSize}) { _address = updateAddress ?? _address; + if (coinsPerUtxoSize != null) { + minAdaValue.updateBalance(minValue(coinsPerUtxoSize)); + } + _checkOutput(); } void updateBalance(BigInt val, {int? coinsPerUtxoSize}) { @@ -40,6 +68,7 @@ class CardanoOutputWithBalance { if (coinsPerUtxoSize != null) { minAdaValue.updateBalance(minValue(coinsPerUtxoSize)); } + _checkOutput(); } TransactionOutput toOutput() { diff --git a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxo.dart b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxo.dart index 5acb3ad6..203f3b80 100644 --- a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxo.dart +++ b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxo.dart @@ -1,5 +1,5 @@ import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; -import 'account_utxos.dart'; +import 'utxos.dart'; class CardanoUtxo { final ADAAccountUTXOs utxo; diff --git a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxo_multi_asset.dart b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxo_multi_asset.dart index 342b7fdb..72dc23d8 100644 --- a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxo_multi_asset.dart +++ b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxo_multi_asset.dart @@ -1,7 +1,7 @@ import 'package:blockchain_utils/binary/binary.dart'; import 'package:mrt_wallet/models/wallet_models/currency_balance/currency_balance.dart'; import 'package:on_chain/on_chain.dart'; -import 'account_utxos.dart'; +import 'utxos.dart'; import 'utxo_asset.dart'; /// Represents a collection of multiple assets associated with policy IDs. diff --git a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxo_with_owner.dart b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxo_with_owner.dart index 159f5a1d..f9236f22 100644 --- a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxo_with_owner.dart +++ b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxo_with_owner.dart @@ -1,7 +1,7 @@ import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/utxo.dart'; import 'package:mrt_wallet/models/wallet_models/wallet_models.dart'; import 'package:on_chain/ada/src/address/address.dart'; -import 'account_utxos.dart'; +import 'utxos.dart'; class CardanoUtxoWithOwner { final ADAAddress owner; diff --git a/mrt_wallet/lib/models/wallet_models/network/custom/cardano/account_utxos.dart b/mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxos.dart similarity index 100% rename from mrt_wallet/lib/models/wallet_models/network/custom/cardano/account_utxos.dart rename to mrt_wallet/lib/models/wallet_models/network/custom/cardano/utxos.dart diff --git a/mrt_wallet/lib/models/wallet_models/network/params/core/network_params.dart b/mrt_wallet/lib/models/wallet_models/network/params/core/network_params.dart index 84dce3af..5f946147 100644 --- a/mrt_wallet/lib/models/wallet_models/network/params/core/network_params.dart +++ b/mrt_wallet/lib/models/wallet_models/network/params/core/network_params.dart @@ -11,5 +11,4 @@ abstract class NetworkCoinParams with CborSerializable { abstract final List providers; String? getAccountExplorer(String address); String? getTransactionExplorer(String txId); - } diff --git a/mrt_wallet/lib/models/wallet_models/signing_request/networks/cardano/cardano.dart b/mrt_wallet/lib/models/wallet_models/signing_request/networks/cardano/cardano.dart index b2879203..8d96e6e3 100644 --- a/mrt_wallet/lib/models/wallet_models/signing_request/networks/cardano/cardano.dart +++ b/mrt_wallet/lib/models/wallet_models/signing_request/networks/cardano/cardano.dart @@ -1,16 +1,20 @@ import 'package:mrt_wallet/models/wallet_models/address/core/address.dart'; import 'package:mrt_wallet/models/wallet_models/address/core/derivation/address_derivation.dart'; +import 'package:mrt_wallet/models/wallet_models/address/network_address/cardano/cardano.dart'; import 'package:mrt_wallet/models/wallet_models/network/core/network.dart'; import 'package:mrt_wallet/models/wallet_models/signing_request/core/signing_request.dart'; import 'package:on_chain/ada/src/builder/builder/transaction_builder.dart'; class CardanoSigningRequest implements SigningRequest { - const CardanoSigningRequest( - {required this.addresses, + CardanoSigningRequest( + {required List addresses, required this.network, - required this.transaction}); + required this.transaction, + required List signers}) + : addresses = List.unmodifiable(addresses), + signers = List.unmodifiable(signers); @override - final List addresses; + final List addresses; @override final AppNetworkImpl network; @@ -18,6 +22,5 @@ class CardanoSigningRequest implements SigningRequest { final ADATransactionBuilder transaction; @override - List get signers => - addresses.map((e) => e.keyIndex).toList(); + final List signers; } diff --git a/mrt_wallet/lib/models/wallet_models/signing_request/networks/ripple/ripple_signing_request.dart b/mrt_wallet/lib/models/wallet_models/signing_request/networks/ripple/ripple_signing_request.dart index a4202ede..8ad176ce 100644 --- a/mrt_wallet/lib/models/wallet_models/signing_request/networks/ripple/ripple_signing_request.dart +++ b/mrt_wallet/lib/models/wallet_models/signing_request/networks/ripple/ripple_signing_request.dart @@ -25,15 +25,6 @@ class RippleSigningRequest implements SigningRequest { return [addresses.first.keyIndex]; } - List get publicKeys { - if (isMultiSig) { - final MultiSigCryptoAccountAddress addr = - addresses.first as MultiSigCryptoAccountAddress; - return addr.keyDetails.map((e) => e.$1).toList(); - } - return addresses.first.signers; - } - bool get needMultiSignature { if (isMultiSig) { final IXRPMultisigAddress addr = addresses.first as IXRPMultisigAddress; diff --git a/mrt_wallet/lib/models/wallet_models/signing_request/networks/secp256k1/signing_request.dart b/mrt_wallet/lib/models/wallet_models/signing_request/networks/secp256k1/signing_request.dart index fda972ef..ed521dac 100644 --- a/mrt_wallet/lib/models/wallet_models/signing_request/networks/secp256k1/signing_request.dart +++ b/mrt_wallet/lib/models/wallet_models/signing_request/networks/secp256k1/signing_request.dart @@ -27,13 +27,4 @@ class Secp256k1SigningRequest implements SigningRequest { } return [addresses.first.keyIndex]; } - - List get publicKeys { - if (isMultiSig) { - final MultiSigCryptoAccountAddress addr = - addresses.first as MultiSigCryptoAccountAddress; - return addr.keyDetails.map((e) => e.$1).toList(); - } - return addresses.first.signers; - } } diff --git a/mrt_wallet/lib/provider/api/networks/bitcoin/bitcoin.dart b/mrt_wallet/lib/provider/api/networks/bitcoin/bitcoin.dart index dcf37126..363b8839 100644 --- a/mrt_wallet/lib/provider/api/networks/bitcoin/bitcoin.dart +++ b/mrt_wallet/lib/provider/api/networks/bitcoin/bitcoin.dart @@ -1,4 +1,4 @@ export 'core/core.dart'; export 'electrum/electrum.dart'; export 'explorer/bitcoin_explorer_provider.dart'; -export 'core/electrum_api_provider_service.dart'; \ No newline at end of file +export 'core/electrum_api_provider_service.dart'; diff --git a/mrt_wallet/lib/provider/api/networks/bitcoin/electrum/custom_request/script_hash_balance.dart b/mrt_wallet/lib/provider/api/networks/bitcoin/electrum/custom_request/script_hash_balance.dart index bee59e0f..40d5b563 100644 --- a/mrt_wallet/lib/provider/api/networks/bitcoin/electrum/custom_request/script_hash_balance.dart +++ b/mrt_wallet/lib/provider/api/networks/bitcoin/electrum/custom_request/script_hash_balance.dart @@ -1,4 +1,3 @@ - import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/numbers/bigint_utils.dart'; diff --git a/mrt_wallet/lib/provider/api/networks/cardano/cardano_api_provider.dart b/mrt_wallet/lib/provider/api/networks/cardano/cardano_api_provider.dart index 1a9be67a..9ce97b12 100644 --- a/mrt_wallet/lib/provider/api/networks/cardano/cardano_api_provider.dart +++ b/mrt_wallet/lib/provider/api/networks/cardano/cardano_api_provider.dart @@ -1,7 +1,7 @@ import 'package:mrt_wallet/models/api/api_provider_tracker.dart'; import 'package:mrt_wallet/models/wallet_models/address/network_address/cardano/cardano.dart'; import 'package:mrt_wallet/models/wallet_models/network/core/network.dart'; -import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/account_utxos.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/utxos.dart'; import 'package:mrt_wallet/provider/api/api_provider.dart'; import 'package:on_chain/on_chain.dart'; @@ -35,11 +35,7 @@ class CardanoApiProvider implements NetworkApiProvider { } Future broadcastTransaction(List txCborBytes) async { - try { - return await provider.request(BlockfrostRequestSubmitTransaction( - transactionCborBytes: txCborBytes)); - } catch (e) { - rethrow; - } + return await provider.request( + BlockfrostRequestSubmitTransaction(transactionCborBytes: txCborBytes)); } } diff --git a/mrt_wallet/lib/provider/api/networks/cardano/custom_request/utxos.dart b/mrt_wallet/lib/provider/api/networks/cardano/custom_request/utxos.dart index 057bf8eb..3aeb756f 100644 --- a/mrt_wallet/lib/provider/api/networks/cardano/custom_request/utxos.dart +++ b/mrt_wallet/lib/provider/api/networks/cardano/custom_request/utxos.dart @@ -1,4 +1,4 @@ -import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/account_utxos.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/utxos.dart'; import 'package:on_chain/ada/src/address/era/core/address.dart'; import 'package:on_chain/ada/src/provider/blockfrost/core/blockfrost.dart'; import 'package:on_chain/ada/src/provider/blockfrost/core/core.dart'; diff --git a/mrt_wallet/lib/provider/api/networks/ethereum/evm_api_providr.dart b/mrt_wallet/lib/provider/api/networks/ethereum/evm_api_providr.dart index 2c7670de..0ec1fb98 100644 --- a/mrt_wallet/lib/provider/api/networks/ethereum/evm_api_providr.dart +++ b/mrt_wallet/lib/provider/api/networks/ethereum/evm_api_providr.dart @@ -38,11 +38,15 @@ class EVMApiProvider implements NetworkApiProvider { Future<(BigInt, bool)> getNetworkInfo() async { final BigInt chainId = await provider.request(RPCGetChainId()); - final eip = await provider.request(RPCGetFeeHistory( - blockCount: 25, - newestBlock: BlockTagOrNumber.pending, - rewardPercentiles: [25, 50, 90])); - return (chainId, eip != null); + try { + final eip = await provider.request(RPCGetFeeHistory( + blockCount: 25, + newestBlock: BlockTagOrNumber.pending, + rewardPercentiles: [25, 50, 90])); + return (chainId, eip != null); + } on RPCError { + return (chainId, false); + } } Future gasPrice() async { diff --git a/mrt_wallet/lib/provider/transaction_validator/solana/transfer/create_account.dart b/mrt_wallet/lib/provider/transaction_validator/solana/transfer/create_account.dart index e418175e..cf5da425 100644 --- a/mrt_wallet/lib/provider/transaction_validator/solana/transfer/create_account.dart +++ b/mrt_wallet/lib/provider/transaction_validator/solana/transfer/create_account.dart @@ -135,7 +135,6 @@ class SolanaCreateAccountValidator extends SolanaTransactionValidator { @override String? validateError({ISolanaAddress? account}) { - WalletLogging.print(lamports.hasValue); for (final i in fields) { if (!i.optional && !i.hasValue) { return "field_is_req".tr.replaceOne(i.name.tr); diff --git a/mrt_wallet/lib/provider/wallet/constant/constant.dart b/mrt_wallet/lib/provider/wallet/constant/constant.dart index e3e5bd1e..37b8aa1d 100644 --- a/mrt_wallet/lib/provider/wallet/constant/constant.dart +++ b/mrt_wallet/lib/provider/wallet/constant/constant.dart @@ -15,10 +15,8 @@ class WalletModelCborTagsConst { static const List iAccount = [200]; static const List address = [200, 80]; static const List accoutKeyIndex = [200, 81]; - static const List importedAccountKeyIndex = [200, 82]; static const List multiSigAccountKeyIndex = [200, 83]; - static const List byronLegacyKeyIndex = [200, 84]; - // bitcoin + static const List bitcoinCashAccount = [200, 191]; static const List bitcoinCashMultiSigAccount = [200, 191, 1]; static const List bitcoinAccount = [200, 192]; diff --git a/mrt_wallet/lib/provider/wallet/core/core.dart b/mrt_wallet/lib/provider/wallet/core/core.dart index 542e29f9..4dbd26ed 100644 --- a/mrt_wallet/lib/provider/wallet/core/core.dart +++ b/mrt_wallet/lib/provider/wallet/core/core.dart @@ -34,7 +34,7 @@ abstract class WalletCore extends _WalletCore with WalletStatusImpl { WalletBackupCore backup, String password) async { try { final result = await _callSynchronized( - () async => await _setupBackupV2(backup, password), + () async => await _setupBackup(backup, password), conditionStatus: WalletStatus.setup, onSuccessStatus: WalletStatus.lock); @@ -203,7 +203,7 @@ abstract class WalletCore extends _WalletCore with WalletStatusImpl { String password) { final result = _callSynchronized(() async { _validatePassword(password); - return _massterKey!.customKeys; + return List.from(_massterKey!.customKeys); }, conditionStatus: WalletStatus.unlock); return result; } @@ -250,35 +250,21 @@ abstract class WalletCore extends _WalletCore with WalletStatusImpl { notify(); } - Future> accsess( + Future>> getAccountPubKys( + {required CryptoAccountAddress account}) async { + final result = await _callSynchronized( + () async => _getAccountPubKys(account: account), + conditionStatus: WalletStatus.unlock); + return result; + } + + Future>> accsess( WalletAccsessType accsessType, String password, {CryptoAccountAddress? account, String? accountId}) async { - final result = await _callSynchronized(() async { - if (accsessType == WalletAccsessType.privateKey && - account == null && - accountId == null) { - throw WalletException.invalidArgruments( - ["CryptoAccountAddress", "null"]); - } - - if (accsessType == WalletAccsessType.privateKey) { - if (account != null) { - final privateKey = - _getPrivateKey(_validatePassword(password), account.keyIndex); - return BytesUtils.toHexString(privateKey.privateKey.raw); - } else { - final extendedKey = - _getImportedKeyFromId(_validatePassword(password), accountId!); - return extendedKey; - } - } else if (accsessType == WalletAccsessType.seed) { - final mnemoic = await _unlockMnemonic(password); - return mnemoic.toStr(); - } else { - _validatePassword(password); - return ""; - } - }, conditionStatus: WalletStatus.unlock); + final result = await _callSynchronized( + () async => await _accsess(accsessType, password, + account: account, accountId: accountId), + conditionStatus: WalletStatus.unlock); return result; } @@ -489,11 +475,14 @@ abstract class WalletCore extends _WalletCore with WalletStatusImpl { }); } - List getNetworkImportedKeys() { + List getCustomKeysForCoin(CryptoCoins coin) { if (walletIsUnlock) { - return _massterKey!.customKeys - .where((element) => network.keyTypes.contains(element.type)) - .toList(); + return List.from( + _massterKey!.customKeys.where((element) { + return (element.coin == coin) || + (element.coin.conf.type == coin.conf.type && + element.keyType.isPrivateKey); + })); } return []; } diff --git a/mrt_wallet/lib/provider/wallet/crypto/master_key.dart b/mrt_wallet/lib/provider/wallet/crypto/master_key.dart index 73df7ca1..c7288552 100644 --- a/mrt_wallet/lib/provider/wallet/crypto/master_key.dart +++ b/mrt_wallet/lib/provider/wallet/crypto/master_key.dart @@ -21,115 +21,36 @@ mixin MasterKeyImpl on WalletCryptoImpl { customKeys: mn.customKeys .map((e) => EncryptedCustomKey( publicKey: e.publicKey, - type: e.type, + coin: e.coin, id: e.checksum, created: e.created, - name: e.name)) + name: e.name, + keyType: e.keyType)) .toList()); } Bip32Base _getPrivateKey(List password, AddressDerivationIndex keyIndex, - {SeedGenerationType seedType = SeedGenerationType.bip39}) { - try { - switch (keyIndex.runtimeType) { - case Bip32AddressIndex: - case ByronLegacyAddressIndex: - case ImportedAddressIndex: - final WalletMasterKeys walletMasterKeys = - _fromMemoryStorage(password, _massterKey!); - - return _getLocalBip32FromKeyIndex(keyIndex, walletMasterKeys, - seedType: seedType); - default: - throw WalletExceptionConst.privateKeyIsNotAvailable; - } - } catch (e) { - rethrow; - } finally {} - } - - bool _validateBcakupAccounts( - WalletMasterKeys masterKey, CryptoAccountAddress address) { - if (!address.multiSigAccount) { - final bip32 = _getBip32FromKeyIndex(address.keyIndex, masterKey, - seedType: address.keyIndex.seedGeneration); - if (!address.signers.contains(bip32.publicKey.toHex())) { - return false; - } - } else { - return _validateMultiSigAccount(masterKey, address); - } - return true; - } - - bool _validateMultiSigAccount( - WalletMasterKeys masterKey, CryptoAccountAddress address) { - final List<(String, AddressDerivationIndex)> keyDetails = - (address as MultiSigCryptoAccountAddress).keyDetails; - for (final i in keyDetails) { - final bip32 = _getBip32FromKeyIndex(i.$2, masterKey); - if (!address.signers.contains(bip32.publicKey.toHex())) { - return false; - } + {SeedGenerationType seedType = SeedGenerationType.bip39, + Bip44Levels maxLevel = Bip44Levels.addressIndex}) { + if (keyIndex.derivationType.isMultiSig) { + throw WalletExceptionConst.privateKeyIsNotAvailable; } - return true; - } - - String _getImportedKeyFromId(List password, String accountId) { final WalletMasterKeys walletMasterKeys = _fromMemoryStorage(password, _massterKey!); - final acc = walletMasterKeys.getKeyById(accountId); - if (acc == null) { - throw WalletExceptionConst.privateKeyIsNotAvailable; - } - return acc.extendedPrivateKey; + return walletMasterKeys.toKey(keyIndex, maxLevel: maxLevel); } - Bip32Base _getBip32FromKeyIndex( - AddressDerivationIndex keyIndex, WalletMasterKeys masterKey, - {SeedGenerationType seedType = SeedGenerationType.bip39}) { - if (keyIndex is MultiSigAddressIndex) { - throw WalletExceptionConst.privateKeyIsNotAvailable; - } - - if (keyIndex is ByronLegacyAddressIndex) { - final legacyBip32 = - CardanoByronLegacyBip32.fromSeed(masterKey.getSeed(type: seedType)); - final legacy = CardanoByronLegacy.fromBip32(legacyBip32); - return legacy.bip32.derivePath(keyIndex.path); - } else if (keyIndex is ImportedAddressIndex) { - final key = masterKey.getKeyById(keyIndex.accountId)!; - final Bip32Base bip32 = key.toBip32(); - return keyIndex.derive(bip32); - } - final Bip32Base bip32 = BlockchainUtils.seedToBip32( - masterKey.getSeed(type: seedType), - type: keyIndex.curve, - coin: keyIndex.currencyCoin); - return keyIndex.derive(bip32); - } - - Bip32Base _getLocalBip32FromKeyIndex( - AddressDerivationIndex keyIndex, WalletMasterKeys masterKey, - {SeedGenerationType seedType = SeedGenerationType.bip39}) { - if (keyIndex is MultiSigAddressIndex) { + AccessPrivateKeyResponse _getImportedKeyFromId( + List password, String keyId) { + final WalletMasterKeys walletMasterKeys = + _fromMemoryStorage(password, _massterKey!); + final key = walletMasterKeys.getKeyById(keyId); + if (key == null) { throw WalletExceptionConst.privateKeyIsNotAvailable; } - final keyId = keyIndex.storageKey(); - final derivedKey = masterKey.getDerivedKey(keyId); - - if (derivedKey == null) { - return _getBip32FromKeyIndex(keyIndex, masterKey, seedType: seedType); - } - if (keyIndex is ByronLegacyAddressIndex) { - final legacyBip32 = - CardanoByronLegacyBip32.fromExtendedKey(derivedKey.extendedKey); - final legacy = CardanoByronLegacy.fromBip32(legacyBip32); - return legacy.bip32.derivePath(keyIndex.path); - } - return BlockchainUtils.extendedToBip32(derivedKey.extendedKey, - type: keyIndex.curve, coin: keyIndex.currencyCoin); + return AccessPrivateKeyResponse.fromBip32( + account: key.toKey(null), coin: key.coin, keyName: keyId); } Future _readMnemonic( @@ -140,106 +61,69 @@ mixin MasterKeyImpl on WalletCryptoImpl { return mn.mnemonic; } - (Bip32Base, DerivedKey?) _getDerivedKey( - NewAccountParams params, WalletMasterKeys masterKey, - {Bip44Levels maxLevel = Bip44Levels.addressIndex}) { - final keyIndex = params.deriveIndex; - final String keyId = keyIndex.storageKey(maxLevel: maxLevel); - final localKeyIndex = masterKey.getDerivedKey(keyId); - if (localKeyIndex != null) { - if (keyIndex is ByronLegacyAddressIndex) { - final key = - CardanoByronLegacyBip32.fromExtendedKey(localKeyIndex.extendedKey); - return (key, null); - } - final key = BlockchainUtils.extendedToBip32(localKeyIndex.extendedKey, - coin: params.coin); - return (key, null); - } - Bip32Base bip32; - if (keyIndex is ImportedAddressIndex) { - final WalletCustomKeys key = masterKey.customKeys - .firstWhere((element) => element.checksum == keyIndex.accountId); - bip32 = key.toBip32(); - } else if (keyIndex is ByronLegacyAddressIndex) { - bip32 = CardanoByronLegacyBip32.fromSeed( - masterKey.getSeed(type: params.deriveIndex.seedGeneration)); - } else { - bip32 = BlockchainUtils.seedToBip32( - masterKey.getSeed(type: params.deriveIndex.seedGeneration), - coin: params.coin); - } - if (keyIndex is! ByronLegacyAddressIndex) { - bip32 = keyIndex.derive(bip32, maxLevel: maxLevel); - } - final derivedKey = - DerivedKey(extendedKey: bip32.privateKey.toExtended, id: keyId); - return (bip32, derivedKey); - } - - (CardanoNewAddressParams, DerivedKey?, DerivedKey?) _deriveCardanoNewAddress( + CardanoNewAddressParams _deriveCardanoNewAddress( CardanoNewAddressParams params, WalletMasterKeys keys) { - final bip = _getDerivedKey(params, keys); - DerivedKey? stakeDerivedKey; + final bool byronLegacy = params.coin.proposal == CustomProposal.cip0019; + final bip = keys.toKey(params.deriveIndex, + maxLevel: byronLegacy ? Bip44Levels.master : Bip44Levels.addressIndex); final CardanoAddrDetails addrDetails; switch (params.addressType) { case ADAAddressType.base: - final stake = - _getDerivedKey(params, keys, maxLevel: Bip44Levels.account); - stakeDerivedKey = stake.$2; + final stake = keys.toKey(params.rewardKeyIndex!); addrDetails = CardanoAddrDetails.shelley( - publicKey: bip.$1.publicKey.compressed, - stakePubkey: stake.$1.derivePath("2/0").publicKey.compressed, + publicKey: bip.publicKey.compressed, + stakePubkey: stake.publicKey.compressed, addressType: params.addressType, seedGeneration: params.deriveIndex.seedGeneration); break; case ADAAddressType.enterprise: case ADAAddressType.reward: addrDetails = CardanoAddrDetails.shelley( - publicKey: bip.$1.publicKey.compressed, + publicKey: bip.publicKey.compressed, addressType: params.addressType, seedGeneration: params.deriveIndex.seedGeneration); break; case ADAAddressType.byron: - if (params.deriveIndex is ByronLegacyAddressIndex) { - final ByronLegacyAddressIndex deriveIndex = - params.deriveIndex as ByronLegacyAddressIndex; - final legacy = CardanoByronLegacy.fromBip32(bip.$1); - final pubkey = legacy.deriveKey(Bip32KeyIndex(deriveIndex.firstIndex), - Bip32KeyIndex(deriveIndex.secondIndex)); - addrDetails = CardanoAddrDetails.byron( - publicKey: pubkey.publicKey.compressed, - chainCode: pubkey.publicKey.chainCode.toBytes(), - seedGeneration: params.deriveIndex.seedGeneration, - hdPathKey: legacy.hdPathKey, - hdPath: deriveIndex.path); - break; + if (byronLegacy) { + try { + final legacy = CardanoByronLegacy.fromBip32(bip); + Bip32Base pubkey = legacy.bip32; + if (params.deriveIndex.hdPath != null) { + pubkey = legacy.bip32.derivePath(params.deriveIndex.hdPath!); + } + addrDetails = CardanoAddrDetails.byron( + publicKey: pubkey.publicKey.compressed, + chainCode: pubkey.publicKey.chainCode.toBytes(), + seedGeneration: params.deriveIndex.seedGeneration, + hdPathKey: params.customHdPathKey ?? legacy.hdPathKey, + hdPath: params.customHdPath ?? params.deriveIndex.hdPath); + + break; + } catch (e) { + rethrow; + } } + addrDetails = CardanoAddrDetails.byron( - publicKey: bip.$1.publicKey.compressed, - chainCode: bip.$1.chainCode.toBytes(), + publicKey: bip.publicKey.compressed, + chainCode: bip.chainCode.toBytes(), seedGeneration: params.deriveIndex.seedGeneration); break; default: throw UnimplementedError(); } - return ( - params.copyWith(addressDetails: addrDetails), - bip.$2, - stakeDerivedKey - ); + return params.copyWith(addressDetails: addrDetails); } WalletMasterKeys _importCustomKey( WalletCustomKeys newKey, List password) { final WalletMasterKeys keys = _fromMemoryStorage(password, _massterKey!); - final Bip32Base bip32 = newKey.toBip32(); - final checkshum = bip32.publicKey.fingerPrint.toHex(); + final Bip32Base bip32 = newKey.toKey(null); + final checkshum = BlockchainUtils.createCustomKeyChecksum(bip32); final publicKey = bip32.publicKey.toHex(); if (checkshum != newKey.checksum || newKey.publicKey != publicKey) { throw WalletExceptionConst.invalidAccountDetails; } - if (keys.customKeys.contains(newKey)) { throw WalletExceptionConst.keyAlreadyExist; } diff --git a/mrt_wallet/lib/provider/wallet/network/network_imp.dart b/mrt_wallet/lib/provider/wallet/network/network_imp.dart index ff9f2e14..139c1ae4 100644 --- a/mrt_wallet/lib/provider/wallet/network/network_imp.dart +++ b/mrt_wallet/lib/provider/wallet/network/network_imp.dart @@ -36,15 +36,11 @@ mixin WalletNetworkImpl on WalletCryptoImpl, WalletStorageImpl, MasterKeyImpl { List publicKey, ) async { final acc = chain; - try { - final address = acc.account.addNewAddress(publicKey, accountParams); - await _saveAccount(acc); - _updateAccountBalance(acc, address: address); - await MethodCaller.wait(); - return address; - } catch (e) { - rethrow; - } + final address = acc.account.addNewAddress(publicKey, accountParams); + await _saveAccount(acc); + _updateAccountBalance(acc, address: address); + await MethodCaller.wait(); + return address; } Future _addNewContact(ContactCore newContact) async { @@ -161,39 +157,40 @@ mixin WalletNetworkImpl on WalletCryptoImpl, WalletStorageImpl, MasterKeyImpl { await _saveNetwork(currentNetwork); } - Future _clenCustomKeysAccount( - NetworkAccountCore account, List existKeys) async { - final signers = existKeys.map((e) => e.publicKey).toList(); + List _notAccessAddresses( + NetworkAccountCore account, List existKeys) { + final signers = + List.unmodifiable(existKeys.map((e) => e.id).toList()); List removeList = []; - for (final a in account.addresses) { - if (a.keyIndex is Bip32AddressIndex) continue; - List pubKyes = []; - if (a.multiSigAccount) { - final multiSigAccount = a as MultiSigCryptoAccountAddress; - bool hasImportedKey = multiSigAccount.keyDetails - .where((element) => element.$2 is ImportedAddressIndex) - .isEmpty; - if (hasImportedKey) continue; + for (final address in account.addresses) { + if (address.multiSigAccount) { + final multiSigAccount = address as MultiSigCryptoAccountAddress; + for (final i in multiSigAccount.keyDetails) { + final key = i.$2 as Bip32AddressIndex; + if (!key.isImportedKey) continue; + if (signers.contains(key.importedKeyId)) continue; + removeList.add(address); + break; + } } else { - pubKyes.addAll(a.signers); - } - for (final s in pubKyes) { - if (signers.contains(s)) continue; - removeList.add(a); - break; + final key = address.keyIndexes as Bip32AddressIndex; + if (!key.isImportedKey) continue; + if (signers.contains(key.importedKeyId)) continue; + removeList.add(address); } } - for (final r in removeList) { - try { - account.removeAccount(r); - } on WalletException { - continue; - } - } - if (removeList.isNotEmpty) { - await _saveAccount(_appChains.fromAccount(account)); + return removeList; + } + + Future _cleanUpAccount( + {required NetworkAccountCore account, + required List existKeys}) async { + final removedAddresses = _notAccessAddresses(account, existKeys); + if (removedAddresses.isEmpty) return; + for (final address in removedAddresses) { + MethodCaller.nullOnException(() => account.removeAccount(address)); } - return account; + await _saveAccount(_appChains.fromAccount(account)); } final Cancelable _balanceUpdaterCancelable = Cancelable(); diff --git a/mrt_wallet/lib/provider/wallet/signer/signer_impl.dart b/mrt_wallet/lib/provider/wallet/signer/signer_impl.dart index eb6bd5d1..ed4b82ba 100644 --- a/mrt_wallet/lib/provider/wallet/signer/signer_impl.dart +++ b/mrt_wallet/lib/provider/wallet/signer/signer_impl.dart @@ -41,9 +41,10 @@ mixin Signer on MasterKeyImpl { {required RippleSigningRequest request, required List password}) { if (!request.needMultiSignature) { final keyIndex = request.signers.first; - request.transaction.signingPubKey = - RippleUtils.toRipplePublicKey(request.publicKeys.first); final prv = _getPrivateKey(password, keyIndex); + request.transaction.signingPubKey = RippleUtils.toRipplePublicKey( + BytesUtils.toHexString(prv.publicKey.compressed)); + final xrpPrivateKey = XRPPrivateKey.fromBytes(prv.privateKey.raw, algorithm: XRPKeyAlgorithm.values .firstWhere((element) => element.curveType == prv.curveType)); @@ -127,7 +128,6 @@ mixin Signer on MasterKeyImpl { SolanaTransaction _signSolanaTransaction( {required SolanaSigningRequest request, required List password}) { - WalletLogging.print("signers len ${request.signers.length}"); for (int i = 0; i < request.signers.length; i++) { final signier = request.signers.elementAt(i); final addr = request.addresses.elementAt(i).networkAddress; @@ -162,32 +162,29 @@ mixin Signer on MasterKeyImpl { ADATransaction _signCardanoTransaction( {required CardanoSigningRequest request, required List password}) { - try { - return request.transaction.signAndBuildTransaction( - ({required address, required digest}) { - final signer = request.addresses.indexWhere( - (element) => element.address.toAddress == address.address); - if (signer < -1) { - throw MessageException("Signer account does not found.", - details: {"address": address.address}); - } - final ICardanoAddress addr = - request.addresses.elementAt(signer) as ICardanoAddress; - final keyIndex = request.signers.elementAt(signer); - final bipKey = _getPrivateKey(password, keyIndex, - seedType: addr.addressDetails.seedGeneration); - final adaPrivateKey = AdaPrivateKey.fromBytes(bipKey.privateKey.raw); - if (address.addressType == ADAAddressType.byron) { - return adaPrivateKey.createBootstrapWitness( - digest: digest, - address: address as ADAByronAddress, - chainCode: bipKey.chainCode.toBytes()); - } - return adaPrivateKey.createSignatureWitness(digest); - }, - ); - } catch (e) { - rethrow; - } + return request.transaction.signAndBuildTransaction( + ({required address, required digest}) { + final int addressIndex = request.addresses.indexWhere((element) => + element.networkAddress == address || + element.rewardAddress == address); + if (addressIndex < 0) { + throw MessageException("Signer account does not found.", + details: {"address": address.address}); + } + final keyIndex = request.signers.elementAt(addressIndex); + final signerAddress = request.addresses.elementAt(addressIndex); + final bipKey = _getPrivateKey(password, keyIndex, + seedType: signerAddress.keyIndex.seedGeneration); + final adaPrivateKey = AdaPrivateKey.fromBytes(bipKey.privateKey.raw); + if (address.addressType == ADAAddressType.byron) { + return adaPrivateKey.createBootstrapWitness( + digest: digest, + address: address as ADAByronAddress, + chainCode: bipKey.chainCode.toBytes()); + } + + return adaPrivateKey.createSignatureWitness(digest); + }, + ); } } diff --git a/mrt_wallet/lib/provider/wallet/status/status_impl.dart b/mrt_wallet/lib/provider/wallet/status/status_impl.dart index 110c6ab3..b274f5bb 100644 --- a/mrt_wallet/lib/provider/wallet/status/status_impl.dart +++ b/mrt_wallet/lib/provider/wallet/status/status_impl.dart @@ -41,12 +41,12 @@ mixin WalletStatusImpl on _WalletCore { Future _deriveNewAccount( NewAccountParams newAccountParams) async { - if (!network.coins.contains(newAccountParams.coin)) { - throw WalletExceptionConst.incorrectNetwork; - } if (newAccountParams.isMultiSig) { return await _addNewAccountToNetwork(newAccountParams, const []); } + if (!network.coins.contains(newAccountParams.coin)) { + throw WalletExceptionConst.incorrectNetwork; + } WalletMasterKeys keys = _fromMemoryStorage(_password!, _massterKey!); final int derivedKeys = keys.derivedKeys.length; final CryptoAccountAddress newAccount; @@ -54,22 +54,17 @@ mixin WalletStatusImpl on _WalletCore { if (newAccountParams is CardanoNewAddressParams) { final cardanoDeriveResult = _deriveCardanoNewAddress(newAccountParams, keys); - final cardanoAcc = cardanoDeriveResult.$1; - if (cardanoDeriveResult.$2 != null) { - keys = keys.addDerivedKey(cardanoDeriveResult.$2!); - } - if (cardanoDeriveResult.$3 != null) { - keys = keys.addDerivedKey(cardanoDeriveResult.$3!); - } newAccount = await _addNewAccountToNetwork( - cardanoAcc, cardanoAcc.addressDetails!.publicKey); + cardanoDeriveResult, + cardanoDeriveResult.addressDetails!.publicKey, + ); } else { - final derivedResult = _getDerivedKey(newAccountParams, keys); - final derivedBip = derivedResult.$1; - newAccount = await _addNewAccountToNetwork( - newAccountParams, derivedBip.publicKey.compressed); - if (derivedResult.$2 != null) { - keys = keys.addDerivedKey(derivedResult.$2!); + try { + final derivedResult = keys.toKey(newAccountParams.deriveIndex); + newAccount = await _addNewAccountToNetwork( + newAccountParams, derivedResult.publicKey.compressed); + } catch (e) { + rethrow; } } if (derivedKeys != keys.derivedKeys.length) { @@ -103,7 +98,8 @@ mixin WalletStatusImpl on _WalletCore { final encrypt = await _forStorage(key, pw); await _setupMasterKey(encrypt, pw); for (final i in _appChains.networks.values) { - await _clenCustomKeysAccount(i.account, _massterKey!.customKeys); + await _cleanUpAccount( + account: i.account, existKeys: _massterKey!.customKeys); } await _writeWallet(encrypt, _toStorageChecksum()); @@ -117,18 +113,11 @@ mixin WalletStatusImpl on _WalletCore { await _login(password); } - Future _setupBackupV2(WalletBackupCore backup, String password) async { + Future _setupBackup(WalletBackupCore backup, String password) async { try { backup as WalletBackupV2; BlockchainUtils.validateMnemonic(backup.masterKeys.mnemonic.toStr()); - for (final i in backup.chains) { - for (final b in i.account.addresses) { - final validate = _validateBcakupAccounts(backup.masterKeys, b); - if (!validate) { - throw WalletExceptionConst.invalidBackup; - } - } - } + await _setup(backup.masterKeys, password); for (final i in backup.chains) { await _saveAccount(i); @@ -236,4 +225,45 @@ mixin WalletStatusImpl on _WalletCore { return SecretStorageCompute.encrypt(backup.toCbor().toCborHex(), password, encoding: encoding); } + + Future> _accsess( + WalletAccsessType accsessType, String password, + {CryptoAccountAddress? account, String? accountId}) async { + if (accsessType.isAccsessKey && account == null && accountId == null) { + throw WalletException.invalidArgruments(["CryptoAccountAddress", "null"]); + } + + if (accsessType.isAccsessKey) { + if (account != null) { + final accountKeys = account.keyIndexes.map((e) { + final key = _getPrivateKey(_validatePassword(password), e); + return AccessPrivateKeyResponse.fromBip32( + account: key, + coin: account.keyIndex.currencyCoin, + keyName: e.name); + }).toList(); + _getPrivateKey(_validatePassword(password), account.keyIndex); + return accountKeys; + } else { + final importedKey = + _getImportedKeyFromId(_validatePassword(password), accountId!); + return [importedKey]; + } + } else if (accsessType == WalletAccsessType.seed) { + final mnemoic = await _unlockMnemonic(password); + return [AccessMnemonicResponse(mnemoic)]; + } else { + _validatePassword(password); + return [AccessFakeResponse()]; + } + } + + List _getAccountPubKys( + {required CryptoAccountAddress account}) { + return account.keyIndexes.map((e) { + final key = _getPrivateKey(_password!, e); + return AccessPubliKeyResponse.fromBip32( + account: key, coin: account.keyIndex.currencyCoin, keyName: e.name); + }).toList(); + } } diff --git a/mrt_wallet/lib/provider/wallet/wallet_provider.dart b/mrt_wallet/lib/provider/wallet/wallet_provider.dart index 5313d849..9bfa3567 100644 --- a/mrt_wallet/lib/provider/wallet/wallet_provider.dart +++ b/mrt_wallet/lib/provider/wallet/wallet_provider.dart @@ -3,6 +3,7 @@ library wallet_provier; import 'dart:async'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/bip/bip/bip32/base/bip32_base.dart'; +import 'package:blockchain_utils/bip/bip/conf/bip_coins.dart'; import 'package:blockchain_utils/bip/mnemonic/mnemonic.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:cosmos_sdk/cosmos_sdk.dart'; @@ -13,9 +14,8 @@ import 'package:mrt_wallet/future/pages/wallet_pages/security_pages/security.dar import 'package:mrt_wallet/future/widgets/custom_widgets.dart'; import 'package:mrt_wallet/models/serializable/serializable.dart'; import 'package:mrt_wallet/models/wallet_models/address/network_address/bitcoin/bitcoin_multi_sig_core.dart'; -import 'package:mrt_wallet/models/wallet_models/address/network_address/cardano/cardano.dart'; import 'package:mrt_wallet/models/wallet_models/keys/encrypted_derived_key.dart'; -import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/cardano_address_details.dart'; +import 'package:mrt_wallet/models/wallet_models/network/custom/cardano/address_details.dart'; import 'package:mrt_wallet/provider/api/api_provider.dart'; import 'package:mrt_wallet/app/utility/lifecycle_listener/life_cycle_tracker.dart'; import 'package:mrt_wallet/provider/wallet/constant/constant.dart'; diff --git a/mrt_wallet/macos/Runner.xcodeproj/project.pbxproj b/mrt_wallet/macos/Runner.xcodeproj/project.pbxproj index 7d623442..a772a8b7 100644 --- a/mrt_wallet/macos/Runner.xcodeproj/project.pbxproj +++ b/mrt_wallet/macos/Runner.xcodeproj/project.pbxproj @@ -575,7 +575,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = LNA7QWNNXZ; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "MRT Wallet"; @@ -584,7 +584,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 0.0.2; + MARKETING_VERSION = 0.0.3; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; @@ -714,7 +714,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = LNA7QWNNXZ; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "MRT Wallet"; @@ -723,7 +723,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 0.0.2; + MARKETING_VERSION = 0.0.3; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -741,7 +741,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = LNA7QWNNXZ; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "MRT Wallet"; @@ -750,7 +750,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 0.0.2; + MARKETING_VERSION = 0.0.3; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; diff --git a/mrt_wallet/pubspec.lock b/mrt_wallet/pubspec.lock index 59a03855..23e0956b 100644 --- a/mrt_wallet/pubspec.lock +++ b/mrt_wallet/pubspec.lock @@ -275,10 +275,10 @@ packages: dependency: "direct main" description: name: on_chain - sha256: "3b43818c59437994bda87f1d72fe29a20c8bfc6811b16f41c97c88c77a42a1f5" + sha256: ccfd3f2ddd4e41db6f833c31bebf3100bd685827d6d81ca7c8c68f1af673aae2 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.2.0" package_config: dependency: transitive description: diff --git a/mrt_wallet/pubspec.yaml b/mrt_wallet/pubspec.yaml index 206ab2ac..294a23d2 100644 --- a/mrt_wallet/pubspec.yaml +++ b/mrt_wallet/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 5.1.0+7 +version: 5.2.0+8 homepage: "https://github.com/mrtnetwork/mrtwallet" repository: "https://github.com/mrtnetwork/mrtwallet" @@ -48,7 +48,9 @@ dependencies: blockchain_utils: ^2.1.2 bitcoin_base: ^4.2.1 xrpl_dart: ^4.1.2 - on_chain: ^3.1.0 + on_chain: ^3.2.0 + # on_chain: + # path: ../../on_chain cosmos_sdk: ^0.0.5 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons.