Repository: appditto/blaise_wallet_flutter Branch: master Commit: a6891caafff3 Files: 330 Total size: 2.1 MB Directory structure: gitextract_ckzg3n8o/ ├── .github/ │ └── workflows/ │ ├── ci.yml │ ├── deploy_beta.yml │ ├── deploy_release.yml │ ├── deploy_release_android.yml │ └── deploy_release_ios.yml ├── .gitignore ├── LICENSE ├── README.md ├── android/ │ ├── Gemfile │ ├── app/ │ │ ├── build.gradle │ │ ├── google-services.json │ │ ├── proguard-rules.pro │ │ └── src/ │ │ ├── debug/ │ │ │ └── AndroidManifest.xml │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── appditto/ │ │ │ │ └── blaise/ │ │ │ │ ├── LegacyStorage.java │ │ │ │ ├── MainActivity.java │ │ │ │ ├── MultidexApplication.java │ │ │ │ └── Vault.java │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ └── launch_background.xml │ │ │ ├── drawable-v24/ │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── mipmap-anydpi-v26/ │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── values/ │ │ │ │ └── styles.xml │ │ │ └── xml/ │ │ │ └── network_security_config.xml │ │ └── profile/ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── fastlane/ │ │ ├── Appfile │ │ ├── Fastfile │ │ ├── flutter_build.sh │ │ ├── flutter_test.sh │ │ └── metadata/ │ │ └── android/ │ │ └── en-US/ │ │ ├── changelogs/ │ │ │ ├── 1.txt │ │ │ ├── 17.txt │ │ │ ├── 18.txt │ │ │ ├── 20.txt │ │ │ ├── 22.txt │ │ │ ├── 23.txt │ │ │ └── 24.txt │ │ ├── full_description.txt │ │ ├── short_description.txt │ │ ├── title.txt │ │ └── video.txt │ ├── gradle/ │ │ └── wrapper/ │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ └── settings.gradle ├── assets/ │ ├── animation_get_account.flr │ ├── animation_get_account_copper.flr │ ├── animation_get_account_dark.flr │ ├── animation_name_change.flr │ ├── animation_name_change_copper.flr │ ├── animation_name_change_dark.flr │ ├── animation_sale.flr │ ├── animation_sale_copper.flr │ ├── animation_sale_dark.flr │ ├── animation_search.flr │ ├── animation_send.flr │ ├── animation_send_copper.flr │ ├── animation_send_dark.flr │ ├── animation_transfer.flr │ ├── animation_transfer_copper.flr │ ├── animation_transfer_dark.flr │ ├── animation_welcome.flr │ ├── animation_welcome_copper.flr │ ├── animation_welcome_dark.flr │ └── country_phone_map.json ├── bin/ │ ├── arb_to_pojson.py │ ├── pojson_to_arb.py │ ├── poupdate.py │ └── settings.py.example ├── ci/ │ ├── get_version.sh │ ├── tag_version.sh │ └── upload_android_github.sh ├── ios/ │ ├── Flutter/ │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ ├── Flutter.podspec │ │ └── Release.xcconfig │ ├── Gemfile │ ├── Podfile │ ├── Runner/ │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── LaunchImage.imageset/ │ │ │ ├── Contents.json │ │ │ └── README.md │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── GoogleService-Info.plist │ │ ├── Info.plist │ │ ├── Runner-Bridging-Header.h │ │ ├── Runner.entitlements │ │ ├── clipboard.swift │ │ └── main.m │ ├── Runner.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── IDEWorkspaceChecks.plist │ └── fastlane/ │ ├── Appfile │ ├── Deliverfile │ ├── Fastfile │ ├── Matchfile │ ├── flutter_build.sh │ ├── flutter_test.sh │ └── metadata/ │ ├── copyright.txt │ ├── en-US/ │ │ ├── apple_tv_privacy_policy.txt │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ ├── primary_category.txt │ ├── primary_first_sub_category.txt │ ├── primary_second_sub_category.txt │ ├── secondary_category.txt │ ├── secondary_first_sub_category.txt │ └── secondary_second_sub_category.txt ├── lib/ │ ├── appstate_container.dart │ ├── bus/ │ │ ├── authenticated_event.dart │ │ ├── conn_status_event.dart │ │ ├── contact_added_event.dart │ │ ├── contact_modified_event.dart │ │ ├── contact_removed_event.dart │ │ ├── daemon_changed_event.dart │ │ ├── disable_lock_timeout_event.dart │ │ ├── events.dart │ │ ├── new_operation_event.dart │ │ ├── payload_changed_event.dart │ │ ├── price_event.dart │ │ ├── subscribe_event.dart │ │ └── update_history_event.dart │ ├── constants.dart │ ├── l10n/ │ │ ├── intl_ar.arb │ │ ├── intl_ca.arb │ │ ├── intl_de.arb │ │ ├── intl_en.arb │ │ ├── intl_es.arb │ │ ├── intl_messages.arb │ │ ├── intl_tr.arb │ │ ├── intl_zh-Hans.arb │ │ ├── messages_all.dart │ │ ├── messages_ar.dart │ │ ├── messages_ca.dart │ │ ├── messages_de.dart │ │ ├── messages_en.dart │ │ ├── messages_es.dart │ │ ├── messages_messages.dart │ │ ├── messages_tr.dart │ │ └── messages_zh-Hans.dart │ ├── localization.dart │ ├── main.dart │ ├── model/ │ │ ├── authentication_method.dart │ │ ├── available_currency.dart │ │ ├── available_languages.dart │ │ ├── available_themes.dart │ │ ├── db/ │ │ │ ├── appdb.dart │ │ │ ├── contact.dart │ │ │ └── contact.g.dart │ │ ├── lock_timeout.dart │ │ ├── notification_enabled.dart │ │ ├── setting_item.dart │ │ └── unlock_setting.dart │ ├── network/ │ │ ├── http_client.dart │ │ ├── model/ │ │ │ ├── base_request.dart │ │ │ ├── request/ │ │ │ │ ├── borrow_request.dart │ │ │ │ ├── borrow_request.g.dart │ │ │ │ ├── fcm_delete_account_request.dart │ │ │ │ ├── fcm_delete_account_request.g.dart │ │ │ │ ├── fcm_update_bulk_request.dart │ │ │ │ ├── fcm_update_bulk_request.g.dart │ │ │ │ ├── fcm_update_request.dart │ │ │ │ ├── fcm_update_request.g.dart │ │ │ │ ├── freepasa_get_request.dart │ │ │ │ ├── freepasa_get_request.g.dart │ │ │ │ ├── freepasa_verify_request.dart │ │ │ │ ├── freepasa_verify_request.g.dart │ │ │ │ ├── getborrowed_request.dart │ │ │ │ ├── getborrowed_request.g.dart │ │ │ │ ├── subscribe_request.dart │ │ │ │ └── subscribe_request.g.dart │ │ │ ├── request_item.dart │ │ │ └── response/ │ │ │ ├── accounts_response_borrowed.dart │ │ │ ├── accounts_response_borrowed.g.dart │ │ │ ├── borrow_response.dart │ │ │ ├── borrow_response.g.dart │ │ │ ├── error_response.dart │ │ │ ├── error_response.g.dart │ │ │ ├── getborrowed_response.dart │ │ │ ├── getborrowed_response.g.dart │ │ │ ├── price_response.dart │ │ │ ├── price_response.g.dart │ │ │ ├── subscribe_response.dart │ │ │ └── subscribe_response.g.dart │ │ └── ws_client.dart │ ├── service_locator.dart │ ├── store/ │ │ ├── account/ │ │ │ ├── account.dart │ │ │ └── account.g.dart │ │ └── wallet/ │ │ ├── wallet.dart │ │ └── wallet.g.dart │ ├── themes.dart │ ├── ui/ │ │ ├── account/ │ │ │ ├── account.dart │ │ │ ├── operation_details_sheet.dart │ │ │ ├── operation_sheet.dart │ │ │ ├── other_operations/ │ │ │ │ ├── change_name/ │ │ │ │ │ ├── change_name_sheet.dart │ │ │ │ │ ├── changed_name_sheet.dart │ │ │ │ │ └── changing_name_sheet.dart │ │ │ │ ├── delist_for_sale/ │ │ │ │ │ ├── delisted_for_sale.dart │ │ │ │ │ └── delisting_for_sale.dart │ │ │ │ ├── list_for_sale/ │ │ │ │ │ ├── list_for_sale_sheet.dart │ │ │ │ │ ├── listed_for_sale_sheet.dart │ │ │ │ │ └── listing_for_sale_sheet.dart │ │ │ │ ├── private_sale/ │ │ │ │ │ ├── create_private_sale_sheet.dart │ │ │ │ │ ├── created_private_sale_sheet.dart │ │ │ │ │ └── creating_private_sale_sheet.dart │ │ │ │ └── transfer_account/ │ │ │ │ ├── transfer_account_sheet.dart │ │ │ │ ├── transferred_account_sheet.dart │ │ │ │ └── transferring_account_sheet.dart │ │ │ ├── receive/ │ │ │ │ ├── receive_sheet.dart │ │ │ │ └── request_sheet.dart │ │ │ └── send/ │ │ │ ├── send_sheet.dart │ │ │ ├── sending_sheet.dart │ │ │ └── sent_sheet.dart │ │ ├── intro/ │ │ │ ├── intro_backup_confirm.dart │ │ │ ├── intro_decrypt_and_import_private_key.dart │ │ │ ├── intro_import_private_key.dart │ │ │ ├── intro_new_private_key.dart │ │ │ ├── intro_security_first.dart │ │ │ └── intro_welcome.dart │ │ ├── lockscreen/ │ │ │ └── lock_screen.dart │ │ ├── overview/ │ │ │ ├── buy_account_sheet.dart │ │ │ ├── confirm_free_account_sheet.dart │ │ │ ├── get_account_sheet.dart │ │ │ ├── get_free_account_sheet.dart │ │ │ ├── overview.dart │ │ │ └── public_key_overview_sheet.dart │ │ ├── settings/ │ │ │ ├── backup_private_key/ │ │ │ │ ├── backup_private_key_sheet.dart │ │ │ │ ├── encrypt_private_key_sheet.dart │ │ │ │ ├── encrypted_private_key_sheet.dart │ │ │ │ └── unencrypted_private_key_sheet.dart │ │ │ ├── change_daemon_sheet.dart │ │ │ ├── contacts/ │ │ │ │ ├── add_contact_sheet.dart │ │ │ │ ├── contact_detail_sheet.dart │ │ │ │ └── contacts.dart │ │ │ ├── public_key_sheet.dart │ │ │ ├── security.dart │ │ │ └── settings.dart │ │ ├── util/ │ │ │ ├── app_icons.dart │ │ │ ├── formatters.dart │ │ │ ├── margins.dart │ │ │ ├── routes.dart │ │ │ └── text_styles.dart │ │ └── widgets/ │ │ ├── account_card.dart │ │ ├── app_text_field.dart │ │ ├── buttons.dart │ │ ├── error_container.dart │ │ ├── fee_container.dart │ │ ├── operation_list_item.dart │ │ ├── overlay_dialog.dart │ │ ├── payload.dart │ │ ├── pin_screen.dart │ │ ├── placeholder_account_card.dart │ │ ├── placeholder_operation_list_item.dart │ │ ├── reactive_refresh.dart │ │ ├── settings_list_item.dart │ │ ├── sheets.dart │ │ ├── svg_repaint.dart │ │ ├── tap_outside_unfocus.dart │ │ └── webview.dart │ └── util/ │ ├── authentication.dart │ ├── haptic_util.dart │ ├── number_util.dart │ ├── pascal_util.dart │ ├── salsa20crypt.dart │ ├── sharedprefs_util.dart │ ├── ui_util.dart │ ├── user_data_util.dart │ └── vault.dart ├── pubspec.yaml └── test/ ├── common/ │ ├── base58_test.dart │ ├── coding/ │ │ └── pascal/ │ │ ├── accountname_coder_test.dart │ │ ├── accountnumber_coder_test.dart │ │ ├── currency_coder_test.dart │ │ ├── keys/ │ │ │ ├── curve_coder_test.dart │ │ │ ├── privatekey_coder_test.dart │ │ │ └── publickey_coder_test.dart │ │ ├── ophash_coder_test.dart │ │ └── optype_coder_test.dart │ ├── fixtures/ │ │ ├── operation_hash.dart │ │ ├── privatekey.dart │ │ └── publickey.dart │ ├── model/ │ │ ├── AccountName_test.dart │ │ ├── AccountNumber_test.dart │ │ ├── Currency_test.dart │ │ ├── OperationHash_test.dart │ │ └── keys/ │ │ ├── Curves_test.dart │ │ ├── KeyPair_test.dart │ │ ├── PrivateKey_test.dart │ │ └── PublicKey_test.dart │ ├── sha_test.dart │ └── util_test.dart ├── crypto/ │ ├── encrypt/ │ │ ├── aes/ │ │ │ └── cbcpkcs7_test.dart │ │ └── pascal/ │ │ ├── ecies_crypt_test.dart │ │ ├── kdf_test.dart │ │ └── privatekey_crypt_test.dart │ ├── fixtures/ │ │ ├── ecies.dart │ │ └── privatekey.dart │ └── keys_test.dart ├── json_rpc/ │ └── model/ │ ├── pascal_account_test.dart │ ├── pascal_block_test.dart │ ├── pascal_operation_test.dart │ └── request/ │ ├── executeoperations_request_test.dart │ ├── findaccounts_request_test.dart │ ├── findoperation_request_test.dart │ ├── getaccount_request_test.dart │ ├── getaccountoperations_test.dart │ ├── getblock_request_test.dart │ ├── getblockoperation_request_test.dart │ ├── getblockoperations_test.dart │ ├── getblocks_request_test.dart │ ├── getpendings_request_test.dart │ └── getwalletaccounts_request_test.dart └── signing/ └── operations/ ├── buyaccount_operation_test.dart ├── changeaccountinfo_operation_test.dart ├── changekey_operation_test.dart ├── changekeysigned_operation_test.dart ├── delist_forsale_operation_test.dart ├── list_forsale_operation_test.dart └── transaction_operation_test.dart ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: push: branches: [ master ] jobs: run_tests: name: Run tests runs-on: self-hosted steps: - uses: actions/checkout@master - name: Run tests run: | flutter pub get flutter test build_android: name: Build android app needs: run_tests runs-on: self-hosted steps: - uses: actions/checkout@master - name: Build artifacts working-directory: android env: FLUTTER_PATH: /Users/adapps/flutter ANDROID_SDK_PATH: /Users/adapps/Library/Android/Sdk GEM_HOME: ~/.gems run: | echo flutter.sdk=${{ env.FLUTTER_PATH }} > local.properties echo sdk.dir=${{ env.ANDROID_SDK_PATH }} >> local.properties echo flutter.buildMode=release >> local.properties echo storeFile=${{ secrets.ANDROID_KEYSTORE_PATH }} > key.properties echo keyAlias=${{ secrets.ANDROID_KEY_ALIAS }} >> key.properties echo keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }} >> key.properties echo storePassword=${{ secrets.ANDROID_KEY_STORE_PASSWORD }} >> key.properties /usr/local/bin/fastlane build_android production:true rm -f key.properties - name: Upload APK if: success() uses: actions/upload-artifact@v1 with: name: android-apk path: build/app/outputs/apk/release/app-release.apk - name: Upload Bundle if: success() uses: actions/upload-artifact@v1 with: name: android-aab path: build/app/outputs/bundle/release/app-release.aab build_ios: name: Build iOS app needs: run_tests runs-on: self-hosted steps: - uses: actions/checkout@master - name: Build artifacts working-directory: ios env: FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} TEMP_KEYCHAIN_NAME: ${{ secrets.TEMP_KEYCHAIN_NAME }} TEMP_KEYCHAIN_PASSWORD: ${{ secrets.TEMP_KEYCHAIN_PASSWORD }} API_KEY_ID: ${{ secrets.FASTLANE_API_KEY_ID }} API_KEY_ISSUER: ${{ secrets.FASTLANE_API_KEY_ISSUER }} API_KEY_FILEPATH: ${{ secrets.FASTLANE_API_KEY_FILEPATH }} GEM_HOME: ~/.gems run: | /usr/local/bin/fastlane build_ios - name: Upload IPA if: success() uses: actions/upload-artifact@v1 with: name: ios-ipa path: ios/Runner.ipa ================================================ FILE: .github/workflows/deploy_beta.yml ================================================ name: DEPLOY_BETA on: push: tags: - 'v*-beta' jobs: run_tests: name: Run tests runs-on: self-hosted steps: - uses: actions/checkout@master - name: Run tests run: | flutter pub get flutter test build_android: name: Build android app needs: run_tests runs-on: self-hosted steps: - uses: actions/checkout@master - name: Build artifacts working-directory: android env: FLUTTER_PATH: /Users/adapps/flutter ANDROID_SDK_PATH: /Users/adapps/Library/Android/Sdk GEM_HOME: ~/.gems run: | echo flutter.sdk=${{ env.FLUTTER_PATH }} > local.properties echo sdk.dir=${{ env.ANDROID_SDK_PATH }} >> local.properties echo flutter.buildMode=release >> local.properties echo storeFile=${{ secrets.ANDROID_KEYSTORE_PATH }} > key.properties echo keyAlias=${{ secrets.ANDROID_KEY_ALIAS }} >> key.properties echo keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }} >> key.properties echo storePassword=${{ secrets.ANDROID_KEY_STORE_PASSWORD }} >> key.properties /usr/local/bin/fastlane build_android production:true rm -f key.properties - name: Upload APK if: success() uses: actions/upload-artifact@v1 with: name: android-apk path: build/app/outputs/apk/release/app-release.apk - name: Upload Bundle if: success() uses: actions/upload-artifact@v1 with: name: android-aab path: build/app/outputs/bundle/release/app-release.aab build_ios: name: Build iOS app needs: run_tests runs-on: self-hosted steps: - uses: actions/checkout@master - name: Build artifacts working-directory: ios env: FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} TEMP_KEYCHAIN_NAME: ${{ secrets.TEMP_KEYCHAIN_NAME }} TEMP_KEYCHAIN_PASSWORD: ${{ secrets.TEMP_KEYCHAIN_PASSWORD }} API_KEY_ID: ${{ secrets.FASTLANE_API_KEY_ID }} API_KEY_ISSUER: ${{ secrets.FASTLANE_API_KEY_ISSUER }} API_KEY_FILEPATH: ${{ secrets.FASTLANE_API_KEY_FILEPATH }} GEM_HOME: ~/.gems run: | /usr/local/bin/fastlane build_ios - name: Upload IPA if: success() uses: actions/upload-artifact@v1 with: name: ios-ipa path: ios/Runner.ipa deploy_android: name: Deploy android internal needs: build_android runs-on: self-hosted steps: - uses: actions/checkout@master - name: Download AAB uses: actions/download-artifact@v2 with: name: android-aab - name: Set workspace in env run: echo "workspace=$GITHUB_WORKSPACE" >> $GITHUB_ENV - name: Deploy android if: success() working-directory: android env: AAB_PATH: ${{ env.workspace }}/app-release.aab GEM_HOME: ~/.gems run: | /usr/local/bin/fastlane deploy_android internal:true deploy_ios: name: Deploy iOS testflight needs: build_ios runs-on: self-hosted steps: - uses: actions/checkout@master - name: Download IPA uses: actions/download-artifact@v2 with: name: ios-ipa - name: Set workspace in env run: echo "workspace=$GITHUB_WORKSPACE" >> $GITHUB_ENV - name: Deploy iOS if: success() working-directory: ios env: IPA_PATH: ${{ env.workspace }}/Runner.ipa FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} API_KEY_ID: ${{ secrets.FASTLANE_API_KEY_ID }} API_KEY_ISSUER: ${{ secrets.FASTLANE_API_KEY_ISSUER }} API_KEY_FILEPATH: ${{ secrets.FASTLANE_API_KEY_FILEPATH }} GEM_HOME: ~/.gems run: | /usr/local/bin/fastlane deploy_ios testflight:true ================================================ FILE: .github/workflows/deploy_release.yml ================================================ name: DEPLOY_RELEASE on: push: tags: - 'v*-release' jobs: run_tests: name: Run tests runs-on: self-hosted steps: - uses: actions/checkout@master - name: Run tests run: | flutter pub get flutter test build_android: name: Build android app needs: run_tests runs-on: self-hosted steps: - uses: actions/checkout@master - name: Build artifacts working-directory: android env: FLUTTER_PATH: /Users/adapps/flutter ANDROID_SDK_PATH: /Users/adapps/Library/Android/Sdk GEM_HOME: ~/.gems run: | echo flutter.sdk=${{ env.FLUTTER_PATH }} > local.properties echo sdk.dir=${{ env.ANDROID_SDK_PATH }} >> local.properties echo flutter.buildMode=release >> local.properties echo storeFile=${{ secrets.ANDROID_KEYSTORE_PATH }} > key.properties echo keyAlias=${{ secrets.ANDROID_KEY_ALIAS }} >> key.properties echo keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }} >> key.properties echo storePassword=${{ secrets.ANDROID_KEY_STORE_PASSWORD }} >> key.properties /usr/local/bin/fastlane build_android production:true rm -f key.properties - name: Upload APK if: success() uses: actions/upload-artifact@v1 with: name: android-apk path: build/app/outputs/apk/release/app-release.apk - name: Upload Bundle if: success() uses: actions/upload-artifact@v1 with: name: android-aab path: build/app/outputs/bundle/release/app-release.aab build_ios: name: Build iOS app needs: run_tests runs-on: self-hosted steps: - uses: actions/checkout@master - name: Build artifacts working-directory: ios env: FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} TEMP_KEYCHAIN_NAME: ${{ secrets.TEMP_KEYCHAIN_NAME }} TEMP_KEYCHAIN_PASSWORD: ${{ secrets.TEMP_KEYCHAIN_PASSWORD }} API_KEY_ID: ${{ secrets.FASTLANE_API_KEY_ID }} API_KEY_ISSUER: ${{ secrets.FASTLANE_API_KEY_ISSUER }} API_KEY_FILEPATH: ${{ secrets.FASTLANE_API_KEY_FILEPATH }} GEM_HOME: ~/.gems run: | /usr/local/bin/fastlane build_ios - name: Upload IPA if: success() uses: actions/upload-artifact@v1 with: name: ios-ipa path: ios/Runner.ipa deploy_android: name: Deploy android production needs: build_android runs-on: self-hosted steps: - uses: actions/checkout@master - name: Download AAB uses: actions/download-artifact@v2 with: name: android-aab - name: Set workspace in env run: echo "workspace=$GITHUB_WORKSPACE" >> $GITHUB_ENV - name: Deploy android if: success() working-directory: android env: AAB_PATH: ${{ env.workspace }}/app-release.aab GEM_HOME: ~/.gems run: | /usr/local/bin/fastlane deploy_android production:true deploy_ios: name: Deploy iOS production needs: build_ios runs-on: self-hosted steps: - uses: actions/checkout@master - name: Download IPA uses: actions/download-artifact@v2 with: name: ios-ipa - name: Set workspace in env run: echo "workspace=$GITHUB_WORKSPACE" >> $GITHUB_ENV - name: Deploy iOS if: success() working-directory: ios env: IPA_PATH: ${{ env.workspace }}/Runner.ipa FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} API_KEY_ID: ${{ secrets.FASTLANE_API_KEY_ID }} API_KEY_ISSUER: ${{ secrets.FASTLANE_API_KEY_ISSUER }} API_KEY_FILEPATH: ${{ secrets.FASTLANE_API_KEY_FILEPATH }} GEM_HOME: ~/.gems run: | /usr/local/bin/fastlane deploy_ios ================================================ FILE: .github/workflows/deploy_release_android.yml ================================================ name: DEPLOY_RELEASE on: push: tags: - 'v*-release-android' jobs: run_tests: name: Run tests runs-on: self-hosted steps: - uses: actions/checkout@master - name: Run tests run: | flutter pub get flutter test build_android: name: Build android app needs: run_tests runs-on: self-hosted steps: - uses: actions/checkout@master - name: Build artifacts working-directory: android env: FLUTTER_PATH: /Users/adapps/flutter ANDROID_SDK_PATH: /Users/adapps/Library/Android/Sdk GEM_HOME: ~/.gems run: | echo flutter.sdk=${{ env.FLUTTER_PATH }} > local.properties echo sdk.dir=${{ env.ANDROID_SDK_PATH }} >> local.properties echo flutter.buildMode=release >> local.properties echo storeFile=${{ secrets.ANDROID_KEYSTORE_PATH }} > key.properties echo keyAlias=${{ secrets.ANDROID_KEY_ALIAS }} >> key.properties echo keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }} >> key.properties echo storePassword=${{ secrets.ANDROID_KEY_STORE_PASSWORD }} >> key.properties /usr/local/bin/fastlane build_android production:true rm -f key.properties - name: Upload APK if: success() uses: actions/upload-artifact@v1 with: name: android-apk path: build/app/outputs/apk/release/app-release.apk - name: Upload Bundle if: success() uses: actions/upload-artifact@v1 with: name: android-aab path: build/app/outputs/bundle/release/app-release.aab deploy_android: name: Deploy android production needs: build_android runs-on: self-hosted steps: - uses: actions/checkout@master - name: Download AAB uses: actions/download-artifact@v2 with: name: android-aab - name: Set workspace in env run: echo "workspace=$GITHUB_WORKSPACE" >> $GITHUB_ENV - name: Deploy android if: success() working-directory: android env: AAB_PATH: ${{ env.workspace }}/app-release.aab GEM_HOME: ~/.gems run: | /usr/local/bin/fastlane deploy_android production:true ================================================ FILE: .github/workflows/deploy_release_ios.yml ================================================ name: DEPLOY_RELEASE on: push: tags: - 'v*-release-ios' jobs: run_tests: name: Run tests runs-on: self-hosted steps: - uses: actions/checkout@master - name: Run tests run: | flutter pub get flutter test build_ios: name: Build iOS app needs: run_tests runs-on: self-hosted steps: - uses: actions/checkout@master - name: Build artifacts working-directory: ios env: FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} TEMP_KEYCHAIN_NAME: ${{ secrets.TEMP_KEYCHAIN_NAME }} TEMP_KEYCHAIN_PASSWORD: ${{ secrets.TEMP_KEYCHAIN_PASSWORD }} API_KEY_ID: ${{ secrets.FASTLANE_API_KEY_ID }} API_KEY_ISSUER: ${{ secrets.FASTLANE_API_KEY_ISSUER }} API_KEY_FILEPATH: ${{ secrets.FASTLANE_API_KEY_FILEPATH }} GEM_HOME: ~/.gems run: | /usr/local/bin/fastlane build_ios - name: Upload IPA if: success() uses: actions/upload-artifact@v1 with: name: ios-ipa path: ios/Runner.ipa deploy_ios: name: Deploy iOS production needs: build_ios runs-on: self-hosted steps: - uses: actions/checkout@master - name: Download IPA uses: actions/download-artifact@v2 with: name: ios-ipa - name: Set workspace in env run: echo "workspace=$GITHUB_WORKSPACE" >> $GITHUB_ENV - name: Deploy iOS if: success() working-directory: ios env: IPA_PATH: ${{ env.workspace }}/Runner.ipa FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} API_KEY_ID: ${{ secrets.FASTLANE_API_KEY_ID }} API_KEY_ISSUER: ${{ secrets.FASTLANE_API_KEY_ISSUER }} API_KEY_FILEPATH: ${{ secrets.FASTLANE_API_KEY_FILEPATH }} GEM_HOME: ~/.gems run: | /usr/local/bin/fastlane deploy_ios ================================================ FILE: .gitignore ================================================ # Miscellaneous *.class *.lock *.log *.pyc *.swp .DS_Store .atom/ .buildlog/ .history .svn/ # Eclipse project files .classpath .project .metadata .settings # IntelliJ related *.iml *.ipr *.iws .idea/ # Visual Studio Code related .vscode/ # Flutter repo-specific /bin/cache/ /bin/mingit/ /dev/benchmarks/mega_gallery/ /dev/bots/.recipe_deps /dev/bots/android_tools/ /dev/docs/doc/ /dev/docs/flutter.docs.zip /dev/docs/lib/ /dev/docs/pubspec.yaml /packages/flutter/coverage/ version # packages file containing multi-root paths .packages.generated # Flutter/Dart/Pub related **/doc/api/ .dart_tool/ .flutter-plugins .flutter-plugins-dependencies .packages .pub-cache/ .pub/ build/ flutter_*.png linked_*.ds unlinked.ds unlinked_spec.ds # Android related **/android/**/gradle-wrapper.jar **/android/.gradle **/android/captures/ **/android/gradlew **/android/gradlew.bat **/android/local.properties **/android/**/GeneratedPluginRegistrant.java **/android/key.properties *.jks # iOS/XCode related **/ios/**/*.mode1v3 **/ios/**/*.mode2v3 **/ios/**/*.moved-aside **/ios/**/*.pbxuser **/ios/**/*.perspectivev3 **/ios/**/*sync/ **/ios/**/.sconsign.dblite **/ios/**/.tags* **/ios/**/.vagrant/ **/ios/**/DerivedData/ **/ios/**/Icon? **/ios/**/Pods/ **/ios/**/.symlinks/ **/ios/**/profile **/ios/**/xcuserdata **/ios/.generated/ **/ios/Flutter/flutter_export_environment.sh **/ios/Flutter/App.framework **/ios/Flutter/Flutter.framework **/ios/Flutter/Generated.xcconfig **/ios/Flutter/app.flx **/ios/Flutter/app.zip **/ios/Flutter/flutter_assets/ **/ios/ServiceDefinitions.json **/ios/Runner/GeneratedPluginRegistrant.* **/ios/*.ipa **/ios/*.app.dSYM.zip **/ios/flutter_export_environment.sh # Coverage coverage/ # Exceptions to above rules. !**/ios/**/default.mode1v3 !**/ios/**/default.mode2v3 !**/ios/**/default.pbxuser !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages # Translations lib/l10n/*.json bin/settings.py # Fastlane android/fastlane/report.xml ios/fastlane/README.md ios/fastlane/report.xml ios/fastlane/Preview.html ios/fastlane/metadata/review_information ios/fastlane/metadata/trade_representative_contact_information ================================================ FILE: LICENSE ================================================ The Blaise logo, animations, and all assets in the "assets" folder are copyrighted by Appditto LLC and used by permission for this project only. All code is copyrighted by Appditto LLC under the MIT license. Copyright (c) 2019-2022 Appditto LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Blaise - Simple, Sleek & Secure PASCAL Wallet [![GitHub release (latest)](https://img.shields.io/github/v/release/appditto/blaise_wallet_flutter)](https://github.com/appditto/blaise_wallet_flutter/releases) [![License](https://img.shields.io/github/license/appditto/blaise_wallet_flutter)](https://github.com/appditto/blaise_wallet_flutter/blob/master/LICENSE) [[![CI](https://github.com/appditto/blaise_wallet_flutter/workflows/CI/badge.svg)](https://github.com/appditto/blaise_wallet_flutter/actions?query=workflow%3ACI) [![Twitter Follow](https://img.shields.io/twitter/follow/Appditto?style=social)](https://twitter.com/intent/follow?screen_name=Appditto) ## What is Blaise? ![Blaise Hero](https://blaisewallet.com/assets/hero-mockup.png) Blaise is a cross-platform mobile wallet for the PASCAL cryptocurrency. It is written in Dart using [Flutter](https://flutter.io). ![Blaise Borrow](https://blaisewallet.com/assets/blaise-borrow.gif) ![Blaise Send](https://blaisewallet.com/assets/blaise-send.gif) Private keys are stored on the device and never transmitted to the server. Signing and other low-level operations are performed using [PascalDart](https://pub.dev/packages/pascaldart) | Link | Description | | :----- | :------ | [blaisewallet.com](https://blaisewallet.com) | Blaise Homepage [pascalcoin.org](https://pascalcoin.org) | PACAL Cryptocurrency Homepage [appditto.com](https://appditto.com) | Appditto Homepage ## Server Blaise can interact with any PascalCoin Daemon, a high-performance Python server is used for push notifications, periodic price updates, and the PASA purchase APIs. Blaise's backend server source code can be found [here](https://github.com/appditto/blaise_wallet_server) ## Contributing * Fork the repository and clone it to your local machine * Follow the instructions [here](https://flutter.io/docs/get-started/install) to install the Flutter SDK * Setup [Android Studio](https://flutter.io/docs/development/tools/android-studio) or [Visual Studio Code](https://flutter.io/docs/development/tools/vs-code). ## Building Android: `flutter build apk` iOS: `flutter build ios` If you have a connected device or emulator you can run and deploy the app with `flutter run` ## Have a question? If you need any help, feel free to file an issue if you do not manage to find a solution. ## License Blaise is released under the MIT License ### Update translations ``` flutter pub pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/localization.dart flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n \ --no-use-deferred-loading lib/localization.dart lib/l10n/intl_*.arb ``` ================================================ FILE: android/Gemfile ================================================ source "https://rubygems.org" gem "fastlane" ================================================ FILE: android/app/build.gradle ================================================ def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { localPropertiesFile.withReader('UTF-8') { reader -> localProperties.load(reader) } } def flutterRoot = localProperties.getProperty('flutter.sdk') if (flutterRoot == null) { throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") } def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' } def flutterVersionName = localProperties.getProperty('flutter.versionName') if (flutterVersionName == null) { flutterVersionName = '1.0' } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply plugin: 'com.google.gms.google-services' def keystoreProperties = new Properties() def keystorePropertiesFile = rootProject.file('key.properties') if (keystorePropertiesFile.exists()) { keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) } android { compileSdkVersion 31 lintOptions { disable 'InvalidPackage' } defaultConfig { applicationId "com.appditto.blaise" minSdkVersion 19 targetSdkVersion 31 multiDexEnabled true versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } signingConfigs { release { if (keystorePropertiesFile.exists()) { keyAlias keystoreProperties['keyAlias'] keyPassword keystoreProperties['keyPassword'] storeFile file(keystoreProperties['storeFile']) storePassword keystoreProperties['storePassword'] } } } buildTypes { release { if (keystorePropertiesFile.exists()) { signingConfig signingConfigs.release minifyEnabled true useProguard true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } else { signingConfig signingConfigs.debug } } } } flutter { source '../..' } dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.11" implementation "com.bottlerocketstudios:vault:1.4.2" implementation "androidx.multidex:multidex:2.0.0" } ================================================ FILE: android/app/google-services.json ================================================ { "project_info": { "project_number": "786466973238", "firebase_url": "https://blaise-a22fa.firebaseio.com", "project_id": "blaise-a22fa", "storage_bucket": "blaise-a22fa.appspot.com" }, "client": [ { "client_info": { "mobilesdk_app_id": "1:786466973238:android:59d3b6ab52a943d3", "android_client_info": { "package_name": "com.appditto.blaise" } }, "oauth_client": [ { "client_id": "786466973238-luvav6kcq097806661753c4jh98q7dre.apps.googleusercontent.com", "client_type": 3 } ], "api_key": [ { "current_key": "AIzaSyAb7sG6W-UFHg9Fbb5Mx3LaIkE5e3wxrwo" } ], "services": { "appinvite_service": { "other_platform_oauth_client": [ { "client_id": "786466973238-luvav6kcq097806661753c4jh98q7dre.apps.googleusercontent.com", "client_type": 3 } ] } } } ], "configuration_version": "1" } ================================================ FILE: android/app/proguard-rules.pro ================================================ -dontwarn android.** ## Flutter wrapper -keep class io.flutter.app.** { *; } -keep class io.flutter.plugin.** { *; } -keep class io.flutter.util.** { *; } -keep class io.flutter.view.** { *; } -keep class io.flutter.** { *; } -keep class io.flutter.plugins.** { *; } ## Blaise -keep class com.appditto.** { *; } ## QR Scanner -keep class com.apptreesoftware.** { *; } ## Webview plugin -keep class com.flutter_webview_plugin.** { *; } ## Realm -keep class io.realm.** { *; } ## Vault -keep class com.bottlerocketstudios.** { *; } ## File picker -keep class androidx.lifecycle.DefaultLifecycleObserver ================================================ FILE: android/app/src/debug/AndroidManifest.xml ================================================ ================================================ FILE: android/app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: android/app/src/main/java/com/appditto/blaise/LegacyStorage.java ================================================ package com.appditto.blaise; import android.util.Base64; public class LegacyStorage { public String getSecret() { return generateEncryptionKey(); } private String generateEncryptionKey() { if (Vault.getVault().getString(Vault.ENCRYPTION_KEY_NAME, null) == null) { Vault.getVault() .edit() .putString(Vault.ENCRYPTION_KEY_NAME, Base64.encodeToString(Vault.generateKey(), Base64.DEFAULT)) .apply(); } return Vault.getVault().getString(Vault.ENCRYPTION_KEY_NAME, null); } } ================================================ FILE: android/app/src/main/java/com/appditto/blaise/MainActivity.java ================================================ package com.appditto.blaise; import android.os.Bundle; import androidx.annotation.NonNull; import io.flutter.embedding.android.FlutterFragmentActivity; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugins.GeneratedPluginRegistrant; import io.flutter.view.FlutterMain; public class MainActivity extends FlutterFragmentActivity { private static final String CHANNEL = "fappchannel"; @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine); new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL) .setMethodCallHandler( (call, result) -> { if (call.method.equals("getSecret")) { result.success(new LegacyStorage().getSecret()); } else { result.notImplemented(); } } ); } } ================================================ FILE: android/app/src/main/java/com/appditto/blaise/MultidexApplication.java ================================================ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package com.appditto.blaise; import android.util.Base64; import android.app.Activity; import android.app.Application; import androidx.annotation.CallSuper; import android.content.Context; import androidx.multidex.MultiDex; import io.flutter.view.FlutterMain; /** * Flutter implementation of {@link android.app.Application}, managing * application-level global initializations. */ public class MultidexApplication extends Application { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); } @Override @CallSuper public void onCreate() { super.onCreate(); try { Vault.initializeVault(this); generateEncryptionKey(); } catch (Exception e) { } FlutterMain.startInitialization(this); } private Activity mCurrentActivity = null; public Activity getCurrentActivity() { return mCurrentActivity; } public void setCurrentActivity(Activity mCurrentActivity) { this.mCurrentActivity = mCurrentActivity; } /** * generate an encryption key and store in the vault */ private void generateEncryptionKey() { if (Vault.getVault().getString(Vault.ENCRYPTION_KEY_NAME, null) == null) { Vault.getVault() .edit() .putString(Vault.ENCRYPTION_KEY_NAME, Base64.encodeToString(Vault.generateKey(), Base64.DEFAULT)) .apply(); } } } ================================================ FILE: android/app/src/main/java/com/appditto/blaise/Vault.java ================================================ package com.appditto.blaise; import android.content.Context; import com.bottlerocketstudios.vault.SharedPreferenceVault; import com.bottlerocketstudios.vault.SharedPreferenceVaultFactory; import com.bottlerocketstudios.vault.SharedPreferenceVaultRegistry; import java.security.GeneralSecurityException; import java.security.SecureRandom; public class Vault { public static final String ENCRYPTION_KEY_NAME = "key"; private static final String TAG = Vault.class.getSimpleName(); private static final String AUTOMATICALLY_KEYED_PREF_FILE_NAME = "com.appditto.blaise.automaticallyKeyedPref"; private static final String AUTOMATICALLY_KEYED_KEY_FILE_NAME = "com.appditto.blaise.automaticallyKeyedKey"; private static final String AUTOMATICALLY_KEYED_KEY_ALIAS = "com.appditto.blaise.automaticallyKeyed"; private static final int AUTOMATICALLY_KEYED_KEY_INDEX = 3; private static final String AUTOMATICALLY_KEYED_PRESHARED_SECRET = "8.b;(xYpB ================================================ FILE: android/app/src/main/res/drawable-v24/ic_launcher_background.xml ================================================ ================================================ FILE: android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml ================================================ ================================================ FILE: android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml ================================================ ================================================ FILE: android/app/src/main/res/values/styles.xml ================================================ ================================================ FILE: android/app/src/main/res/xml/network_security_config.xml ================================================ explorer.pascalcoin.org ================================================ FILE: android/app/src/profile/AndroidManifest.xml ================================================ ================================================ FILE: android/build.gradle ================================================ buildscript { ext.kotlin_version = '1.4.31' repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:4.1.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.google.gms:google-services:4.3.4' } } allprojects { repositories { google() jcenter() } } rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } subprojects { project.evaluationDependsOn(':app') } task clean(type: Delete) { delete rootProject.buildDir } ================================================ FILE: android/fastlane/Appfile ================================================ json_key_file("~/appditto/keys/playapi_key.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one package_name("com.appditto.blaise") # e.g. com.krausefx.app ================================================ FILE: android/fastlane/Fastfile ================================================ # This file contains the fastlane.tools configuration # You can find the documentation at https://docs.fastlane.tools # # For a list of all available actions, check out # # https://docs.fastlane.tools/actions # # For a list of all available plugins, check out # # https://docs.fastlane.tools/plugins/available-plugins # default_platform(:android) platform :android do before_all do |lane, options| update_fastlane end desc "Build android aab and apk" lane :build_android do |options| sh "./flutter_build.sh --clean" sh "./flutter_build.sh --apk" end desc "Deploy (Upload to play store)" lane :deploy_android do |options| upload_to_play_store( track: options[:production] ? 'production' : options[:internal] ? 'internal' : options[:alpha] ? 'alpha' : 'beta', aab: ENV['AAB_PATH'], skip_upload_images: true, skip_upload_screenshots: true, ) end end ================================================ FILE: android/fastlane/flutter_build.sh ================================================ #!/bin/bash cd ../../ if [ "$1" == "--clean" ] then echo "Running clean..." flutter clean else echo "Skipping clean..." fi if [ "$1" == "--apk" ] then echo "Building APK..." flutter build apk --release else echo "Building AAB..." flutter build appbundle --release fi ================================================ FILE: android/fastlane/flutter_test.sh ================================================ #!/bin/bash cd ../../ echo "Running tests" flutter test ================================================ FILE: android/fastlane/metadata/android/en-US/changelogs/1.txt ================================================ Initial Beta Release ================================================ FILE: android/fastlane/metadata/android/en-US/changelogs/17.txt ================================================ - Add themes to QR scanner ================================================ FILE: android/fastlane/metadata/android/en-US/changelogs/18.txt ================================================ - Add themes to QR scanner ================================================ FILE: android/fastlane/metadata/android/en-US/changelogs/20.txt ================================================ - Improve QR Scanner reliability on some devices - Add Catalan language - Performance improvements and bug fixes ================================================ FILE: android/fastlane/metadata/android/en-US/changelogs/22.txt ================================================ - Live chat removed ================================================ FILE: android/fastlane/metadata/android/en-US/changelogs/23.txt ================================================ - General bug fixes & improvements ================================================ FILE: android/fastlane/metadata/android/en-US/changelogs/24.txt ================================================ - Disallow 0-fee transactions - Android 12 compatibility - Performance improvements - Bug fixes ================================================ FILE: android/fastlane/metadata/android/en-US/full_description.txt ================================================ Features: - Create a new Pascal wallet or import an existing one - Manage multiple Pascal accounts (PASAs) - Send Pascal instantly to anyone, anywhere in the world - Change account names, transfer accounts, or list them for sale - Manage contacts in an intuitive, easy-to-use address book - View your accounts operations history - Easily obtain new accounts by borrowing them or using the built-in freepasa integration - Receive push notifications when you receive Pascal - If you have questions, we can help - get live support directly from the developers IMPORTANT: Remember to backup your private key and store it in a safe place. It is the only way to recover your funds if you sign out of the wallet or lose your device! If someone else gets your unencrypted private key, they will have full control over your funds. For support: help@appditto.com PascalCoin website: https://pascalcoin.org ================================================ FILE: android/fastlane/metadata/android/en-US/short_description.txt ================================================ Simple, Sleek, & Secure Pascal Wallet ================================================ FILE: android/fastlane/metadata/android/en-US/title.txt ================================================ Blaise - Pascal Wallet ================================================ FILE: android/fastlane/metadata/android/en-US/video.txt ================================================ ================================================ FILE: android/gradle/wrapper/gradle-wrapper.properties ================================================ #Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip ================================================ FILE: android/gradle.properties ================================================ org.gradle.jvmargs=-Xmx1536M android.enableJetifier=true android.useAndroidX=true android.enableR8=true ================================================ FILE: android/settings.gradle ================================================ include ':app' def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() def plugins = new Properties() def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') if (pluginsFile.exists()) { pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } } plugins.each { name, path -> def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() include ":$name" project(":$name").projectDir = pluginDirectory } ================================================ FILE: assets/country_phone_map.json ================================================ [ { "iso": "AF", "text": "Afghanistan (+93)" }, { "iso": "AL", "text": "Albania (+355)" }, { "iso": "DZ", "text": "Algeria (+213)" }, { "iso": "AS", "text": "American Samoa (+1)" }, { "iso": "AD", "text": "Andorra (+376)" }, { "iso": "AO", "text": "Angola (+244)" }, { "iso": "AI", "text": "Anguilla (+1)" }, { "iso": "AG", "text": "Antigua & Barbuda (+1)" }, { "iso": "AR", "text": "Argentina (+54)" }, { "iso": "AM", "text": "Armenia (+374)" }, { "iso": "AW", "text": "Aruba (+297)" }, { "iso": "AC", "text": "Ascension Island (+247)" }, { "iso": "AU", "text": "Australia (+61)" }, { "iso": "AT", "text": "Austria (+43)" }, { "iso": "AZ", "text": "Azerbaijan (+994)" }, { "iso": "BS", "text": "Bahamas (+1)" }, { "iso": "BH", "text": "Bahrain (+973)" }, { "iso": "BD", "text": "Bangladesh (+880)" }, { "iso": "BB", "text": "Barbados (+1)" }, { "iso": "BY", "text": "Belarus (+375)" }, { "iso": "BE", "text": "Belgium (+32)" }, { "iso": "BZ", "text": "Belize (+501)" }, { "iso": "BJ", "text": "Benin (+229)" }, { "iso": "BM", "text": "Bermuda (+1)" }, { "iso": "BT", "text": "Bhutan (+975)" }, { "iso": "BO", "text": "Bolivia (+591)" }, { "iso": "BA", "text": "Bosnia & Herzegovina (+387)" }, { "iso": "BW", "text": "Botswana (+267)" }, { "iso": "BR", "text": "Brazil (+55)" }, { "iso": "IO", "text": "British Indian Ocean Territory (+246)" }, { "iso": "VG", "text": "British Virgin Islands (+1)" }, { "iso": "BN", "text": "Brunei (+673)" }, { "iso": "BG", "text": "Bulgaria (+359)" }, { "iso": "BF", "text": "Burkina Faso (+226)" }, { "iso": "BI", "text": "Burundi (+257)" }, { "iso": "KH", "text": "Cambodia (+855)" }, { "iso": "CM", "text": "Cameroon (+237)" }, { "iso": "CA", "text": "Canada (+1)" }, { "iso": "CV", "text": "Cape Verde (+238)" }, { "iso": "BQ", "text": "Caribbean Netherlands (+599)" }, { "iso": "KY", "text": "Cayman Islands (+1)" }, { "iso": "CF", "text": "Central African Republic (+236)" }, { "iso": "TD", "text": "Chad (+235)" }, { "iso": "CL", "text": "Chile (+56)" }, { "iso": "CN", "text": "China (+86)" }, { "iso": "CX", "text": "Christmas Island (+61)" }, { "iso": "CC", "text": "Cocos (Keeling) Islands (+61)" }, { "iso": "CO", "text": "Colombia (+57)" }, { "iso": "KM", "text": "Comoros (+269)" }, { "iso": "CG", "text": "Congo - Brazzaville (+242)" }, { "iso": "CD", "text": "Congo - Kinshasa (+243)" }, { "iso": "CK", "text": "Cook Islands (+682)" }, { "iso": "CR", "text": "Costa Rica (+506)" }, { "iso": "HR", "text": "Croatia (+385)" }, { "iso": "CU", "text": "Cuba (+53)" }, { "iso": "CW", "text": "Curaçao (+599)" }, { "iso": "CY", "text": "Cyprus (+357)" }, { "iso": "CZ", "text": "Czechia (+420)" }, { "iso": "CI", "text": "Côte d’Ivoire (+225)" }, { "iso": "DK", "text": "Denmark (+45)" }, { "iso": "DJ", "text": "Djibouti (+253)" }, { "iso": "DM", "text": "Dominica (+1)" }, { "iso": "DO", "text": "Dominican Republic (+1)" }, { "iso": "EC", "text": "Ecuador (+593)" }, { "iso": "EG", "text": "Egypt (+20)" }, { "iso": "SV", "text": "El Salvador (+503)" }, { "iso": "GQ", "text": "Equatorial Guinea (+240)" }, { "iso": "ER", "text": "Eritrea (+291)" }, { "iso": "EE", "text": "Estonia (+372)" }, { "iso": "ET", "text": "Ethiopia (+251)" }, { "iso": "FK", "text": "Falkland Islands (+500)" }, { "iso": "FO", "text": "Faroe Islands (+298)" }, { "iso": "FJ", "text": "Fiji (+679)" }, { "iso": "FI", "text": "Finland (+358)" }, { "iso": "FR", "text": "France (+33)" }, { "iso": "GF", "text": "French Guiana (+594)" }, { "iso": "PF", "text": "French Polynesia (+689)" }, { "iso": "GA", "text": "Gabon (+241)" }, { "iso": "GM", "text": "Gambia (+220)" }, { "iso": "GE", "text": "Georgia (+995)" }, { "iso": "DE", "text": "Germany (+49)" }, { "iso": "GH", "text": "Ghana (+233)" }, { "iso": "GI", "text": "Gibraltar (+350)" }, { "iso": "GR", "text": "Greece (+30)" }, { "iso": "GL", "text": "Greenland (+299)" }, { "iso": "GD", "text": "Grenada (+1)" }, { "iso": "GP", "text": "Guadeloupe (+590)" }, { "iso": "GU", "text": "Guam (+1)" }, { "iso": "GT", "text": "Guatemala (+502)" }, { "iso": "GG", "text": "Guernsey (+44)" }, { "iso": "GN", "text": "Guinea (+224)" }, { "iso": "GW", "text": "Guinea-Bissau (+245)" }, { "iso": "GY", "text": "Guyana (+592)" }, { "iso": "HT", "text": "Haiti (+509)" }, { "iso": "HN", "text": "Honduras (+504)" }, { "iso": "HK", "text": "Hong Kong SAR China (+852)" }, { "iso": "HU", "text": "Hungary (+36)" }, { "iso": "IS", "text": "Iceland (+354)" }, { "iso": "IN", "text": "India (+91)" }, { "iso": "ID", "text": "Indonesia (+62)" }, { "iso": "IR", "text": "Iran (+98)" }, { "iso": "IQ", "text": "Iraq (+964)" }, { "iso": "IE", "text": "Ireland (+353)" }, { "iso": "IM", "text": "Isle of Man (+44)" }, { "iso": "IL", "text": "Israel (+972)" }, { "iso": "IT", "text": "Italy (+39)" }, { "iso": "JM", "text": "Jamaica (+1)" }, { "iso": "JP", "text": "Japan (+81)" }, { "iso": "JE", "text": "Jersey (+44)" }, { "iso": "JO", "text": "Jordan (+962)" }, { "iso": "KZ", "text": "Kazakhstan (+7)" }, { "iso": "KE", "text": "Kenya (+254)" }, { "iso": "KI", "text": "Kiribati (+686)" }, { "iso": "XK", "text": "Kosovo (+383)" }, { "iso": "KW", "text": "Kuwait (+965)" }, { "iso": "KG", "text": "Kyrgyzstan (+996)" }, { "iso": "LA", "text": "Laos (+856)" }, { "iso": "LV", "text": "Latvia (+371)" }, { "iso": "LB", "text": "Lebanon (+961)" }, { "iso": "LS", "text": "Lesotho (+266)" }, { "iso": "LR", "text": "Liberia (+231)" }, { "iso": "LY", "text": "Libya (+218)" }, { "iso": "LI", "text": "Liechtenstein (+423)" }, { "iso": "LT", "text": "Lithuania (+370)" }, { "iso": "LU", "text": "Luxembourg (+352)" }, { "iso": "MO", "text": "Macau SAR China (+853)" }, { "iso": "MK", "text": "Macedonia (+389)" }, { "iso": "MG", "text": "Madagascar (+261)" }, { "iso": "MW", "text": "Malawi (+265)" }, { "iso": "MY", "text": "Malaysia (+60)" }, { "iso": "MV", "text": "Maldives (+960)" }, { "iso": "ML", "text": "Mali (+223)" }, { "iso": "MT", "text": "Malta (+356)" }, { "iso": "MH", "text": "Marshall Islands (+692)" }, { "iso": "MQ", "text": "Martinique (+596)" }, { "iso": "MR", "text": "Mauritania (+222)" }, { "iso": "MU", "text": "Mauritius (+230)" }, { "iso": "YT", "text": "Mayotte (+262)" }, { "iso": "MX", "text": "Mexico (+52)" }, { "iso": "FM", "text": "Micronesia (+691)" }, { "iso": "MD", "text": "Moldova (+373)" }, { "iso": "MC", "text": "Monaco (+377)" }, { "iso": "MN", "text": "Mongolia (+976)" }, { "iso": "ME", "text": "Montenegro (+382)" }, { "iso": "MS", "text": "Montserrat (+1)" }, { "iso": "MA", "text": "Morocco (+212)" }, { "iso": "MZ", "text": "Mozambique (+258)" }, { "iso": "MM", "text": "Myanmar (Burma) (+95)" }, { "iso": "NA", "text": "Namibia (+264)" }, { "iso": "NR", "text": "Nauru (+674)" }, { "iso": "NP", "text": "Nepal (+977)" }, { "iso": "NL", "text": "Netherlands (+31)" }, { "iso": "NC", "text": "New Caledonia (+687)" }, { "iso": "NZ", "text": "New Zealand (+64)" }, { "iso": "NI", "text": "Nicaragua (+505)" }, { "iso": "NE", "text": "Niger (+227)" }, { "iso": "NG", "text": "Nigeria (+234)" }, { "iso": "NU", "text": "Niue (+683)" }, { "iso": "NF", "text": "Norfolk Island (+672)" }, { "iso": "KP", "text": "North Korea (+850)" }, { "iso": "MP", "text": "Northern Mariana Islands (+1)" }, { "iso": "NO", "text": "Norway (+47)" }, { "iso": "OM", "text": "Oman (+968)" }, { "iso": "PK", "text": "Pakistan (+92)" }, { "iso": "PW", "text": "Palau (+680)" }, { "iso": "PS", "text": "Palestinian Territories (+970)" }, { "iso": "PA", "text": "Panama (+507)" }, { "iso": "PG", "text": "Papua New Guinea (+675)" }, { "iso": "PY", "text": "Paraguay (+595)" }, { "iso": "PE", "text": "Peru (+51)" }, { "iso": "PH", "text": "Philippines (+63)" }, { "iso": "PL", "text": "Poland (+48)" }, { "iso": "PT", "text": "Portugal (+351)" }, { "iso": "PR", "text": "Puerto Rico (+1)" }, { "iso": "QA", "text": "Qatar (+974)" }, { "iso": "RO", "text": "Romania (+40)" }, { "iso": "RU", "text": "Russia (+7)" }, { "iso": "RW", "text": "Rwanda (+250)" }, { "iso": "RE", "text": "Réunion (+262)" }, { "iso": "WS", "text": "Samoa (+685)" }, { "iso": "SM", "text": "San Marino (+378)" }, { "iso": "SA", "text": "Saudi Arabia (+966)" }, { "iso": "SN", "text": "Senegal (+221)" }, { "iso": "RS", "text": "Serbia (+381)" }, { "iso": "SC", "text": "Seychelles (+248)" }, { "iso": "SL", "text": "Sierra Leone (+232)" }, { "iso": "SG", "text": "Singapore (+65)" }, { "iso": "SX", "text": "Sint Maarten (+1)" }, { "iso": "SK", "text": "Slovakia (+421)" }, { "iso": "SI", "text": "Slovenia (+386)" }, { "iso": "SB", "text": "Solomon Islands (+677)" }, { "iso": "SO", "text": "Somalia (+252)" }, { "iso": "ZA", "text": "South Africa (+27)" }, { "iso": "KR", "text": "South Korea (+82)" }, { "iso": "SS", "text": "South Sudan (+211)" }, { "iso": "ES", "text": "Spain (+34)" }, { "iso": "LK", "text": "Sri Lanka (+94)" }, { "iso": "BL", "text": "St. Barthélemy (+590)" }, { "iso": "SH", "text": "St. Helena (+290)" }, { "iso": "KN", "text": "St. Kitts & Nevis (+1)" }, { "iso": "LC", "text": "St. Lucia (+1)" }, { "iso": "MF", "text": "St. Martin (+590)" }, { "iso": "PM", "text": "St. Pierre & Miquelon (+508)" }, { "iso": "VC", "text": "St. Vincent & Grenadines (+1)" }, { "iso": "SD", "text": "Sudan (+249)" }, { "iso": "SR", "text": "Suriname (+597)" }, { "iso": "SJ", "text": "Svalbard & Jan Mayen (+47)" }, { "iso": "SZ", "text": "Swaziland (+268)" }, { "iso": "SE", "text": "Sweden (+46)" }, { "iso": "CH", "text": "Switzerland (+41)" }, { "iso": "SY", "text": "Syria (+963)" }, { "iso": "ST", "text": "São Tomé & Príncipe (+239)" }, { "iso": "TW", "text": "Taiwan (+886)" }, { "iso": "TJ", "text": "Tajikistan (+992)" }, { "iso": "TZ", "text": "Tanzania (+255)" }, { "iso": "TH", "text": "Thailand (+66)" }, { "iso": "TL", "text": "Timor-Leste (+670)" }, { "iso": "TG", "text": "Togo (+228)" }, { "iso": "TK", "text": "Tokelau (+690)" }, { "iso": "TO", "text": "Tonga (+676)" }, { "iso": "TT", "text": "Trinidad & Tobago (+1)" }, { "iso": "TA", "text": "Tristan da Cunha (+290)" }, { "iso": "TN", "text": "Tunisia (+216)" }, { "iso": "TR", "text": "Turkey (+90)" }, { "iso": "TM", "text": "Turkmenistan (+993)" }, { "iso": "TC", "text": "Turks & Caicos Islands (+1)" }, { "iso": "TV", "text": "Tuvalu (+688)" }, { "iso": "VI", "text": "U.S. Virgin Islands (+1)" }, { "iso": "UG", "text": "Uganda (+256)" }, { "iso": "UA", "text": "Ukraine (+380)" }, { "iso": "AE", "text": "United Arab Emirates (+971)" }, { "iso": "GB", "text": "United Kingdom (+44)" }, { "iso": "US", "text": "United States (+1)" }, { "iso": "UY", "text": "Uruguay (+598)" }, { "iso": "UZ", "text": "Uzbekistan (+998)" }, { "iso": "VU", "text": "Vanuatu (+678)" }, { "iso": "VA", "text": "Vatican City (+39)" }, { "iso": "VE", "text": "Venezuela (+58)" }, { "iso": "VN", "text": "Vietnam (+84)" }, { "iso": "WF", "text": "Wallis & Futuna (+681)" }, { "iso": "EH", "text": "Western Sahara (+212)" }, { "iso": "YE", "text": "Yemen (+967)" }, { "iso": "ZM", "text": "Zambia (+260)" }, { "iso": "ZW", "text": "Zimbabwe (+263)" }, { "iso": "AX", "text": "Åland Islands (+358)" } ] ================================================ FILE: bin/arb_to_pojson.py ================================================ #!/usr/bin/env python import argparse import json import sys parser = argparse.ArgumentParser() parser.add_argument('-f', '--file', help='Path to arb file', required=True) options = parser.parse_args() if not options.file.endswith("arb"): print(f"Can't process {options.file}, file must be a .arb") parser.print_help() sys.exit(0) out_file = options.file.replace(".arb", ".json") ret = [] with open(options.file) as arb_file: data = json.load(arb_file) obj = {} for key, value in data.items(): if key.startswith("@@"): continue if key.startswith("@"): obj['context'] = value['description'] ret.append(obj) obj = {} else: obj['reference'] = key obj['term'] = value.replace("\n", "") with open(out_file, 'w') as outf: json.dump(ret, outf, indent=4, ensure_ascii=False) print(f"Wrote {out_file}") ================================================ FILE: bin/pojson_to_arb.py ================================================ #!/usr/bin/env python import argparse import json import sys parser = argparse.ArgumentParser() parser.add_argument('-f', '--file', help='Path to json file', required=True) options = parser.parse_args() if not options.file.endswith("json"): print(f"Can't process {options.file}, file must be a .jon") parser.print_help() sys.exit(0) out_file = options.file.replace(".json", ".arb") ret = {} with open(options.file) as json_file: data = json.load(json_file) for obj in data: ret[obj['reference']] = obj['definition'].replace("", "\n") with open(out_file, 'w') as outf: json.dump(ret, outf, indent=2, ensure_ascii=False) print(f"Wrote {out_file}") ================================================ FILE: bin/poupdate.py ================================================ import os import json import subprocess from poeditor import POEditorAPI from settings import PO_API_KEY, PROJECT_ID client = POEditorAPI(api_token=PO_API_KEY) languages = client.list_project_languages(PROJECT_ID) language_codes = [] for l in languages: if l['percentage'] > 50: language_codes.append(l['code']) for code in language_codes: print(f"Downloading {code}...") client.export(PROJECT_ID, code, file_type='json', local_file=f'lib/l10n/intl_{code}.json') print(f"Downloaded {code}") for fname in os.listdir('lib/l10n'): if fname.endswith('.json'): fname = f'lib/l10n/{fname}' out_file = fname.replace(".json", ".arb") ret = {} with open(fname) as json_file: data = json.load(json_file) for obj in data: if 'reference' in obj and 'definition' in obj and obj['definition'] is not None: ret[obj['reference']] = obj['definition'].replace("", "\n") with open(out_file, 'w') as outf: json.dump(ret, outf, indent=2, ensure_ascii=False) print(f"Wrote {out_file}") os.remove(fname) subprocess.run('flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/localization.dart lib/l10n/intl_*.arb', shell=True) ================================================ FILE: bin/settings.py.example ================================================ PO_API_KEY='1234' PROJECT_ID=1234 ================================================ FILE: ci/get_version.sh ================================================ #!/bin/bash # Parse yaml file parse_yaml() { local prefix=$2 local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034') sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \ -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 | awk -F$fs '{ indent = length($1)/2; vname[indent] = $2; for (i in vname) {if (i > indent) {delete vname[i]}} if (length($3) > 0) { vn=""; for (i=0; i/dev/null 2>&1 #then # echo "Tag already exists" # exit 0 #fi #echo "Tag not found, creating..." #git tag -d $VERSION #git tag $VERSION HEAD #git push origin :$VERSION #git push origin $VERSION #if [ $? -ne 0 ] #then # echo "Fatal, failed to push tag $VERSION" # exit 1 #fi pubresponse=$(curl -u "$GITHUB_OAUTH_BASIC" -sH "$AUTH" --data '{"tag_name":"'"$VERSION"'", "name":"Blaise '"${VERSION}"'", "draft":false, "prerelease":false}' $GH_PUBLISH) if [[ "$pubresponse" == *"already_exists"* ]]; then echo "Tag already exists, skipping" exit 0 elif [[ "$pubresponse" == *"Validation Failed"* ]]; then echo "Error! ${pubresponse}" exit 1 fi echo "Release created" exit 0 ================================================ FILE: ci/upload_android_github.sh ================================================ #!/bin/bash # # Based on: # https://gist.github.com/stefanbuck/ce788fee19ab6eb0b4447a85fc99f447 # # Uploads our android release APK to a corresponding release on github releases page # source ./ci/get_version.sh VERSION=$(get_version) # Define variables. GH_TAGS="$GH_REPO/releases/tags/$VERSION" GH_PUBLISH="$GH_REPO/releases" WGET_ARGS="--content-disposition --auth-no-challenge --no-cookie" CURL_ARGS="-LJO#" filename=./build/app/outputs/apk/release/app-release.apk GITHUB_OAUTH_BASIC="${GITHUB_API_TOKEN}:x-oauth-basic" AUTH="Authorization: token $GITHUB_API_TOKEN" # Validate token. curl -o /dev/null -sH "$AUTH" $GH_REPO || { echo "Error: Invalid repo, token or network issue!"; exit 1; } # Read asset tags. response=$(curl -u "$GITHUB_OAUTH_BASIC" -sH "$AUTH" $GH_TAGS) # Get ID of the asset based on given filename. eval $(echo "$response" | grep -m 1 "id.:" | grep -w id | tr : = | tr -cd '[[:alnum:]]=') [ "$id" ] || { echo "Error: Failed to get release id for tag: $VERSION"; echo "$response" awk 'length($0)<100' >&2; exit 1; } # Upload asset echo "Uploading asset... $localAssetPath" >&2 # Construct url GH_ASSET="https://uploads.github.com/repos/$GH_OWNER/$GH_REPO_NAME/releases/$id/assets?name=blaise_${VERSION}.apk" curl -u "$GITHUB_OAUTH_BASIC" --data-binary @"$filename" -H "$AUTH" -H "Content-Type: application/octet-stream" $GH_ASSET ================================================ FILE: ios/Flutter/AppFrameworkInfo.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable App CFBundleIdentifier io.flutter.flutter.app CFBundleInfoDictionaryVersion 6.0 CFBundleName App CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1.0 MinimumOSVersion 9.0 ================================================ FILE: ios/Flutter/Debug.xcconfig ================================================ #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" ================================================ FILE: ios/Flutter/Flutter.podspec ================================================ # # NOTE: This podspec is NOT to be published. It is only used as a local source! # This is a generated file; do not edit or check into version control. # Pod::Spec.new do |s| s.name = 'Flutter' s.version = '1.0.0' s.summary = 'High-performance, high-fidelity mobile apps.' s.homepage = 'https://flutter.io' s.license = { :type => 'MIT' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } s.ios.deployment_target = '9.0' # Framework linking is handled by Flutter tooling, not CocoaPods. # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. s.vendored_frameworks = 'path/to/nothing' end ================================================ FILE: ios/Flutter/Release.xcconfig ================================================ #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" ================================================ FILE: ios/Gemfile ================================================ source "https://rubygems.org" gem "fastlane" gem "cocoapods" ================================================ FILE: ios/Podfile ================================================ # Uncomment this line to define a global platform for your project # platform :ios, '9.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' project 'Runner', { 'Debug' => :debug, 'Profile' => :release, 'Release' => :release, } def flutter_root generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) unless File.exist?(generated_xcode_build_settings_path) raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" end File.foreach(generated_xcode_build_settings_path) do |line| matches = line.match(/FLUTTER_ROOT\=(.*)/) return matches[1].strip if matches end raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" end require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) flutter_ios_podfile_setup target 'Runner' do use_frameworks! use_modular_headers! flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) end end ================================================ FILE: ios/Runner/AppDelegate.h ================================================ #import #import @interface AppDelegate : FlutterAppDelegate - (void)lc_setAlternateIconName:(NSString*)iconName; @end ================================================ FILE: ios/Runner/AppDelegate.m ================================================ #include "AppDelegate.h" #include "GeneratedPluginRegistrant.h" #import "Runner-Swift.h" @implementation AppDelegate - (void)lc_setAlternateIconName:(NSString*)iconName { if ([[UIApplication sharedApplication] respondsToSelector:@selector(supportsAlternateIcons)] && [[UIApplication sharedApplication] supportsAlternateIcons]) { NSMutableString *selectorString = [[NSMutableString alloc] initWithCapacity:40]; [selectorString appendString:@"_setAlternate"]; [selectorString appendString:@"IconName:"]; [selectorString appendString:@"completionHandler:"]; SEL selector = NSSelectorFromString(selectorString); IMP imp = [[UIApplication sharedApplication] methodForSelector:selector]; void (*func)(id, SEL, id, id) = (void *)imp; if (func) { func([UIApplication sharedApplication], selector, iconName, ^(NSError * _Nullable error) {}); } } } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController; FlutterMethodChannel* appChannel = [FlutterMethodChannel methodChannelWithName:@"fappchannel" binaryMessenger:controller]; [appChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { if ([@"changeIcon" isEqualToString:call.method]) { NSDictionary *arguments = [call arguments]; NSString *icon = arguments[@"icon"]; if (icon == NULL || icon.length == 0) { result([FlutterError errorWithCode:@"error" message:@"Icon is required" details:nil]); return; } if ([@"Light" isEqualToString:icon ]) { [self lc_setAlternateIconName:nil]; } else if ([@"Dark" isEqualToString:icon]) { [self lc_setAlternateIconName:@"Dark"]; } else if ([@"Copper" isEqualToString:icon]) { [self lc_setAlternateIconName:@"Copper"]; } } else if ([@"setSecureClipboardItem" isEqualToString:call.method]) { NSDictionary *arguments = [call arguments]; NSString *value = arguments[@"value"]; [SecureClipboard setClipboardItem:value]; } else { result(FlutterMethodNotImplemented); } }]; [GeneratedPluginRegistrant registerWithRegistry:self]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } @end ================================================ FILE: ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "size" : "20x20", "idiom" : "iphone", "filename" : "Icon-App-20x20@2x.png", "scale" : "2x" }, { "size" : "20x20", "idiom" : "iphone", "filename" : "Icon-App-20x20@3x.png", "scale" : "3x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "Icon-App-29x29@1x.png", "scale" : "1x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "Icon-App-29x29@2x.png", "scale" : "2x" }, { "size" : "29x29", "idiom" : "iphone", "filename" : "Icon-App-29x29@3x.png", "scale" : "3x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "Icon-App-40x40@2x.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "iphone", "filename" : "Icon-App-40x40@3x.png", "scale" : "3x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "Icon-App-60x60@2x.png", "scale" : "2x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "Icon-App-60x60@3x.png", "scale" : "3x" }, { "size" : "20x20", "idiom" : "ipad", "filename" : "Icon-App-20x20@1x.png", "scale" : "1x" }, { "size" : "20x20", "idiom" : "ipad", "filename" : "Icon-App-20x20@2x.png", "scale" : "2x" }, { "size" : "29x29", "idiom" : "ipad", "filename" : "Icon-App-29x29@1x.png", "scale" : "1x" }, { "size" : "29x29", "idiom" : "ipad", "filename" : "Icon-App-29x29@2x.png", "scale" : "2x" }, { "size" : "40x40", "idiom" : "ipad", "filename" : "Icon-App-40x40@1x.png", "scale" : "1x" }, { "size" : "40x40", "idiom" : "ipad", "filename" : "Icon-App-40x40@2x.png", "scale" : "2x" }, { "size" : "76x76", "idiom" : "ipad", "filename" : "Icon-App-76x76@1x.png", "scale" : "1x" }, { "size" : "76x76", "idiom" : "ipad", "filename" : "Icon-App-76x76@2x.png", "scale" : "2x" }, { "size" : "83.5x83.5", "idiom" : "ipad", "filename" : "Icon-App-83.5x83.5@2x.png", "scale" : "2x" }, { "size" : "1024x1024", "idiom" : "ios-marketing", "filename" : "Icon-App-1024x1024@1x.png", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "LaunchImage.png", "scale" : "1x" }, { "idiom" : "universal", "filename" : "LaunchImage@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "LaunchImage@3x.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md ================================================ # Launch Screen Assets You can customize the launch screen with your own desired assets by replacing the image files in this directory. You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. ================================================ FILE: ios/Runner/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: ios/Runner/Base.lproj/Main.storyboard ================================================ ================================================ FILE: ios/Runner/GoogleService-Info.plist ================================================ CLIENT_ID 786466973238-8v19gq83o927niadorolmvts501v3jn6.apps.googleusercontent.com REVERSED_CLIENT_ID com.googleusercontent.apps.786466973238-8v19gq83o927niadorolmvts501v3jn6 API_KEY AIzaSyC-BXfz1vd342KBhsEK-7HwkVDZ9OFGCbg GCM_SENDER_ID 786466973238 PLIST_VERSION 1 BUNDLE_ID com.appditto.blaise PROJECT_ID blaise-a22fa STORAGE_BUCKET blaise-a22fa.appspot.com IS_ADS_ENABLED IS_ANALYTICS_ENABLED IS_APPINVITE_ENABLED IS_GCM_ENABLED IS_SIGNIN_ENABLED GOOGLE_APP_ID 1:786466973238:ios:59d3b6ab52a943d3 DATABASE_URL https://blaise-a22fa.firebaseio.com ================================================ FILE: ios/Runner/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleDisplayName Blaise CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName blaise_wallet_flutter CFBundlePackageType APPL CFBundleShortVersionString $(FLUTTER_BUILD_NAME) CFBundleSignature ???? CFBundleVersion $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS NSCameraUsageDescription Camera permission is required for barcode scanning. NSFaceIDUsageDescription App needs to authenticate using faces. NSPhotoLibraryAddUsageDescription This app requires access to the photo library. NSPhotoLibraryUsageDescription This app requires access to the photo library. UIBackgroundModes fetch remote-notification UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance CFBundleIcons CFBundlePrimaryIcon CFBundleIconFiles Icon-App UIPrerenderedIcon CFBundleAlternateIcons Dark CFBundleIconFiles Dark-Icon-App UIPrerenderedIcon Copper CFBundleIconFiles Copper-Icon-App UIPrerenderedIcon ================================================ FILE: ios/Runner/Runner-Bridging-Header.h ================================================ // // Use this file to import your target's public headers that you would like to expose to Swift. // ================================================ FILE: ios/Runner/Runner.entitlements ================================================ aps-environment development ================================================ FILE: ios/Runner/clipboard.swift ================================================ import Foundation import UIKit import MobileCoreServices @objcMembers class SecureClipboard: NSObject { static func setClipboardItem(_ value: String) { let pasteboardItems = [[ kUTTypePlainText as String: value ]] let pasteboardOptions : [UIPasteboard.OptionsKey: Any] = [ UIPasteboard.OptionsKey.expirationDate: Date().addingTimeInterval(60 * 2), UIPasteboard.OptionsKey.localOnly: true ] UIPasteboard.general.setItems(pasteboardItems, options: pasteboardOptions) } } ================================================ FILE: ios/Runner/main.m ================================================ #import #import #import "AppDelegate.h" int main(int argc, char* argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ================================================ FILE: ios/Runner.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 24FF04FFD6FD224422092912 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42074951F44019CE5ACED5F2 /* Pods_Runner.framework */; }; 3961A6B22304917B0088A4FD /* Dark-Icon-App@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3961A6A92304917A0088A4FD /* Dark-Icon-App@3x.png */; }; 3961A6B32304917B0088A4FD /* Icon-App@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3961A6AA2304917A0088A4FD /* Icon-App@3x.png */; }; 3961A6B42304917B0088A4FD /* Dark-Icon-App@1x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3961A6AB2304917A0088A4FD /* Dark-Icon-App@1x.png */; }; 3961A6B52304917B0088A4FD /* Icon-App@1x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3961A6AC2304917A0088A4FD /* Icon-App@1x.png */; }; 3961A6B62304917B0088A4FD /* Copper-Icon-App@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3961A6AD2304917A0088A4FD /* Copper-Icon-App@3x.png */; }; 3961A6B72304917B0088A4FD /* Dark-Icon-App@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3961A6AE2304917A0088A4FD /* Dark-Icon-App@2x.png */; }; 3961A6B82304917B0088A4FD /* Copper-Icon-App@1x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3961A6AF2304917A0088A4FD /* Copper-Icon-App@1x.png */; }; 3961A6B92304917B0088A4FD /* Copper-Icon-App@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3961A6B02304917A0088A4FD /* Copper-Icon-App@2x.png */; }; 3961A6BA2304917B0088A4FD /* Icon-App@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3961A6B12304917B0088A4FD /* Icon-App@2x.png */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 761828152358E8C4001F6E1D /* clipboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761828142358E8C4001F6E1D /* clipboard.swift */; }; 7693980122F353FF009A1CF6 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7693980022F353FF009A1CF6 /* GoogleService-Info.plist */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 2D7AF7B673C6DFA1AB52610D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 3961A6A92304917A0088A4FD /* Dark-Icon-App@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Dark-Icon-App@3x.png"; sourceTree = ""; }; 3961A6AA2304917A0088A4FD /* Icon-App@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-App@3x.png"; sourceTree = ""; }; 3961A6AB2304917A0088A4FD /* Dark-Icon-App@1x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Dark-Icon-App@1x.png"; sourceTree = ""; }; 3961A6AC2304917A0088A4FD /* Icon-App@1x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-App@1x.png"; sourceTree = ""; }; 3961A6AD2304917A0088A4FD /* Copper-Icon-App@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Copper-Icon-App@3x.png"; sourceTree = ""; }; 3961A6AE2304917A0088A4FD /* Dark-Icon-App@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Dark-Icon-App@2x.png"; sourceTree = ""; }; 3961A6AF2304917A0088A4FD /* Copper-Icon-App@1x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Copper-Icon-App@1x.png"; sourceTree = ""; }; 3961A6B02304917A0088A4FD /* Copper-Icon-App@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Copper-Icon-App@2x.png"; sourceTree = ""; }; 3961A6B12304917B0088A4FD /* Icon-App@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-App@2x.png"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 42074951F44019CE5ACED5F2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 761828142358E8C4001F6E1D /* clipboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = clipboard.swift; sourceTree = ""; }; 766334DC22D66099008DDB1D /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 7693980022F353FF009A1CF6 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 7693980222F35413009A1CF6 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 95A7889671BF4F1B90796A25 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; A4ECA53D14AF7F1AF649555F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 24FF04FFD6FD224422092912 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 0782DE0FD6A6B5B3DE006797 /* Pods */ = { isa = PBXGroup; children = ( 95A7889671BF4F1B90796A25 /* Pods-Runner.debug.xcconfig */, A4ECA53D14AF7F1AF649555F /* Pods-Runner.release.xcconfig */, 2D7AF7B673C6DFA1AB52610D /* Pods-Runner.profile.xcconfig */, ); name = Pods; sourceTree = ""; }; 3961A6BB230491C40088A4FD /* resources */ = { isa = PBXGroup; children = ( 3961A6AF2304917A0088A4FD /* Copper-Icon-App@1x.png */, 3961A6B02304917A0088A4FD /* Copper-Icon-App@2x.png */, 3961A6AD2304917A0088A4FD /* Copper-Icon-App@3x.png */, 3961A6AB2304917A0088A4FD /* Dark-Icon-App@1x.png */, 3961A6AE2304917A0088A4FD /* Dark-Icon-App@2x.png */, 3961A6A92304917A0088A4FD /* Dark-Icon-App@3x.png */, 3961A6AC2304917A0088A4FD /* Icon-App@1x.png */, 3961A6B12304917B0088A4FD /* Icon-App@2x.png */, 3961A6AA2304917A0088A4FD /* Icon-App@3x.png */, ); path = resources; sourceTree = ""; }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, ); name = Flutter; sourceTree = ""; }; 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 0782DE0FD6A6B5B3DE006797 /* Pods */, B45E5D8B972749CD5E5098A6 /* Frameworks */, ); sourceTree = ""; }; 97C146EF1CF9000F007C117D /* Products */ = { isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, ); name = Products; sourceTree = ""; }; 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( 3961A6BB230491C40088A4FD /* resources */, 7693980222F35413009A1CF6 /* Runner.entitlements */, 7693980022F353FF009A1CF6 /* GoogleService-Info.plist */, 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 766334DC22D66099008DDB1D /* Runner-Bridging-Header.h */, 761828142358E8C4001F6E1D /* clipboard.swift */, ); path = Runner; sourceTree = ""; }; 97C146F11CF9000F007C117D /* Supporting Files */ = { isa = PBXGroup; children = ( 97C146F21CF9000F007C117D /* main.m */, ); name = "Supporting Files"; sourceTree = ""; }; B45E5D8B972749CD5E5098A6 /* Frameworks */ = { isa = PBXGroup; children = ( 42074951F44019CE5ACED5F2 /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( 2975C03BF22FC6C9407CF999 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, D745A3CD92FDF2233DC810C1 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = Runner; productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 1300; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; DevelopmentTeam = 44R7KF7645; LastSwiftMigration = 1020; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.BackgroundModes = { enabled = 1; }; com.apple.Push = { enabled = 1; }; }; }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 97C146E51CF9000F007C117D; productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3961A6B22304917B0088A4FD /* Dark-Icon-App@3x.png in Resources */, 3961A6B52304917B0088A4FD /* Icon-App@1x.png in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 3961A6B62304917B0088A4FD /* Copper-Icon-App@3x.png in Resources */, 3961A6BA2304917B0088A4FD /* Icon-App@2x.png in Resources */, 7693980122F353FF009A1CF6 /* GoogleService-Info.plist in Resources */, 3961A6B32304917B0088A4FD /* Icon-App@3x.png in Resources */, 3961A6B72304917B0088A4FD /* Dark-Icon-App@2x.png in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 3961A6B82304917B0088A4FD /* Copper-Icon-App@1x.png in Resources */, 3961A6B42304917B0088A4FD /* Dark-Icon-App@1x.png in Resources */, 3961A6B92304917B0088A4FD /* Copper-Icon-App@2x.png in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 2975C03BF22FC6C9407CF999 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Thin Binary"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Run Script"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; D745A3CD92FDF2233DC810C1 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework", "${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework", "${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework", "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", "${BUILT_PRODUCTS_DIR}/FirebaseCoreDiagnostics/FirebaseCoreDiagnostics.framework", "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", "${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework", "${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework", "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", "${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework", "${BUILT_PRODUCTS_DIR}/barcode_scan/barcode_scan.framework", "${BUILT_PRODUCTS_DIR}/device_info/device_info.framework", "${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework", "${BUILT_PRODUCTS_DIR}/flutter_secure_storage/flutter_secure_storage.framework", "${BUILT_PRODUCTS_DIR}/flutter_webview_plugin/flutter_webview_plugin.framework", "${BUILT_PRODUCTS_DIR}/local_auth/local_auth.framework", "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", "${BUILT_PRODUCTS_DIR}/package_info/package_info.framework", "${BUILT_PRODUCTS_DIR}/path_provider_ios/path_provider_ios.framework", "${BUILT_PRODUCTS_DIR}/share/share.framework", "${BUILT_PRODUCTS_DIR}/shared_preferences_ios/shared_preferences_ios.framework", "${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework", "${BUILT_PRODUCTS_DIR}/vibrate/vibrate.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreDiagnostics.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/barcode_scan.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_webview_plugin.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_ios.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_ios.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/vibrate.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 97C146F31CF9000F007C117D /* main.m in Sources */, 761828152358E8C4001F6E1D /* clipboard.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 97C146FB1CF9000F007C117D /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 97C147001CF9000F007C117D /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Profile; }; 249021D4217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 44R7KF7645; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.3; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); PRODUCT_BUNDLE_IDENTIFIER = com.appditto.blaise; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "Runner-Swift.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; 97C147061CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 44R7KF7645; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.3; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); PRODUCT_BUNDLE_IDENTIFIER = com.appditto.blaise; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "Runner-Swift.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; 97C147071CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 44R7KF7645; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.3; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); PRODUCT_BUNDLE_IDENTIFIER = com.appditto.blaise; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "Runner-Swift.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( 97C147031CF9000F007C117D /* Debug */, 97C147041CF9000F007C117D /* Release */, 249021D3217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( 97C147061CF9000F007C117D /* Debug */, 97C147071CF9000F007C117D /* Release */, 249021D4217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } ================================================ FILE: ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme ================================================ ================================================ FILE: ios/Runner.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: ios/fastlane/Appfile ================================================ app_identifier("com.appditto.blaise") # The bundle identifier of your app apple_id("appdittoapps@gmail.com") # Your Apple email address itc_team_id("119652752") # App Store Connect Team ID team_id("44R7KF7645") # Developer Portal Team ID # For more information about the Appfile, see: # https://docs.fastlane.tools/advanced/#appfile ================================================ FILE: ios/fastlane/Deliverfile ================================================ release_notes({ 'default' => File.read('./fastlane/metadata/en-US/release_notes.txt'), 'en-US' => File.read('./fastlane/metadata/en-US/release_notes.txt') }); submission_information({ add_id_info_serves_ads: false, add_id_info_tracks_action: false, add_id_info_tracks_install: false, add_id_info_uses_idfa: false, content_rights_has_rights: true, content_rights_contains_third_party_content: true, export_compliance_platform: 'ios', export_compliance_compliance_required: false, export_compliance_encryption_updated: false, export_compliance_app_type: nil, export_compliance_uses_encryption: true, export_compliance_is_exempt: true, export_compliance_contains_third_party_cryptography: true, export_compliance_contains_proprietary_cryptography: false, export_compliance_available_on_french_store: true }); ================================================ FILE: ios/fastlane/Fastfile ================================================ # This file contains the fastlane.tools configuration # You can find the documentation at https://docs.fastlane.tools # # For a list of all available actions, check out # # https://docs.fastlane.tools/actions # # For a list of all available plugins, check out # # https://docs.fastlane.tools/plugins/available-plugins # default_platform(:ios) TEMP_KEYCHAIN_NAME_DEFAULT = "fastlane_flutter" TEMP_KEYCHAN_PASSWORD_DEFAULT = "temppassword" SIGH_PROVISIONING_PROFILE_NAME = "com.appditto.blaise AppStore" def delete_temp_keychain(name) delete_keychain( name: name ) if File.exist? File.expand_path("~/Library/Keychains/#{name}-db") end def create_temp_keychain(name, password) create_keychain( name: name, password: password, unlock: false, timeout: 0 ) end def ensure_temp_keychain(name, password) delete_temp_keychain(name) create_temp_keychain(name, password) end platform :ios do before_all do |lane, options| update_fastlane end desc "Build & sign iOS app" lane :build_ios do |options| begin sh "cp -Rp ../Runner.xcodeproj ../Runner.xcodeproj.bak" keychain_name = ENV['TEMP_KEYCHAIN_NAME'] || TEMP_KEYCHAIN_NAME_DEFAULT keychain_password = ENV['TEMP_KEYCHAIN_PASSWORD'] || TEMP_KEYCHAN_PASSWORD_DEFAULT ensure_temp_keychain(keychain_name, keychain_password) api_key = app_store_connect_api_key( key_id: ENV['API_KEY_ID'], issuer_id: ENV['API_KEY_ISSUER'], key_filepath: ENV['API_KEY_FILEPATH'], duration: 1200, in_house: false ) match( app_identifier: CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier), type: "appstore", keychain_name: keychain_name, keychain_password: keychain_password, readonly: false, skip_provisioning_profiles: true, api_key: api_key ) cert( keychain_path: "~/Library/Keychains/#{keychain_name}-db", keychain_password: keychain_password, api_key: api_key ) sigh( app_identifier: CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier), readonly: false, cert_id: lane_context[SharedValues::CERT_CERTIFICATE_ID], provisioning_name: ENV['SIGH_PROVISIONING_PROFILE_NAME'], ignore_profiles_with_different_name: true, api_key: api_key ) disable_automatic_code_signing( path: "./Runner.xcodeproj", team_id: CredentialsManager::AppfileConfig.try_fetch_value(:team_id), profile_name: lane_context[SharedValues::SIGH_NAME], profile_uuid: lane_context[SharedValues::SIGH_UUID], code_sign_identity: "iPhone Distribution" ) sh "./flutter_build.sh --clean" build_ios_app( export_options: { method: "app-store", provisioningProfiles: { "com.appditto.blaise" => ENV['SIGH_PROVISIONING_PROFILE_NAME'], } } ) rescue SystemExit, Interrupt raise rescue Exception => e delete_temp_keychain(keychain_name) sh "cp -Rp ../Runner.xcodeproj.bak/* ../Runner.xcodeproj/ || true" sh "rm -rf ../Runner.xcodeproj.bak" raise ensure delete_temp_keychain(keychain_name) sh "cp -Rp ../Runner.xcodeproj.bak/* ../Runner.xcodeproj/ || true" sh "rm -rf ../Runner.xcodeproj.bak" end end desc "Upload iOS app to app store" lane :deploy_ios do |options| api_key = app_store_connect_api_key( key_id: ENV['API_KEY_ID'], issuer_id: ENV['API_KEY_ISSUER'], key_filepath: ENV['API_KEY_FILEPATH'], duration: 1200, in_house: false ) if options[:testflight] upload_to_testflight( api_key: api_key, skip_submission: true, ipa: ENV['IPA_PATH'] ) else deliver( api_key: api_key, skip_metadata: false, skip_screenshots: true, submit_for_review: true, force: true, ipa: ENV['IPA_PATH'], precheck_include_in_app_purchases: false ) end end end ================================================ FILE: ios/fastlane/Matchfile ================================================ git_url("git@github.com:appditto/ios-certificates.git") storage_mode("git") type("development") # The default type, can be: appstore, adhoc, enterprise or development # app_identifier(["tools.fastlane.app", "tools.fastlane.app2"]) # username("user@fastlane.tools") # Your Apple Developer Portal username # For all available options run `fastlane match --help` # Remove the # in the beginning of the line to enable the other options # The docs are available on https://docs.fastlane.tools/actions/match ================================================ FILE: ios/fastlane/flutter_build.sh ================================================ #!/bin/bash cd ../../ if [ "$1" == "--clean" ] then echo "Running clean..." flutter clean else echo "Skipping clean..." fi flutter build ios --release --no-codesign ================================================ FILE: ios/fastlane/flutter_test.sh ================================================ #!/bin/bash cd ../../ echo "Running tests" flutter test ================================================ FILE: ios/fastlane/metadata/copyright.txt ================================================ 2022 Avenge Media LLC ================================================ FILE: ios/fastlane/metadata/en-US/apple_tv_privacy_policy.txt ================================================ ================================================ FILE: ios/fastlane/metadata/en-US/description.txt ================================================ Blaise is a simple, sleek & secure Pascal wallet. Features: - Create a new Pascal wallet or import an existing one - Manage multiple Pascal accounts (PASAs) - Send Pascal instantly to anyone, anywhere in the world - Change account names, transfer accounts, or list them for sale - Manage contacts in an intuitive, easy-to-use address book - View your accounts operations history - Customize the wallet with various themes - Select one of the 5+ languages available IMPORTANT: Remember to backup your private key and store it in a safe place. It is the only way to recover your funds if you sign out of the wallet or lose your device! If someone else gets your unencrypted private key, they will have full control over your funds. ================================================ FILE: ios/fastlane/metadata/en-US/keywords.txt ================================================ pascal, pascalcoin, cryptocurrency, crypto, wallet, bitcoin, ethereum, exchange, blockchain, flutter ================================================ FILE: ios/fastlane/metadata/en-US/marketing_url.txt ================================================ https://blaisewallet.com ================================================ FILE: ios/fastlane/metadata/en-US/name.txt ================================================ Blaise - Pascal Wallet ================================================ FILE: ios/fastlane/metadata/en-US/privacy_url.txt ================================================ https://appditto.github.io/blaise/privacy_policy.html ================================================ FILE: ios/fastlane/metadata/en-US/promotional_text.txt ================================================ Simple, Sleek & Secure Pascal Wallet ================================================ FILE: ios/fastlane/metadata/en-US/release_notes.txt ================================================ - Disallow 0-fee transactions - Performance improvements - Bug fixes ================================================ FILE: ios/fastlane/metadata/en-US/subtitle.txt ================================================ Securely send & receive Pascal ================================================ FILE: ios/fastlane/metadata/en-US/support_url.txt ================================================ https://help.blaisewallet.com/ ================================================ FILE: ios/fastlane/metadata/primary_category.txt ================================================ MZGenre.Finance ================================================ FILE: ios/fastlane/metadata/primary_first_sub_category.txt ================================================ ================================================ FILE: ios/fastlane/metadata/primary_second_sub_category.txt ================================================ ================================================ FILE: ios/fastlane/metadata/secondary_category.txt ================================================ MZGenre.Utilities ================================================ FILE: ios/fastlane/metadata/secondary_first_sub_category.txt ================================================ ================================================ FILE: ios/fastlane/metadata/secondary_second_sub_category.txt ================================================ ================================================ FILE: lib/appstate_container.dart ================================================ import 'dart:async'; import 'package:blaise_wallet_flutter/bus/events.dart'; import 'package:blaise_wallet_flutter/model/available_currency.dart'; import 'package:blaise_wallet_flutter/model/available_languages.dart'; import 'package:blaise_wallet_flutter/model/available_themes.dart'; import 'package:blaise_wallet_flutter/model/db/appdb.dart'; import 'package:blaise_wallet_flutter/model/db/contact.dart'; import 'package:blaise_wallet_flutter/network/model/request/subscribe_request.dart'; import 'package:blaise_wallet_flutter/network/model/response/subscribe_response.dart'; import 'package:blaise_wallet_flutter/network/ws_client.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/store/wallet/wallet.dart'; import 'package:blaise_wallet_flutter/themes.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:pascaldart/pascaldart.dart'; final Wallet walletState = Wallet(); const List PREACHED_SVG_ASSETS = [ 'assets/illustration_backup.svg', 'assets/illustration_backup_dark.svg', 'assets/illustration_new_wallet.svg', 'assets/illustration_new_wallet_dark.svg', 'assets/illustration_security.svg', 'assets/illustration_security_dark.svg', 'assets/illustration_two_options.svg', 'assets/illustration_two_options_dark.svg', 'assets/illustration_borrowed.svg', 'assets/illustration_borrowed_dark.svg', ]; class _InheritedStateContainer extends InheritedWidget { // Data is your entire state. In our case just 'User' final StateContainerState data; // You must pass through a child and your state. _InheritedStateContainer({ Key key, @required this.data, @required Widget child, }) : super(key: key, child: child); // This is a built in method which you can use to check if // any state has changed. If not, no reason to rebuild all the widgets // that rely on your state. @override bool updateShouldNotify(_InheritedStateContainer old) => true; } class StateContainer extends StatefulWidget { // You must pass through a child. final Widget child; StateContainer({@required this.child}); // This is the secret sauce. Write your own 'of' method that will behave // Exactly like MediaQuery.of and Theme.of // It basically says 'get the data from the widget of this type. static StateContainerState of(BuildContext context) { return context .dependOnInheritedWidgetOfExactType<_InheritedStateContainer>() .data; } @override StateContainerState createState() => StateContainerState(); } /// App InheritedWidget /// This is where we handle the global state and also where /// we interact with the server and make requests/handle+propagate responses /// /// Basically the central hub behind the entire app class StateContainerState extends State { // Theme BaseTheme curTheme = BlaiseLightTheme(); // Language LanguageSetting curLanguage = LanguageSetting(AvailableLanguage.DEFAULT); // Currency String currencyLocale; Locale deviceLocale = Locale('en', 'US'); AvailableCurrency curCurrency = AvailableCurrency(AvailableCurrencyEnum.USD); // Helper FN to precache SVG assets for performance Future _precacheSvgs() async { PREACHED_SVG_ASSETS.forEach((asset) { precachePicture( ExactAssetPicture(SvgPicture.svgStringDecoder, asset), context); }); } // Change language void updateLanguage(LanguageSetting language) { setState(() { curLanguage = language; }); } // Change the theme Future updateTheme(ThemeSetting theme, {bool setIcon = true}) async { if (theme != null && theme.getTheme() != curTheme) { if (mounted) { setState(() { this.curTheme = theme.getTheme(); }); } await sl.get().setTheme(theme); } if (setIcon) { AppIcon.setAppIcon(theme.getTheme().appIcon); } } /// Add donations contact if it hasnt already been added Future _addSampleContact() async { bool contactAdded = await sl.get().getFirstContactAdded(); if (!contactAdded) { bool addressExists = await sl .get() .contactExistsWithAccount(AccountNumber.fromInt(1185729)); if (addressExists) { return; } bool nameExists = await sl.get().contactExistsWithName("BlaiseDonations"); if (nameExists) { return; } await sl.get().setFirstContactAdded(true); Contact c = Contact( name: "BlaiseDonations", account: AccountNumber.fromInt(1185729), payload: "Thanks!"); await sl.get().saveContact(c); } } StreamSubscription _connStatusSub; StreamSubscription _subscribeEventSub; StreamSubscription _priceEventSub; StreamSubscription _newOpSub; // Register RX event listenerss void _registerBus() { _subscribeEventSub = EventTaxiImpl.singleton().registerTo().listen((event) { handleSubscribeResponse(event.response); }); _priceEventSub = EventTaxiImpl.singleton().registerTo().listen((event) { // PriceResponse's get pushed periodically, it wasn't a request we made so don't pop the queue walletState.btcPrice = event.response.btcPrice; walletState.localCurrencyPrice = event.response.price; }); _connStatusSub = EventTaxiImpl.singleton().registerTo().listen((event) { if (event.status == ConnectionStatus.CONNECTED) { walletState.requestUpdate(); } else if (event.status == ConnectionStatus.DISCONNECTED && !sl.get().suspended) { sl.get().initCommunication(); } }); _newOpSub = EventTaxiImpl.singleton() .registerTo() .listen((event) { walletState.addNewOp(event.operation); }); } void _destroyBus() { if (_connStatusSub != null) { _connStatusSub.cancel(); } if (_subscribeEventSub != null) { _subscribeEventSub.cancel(); } if (_priceEventSub != null) { _priceEventSub.cancel(); } if (_newOpSub != null) { _newOpSub.cancel(); } } @override void initState() { super.initState(); _registerBus(); // Precache SVG Assets _precacheSvgs(); // Set initial theme sl.get().getTheme().then((themeSetting) { updateTheme(themeSetting, setIcon: false); }); // Add initial contact if not already present _addSampleContact(); // Set currency locale here for the UI to access sl.get().getCurrency(deviceLocale).then((currency) { setState(() { currencyLocale = currency.getLocale().toString(); curCurrency = currency; }); }); // Get default language setting sl.get().getLanguage().then((language) { setState(() { curLanguage = language; }); }); } @override void dispose() { _destroyBus(); super.dispose(); } /// Handle account_subscribe response void handleSubscribeResponse(SubscribeResponse response) { // Set currency locale here for the UI to access sl.get().getCurrency(deviceLocale).then((currency) { setState(() { currencyLocale = currency.getLocale().toString(); curCurrency = currency; }); }); // Server gives us a UUID for future requests on subscribe if (response.uuid != null) { sl.get().setUuid(response.uuid); } walletState.localCurrencyPrice = response.price; walletState.btcPrice = response.btcPrice; walletState.hasExceededBorrowLimit = !response.borrowEligible; walletState.hasReceivedSubscribeResponse = true; sl.get().pop(); sl.get().processQueue(); } @override Widget build(BuildContext context) { return _InheritedStateContainer( data: this, child: widget.child, ); } } ================================================ FILE: lib/bus/authenticated_event.dart ================================================ import 'package:event_taxi/event_taxi.dart'; enum AUTH_EVENT_TYPE { SEND, TRANSFER, CHANGE, BACKUP, LIST_FORSALE, DELIST_FORSALE } class AuthenticatedEvent implements Event { final AUTH_EVENT_TYPE authType; AuthenticatedEvent(this.authType); } ================================================ FILE: lib/bus/conn_status_event.dart ================================================ import 'package:event_taxi/event_taxi.dart'; // Bus event for connection status changing enum ConnectionStatus { CONNECTED, DISCONNECTED } class ConnStatusEvent implements Event { final ConnectionStatus status; ConnStatusEvent({this.status}); } ================================================ FILE: lib/bus/contact_added_event.dart ================================================ import 'package:blaise_wallet_flutter/model/db/contact.dart'; import 'package:event_taxi/event_taxi.dart'; class ContactAddedEvent implements Event { final Contact contact; ContactAddedEvent({this.contact}); } ================================================ FILE: lib/bus/contact_modified_event.dart ================================================ import 'package:blaise_wallet_flutter/model/db/contact.dart'; import 'package:event_taxi/event_taxi.dart'; class ContactModifiedEvent implements Event { final Contact contact; ContactModifiedEvent({this.contact}); } ================================================ FILE: lib/bus/contact_removed_event.dart ================================================ import 'package:blaise_wallet_flutter/model/db/contact.dart'; import 'package:event_taxi/event_taxi.dart'; class ContactRemovedEvent implements Event { final Contact contact; ContactRemovedEvent({this.contact}); } ================================================ FILE: lib/bus/daemon_changed_event.dart ================================================ import 'package:event_taxi/event_taxi.dart'; class DaemonChangedEvent implements Event { final String newDaemon; DaemonChangedEvent({this.newDaemon}); } ================================================ FILE: lib/bus/disable_lock_timeout_event.dart ================================================ import 'package:event_taxi/event_taxi.dart'; class DisableLockTimeoutEvent implements Event { final bool disable; DisableLockTimeoutEvent({this.disable}); } ================================================ FILE: lib/bus/events.dart ================================================ export 'authenticated_event.dart'; export 'contact_modified_event.dart'; export 'contact_added_event.dart'; export 'contact_removed_event.dart'; export 'update_history_event.dart'; export 'daemon_changed_event.dart'; export 'payload_changed_event.dart'; export 'disable_lock_timeout_event.dart'; export 'conn_status_event.dart'; export 'subscribe_event.dart'; export 'price_event.dart'; export 'new_operation_event.dart'; ================================================ FILE: lib/bus/new_operation_event.dart ================================================ import 'package:event_taxi/event_taxi.dart'; import 'package:pascaldart/pascaldart.dart'; class NewOperationEvent implements Event { final PascalOperation operation; NewOperationEvent({this.operation}); } ================================================ FILE: lib/bus/payload_changed_event.dart ================================================ import 'package:event_taxi/event_taxi.dart'; class PayloadChangedEvent implements Event { final String payload; PayloadChangedEvent({this.payload}); } ================================================ FILE: lib/bus/price_event.dart ================================================ import 'package:event_taxi/event_taxi.dart'; import 'package:blaise_wallet_flutter/network/model/response/price_response.dart'; class PriceEvent implements Event { final PriceResponse response; PriceEvent({this.response}); } ================================================ FILE: lib/bus/subscribe_event.dart ================================================ import 'package:blaise_wallet_flutter/network/model/response/subscribe_response.dart'; import 'package:event_taxi/event_taxi.dart'; class SubscribeEvent implements Event { final SubscribeResponse response; SubscribeEvent({this.response}); } ================================================ FILE: lib/bus/update_history_event.dart ================================================ import 'package:event_taxi/event_taxi.dart'; class UpdateHistoryEvent implements Event { } ================================================ FILE: lib/constants.dart ================================================ class AppConstants { static const String DEFAULT_RPC_HTTP_URL = 'https://blaiseapi.appditto.com/rawrpc'; static const String WS_API_URL = 'wss://blaiseapi.appditto.com'; static const String EULA_URL = 'https://appditto.github.io/blaise/eula.html'; static const String PRIVACY_POLICY_URL = 'https://appditto.github.io/blaise/privacy_policy.html'; } ================================================ FILE: lib/l10n/intl_ar.arb ================================================ { "newPrivateKeyButton": "مفتاح خاص جديد", "importPrivateKeyButton": "استيراد مفتاح خاص", "gotItButton": "حصلت عليه!", "goBackButton": "رجوع", "copyButton": "نسخ", "copiedButton": "تم النسخ", "keyCopiedButton": "تم نسخ المفتاح", "iHaveBackedItUpButton": "قمت بالنسخ الإحتياطي", "yesImSureButton": "نعم، أنا متأكد", "noGoBackButton": "لا، رجوع", "getAnAccountButton": "أحصل على حساب", "getAFreeAccountButton": "أحصل على حساب بالمجان", "buyAnAccountButton": "إشتري حساب", "sendConfirmationButton": "ارسل تأكيد", "confirmButton": "تأكيد", "borrowAnAccountButton": "استعر حساباً", "importButton": "إستيراد", "receiveButton": "استلام", "sendButton": "إرسال", "copyAddressButton": "نسخ الحساب", "copiedAddressButton": "تم نسخ الحساب", "addToContactsButton": "أضف إلى جهات الإتصال", "operationDetailsButton": "تفاصيل العملية", "openInExplorerButton": "افتح في المستكشف", "requestButton": "طلب", "addAPayloadButton": "+ أضف ملاحظة (Payload)", "addADurationButton": "+ أضف مدة", "scanQRCodeButton": "مسح رمز QR", "cancelButton": "إلغاء", "closeButton": "إغلاق", "changeNameButton": "تغيير الإسم", "transferButton": "نقل", "listForSaleButton": "إدراج للبيع", "createPrivateSaleButton": "إنشاء بيع خاص", "yesAddFeeButton": "نعم، أضف رسوم", "unlockButton": "إلغاء قفل", "unlockWithBiometricsButton": "إلغاء قفل بالبصمة", "unlockWithPINButton": "إلغاء القفل برقم سري", "setToDefaultButton": "اجعله افتراضيا", "addContactButton": "أضف جهة إتصال", "encryptedKeyButton": "مفتاح مشفر", "unencryptedKeyButton": "مفتاح غير مشفر", "encryptButton": "تشفير", "showButton": "اظهر", "hideButton": "إخفي", "copyEncryptedKeyButton": "نسخ مفتاح مشفر", "copyUnencryptedKeyButton": "نسخ مفتاح غير مشفر", "copyKeyButton": "نسخ مفتاح", "copyPublicKeyButton": "نسخ المفتاح عام", "deletePrivateKeyAndLogoutButton": "احذف المفتاح الخاص\nوتسجيل خروج", "searchForNameButton": "بحث عن اسم", "searchAccountNameButton": "ابحث باسم الحساب", "searchNameButton": "إبحث عن إسم", "okayGoBackButton": "موافق، رجوع", "nextButton": "التالي", "welcomeParagraph": "مرحبا بك في محفظة بليز، للبدء، يمكنك إنشاء مفتاح خاص جديد أو إستيراد مفتاح موجود", "newKeySecurityParagraph": "في الشاشة التالية، سوف ترى مفتاحك الخاص الجديد، أنه يمثل كل شئ، بدونه أنت لا تملك أموالك، لذلك من الضروري أن تحفظه في مكان أمن، ولا تشاركة مع أحد.", "uninstallDisclaimerParagraph": "إذا فقدت جهازك أو الغيت تسطيب محفظة بليز، سوف تحتاج للـ \"مفتاح الخاص\" لكي تستعيد أموالك.", "newPrivateKeyParagraph": "يوجد أدناه المفتاح الخاص لمحفظتك الجديدة، من الضروري أن تحفظه في مكان أمن ولا تشاركه مع أحد، ولا تقم بأخذ لقطة للشاشة أو تركه في ملف على جهازك، يستحسن أن تكتبه بيدك على ورقة وتحفظة في مكان أمن.", "newKeyBackUpConfirmParagraph": "هل متأكد من أنك قمت بعمل نسخة إحتياطية من مفتاحك الخاص في مكان أمن؟", "newWalletGreetingParagraph": "مرحباً بك في محفظة بليز.\nيمكنك البدء بالحصول على حساب جديد.", "getAccountFirstParagraph": "هناك خيارين للحصول على حسابك الأول:", "getAccountSecondParagraph": "1. يمكنك الحصول على حساب مجاني بإستخدام رقم تليفونك، فقط حساب واحد لكل رقم تليفون", "getAccountThirdParagraph": "2. يمكنك شراء حساب/حسابات مقابل %1 باسكال (%2).", "enterPhoneNumberParagraph": "أدخل رقم تليفونك أدناه.", "enterConfirmationCodeParagraph": "لقد أرسلنا لك رمز التأكيد، يرجى إدخاله أدناه.", "borrowStarted": "بدأت عملية الشراء لـ %1", "borrowAccountParagraph": "لشراء حساب، أولا سوف نعطيك واحد (على سبيل الإعارة)، إذا أرسلت على الأقل %1 باسكال(%2) إلى هذا الحساب في خلال %3 أيام, الحساب سوف يكون ملكك و %1 باسكال سوف تخصم من رصيدك تلقائياً.\nغير ذلك، سوف نسترد الحساب بعد نهاية %3 أيام ولن يكون ملكك.\nلذلك من المستحسن أولا إرسال مبلغ صغير قبل أن تمتلك الحساب.", "importPrivateKeyParagraph": "أدخل مفتاحك الخاص أدناه.", "looksLikeEncryptedKeyParagraph": "يبدو أن هذا المفتاح الخاص مشفراً، يرجى إدخال كلمة المرور لفك تشفيره واستيراده.", "urlChangedToParagraph": "URL تم تغييره إلى %1", "backupKeyFirstParagraph": "لديك خياران لإجراء نسخ احتياطي للمفتاح الخاص:", "backupKeySecondParagraph": "1. مشفر، مما يعني أنه محمي بكلمة مرور.", "backupKeyThirdParagraph": "2. غير مشفر، مما يعني أنه خام وغير محمي بكلمة مرور.", "backupKeyFourthParagraph": "نوصي بحفظ النسخة غير المشفرة في وضع عدم الاتصال بالإنترنت (أوفلاين)، عن طريق كتابتها على ورقة، و يمكنك حفظ النسخة المشفرة على مدير كلمات المرور (باسورد مانجر) لراحتك.", "encryptKeyParagraph": "قم بإنشاء كلمة مرور جديدة لتشفير مفتاحك الخاص.", "backupEncryptedKeyFirstParagraph": "أدناه هو مفتاحك الخاص المشفر. محمي بكلمة مرور. يمكنك حفظه بأمان على مدير كلمات المرور (باسورد مانجر) لراحتك.", "backupEncryptedKeySecondParagraph": "نظرًا لأنه مشفر بكلمة مرور، إذا فقدت أو نسيت كلمة المرور، فلن تتمكن من فك تشفيره والوصول إلى أموالك.", "backupUnencryptedKeyParagraph": "يوجد أدناه مفتاحك الخاص غير المشفر. ليس محمي بكلمة مرور، مما يعني أنه من الأهمية حفظه في مكان آمن وغير متصل بالإنترنت. نوصي بكتابته على ورقة.", "publicKeyParagraph": "أدناه هو المفتاح العام الخاص بك. كما يوحي الاسم، الغرض منه هو المشاركة بشكل عام لإستقبال الحسابات عليه وانتقال ملكيتها للمفتاح الخاص التابع لهذا المفتاح العام.", "borrowedAccountParagraph": "هذا هو حساب معار إليك.\nإذا أرسلت على الأقل %1 باسكال إلى هذا الحساب في خلال %2 أيام, %3 ساعات, و %4 دقائق, سوف يكون ملكك.", "borrowedAccountPaidParagraph": "تم شراء الحساب!\nنقله إليك قيد المعالجة حاليًا. تستغرق هذه العملية عادةً 15 دقيقة, في بعض الحالات قد تستغرق وقتا أطول قليلا.", "logoutFirstDisclaimerParagraph": "سيؤدي تسجيل الخروج إلى إزالة المفتاح الخاص وكافة البيانات المتعلقة بمحفظتك من على هذا الجهاز. إذا لم يتم عمل نسخة احتياطية من المفتاح الخاص، فلن تتمكن من الوصول إلى أموالك مرة أخرى. إذا قمت بعمل نسخة إحتياطية من مفتاح الخاص، فلا داعي للقلق.", "logoutSecondDisclaimerParagraph": "هل أنت متأكد من أنك قمت بعمل نسخة احتياطية من المفتاح الخاص؟ إذا قمت بعمل نسخة إحتياطية من مفتاحك الخاص، فلا داعي للقلق بشأنه.", "sendingConfirmParagraph": "برجاء مراجعة تفاصيل العملية قبل الإرسال.", "sentParagraph": "تم إرسال العملية بنجاح.", "changeNameParagraph": "أدخل اسمًا أدناه لتغيير اسم الحساب.", "changingNameParagraph": "تأكيد اسم حسابك الجديد للمتابعة.", "changedNameParagraph": "تم تغيير اسم حسابك بنجاح.", "transferParagraph": "أدخل \"مفتاح عام\" أدناه لنقل ملكية هذا الحساب إليه.", "transferringParagraph": "قم بتأكيد \"المفتاح العام\" أدناه لنقل ملكية هذا الحساب إليه.", "transferredParagraph": "تم نقل حسابك بنجاح إلى \"المفتاح العام\" أدناه.", "listForSaleParagraph": "لعرض هذا الحساب للبيع، أدخل سعرًا و\"الحساب\" الذي سيتلقى الدفع.", "listingForSaleParagraph": "برجاء تأكيد السعر والحساب الذي سيتلقى الدفع.", "listedForSaleParagraph": "تم إدراج حسابك للبيع بنجاح. سنخبرك إذا ما قام شخص ما بشرئه.", "createPrivateSaleParagraph": "أدخل السعر, الحساب الذي سيتلقى الدفع, والمفتاح العام أدناه لإنشاء بيع خاص لهذا الحساب.", "creatingPrivateSaleParagraph": "تأكد من المعلومات أدناه.", "createdPrivateSaleParagraph": "تم إنشاء البيع الخاص بنجاح. سنخبرك إذا تم شراؤه.", "delistFromSaleParagraph": "تأكد من رغبتك في حذف هذا الحساب من البيع.", "delistedFromSaleParagraph": "تم حذف حسابك من البيع بنجاح.", "feeRequiredParagraph": "هذه المعاملة تتطلب رسوم.", "feeConfirmAmountParagraph": "يرجى تأكيد إضافة رسوم %1 باسكال لهذه العملية للمتابعة.\n.", "keyTypeNotSupportedParagraph": "لا تدعم محفظة بليز هذا النوع من المفاتيح الخاصة. يمكنك إنشاء مفتاح خاص جديد ونقل حساباتك إليه باستخدام محفظة أخرى.", "enterPINToUnlockParagraph": "أدخل رمز المرور لإلغاء القفل", "authenticateToUnlockParagraph": "المصادقة لإلغاء القفل", "manyFailedAttemptsParagraph": "محاولات كثيرة فاشلة لإلغاء القفل", "authenticateToChangeNameParagraph": "المصادقة لتغيير اسم الحساب إلى \"%1\"", "authenticateToDelistParagraph": "المصادقة لحذف الحساب من البيع", "authenticateToListForSaleParagraph": "المصادقة لإدراج الحساب للبيع", "authenticateToCreatePrivateSaleParagraph": "المصادقة لإنشاء بيع خاص", "authenticateToTransferParagraph": "المصادقة لنقل الحساب", "authenticateToSendParagraph": "المصادقة لإرسال %1 باسكال", "authenticateToBackUpParagraph": "المصادقة لإجراء نسخ احتياطي للمفتاح الخاص", "invalidPINParagraph": "رمز المرور غير صحيح", "noMatchPINParagraph": "رمز المرور غير متطابق", "confirmPINParagraph": "تأكيد رمز المرور", "enterPINParagraph": "أدخل رمز المرور", "createPINParagraph": "إنشاء 6 أرقام رمز مرور", "addedToContactsParagraph": "%1 تم إضافته إلى جهات الإتصال", "removedFromContactsParagraph": "تم إزالة %1 من جهات الإتصال", "failedToRemoveFromContactsParagraph": "فشل في إزالة %1 من جهات الإتصال", "successfullyImportedContactsParagraph": "تم إستيراد %1 جهات إتصال بنجاح", "checkOutBlaiseParagraph": "أنظر، بليز! محفظة \"باسكال\" بسيطة وأنيقة وآمنة لنظامي أندرويد و آي أو إس: https://blaisewallet.com", "newAccountParagraph": "هذا هو حسابك الجديد.\nبمجرد أن تستقبل باسكال, سوف تظهر العمليات كما موضح أدناه.", "settingsHeader": "إعدادات", "preferencesHeader": "تفضيلات", "currencyHeader": "العملة", "languageHeader": "اللغة", "languageColonHeader": "اللغة:", "systemDefaultHeader": "إفتراضي النظام", "themeHeader": "المظهر", "themeLightHeader": "فاتح", "themeDarkHeader": "مظلم", "themeCopperHeader": "نحاسي", "notificationsHeader": "الإشعارات", "securityHeader": "الأمان", "authenticationMethodHeader": "طريقة المصادقة", "authenticationPINHeader": "رمز المرور", "authenticationBiometricsHeader": "البصمة", "authenticateOnLaunchHeader": "المصادقة عند التشغيل", "yesHeader": "نعم", "noHeader": "لا", "automaticallyLockHeader": "قفل تلقائي", "lockInstantHeader": "فوراً", "lock1Header": "بعد %1 دقيقة", "lock5Header": "بعد %1 دقائق", "lock15Header": "بعد %1 دقائق", "lock30Header": "بعد %1 دقائق", "lock60Header": "بعد %1 دقائق", "defaultHeader": "إفتراضي", "manageHeader": "إدارة", "contactsHeader": "جهات الإتصال", "backUpPrivateKeyHeader": "نسخ إحتياطي للمفتاح الخاص", "viewPublicKeyHeader": "عرض المفتاح العام", "shareHeader": "مشاركة بليز", "logoutHeader": "تسجيل خروج", "privacyPolicyHeader": "سياسة الخصوصية", "changeAccountNameHeader": "تغيير اسم الحساب", "transferAccountHeader": "نقل ملكية الحساب", "listAccountForSaleHeader": "إدراج الحساب للبيع", "createPrivateSaleHeader": "إدارج الحساب للبيع الخاص", "delistFromSaleHeader": "حذف الحساب من البيع", "getAccountSheetHeader": "الحصول على حساب", "freeAccountSheetHeader": "حساب مجاني", "buyAccountSheetHeader": "شراء حساب", "sendSheetHeader": "إرسال", "sendingSheetHeader": "جارٍ الإرسال", "sentSheetHeader": "تم الإرسال", "requestSheetHeader": "طلب", "changeNameSheetHeader": "تغيير الإسم", "changingNameSheetHeader": "جارٍ التغيير", "changedNameSheetHeader": "تم التغيير", "transferSheetHeader": "نقل", "transferringSheetHeader": "جارٍ النقل", "transferredSheetHeader": "تم النقل", "listForSaleSheetHeader": "إدراج للبيع", "listingForSaleSheetHeader": "جارٍ الإدراج", "listedForSaleSheetHeader": "تم الإدراج", "createPrivateSaleSheetHeader": "بيع خاص", "creatingPrivateSaleSheetHeader": "إنشاء", "createdPrivateSaleSheetHeader": "تم الإنشاء", "delistingSheetHeader": "جارٍ الحذف", "delistedSheetHeader": "تم الحذف", "addContactSheetHeader": "إضافة جهة إتصال", "contactSheetHeader": "جهة إتصال", "publicKeySheetHeader": "مفتاح عام", "privateKeySheetHeader": "مفتاح خاص", "backUpSheetHeader": "نسخ إحتياطي", "encryptSheetHeader": "تشفير", "changeDaemonSheetHeader": "تغيير Daemon", "securityFirstHeader": "الأمان أولا!", "newPrivateKeyHeader": "مفتاح خاص جديد", "importPrivateKeyHeader": "إستيراد مفتاح خاص", "decryptAndImportKeyHeader": "فك تشفير & إستيراد", "backUpKeyHeader": "نسخ إحتياطي لمفاتيحك", "lockedHeader": "مقفل", "privateKeyTextFieldHeader": "مفتاح خاص", "passwordTextFieldHeader": "كلمه مرور", "newPasswordTextFieldHeader": "كلمة مرور جديدة", "confirmPasswordTextFieldHeader": "تأكيد كلمة المرور", "confirmTextFieldHeader": "تأكيد", "countryCodeTextFieldHeader": "كود البلد", "phoneNumberTextFieldHeader": "رقم التليفون", "confirmationCodeTextFieldHeader": "كود التفعيل", "accountTextFieldHeader": "حساب", "addressTextFieldHeader": "رقم الحساب", "contactNameTextFieldHeader": "أسم جهة الإتصال", "amountTextFieldHeader": "المبلغ", "payloadTextFieldHeader": "ملاحظة (Payload)", "nameTextFieldHeader": "إسم", "newAccountNameTextFieldHeader": "إسم الحساب الجديد", "publicKeyTextFieldHeader": "مفتاح عام", "priceTextFieldHeader": "السعر", "receivingAccountTextFieldHeader": "الحساب متلقي الدفع", "durationTextFieldHeader": "المدة", "feeTextFieldHeader": "رسوم", "otherOperationsHeader": "معاملات أخرى", "warningHeader": "تحذير", "areYouSureHeader": "هل أنت متأكد؟", "addFeeHeader": "إضافة رسوم", "keyTypeNotSupportedHeader": "المفتاح غير مدعوم", "accountToSendFromHeader": "حساب لإرسال من", "sentHeader": "صادر", "receivedHeader": "وأرد", "nameChangedHeader": "تم تغيير الإسم", "listedForSaleHeader": "تم إدراجه للبيع", "privateSaleHeader": "بيع خاص", "delistedFromSaleHeader": "تم حذفه من البيع", "delistedHeader": "تم حذفه", "undefinedHeader": "غير محدد", "transferredHeader": "تم نقله", "balanceHeader": "الرصيد", "totalBalanceHeader": "إجمالي الرصيد", "accountBalanceHeader": "رصيد الحساب", "accountsHeader": "الحسابات", "operationsHeader": "العمليات", "encryptThePayloadHeader": "تشفير الملاحظة (Payload)", "encryptPayloadHeader": "تشفير الملاحظة (Payload)", "forSaleHeader": "للبيع", "borrowedHeader": "حساب معار", "borrowedTransferredHeader": "جارٍ النقل", "borrowedAccountHeader": "حساب معار", "feeColonHeader": "رسوم:", "pendingHeader": "قيد الانتظار", "onHeader": "تعمل", "offHeader": "لا تعمل", "priceRequiredError": "السعر مطلوب", "amountRequiredError": "المبلغ مطلوب", "nameRequiredError": "الإسم مطلوب", "zeroPriceError": "لا يمكن أن يكون السعر 0", "zeroAmountError": "لا يمكن أن يكون المبلغ 0", "invalidAccountNameError": "اسم حساب غير صحيح", "invalidReceivingAccountError": "حساب المستلم غير صحيح", "invalidPublicKeyError": "مفتاح عام غير صالح", "invalidPrivateKeyError": "مفتاح خاص غير صالح", "invalidAddressError": "حساب غير صحيح", "invalidAccountError": "حساب غير صحيح", "invalidDestinationError": "وجهة غير صالحة", "insufficientBalanceError": "رصيد غير كافي", "threeCharacterNameError": "يجب أن يكون 3 أحرف على الأقل", "contactDoesntExistError": "جهة الاتصال غير موجودة", "contactAlreadyExistsError": "جهة الاتصال موجود بالفعل", "cantSendToYourselfError": "لا يمكن أن ترسل إلى نفسك", "somethingWentWrongError": "هناك شئ خاطئ، برجاء المحاولة فى وقت لاحق", "failedToEncryptPayloadError": "فشل في تشفير الملاحظة (Payload)", "emptyPasswordError": "كلمة المرور لا يمكن أن تكون فارغة", "noMatchPasswordError": "كلمات المرور غير متطابقة", "invalidPasswordError": "كلمة مرور خاطئة", "didNotGetResponseError": "لم نحصل على استجابة من الخادم\n", "noContactsToExportError": "لا جهات اتصال للتصدير", "noContactsToImportError": "لا يوجد جهات اتصال لاستيرادها", "failedToImportContactsError": "فشل في استيراد جهات الاتصال", "blockchainRewardOPDetails": "مكافأة البلوكشين (%1)", "transactionOPDetails": "العملية (%1)", "changeKeyOPDetails": "تغيير المفتاح (%1)", "recoverFundsOPDetails": "إسترداد الأموال (%1)", "listAccountForSaleOPDetails": "إدراج الحساب للبيع (%1)", "delistAccountOPDetails": "إلغاء إدراج الحساب (%1)", "buyAccountOPDetails": "شراء حساب (%1)", "changeKeySignedOPDetails": "تغيير مفتاح التوقيع (%1)", "changeAccountInfoOPDetails": "تغيير معلومات الحساب (%1)", "multioperationOPDetails": "عملية متعددة (%1)", "unknownOPDetails": "غير معروف (%1)", "sendingAccountOPDetails": "الحساب المُرسِل", "receivingAccountOPDetails": "الحساب المستقبِل", "changingAccountOPDetails": "تغيير الحساب", "sendAmountOPDetails": "إرسال مبلغ", "payloadOPDetails": "ملاحظة (Payload)", "newPublicKeyOPDetails": "مفتاح عام جديد", "newNameOPDetails": "اسم جديد", "sellerAccountOPDetails": "حساب البائع", "accountPriceOPDetails": "سعر الحساب", "lockedUntilBlockOPDetails": "مغلق حتى الكتلة", "blockOPDetails": "كتلة", "timeOPDetails": "الوقت", "naOPDetails": "غير موجود", "ophashOPDetails": "معرف المعاملة", "feeOPDetails": "رسوم", "noperationOPDetails": "المعاملات المرسلة", "accountOPDetails": "حساب", "noResultsFound": "لا توجد نتائج", "getAccountThirdParagraphAlternative": "2. يمكنك شراء حساب واحد مقابل %1 باسكال (%2). مسموح فقط بـ \"حساب\" واحد لكل مستخدم", "getAccountThirdParagraphAlternative2": "2. يمكنك شراء \"حساب\" مقابل %1 باسكال (%2). يمكنك شراء حتى %3 حساب.", "receiveAccountButton": "استلام حساب", "receiveAnAccountButton": "استلام حساب", "okayButton": "موافق", "liveSupportButton": "مساعدة؟", "invalidPhoneNumberParagraph": "رقم التليفون غير صالح", "confirmationCodeError": "فشل التحقق، تأكد من كتابة الرمز المرسل إليك بشكل صحيح", "freepasaComplete": "تم بنجاح، حسابك الجديد سيظهر بعد 1 تأكيد من الشبكة", "unconfirmedAccountHeader": "حساب (لم يتم تأكيده)", "unconfirmedAccountParagraph": "هذا حساب غير مؤكد. تم نقله إليك، ولكن بإنتظار 1 تأكيد من الشبكة قبل ان تتمكن نم إستخدامه. قد تستغرق هذه العملية 5 دقائق، وعندما تكتمل سيكون بإمكانك استخدام الحساب.", "connectingHeader": "جارٍ الإتصال" } ================================================ FILE: lib/l10n/intl_ca.arb ================================================ { "newPrivateKeyButton": "Nova clau privada", "importPrivateKeyButton": "Importa clau privada", "gotItButton": "Ho tinc!", "goBackButton": "Torna", "copyButton": "Còpia", "copiedButton": "Copiat", "keyCopiedButton": "Clau copiada", "iHaveBackedItUpButton": "Copia realitzada", "yesImSureButton": "Sí, n'estic segur", "noGoBackButton": "No, torna enrere", "getAnAccountButton": "Obtenir un compte", "getAFreeAccountButton": "Obtenir un compte gratuït", "buyAnAccountButton": "Comprar un compte", "sendConfirmationButton": "Confirmar l'enviament", "confirmButton": "Confirmar", "borrowAnAccountButton": "Elimina un compte", "importButton": "Importa", "receiveButton": "Rebre", "sendButton": "Enviar", "copyAddressButton": "Copiar adreça", "copiedAddressButton": "Adreça copiada", "addToContactsButton": "Afegir a contactes", "operationDetailsButton": "Detalls de l'operació", "openInExplorerButton": "Obriu a l'Explorador", "requestButton": "Sol·licitud", "addAPayloadButton": "+ Afegir un concepte (Payload)", "addADurationButton": "+ Afegir una Durada", "scanQRCodeButton": "Escaneig del codi QR", "cancelButton": "Cancel·lar", "closeButton": "Tanca", "changeNameButton": "Canviar el nom", "transferButton": "Transferència", "listForSaleButton": "Posar a la venda", "createPrivateSaleButton": "Crear una venda privada", "yesAddFeeButton": "Sí, afegeix comissió", "unlockButton": "Desbloquejar", "unlockWithBiometricsButton": "Desbloquejar amb Biometrics", "unlockWithPINButton": "Desbloquegeu amb el PIN", "setToDefaultButton": "Establir a valors predeterminats", "changeDaemonButton": "Canvia de servidor", "addContactButton": "Afegeix contacte", "encryptedKeyButton": "Clau xifrada", "unencryptedKeyButton": "Clau no xifrada", "encryptButton": "Xifra", "showButton": "Mostra", "hideButton": "Amaga", "copyEncryptedKeyButton": "Copia la clau xifrada", "copyUnencryptedKeyButton": "Copia la clau sense xifrar", "copyKeyButton": "Copia la clau privada", "copyPublicKeyButton": "Copia la clau pública", "deletePrivateKeyAndLogoutButton": "Suprimeix la clau privada\nTanca la sessió", "searchForNameButton": "Buscar nom", "searchAccountNameButton": "Cerca el nom del compte", "searchNameButton": "Nom de la cerca", "okayGoBackButton": "D'acord, torna", "nextButton": "Pròxim", "welcomeParagraph": "Benvingut a la cartera Blaise. Per començar, podeu crear una clau privada o importar-ne una.", "newKeySecurityParagraph": "A la pantalla següent, veureu la vostra clau privada nova. És una contrasenya per accedir als vostres fons. És fonamental que el feu una còpia de seguretat i no el compartiu mai amb ningú.", "uninstallDisclaimerParagraph": "Si perdeu el dispositiu o desinstalgeu Blaise Wallet, necessitareu la clau privada per recuperar els vostres fons.", "newPrivateKeyParagraph": "A continuació, es mostra la clau privada de la nova cartera. És fonamental que feu una còpia de seguretat de la clau privada i no la guardeu mai com a text de pantalla o captura de pantalla. Recomanem escriure-la en un tros de paper i guardar-la fora de línia.", "newKeyBackUpConfirmParagraph": "Esteu segur que heu creat una còpia de seguretat de la clau privada de la nova billetera?", "newWalletGreetingParagraph": "Benvingut a Blaise Wallet . \n Podeu començar obtenint un compte.", "getAccountFirstParagraph": "Hi ha dues opcions per obtenir el teu primer compte:", "getAccountSecondParagraph": "1- Podeu obtenir un compte gratuït mitjançant el vostre número de telèfon. Només es permet un compte per número de telèfon. ", "getAccountThirdParagraph": "2- Podeu comprar tants comptes com vulgueu per % 1 Pascal (% 2). ", "enterPhoneNumberParagraph": "Introduïu el vostre número de telèfon a continuació.", "enterConfirmationCodeParagraph": "Us hem enviat un codi de confirmació, introduïu-lo a continuació.", "borrowStarted": "S'ha iniciat la compra per a% 1", "borrowAccountParagraph": "Per comprar un compte, primer haurà de demanar un préstec gratuït. Si envieu almenys % 1 Pascal (% 2) al compte dins de % 3 dies , el compte serà vostre i % 1 Pascal es descomptarà automàticament del vostre saldo. \n En cas contrari, ens retornarà al final de % 3 dies i ja no pertanyrà a la vostra cartera. \n Es recomana només envieu una petita quantitat de monedes fins que no teniu el compte.", "importPrivateKeyParagraph": "Introduïu la vostra clau privada a continuació.", "looksLikeEncryptedKeyParagraph": "Sembla que una clau privada xifrada, introduïu la contrasenya per desxifrar-la i importar-la.", "changeDaemonParagraph": "Introduïu una adreça per utilitzar un dimoni Pascal diferent per a sol·licituds RPC.", "urlChangedToParagraph": "L'URL s'ha canviat a% 1", "backupKeyFirstParagraph": "Teniu dues opcions per fer una còpia de seguretat de la vostra clau privada:", "backupKeySecondParagraph": "1- Encriptat, el que significa que està protegit amb una contrasenya.", "backupKeyThirdParagraph": "2- No xifrat, cosa que significa que és brut i no protegit per una contrasenya.", "backupKeyFourthParagraph": "Recomanem guardar fora de línia la versió no xifrada, escrivint-la en un tros de paper. Podeu emmagatzemar la versió xifrada en un gestor de contrasenyes per a la vostra comoditat.", "encryptKeyParagraph": "Creeu una contrasenya nova per xifrar la vostra clau privada.", "backupEncryptedKeyFirstParagraph": "A continuació, trobareu la vostra clau privada xifrada. Està protegit per una contrasenya. Podeu emmagatzemar-lo de forma segura en un gestor de contrasenyes per a la vostra comoditat.", "backupEncryptedKeySecondParagraph": "Com que està xifrada amb la vostra contrasenya, si perds o oblides la teva contrasenya, no podràs desxifrar-la ni accedir als teus fons.", "backupUnencryptedKeyParagraph": "A continuació, trobareu la vostra clau privada sense xifrar. No està protegit per una contrasenya, cosa que significa que és important guardar-la en algun lloc segur i fora de línia. Recomanem escriure-la en un tros de paper.", "publicKeyParagraph": "A continuació, es mostra la vostra clau pública. Com el seu nom indica, es pretén compartir públicament i demostrar que una operació particular pertany a la vostra clau privada.", "borrowedAccountParagraph": "Aquest és un compte prestat . \n Si li envieu almenys % 1 Pascal en els següents % 2 dies,% 3 hores i% 4 minuts , ja serà teu.", "borrowedAccountPaidParagraph": " El vostre compte s'ha comprat! \n La transferència es processa actualment. Aquest procés sol durar uns 15 minuts , en alguns casos pot trigar una mica més.", "logoutFirstDisclaimerParagraph": " En tancar la sessió, suprimireu la clau privada i totes les dades relacionades amb Blaise d'aquest dispositiu. Si la vostra clau privada no està feta una còpia de seguretat, no podreu tornar a accedir als vostres fons. Si hi ha una còpia de seguretat de la teva clau privada, no et preocuparà res.", "logoutSecondDisclaimerParagraph": "Esteu segur que heu fet una còpia de seguretat de la vostra clau privada? Sempre que hàgiu fet una còpia de seguretat de la vostra clau privada, no us haureu de preocupar. ", "sendingConfirmParagraph": "Confirmeu les dades de la transacció a enviar.", "sentParagraph": "La transacció s'ha enviat correctament.", "changeNameParagraph": "Introduïu un nom a continuació per canviar el nom del vostre compte.", "changingNameParagraph": "Confirmeu el nom del compte nou per continuar.", "changedNameParagraph": "El vostre nom del compte s'ha canviat correctament.", "transferParagraph": "Introduïu una clau pública a continuació per transferir-hi la propietat d’aquest compte.", "transferringParagraph": "Confirmeu la clau pública següent per transferir-hi la propietat d’aquest compte.", "transferredParagraph": "El vostre compte s'ha transferit correctament a la clau pública següent.", "listForSaleParagraph": "Introduïu un preu i un compte que rebran el pagament per llistar aquest compte per vendre.", "listingForSaleParagraph": "Confirmeu el preu i el compte que rebrà el pagament.", "listedForSaleParagraph": "El vostre compte s'ha llistat amb èxit per a la venda. Us informarem si algú la compra.", "createPrivateSaleParagraph": "Introduïu un preu, un compte de recepció i una clau pública a continuació per crear una venda privada per a aquest compte.", "creatingPrivateSaleParagraph": "Confirmeu la informació següent.", "createdPrivateSaleParagraph": "La venda privada s'ha creat correctament. Us informarem si es compra.", "delistFromSaleParagraph": "Confirmeu que voleu suprimir aquest compte des de la venda.", "delistedFromSaleParagraph": "El vostre compte s'ha suprimit correctament de la venda.", "feeRequiredParagraph": "Aquesta operació requereix un cànon.", "feeConfirmAmountParagraph": "Per confirmar l’addició de taxa de Pascal% 1 a aquesta operació per continuar.", "keyTypeNotSupportedParagraph": "Aquest tipus de clau privada encara no és compatible amb Blaise. Podeu crear una clau privada nova i transferir-ne els comptes mitjançant una cartera diferent.", "enterPINToUnlockParagraph": "Introduïu el PIN per desbloquejar Blaise", "authenticateToUnlockParagraph": "Autentica per desbloquejar Blaise", "manyFailedAttemptsParagraph": "Massa intents fallits de desbloqueig", "authenticateToChangeNameParagraph": "Autentica per canviar el nom del compte a \"% 1\"", "authenticateToDelistParagraph": "Autentifica't per eliminar la compte des de la venda", "authenticateToListForSaleParagraph": "Autentica la llista del compte en venda", "authenticateToCreatePrivateSaleParagraph": "Autentiqueu-vos per crear venda privada", "authenticateToTransferParagraph": "Autentica la transferència del compte", "authenticateToSendParagraph": "Autentica per enviar% 1 Pascal", "authenticateToBackUpParagraph": "Autentiqueu la còpia de seguretat de clau privada", "invalidPINParagraph": "PIN no vàlid", "noMatchPINParagraph": "Els PIN no coincideixen", "confirmPINParagraph": "Confirmeu el PIN", "enterPINParagraph": "Introduïu el PIN", "createPINParagraph": "Creeu un PIN de 6 dígits", "addedToContactsParagraph": "% 1 afegit als contactes", "removedFromContactsParagraph": "S'ha eliminat% 1 dels contactes", "failedToRemoveFromContactsParagraph": "No s'ha pogut eliminar% 1 dels contactes", "successfullyImportedContactsParagraph": "% 1 contactes importats amb èxit", "checkOutBlaiseParagraph": "Fes una ullada a Blaise! Cartera de Pascal senzilla, elegant i segura per a iOS i Android: https://blaisewallet.com", "newAccountParagraph": "Aquest és el vostre nou compte. \n Un cop rebut Pascal , les operacions es mostraran com a continuació.", "settingsHeader": "Configuració", "preferencesHeader": "Preferències", "currencyHeader": "Moneda", "languageHeader": "Llenguatge", "languageColonHeader": "Llenguatge:", "systemDefaultHeader": "Predeterminat del sistema", "themeHeader": "Tema", "themeLightHeader": "Llum", "themeDarkHeader": "Fosc", "themeCopperHeader": "Coure", "notificationsHeader": "Notificacions", "securityHeader": "Seguretat", "authenticationMethodHeader": "Mètode d'autenticació", "authenticationPINHeader": "PIN", "authenticationBiometricsHeader": "Biometria", "authenticateOnLaunchHeader": "Autentica't a Launch", "yesHeader": "Sí", "noHeader": "No", "automaticallyLockHeader": "Bloquejar automàticament", "lockInstantHeader": "Instantàniament", "lock1Header": "Després de% 1 minut", "lock5Header": "Després de% 1 minuts", "lock15Header": "Després de% 1 minuts", "lock30Header": "Després de% 1 minuts", "lock60Header": "Després de% 1 minuts", "daemonHeader": "Dimoni", "defaultHeader": "Per defecte", "manageHeader": "Gestiona", "contactsHeader": "Contactes", "backUpPrivateKeyHeader": "Còpia de seguretat de clau privada", "viewPublicKeyHeader": "Veure clau pública", "shareHeader": "Comparteix Blaise", "logoutHeader": "Tancar sessió", "privacyPolicyHeader": "Política de privacitat", "changeAccountNameHeader": "Canvieu el nom del compte", "transferAccountHeader": "Compte de transferència", "listAccountForSaleHeader": "Llista de compte en venda", "createPrivateSaleHeader": "Crea venda privada", "delistFromSaleHeader": "Elimina la venda", "getAccountSheetHeader": "Obtén un compte", "freeAccountSheetHeader": "Compte gratuït", "buyAccountSheetHeader": "Compra el compte", "sendSheetHeader": "Envia", "sendingSheetHeader": "S'està enviant", "sentSheetHeader": "Enviat", "requestSheetHeader": "Sol·licitud", "changeNameSheetHeader": "Canvieu el nom", "changingNameSheetHeader": "Canviant", "changedNameSheetHeader": "Canviat", "transferSheetHeader": "Transferència", "transferringSheetHeader": "Transferència", "transferredSheetHeader": "Traspassat", "listForSaleSheetHeader": "Llistat en venda", "listingForSaleSheetHeader": "Llistat", "listedForSaleSheetHeader": "Enumerat", "createPrivateSaleSheetHeader": "Venda privada", "creatingPrivateSaleSheetHeader": "Creació", "createdPrivateSaleSheetHeader": "Creat", "delistingSheetHeader": "Suprimint", "delistedSheetHeader": "Suprimit", "addContactSheetHeader": "Afegeix contacte", "contactSheetHeader": "Contacte", "publicKeySheetHeader": "Clau pública", "privateKeySheetHeader": "Clau privada", "backUpSheetHeader": "Còpia de seguretat", "encryptSheetHeader": "Xifra", "changeDaemonSheetHeader": "Canvia de dimoni", "securityFirstHeader": "Seguretat primer!", "newPrivateKeyHeader": "Nova clau privada", "importPrivateKeyHeader": "Importa clau privada", "decryptAndImportKeyHeader": "Desxifra i importa", "backUpKeyHeader": "Fes una còpia de seguretat de la clau", "lockedHeader": "Bloquejat", "privateKeyTextFieldHeader": "Clau privada", "passwordTextFieldHeader": "Contrasenya", "newPasswordTextFieldHeader": "nova contrasenya", "confirmPasswordTextFieldHeader": "Confirma la contrassenya", "confirmTextFieldHeader": "Confirmeu", "countryCodeTextFieldHeader": "Codi del país", "phoneNumberTextFieldHeader": "Número de telèfon", "confirmationCodeTextFieldHeader": "Codi de confirmació", "accountTextFieldHeader": "Compte", "addressTextFieldHeader": "adreça", "contactNameTextFieldHeader": "Nom de contacte", "amountTextFieldHeader": "Import", "payloadTextFieldHeader": "Càrrega útil", "nameTextFieldHeader": "Nom", "newAccountNameTextFieldHeader": "Nou nom del compte", "publicKeyTextFieldHeader": "Clau pública", "priceTextFieldHeader": "Preu", "receivingAccountTextFieldHeader": "Compte de recepció", "durationTextFieldHeader": "Durada", "feeTextFieldHeader": "Comissió", "otherOperationsHeader": "Altres operacions", "warningHeader": "Avís", "areYouSureHeader": "Estàs segur?", "addFeeHeader": "Afegir tarifa", "keyTypeNotSupportedHeader": "Clau no compatible", "accountToSendFromHeader": "Compte per enviar des", "sentHeader": "Enviat", "receivedHeader": "Rebut", "nameChangedHeader": "Canviat el nom", "listedForSaleHeader": "Llistat en venda", "privateSaleHeader": "Venda privada", "delistedFromSaleHeader": "Eliminat de la venda", "delistedHeader": "Suprimit", "undefinedHeader": "Indefinit", "transferredHeader": "Traspassat", "balanceHeader": "Equilibri", "totalBalanceHeader": "Saldo total", "accountBalanceHeader": "Saldo del compte", "accountsHeader": "Comptes", "operationsHeader": "Operacions", "encryptThePayloadHeader": "Xifra la càrrega útil", "encryptPayloadHeader": "Xifra la càrrega útil", "forSaleHeader": "A la venda", "borrowedHeader": "Prestat", "borrowedTransferredHeader": "Transferència pendent", "borrowedAccountHeader": "Compte prestat", "feeColonHeader": "Taxa:", "pendingHeader": "Pendents", "onHeader": "Encès", "offHeader": "Desactivat", "priceRequiredError": "El preu és obligatori", "amountRequiredError": "Quantitat obligatòria", "nameRequiredError": "El seu nom és obligatori", "zeroPriceError": "El preu no pot ser 0", "zeroAmountError": "La quantitat no pot ser 0", "invalidAccountNameError": "El nom del compte no és vàlid", "invalidReceivingAccountError": "Compte de recepció no vàlid", "invalidPublicKeyError": "Clau pública no vàlida", "invalidPrivateKeyError": "Clau privada no vàlida", "invalidAddressError": "Adreça no vàlida", "invalidAccountError": "Compte no vàlid", "invalidDestinationError": "Destinació no vàlida", "insufficientBalanceError": "Saldo insuficient", "threeCharacterNameError": "Ha de tenir com a mínim 3 caràcters", "contactDoesntExistError": "El contacte no existeix", "contactAlreadyExistsError": "El contacte ja existeix", "cantSendToYourselfError": "No es pot enviar a tu mateix", "somethingWentWrongError": "Alguna cosa ha anat malament. Si us plau torna-ho a intentar després", "failedToEncryptPayloadError": "No s'ha pogut xifrar la càrrega útil", "emptyPasswordError": "La contrasenya no pot estar buida", "noMatchPasswordError": "Les contrasenyes no coincideixen", "invalidPasswordError": "Contrasenya invàlida", "didNotGetResponseError": "No s'ha obtingut resposta del servidor", "noContactsToExportError": "No hi ha contactes per exportar", "noContactsToImportError": "No hi ha contactes a importar", "failedToImportContactsError": "No s'ha pogut importar contactes", "blockchainRewardOPDetails": "Recompensa Blockchain (% 1)", "transactionOPDetails": "Transacció (% 1)", "changeKeyOPDetails": "Canvia la clau (% 1)", "recoverFundsOPDetails": "Recuperació de fons (% 1)", "listAccountForSaleOPDetails": "Llista del compte en venda (% 1)", "delistAccountOPDetails": "Suprimeix el compte (% 1)", "buyAccountOPDetails": "Compra del compte (% 1)", "changeKeySignedOPDetails": "Canvia la signada amb clau (% 1)", "changeAccountInfoOPDetails": "Canvia la informació del compte (% 1)", "multioperationOPDetails": "Multioperació (% 1)", "unknownOPDetails": "Desconegut (% 1)", "sendingAccountOPDetails": "Compte enviant", "receivingAccountOPDetails": "Compte de recepció", "changingAccountOPDetails": "Canvi de compte", "sendAmountOPDetails": "Enviar import", "payloadOPDetails": "Càrrega útil", "newPublicKeyOPDetails": "Nova clau pública", "newNameOPDetails": "Nou nom", "sellerAccountOPDetails": "Compte del venedor", "accountPriceOPDetails": "Preu del compte", "lockedUntilBlockOPDetails": "Bloquejat fins el bloc", "blockOPDetails": "bloc", "optxtOPDetails": "optxt", "timeOPDetails": "temps", "naOPDetails": "N / A", "ophashOPDetails": "ophash", "optypeOPDetails": "òptic", "maturationOPDetails": "maduració", "nullOPDetails": "nul", "feeOPDetails": "quota", "opblockOPDetails": "opbloquejar", "noperationOPDetails": "n_operació", "accountOPDetails": "compte", "signeraccountOPDetails": "signer_account", "noResultsFound": "Sense resultats", "getAccountThirdParagraphAlternative": "2- Podeu comprar un compte per a % 1 Pascal (% 2). Comprar només un compte està permès per usuari. ", "getAccountThirdParagraphAlternative2": "2- Podeu comprar un compte per a % 1 Pascal (% 2). Podeu comprar fins a% 3 comptes. ", "receiveAccountButton": "Rep el compte", "receiveAnAccountButton": "Rebre un compte", "okayButton": "Bé", "liveSupportButton": "Assistència", "invalidPhoneNumberParagraph": "El número de telèfon no és vàlid", "confirmationCodeError": "No s'ha pogut verificar el codi, assegureu-vos que l'heu introduït correctament", "freepasaComplete": "Amb èxit, el nou compte estarà disponible després de 1 confirmació de xarxa", "unconfirmedAccountHeader": "Compte no confirmat", "unconfirmedAccountParagraph": "Aquest és un compte no confirmat . S'ha transferit a tu, però abans de poder utilitzar-lo cal que existeixi una confirmació de xarxa. Generalment triguen uns 5 minuts, un cop finalitzat, podreu utilitzar aquest compte.", "connectingHeader": "Connectant" } ================================================ FILE: lib/l10n/intl_de.arb ================================================ { "newPrivateKeyButton": "Neuer privater Schlüssel", "importPrivateKeyButton": "Schlüssel importieren", "gotItButton": "Verstanden!", "goBackButton": "Zurück", "copyButton": "Kopieren", "copiedButton": "Kopiert", "keyCopiedButton": "Schlüssel kopiert", "iHaveBackedItUpButton": "Ich habe ihn gesichert", "yesImSureButton": "Ja, ich bin sicher", "noGoBackButton": "Nein, zurück", "getAnAccountButton": "Konto anlegen", "getAFreeAccountButton": "Kostenloses Konto erhalten", "buyAnAccountButton": "Konto kaufen", "sendConfirmationButton": "Bestätigung senden", "confirmButton": "Bestätigen", "borrowAnAccountButton": "Konto leihen", "importButton": "Importieren", "receiveButton": "Empfangen", "sendButton": "Senden", "copyAddressButton": "Kontonummer kopieren", "copiedAddressButton": "Kontonummer kopiert", "addToContactsButton": "Zu Kontakten hinzufügen", "operationDetailsButton": "Vorgangs-Details", "openInExplorerButton": "Im explorer anzeigen", "requestButton": "Anfrage", "addAPayloadButton": "+ Betreff festlegen", "addADurationButton": "+ Dauer festlegen", "scanQRCodeButton": "QR Code scannen", "cancelButton": "Abbrechen", "closeButton": "Schließen", "changeNameButton": "Name ändern", "transferButton": "Übertragen", "listForSaleButton": "Verkaufen", "createPrivateSaleButton": "Private verkaufen", "yesAddFeeButton": "Ja, Gebühr hinzufügen", "unlockButton": "Entsperren", "unlockWithBiometricsButton": "Mit Biometrie entsperren", "unlockWithPINButton": "Mit PIN entsperren", "setToDefaultButton": "Zurücksetzen", "changeDaemonButton": "Server wechseln", "addContactButton": "Kontakt hinzufügen", "encryptedKeyButton": "Chiffrierter Schlüssel", "unencryptedKeyButton": "Klartext Schlüssel", "encryptButton": "Verschlüsseln", "showButton": "Anzeigen", "hideButton": "Verstecken", "copyEncryptedKeyButton": "Kopieren", "copyUnencryptedKeyButton": "Kopieren", "copyKeyButton": "Kopieren", "copyPublicKeyButton": "Öffentlichen Schlüssel kopieren", "deletePrivateKeyAndLogoutButton": "Privaten Schlüssel löschen\nund abmelden", "searchForNameButton": "Suche nach Namen", "searchAccountNameButton": "Suche Name", "searchNameButton": "Suche Name", "okayGoBackButton": "Okay, zurück", "nextButton": "Weiter", "welcomeParagraph": "Willkommen bei der Blaise Wallet. Um zu starten musst Du entweder einen neuen privaten Schlüssel generieren oder einen bestehenden importieren.", "newKeySecurityParagraph": "Im nächsten Schritt siehst Du Deinen privaten Schlüssel. Mit diesem Schlüssel hast Du Zugriff auf Deine Pascal. Es ist wichtig, dass Du diesen Schlüssel sicherst und mit niemanden teilst!", "uninstallDisclaimerParagraph": "Wenn Du Dein Gerät verlierst oder die Blaise Wallet deinstallierst, brauchst Du Deinen privaten Schlüssel für die Wiederherstellung.", "newPrivateKeyParagraph": "Nachfolgend findest Du Deinen neuen privaten Schlüssel. Es ist unbedingt erforderlich, dass Du diesen sicherst - aber bitte nicht als Screenshot oder in Klartext auf Deinem Computer oder Smartphone. Wir empfehlen Dir ihn auf einem Blatt Papier zu notieren.", "newKeyBackUpConfirmParagraph": "Hast Du Deinen privaten Schlüssel wirklich gesichert?", "newWalletGreetingParagraph": "Willkommen bei der Blaise Wallet.\n Zum Start musst Du Dir ein Konto anlegen.", "getAccountFirstParagraph": "Es gibt 2 Möglichkeiten, um Dein erstes Konto erhalten:", "getAccountSecondParagraph": "1- Du kannst ein kostenfreies Konto mit Hilfe Deiner Telefonnummer erhalten. Nur 1 Konto pro Telefonnummer erlaubt.", "getAccountThirdParagraph": "2- Du kannst beliebig viele Konten für eine Gebühr von %1 Pascal (%2) erwerben.", "enterPhoneNumberParagraph": "Trage Deine Telefonnummer hier ein.", "enterConfirmationCodeParagraph": "Wir haben Dir einen Bestätigungs-Code gesendet. Bitte hier eintragen.", "borrowStarted": "Konto-Kauf für %1 gestartet", "borrowAccountParagraph": "Um ein Konto zu kaufen, musst Du Dir zu Beginn eines ausleihen. Wenn Du innerhalb von %3 Tage(n) mindestens %1 Pascal (%2) an das geliehene Konto überweist, gehört es Dir und der Betrag von %1 Pascal wird automatisch abgezogen.\nAnsonsten geht das geliehene Konto nach %3 Tage(n) an uns zurück und ist nicht mehr Teil Deiner Wallet.\nWir empfehlen Dir, nicht zu große Beträge an das Konto zu senden, solange es nicht Dir gehört.", "importPrivateKeyParagraph": "Trage Deinen privaten Schlüssel ein", "looksLikeEncryptedKeyParagraph": "Siehst aus als wäre der private Schlüssel chiffriert. Bitte trage das Passwort zum dechiffrieren ein", "changeDaemonParagraph": "Trage eine Adresse zu einem anderen Pascal-Server ein", "urlChangedToParagraph": "Pascal-Server geändert zu %1", "backupKeyFirstParagraph": "Du hast 2 Möglichkeiten, um Deinen privaten Schlüssel zu sichern:", "backupKeySecondParagraph": "1- Chiffrieren mit Hilfe eines Passworts", "backupKeyThirdParagraph": "2- Nicht chiffriert, im Klartext und ungeschützt", "backupKeyFourthParagraph": "Wir empfehlen Dir, die nicht chiffrierte Version des Schlüssels auf einem Blatt Papier zu notieren. Die chiffrierte Version kann in einem Passwort Manager gespeichert werden.", "encryptKeyParagraph": "Neues Passwort erstellen um den privaten Schlüssel verschlüsseln.", "backupEncryptedKeyFirstParagraph": "Hier ist Dein mit einem Passwort chiffrierter privater Schlüssel. Du kannst Ihn problemlos in einem Passwort Manager speichern.", "backupEncryptedKeySecondParagraph": "Wenn Du das Passwort vergisst oder verlierst, kannst Du nicht mehr auf Deine Pascal zugreifen.", "backupUnencryptedKeyParagraph": "Nachfolgend siehst Du Deinen privaten Schlüssel. Dieser ist nicht mit einem Passwort geschützt, deshalb ist es zwingend erforderlich, dass Du ihn sicher offline verwahrst! Wir empfehlen ihn auf Papier zu notieren.", "publicKeyParagraph": "Nachfolgend siehst Du Deinen öffentlichen Schlüssel, der mit jedem geteilt und zur Verifizierung Deiner Vorgänge genutzt werden kann.", "borrowedAccountParagraph": "Dies ist ein geliehenes Konto.\nWenn Du in den nächsten %2 Tage(n), %3 Stunde(n) und %4 Minute(n) mindestens %1 Pascal an das Konto sendest gehört es Dir!", "borrowedAccountPaidParagraph": "Du hast das Konto gekauft!\nDie Übertragung läuft gerade. Dieser Vorgang dauert ungefähr 15 Minuten, manchmal kann es auch etwas länger dauern. Bitte habe ein wenig Geduld, Du kannst in Kürze loslegen.", "logoutFirstDisclaimerParagraph": "Wenn Du dich abmeldest, werden Deine Schlüssel und alle Blaise bezogenen Daten von diesem Gerät gelöscht. Hast Du Deinen privaten Schlüssel gesichert? Wenn nicht, wirst Du alles verlieren. Wenn ja, brauchst Du Dir keine Sorgen zu machen.", "logoutSecondDisclaimerParagraph": "Bist Du Dir sicher, dass Du Deinen privaten Schlüssel gesichert hast? Wenn ja, brauchst Du Dir keine Sorgen zu machen.", "sendingConfirmParagraph": "Bitte bestätige die Überweisungsdaten.", "sentParagraph": "Die Überweisung wurde erfolgreich ausgeführt.", "changeNameParagraph": "Trage einen neuen Namen für Dein Konto ein.", "changingNameParagraph": "Bitte bestätige den neuen Namen für Dein Konto.", "changedNameParagraph": "Der Name des Kontos wurde erfolgreich geändert.", "transferParagraph": "Trage nachfolgend den öffentl. Schlüssel ein, an den das Konto übertragen wird.", "transferringParagraph": "Bitte bestätige den nachfolgenden öffentl. Schlüssel, um das Konto zu übertragen.", "transferredParagraph": "Dein Konto wurde erfolgreich an den folgenden öffentl. Schlüssel übertragen.", "listForSaleParagraph": "Trage 1. den Kaufpreis und 2. den Empfänger des Kaufpreises für dieses Konto ein.", "listingForSaleParagraph": "Bitte bestätige den Betrag und den Empfänger der Überweisung.", "listedForSaleParagraph": "Das Konto steht jetzt zum Verkauf. Sobald es gekauft wurde sagen wir Dir Bescheid.", "createPrivateSaleParagraph": "Trage den Preis, den Empfänger des Kaufpreises und den öffentlichen Schlüssel ein, um das Konto Privat zu verkaufen.", "creatingPrivateSaleParagraph": "Bitte bestätige die folgenden Angaben.", "createdPrivateSaleParagraph": "Das Konto steht jetzt zum privaten Verkauf. Sobald es gekauft wurde sagen wir Dir Bescheid.", "delistFromSaleParagraph": "Bitte bestätige den Abbruch des Konto-Verkaufs.", "delistedFromSaleParagraph": "Der Verkauf wurde erfolgreich abgebrochen.", "feeRequiredParagraph": "Für diesen Vorgang wird eine Gebühr benötigt.", "feeConfirmAmountParagraph": "Bitte bestätige, dass %1 Pascal als Gebühr hinzugefügt werden.", "keyTypeNotSupportedParagraph": "Dieser Schlüssel-Typ wird leider nicht unterstützt. Du kannst einen neuen privaten Schlüssel anlegen und das Konto mit Hilfe einer anderen Wallet auf diesen übertragen.", "enterPINToUnlockParagraph": "PIN eintragen um die Blaise Wallet zu entsperren", "authenticateToUnlockParagraph": "Authentifizieren um die Blaise Wallet zu entsperren", "manyFailedAttemptsParagraph": "Zu viele fehlgeschlagene Entsperrversuche", "authenticateToChangeNameParagraph": "Anmelden um den Namen des Kontos auf \"%1\" zu ändern", "authenticateToDelistParagraph": "Anmelden um den Konto-Verkauf abzubrechen", "authenticateToListForSaleParagraph": "Anmelden um Konto zu verkaufen", "authenticateToCreatePrivateSaleParagraph": "Anmelden um Konto privat zu verkaufen", "authenticateToTransferParagraph": "Anmelden um Konto zu übertragen", "authenticateToSendParagraph": "Anmelden um %1 Pascal zu senden", "authenticateToBackUpParagraph": "Anmelden um den privaten Schlüssel zu sichern", "invalidPINParagraph": "Ungültige PIN", "noMatchPINParagraph": "Die PINs stimmen nicht überein", "confirmPINParagraph": "PIN bestätigen", "enterPINParagraph": "PIN eintragen", "createPINParagraph": "6-stellige PIN erstellen", "addedToContactsParagraph": "%1 zu den Kontakten hinzugefügt", "removedFromContactsParagraph": "%1 aus den Kontakten entfernt", "failedToRemoveFromContactsParagraph": "Fehler beim entfernen von %1 aus den Kontakten", "successfullyImportedContactsParagraph": "%1 Kontakte erfolgreich importiert", "checkOutBlaiseParagraph": "Schau Dir Blaise an! Die sichere und einfach zu bedienende Pascal Wallet für iOS und Android: https://blaisewallet.com", "newAccountParagraph": "Dies ist Dein neues Konto. \nSobald Du Pascal empfängst oder sendest, werden die Überweisungen hier aufgelistet.", "settingsHeader": "Einstellungen", "preferencesHeader": "Konfiguration", "currencyHeader": "Währung", "languageHeader": "Sprache", "languageColonHeader": "Sprache:", "systemDefaultHeader": "Standard", "themeHeader": "Thema", "themeLightHeader": "Hell", "themeDarkHeader": "Dunkel", "themeCopperHeader": "Kupfer", "notificationsHeader": "Benachrichtigungen", "securityHeader": "Sicherheit", "authenticationMethodHeader": "Authentifizierung", "authenticationPINHeader": "PIN", "authenticationBiometricsHeader": "Biometrie", "authenticateOnLaunchHeader": "Anmelden bei Start", "yesHeader": "Ja", "noHeader": "Nein", "automaticallyLockHeader": "Automatisch sperren", "lockInstantHeader": "Sofort", "lock1Header": "Nach %1 Minute", "lock5Header": "Nach %1 Minuten", "lock15Header": "Nach %1 Minuten", "lock30Header": "Nach %1 Minuten", "lock60Header": "Nach %1 Minuten", "daemonHeader": "Server", "defaultHeader": "Standard", "manageHeader": "Verwalten", "contactsHeader": "Kontakte", "backUpPrivateKeyHeader": "Privaten Schlüssel sichern", "viewPublicKeyHeader": "Öffentlichen Schlüssel anzeigen", "shareHeader": "Blaise teilen", "logoutHeader": "Abmelden", "privacyPolicyHeader": "Datenschutzrichtlinie", "changeAccountNameHeader": "Konto Name ändern", "transferAccountHeader": "Konto übertragen", "listAccountForSaleHeader": "Konto verkaufen", "createPrivateSaleHeader": "Privat verkaufen", "delistFromSaleHeader": "Verkauf stoppen", "getAccountSheetHeader": "Konto anlegen", "freeAccountSheetHeader": "Kostenfreies Konto", "buyAccountSheetHeader": "Konto kaufen", "sendSheetHeader": "Senden", "sendingSheetHeader": "Sende", "sentSheetHeader": "Gesendet", "requestSheetHeader": "Anfragen", "changeNameSheetHeader": "Name ändern", "changingNameSheetHeader": "Speichere", "changedNameSheetHeader": "Geändert", "transferSheetHeader": "Übertragen", "transferringSheetHeader": "Übertrage", "transferredSheetHeader": "Übertragen", "listForSaleSheetHeader": "Verkaufen", "listingForSaleSheetHeader": "Verkaufen", "listedForSaleSheetHeader": "Zum Verkauf gelistet", "createPrivateSaleSheetHeader": "Privatverkauf", "creatingPrivateSaleSheetHeader": "Erstelle", "createdPrivateSaleSheetHeader": "Erstellt", "delistingSheetHeader": "Breche Verkauf ab", "delistedSheetHeader": "Verkauf abgebrochen", "addContactSheetHeader": "Kontakt hinzufügen", "contactSheetHeader": "Kontakt", "publicKeySheetHeader": "Öffentlicher Schlüssel", "privateKeySheetHeader": "Privater Schlüssel", "backUpSheetHeader": "Sicherung", "encryptSheetHeader": "Verschlüsseln", "changeDaemonSheetHeader": "Server wechseln", "securityFirstHeader": "Sicherheit zuerst!", "newPrivateKeyHeader": "Neues Schlüsselpaar", "importPrivateKeyHeader": "Schlüssel importieren", "decryptAndImportKeyHeader": "Entschlüsseln und importieren", "backUpKeyHeader": "Schlüssel gesichert?", "lockedHeader": "Gesperrt", "privateKeyTextFieldHeader": "Privater Schlüssel", "passwordTextFieldHeader": "Passwort", "newPasswordTextFieldHeader": "Neues Passwort", "confirmPasswordTextFieldHeader": "Passwort bestätigen", "confirmTextFieldHeader": "Bestätigen", "countryCodeTextFieldHeader": "Länder-Code", "phoneNumberTextFieldHeader": "Telefon-Nummer", "confirmationCodeTextFieldHeader": "Bestätigungs-Code", "accountTextFieldHeader": "Konto", "addressTextFieldHeader": "Kontonummer", "contactNameTextFieldHeader": "Name", "amountTextFieldHeader": "Betrag", "payloadTextFieldHeader": "Betreff", "nameTextFieldHeader": "Name", "newAccountNameTextFieldHeader": "Neuer Konto Name", "publicKeyTextFieldHeader": "Öffentlicher Schlüssel", "priceTextFieldHeader": "Preis", "receivingAccountTextFieldHeader": "Empfänger", "durationTextFieldHeader": "Dauer", "feeTextFieldHeader": "Gebühr", "otherOperationsHeader": "Andere Vorgänge", "warningHeader": "Warnung", "areYouSureHeader": "Bist Du sicher?", "addFeeHeader": "Gebühr hinzufügen", "keyTypeNotSupportedHeader": "Schlüssel nicht unterstützt", "accountToSendFromHeader": "Absender", "sentHeader": "Gesendet", "receivedHeader": "Empfangen", "nameChangedHeader": "Name geändert", "listedForSaleHeader": "Zu verkaufen", "privateSaleHeader": "Privater Verkauf", "delistedFromSaleHeader": "Verkauf abgebrochen", "delistedHeader": "Verkauf abgebrochen", "undefinedHeader": "Unbekannt", "transferredHeader": "Übertragen", "balanceHeader": "Saldo", "totalBalanceHeader": "Kontostand aller Konten", "accountBalanceHeader": "Kontostand", "accountsHeader": "Konten", "operationsHeader": "Vorgänge", "encryptThePayloadHeader": "Betreff verschlüsseln", "encryptPayloadHeader": "Betreff verschlüsseln", "forSaleHeader": "zu verkaufen", "borrowedHeader": "geliehen", "borrowedTransferredHeader": "Übertragung wartend", "borrowedAccountHeader": "Geliehenes Konto", "feeColonHeader": "Gebühr:", "pendingHeader": "ausstehend", "onHeader": "An", "offHeader": "Aus", "priceRequiredError": "Preis wird benötigt", "amountRequiredError": "Betrag wird benötigt", "nameRequiredError": "Name wird benötigt", "zeroPriceError": "Preis kann nicht 0 sein", "zeroAmountError": "Betrag kann nicht 0 sein", "invalidAccountNameError": "Ungültiger Konto Name", "invalidReceivingAccountError": "Ungültiger Empfänger", "invalidPublicKeyError": "Ungültiger öffentl. Schlüssel", "invalidPrivateKeyError": "Ungültiger privater Schlüssel", "invalidAddressError": "Ungültiges Konto", "invalidAccountError": "Ungültiges Konto", "invalidDestinationError": "Ungültiger Empfänger", "insufficientBalanceError": "Ungültiger Betrag", "threeCharacterNameError": "Muss aus mindestens 3 Zeichen bestehen", "contactDoesntExistError": "Kontakt existiert nicht", "contactAlreadyExistsError": "Kontakt existiert bereits", "cantSendToYourselfError": "Du kannst nicht an dich selbst senden", "somethingWentWrongError": "Etwas ist schief gelaufen, probiere es später erneut", "failedToEncryptPayloadError": "Fehler beim verschlüsseln des Betreffs", "emptyPasswordError": "Das Passwort darf nicht leer sein", "noMatchPasswordError": "Die Passwörter stimmen nicht überein", "invalidPasswordError": "Falsches Passwort", "didNotGetResponseError": "Der Server antwortet nicht", "noContactsToExportError": "Keine Kontakte zum exportieren", "noContactsToImportError": "Keine Kontakte zum importieren", "failedToImportContactsError": "Fehler beim importieren der Kontakte", "blockchainRewardOPDetails": "Blockchain Reward (%1)", "transactionOPDetails": "Überweisung (%1)", "changeKeyOPDetails": "Konto-Übertragung (%1)", "recoverFundsOPDetails": "Recover Funds (%1)", "listAccountForSaleOPDetails": "Konto zum Verkauf (%1)", "delistAccountOPDetails": "Verkauf abgebrochen (%1)", "buyAccountOPDetails": "Konto gekauft (%1)", "changeKeySignedOPDetails": "Fremde Konto-Übertragung (%1)", "changeAccountInfoOPDetails": "Konto Daten geändert (%1)", "multioperationOPDetails": "Multi-Vorgang (%1)", "unknownOPDetails": "Unbekannt (%1)", "sendingAccountOPDetails": "Absender", "receivingAccountOPDetails": "Empfänger", "changingAccountOPDetails": "Ändere Konto", "sendAmountOPDetails": "Betrag senden", "payloadOPDetails": "Betreff", "newPublicKeyOPDetails": "Neuer öffentl. Schlüssel", "newNameOPDetails": "Neuer Name", "sellerAccountOPDetails": "Verkäufer", "accountPriceOPDetails": "Konto Preis", "lockedUntilBlockOPDetails": "Gesperrt bis Block", "blockOPDetails": "Block", "optxtOPDetails": "Vorgangs-Info", "timeOPDetails": "Zeitpunkt", "naOPDetails": "n.a.", "ophashOPDetails": "Vorgangs-Prüfsumme", "optypeOPDetails": "Vorgangstyp", "maturationOPDetails": "Alter (in Blöcken)", "nullOPDetails": "null", "feeOPDetails": "Gebühr", "opblockOPDetails": "Position in Block", "noperationOPDetails": "Konto Vorgangsnummer", "accountOPDetails": "Konto", "signeraccountOPDetails": "Signierer", "noResultsFound": "Keine Ergebnisse", "getAccountThirdParagraphAlternative": "2- Du kannst ein Konto für %1 Pascal (%2) kaufen. Der Blaise-Service erlaubt den Erwerb von einem Konto pro Benutzer. Im Normalfall brauchst Du auch nur eins.", "getAccountThirdParagraphAlternative2": "2- Du kannst ein Konto für %1 Pascal (%2) kaufen. Der Blaise-Service erlaubt den Erwerb von 3 Konten pro Benutzer. Im Normalfall brauchst Du lediglich eins.", "receiveAccountButton": "Konto erhalten", "receiveAnAccountButton": "Ein Konto erhalten", "okayButton": "Okay", "liveSupportButton": "Hilfe", "invalidPhoneNumberParagraph": "Keine valide Telefonnummer", "confirmationCodeError": "Der Code war falsch, bitte probiere es erneut", "freepasaComplete": "Dein neues Konto wird nach dem nächsten Block zur Verfügung stehen", "unconfirmedAccountHeader": "Noch nicht bestätigtes Konto", "unconfirmedAccountParagraph": "Dies ist ein nicht bestätigtes Konto. Es wurde Dir bereits zugeordnet, aber Du musst noch einen Block lang warten. Das dauert in der Regel ungefähr 5 Minuten. Sobald der Block abgeschlossen ist, hast Du vollen Zugriff auf dieses Konto.", "connectingHeader": "Verbinde" } ================================================ FILE: lib/l10n/intl_en.arb ================================================ { "@@last_modified": "2019-10-18T09:27:20.827100", "newPrivateKeyButton": "New Private Key", "@newPrivateKeyButton": { "description": "A button that creates a new private key", "type": "text", "placeholders": {} }, "importPrivateKeyButton": "Import Private Key", "@importPrivateKeyButton": { "description": "A button that imports a private key", "type": "text", "placeholders": {} }, "gotItButton": "Got It!", "@gotItButton": { "description": "A button that implies a message is understood", "type": "text", "placeholders": {} }, "goBackButton": "Go Back", "@goBackButton": { "description": "A button to go back to previous screen", "type": "text", "placeholders": {} }, "copyButton": "Copy", "@copyButton": { "description": "A button to copy something", "type": "text", "placeholders": {} }, "copiedButton": "Copied", "@copiedButton": { "description": "A button to inform the user that something has been copied", "type": "text", "placeholders": {} }, "keyCopiedButton": "Key Copied", "@keyCopiedButton": { "description": "A button to inform the user that the key has been copied", "type": "text", "placeholders": {} }, "iHaveBackedItUpButton": "I've Backed It Up", "@iHaveBackedItUpButton": { "description": "A button to confirm that something is backed up", "type": "text", "placeholders": {} }, "yesImSureButton": "Yes, I'm Sure", "@yesImSureButton": { "description": "A button to confirm if the user is sure", "type": "text", "placeholders": {} }, "noGoBackButton": "No, Go Back", "@noGoBackButton": { "description": "A button to go back to previous screen if the user didnt do what the question asks", "type": "text", "placeholders": {} }, "getAnAccountButton": "Get an Account", "@getAnAccountButton": { "description": "A button to start the process of getting an account", "type": "text", "placeholders": {} }, "getAFreeAccountButton": "Get a Free Account", "@getAFreeAccountButton": { "description": "A button to start the process of getting a free account", "type": "text", "placeholders": {} }, "buyAnAccountButton": "Buy an Account", "@buyAnAccountButton": { "description": "A button to start the process of buying an account", "type": "text", "placeholders": {} }, "sendConfirmationButton": "Send Confirmation", "@sendConfirmationButton": { "description": "A button to request a confirmation to be sent", "type": "text", "placeholders": {} }, "confirmButton": "Confirm", "@confirmButton": { "description": "A button to confirm that a process should be executed", "type": "text", "placeholders": {} }, "borrowAnAccountButton": "Borrow An Account", "@borrowAnAccountButton": { "description": "A button to borrow an account", "type": "text", "placeholders": {} }, "importButton": "Import", "@importButton": { "description": "A button to import something", "type": "text", "placeholders": {} }, "receiveButton": "Receive", "@receiveButton": { "description": "A button to receive Pascal", "type": "text", "placeholders": {} }, "sendButton": "Send", "@sendButton": { "description": "A button to send Pascal", "type": "text", "placeholders": {} }, "copyAddressButton": "Copy Address", "@copyAddressButton": { "description": "A button to copy an address", "type": "text", "placeholders": {} }, "copiedAddressButton": "Address Copied", "@copiedAddressButton": { "description": "A button to inform the user that the address has been copied", "type": "text", "placeholders": {} }, "addToContactsButton": "Add to Contacts", "@addToContactsButton": { "description": "A button to add an account to contacts", "type": "text", "placeholders": {} }, "operationDetailsButton": "Operation Details", "@operationDetailsButton": { "description": "A button to view the details of an operation", "type": "text", "placeholders": {} }, "openInExplorerButton": "Open in Explorer", "@openInExplorerButton": { "description": "A button to view the details of an operation on the Pascal explorer", "type": "text", "placeholders": {} }, "requestButton": "Request", "@requestButton": { "description": "A button to request something", "type": "text", "placeholders": {} }, "addAPayloadButton": "+ Add a Payload", "@addAPayloadButton": { "description": "A button to add a payload (note) to an operation", "type": "text", "placeholders": {} }, "addADurationButton": "+ Add a Duration", "@addADurationButton": { "description": "A button to add a duration to the sale", "type": "text", "placeholders": {} }, "scanQRCodeButton": "Scan QR Code", "@scanQRCodeButton": { "description": "A button to scan a QR Code", "type": "text", "placeholders": {} }, "cancelButton": "Cancel", "@cancelButton": { "description": "A button to cancel a process", "type": "text", "placeholders": {} }, "closeButton": "Close", "@closeButton": { "description": "A button to close a screen or a pop-up", "type": "text", "placeholders": {} }, "changeNameButton": "Change Name", "@changeNameButton": { "description": "A button to change the name of an account", "type": "text", "placeholders": {} }, "transferButton": "Transfer", "@transferButton": { "description": "A button to transfer the ownership of an account", "type": "text", "placeholders": {} }, "listForSaleButton": "List for Sale", "@listForSaleButton": { "description": "A button to list an account for sale", "type": "text", "placeholders": {} }, "createPrivateSaleButton": "Create Private Sale", "@createPrivateSaleButton": { "description": "A button to create a private sale for the account", "type": "text", "placeholders": {} }, "yesAddFeeButton": "Yes, Add Fee", "@yesAddFeeButton": { "description": "A button to confirm the addition of a fee to an operation", "type": "text", "placeholders": {} }, "unlockButton": "Unlock", "@unlockButton": { "description": "A button to unlock the wallet", "type": "text", "placeholders": {} }, "unlockWithBiometricsButton": "Unlock with Biometrics", "@unlockWithBiometricsButton": { "description": "A button to unlock the wallet using biometrics", "type": "text", "placeholders": {} }, "unlockWithPINButton": "Unlock with PIN", "@unlockWithPINButton": { "description": "A button to unlock the wallet using PIN", "type": "text", "placeholders": {} }, "setToDefaultButton": "Set to Default", "@setToDefaultButton": { "description": "A button to set something to its default", "type": "text", "placeholders": {} }, "changeDaemonButton": "Change Daemon", "@changeDaemonButton": { "description": "A button to change the Pascal daemon for RPC requests", "type": "text", "placeholders": {} }, "addContactButton": "Add Contact", "@addContactButton": { "description": "A button to add a contact", "type": "text", "placeholders": {} }, "encryptedKeyButton": "Encrypted Key", "@encryptedKeyButton": { "description": "A button to view the encrypted key", "type": "text", "placeholders": {} }, "unencryptedKeyButton": "Unencrypted Key", "@unencryptedKeyButton": { "description": "A button to view the unencrypted key", "type": "text", "placeholders": {} }, "encryptButton": "Encrypt", "@encryptButton": { "description": "A button to encrypt the private key with a password", "type": "text", "placeholders": {} }, "showButton": "Show", "@showButton": { "description": "A button to show something that is hidden", "type": "text", "placeholders": {} }, "hideButton": "Hide", "@hideButton": { "description": "A button to hide something that is shown", "type": "text", "placeholders": {} }, "copyEncryptedKeyButton": "Copy Encrypted Key", "@copyEncryptedKeyButton": { "description": "A button to copy an encrypted key", "type": "text", "placeholders": {} }, "copyUnencryptedKeyButton": "Copy Unencrypted Key", "@copyUnencryptedKeyButton": { "description": "A button to copy an unencrypted key", "type": "text", "placeholders": {} }, "copyKeyButton": "Copy Key", "@copyKeyButton": { "description": "A button to copy a key (private or public key)", "type": "text", "placeholders": {} }, "copyPublicKeyButton": "Copy Public Key", "@copyPublicKeyButton": { "description": "A button to copy a public key", "type": "text", "placeholders": {} }, "deletePrivateKeyAndLogoutButton": "Delete Private Key\nAnd Logout", "@deletePrivateKeyAndLogoutButton": { "description": "A button to delete the private key and logout", "type": "text", "placeholders": {} }, "searchForNameButton": "Search For Name", "@searchForNameButton": { "description": "A button to search for an account name", "type": "text", "placeholders": {} }, "searchAccountNameButton": "Search Account Name", "@searchAccountNameButton": { "description": "A button to search an account name", "type": "text", "placeholders": {} }, "searchNameButton": "Search Name", "@searchNameButton": { "description": "A button to search name", "type": "text", "placeholders": {} }, "okayGoBackButton": "Okay, Go Back", "@okayGoBackButton": { "description": "A button to confirm and go back", "type": "text", "placeholders": {} }, "okayButton": "Okay", "@okayButton": { "description": "A button that simply indicates a neutral action, like closing an informative dialog", "type": "text", "placeholders": {} }, "nextButton": "Next", "@nextButton": { "description": "A button to the next screen", "type": "text", "placeholders": {} }, "receiveAccountButton": "Receive Account", "@receiveAccountButton": { "description": "A button to open up the public key sheet(screen) that displays a QR code to receive an account", "type": "text", "placeholders": {} }, "receiveAnAccountButton": "Receive an Account", "@receiveAnAccountButton": { "description": "A button to open up the public key sheet(screen) that displays a QR code to receive an account", "type": "text", "placeholders": {} }, "supportButton": "Support", "@supportButton": { "description": "A button to open up the live support window", "type": "text", "placeholders": {} }, "liveSupportButton": "Support", "@liveSupportButton": { "description": "A button to open up the live support window", "type": "text", "placeholders": {} }, "welcomeParagraph": "Welcome to Blaise Wallet. To begin, you can create a new private key or import one.", "@welcomeParagraph": { "description": "A paragraph that greets the user in the initial opening", "type": "text", "placeholders": {} }, "newKeySecurityParagraph": "In the next screen, you'll see your new private key. It is a password to access your funds. It is crucial that you back it up and never share it with anyone.", "@newKeySecurityParagraph": { "description": "A paragraph that explains what users should do with their new private key", "type": "text", "placeholders": {} }, "uninstallDisclaimerParagraph": "If you lose your device or uninstall Blaise Wallet, you'll need your private key to recover your funds.", "@uninstallDisclaimerParagraph": { "description": "A paragraph that gives a security disclaimer about what happens if the wallet is uninstalled", "type": "text", "placeholders": {} }, "newPrivateKeyParagraph": "Below is your new wallet’s private key. It is crucial that you backup your private key and never store it as plaintext or a screenshot. We recommend writing it on a piece of paper and storing it offline.", "@newPrivateKeyParagraph": { "description": "A paragraph that explains what users should do with their new private key", "type": "text", "placeholders": {} }, "newKeyBackUpConfirmParagraph": "Are you sure that you have backed up your new wallet’s private key?", "@newKeyBackUpConfirmParagraph": { "description": "A paragraph to confirm if the new private key is backed up", "type": "text", "placeholders": {} }, "newWalletGreetingParagraph": "Welcome to Blaise Wallet.\nYou can start by getting an account.", "@newWalletGreetingParagraph": { "description": "A paragraph to greet the user when a new wallet is created", "type": "text", "placeholders": {} }, "getAccountFirstParagraph": "There are 2 options for getting your first account:", "@getAccountFirstParagraph": { "description": "The first paragraph of the explanation for the process of getting an account", "type": "text", "placeholders": {} }, "getAccountSecondParagraph": "1- You can get a free account using your phone number. Only 1 account per phone number is allowed.", "@getAccountSecondParagraph": { "description": "The second paragraph of the explanation for the process of getting an account", "type": "text", "placeholders": {} }, "getAccountThirdParagraph": "2- You can buy as many accounts as you want for %1 Pascal (%2).", "@getAccountThirdParagraph": { "description": "The third paragraph of the explanation for the process of getting an account", "type": "text", "placeholders": {} }, "getAccountThirdParagraphAlternative": "2- You can buy an account for %1 Pascal (%2). Buying only 1 account is allowed per user.", "@getAccountThirdParagraphAlternative": { "description": "The third paragraph of the explanation for the process of getting an account", "type": "text", "placeholders": {} }, "getAccountThirdParagraphAlternative2": "2- You can buy an account for %1 Pascal (%2). You can buy up to %3 accounts.", "@getAccountThirdParagraphAlternative2": { "description": "The third paragraph of the explanation for the process of getting an account", "type": "text", "placeholders": {} }, "enterPhoneNumberParagraph": "Enter your phone number below.", "@enterPhoneNumberParagraph": { "description": "A paragraph that tells users to enter their phone number to the text field below", "type": "text", "placeholders": {} }, "invalidPhoneNumberParagraph": "Phone number is not valid", "@invalidPhoneNumberParagraph": { "description": "User has entered an invalid phone number", "type": "text", "placeholders": {} }, "enterConfirmationCodeParagraph": "We have sent you a confirmation code, please enter it below.", "@enterConfirmationCodeParagraph": { "description": "A paragraph that tells users to enter the confirmation code to the text field below", "type": "text", "placeholders": {} }, "confirmationCodeError": "Failed to verify code, ensure you've entered it correctly", "@confirmationCodeError": { "description": "When a user enters their freepasa SMS code but it can't be verified", "type": "text", "placeholders": {} }, "freepasaComplete": "Success, your new account will be available after 1 network confirmation", "@freepasaComplete": { "description": "After the freepasa process is complete", "type": "text", "placeholders": {} }, "unconfirmedAccountHeader": "Unconfirmed Account", "@unconfirmedAccountHeader": { "description": "A user has an account in their wallet that has been transferred to them, but isnt confirmed yet. This is the info dialog header.", "type": "text", "placeholders": {} }, "unconfirmedAccountParagraph": "This is an unconfirmed account. It has been transferred to you, but there needs to be 1 network confirmation before you can use it. This usually takes about 5 minutes, once it's complete you'll be able to use this account.", "@unconfirmedAccountParagraph": { "description": "Explaining that an account can't be used until 1 network confirmation to the user.", "type": "text", "placeholders": {} }, "borrowStarted": "Purchase Started for %1", "@borrowStarted": { "description": "Users may see this after starting the account purchase process", "type": "text", "placeholders": {} }, "borrowAccountParagraph": "To buy an account, first you’ll need to borrow one for free. If you send at least %1 Pascal (%2) to the account within %3 days, the account will be yours and %1 Pascal will be deducted from your balance automatically.\nOtherwise, it’ll return back to us at the end of %3 days and won’t belong to your wallet anymore.\nIt is recommended you only send a small amount of coins until you own the account.", "@borrowAccountParagraph": { "description": "A paragraph that explains the process of borrowing & buying an account", "type": "text", "placeholders": {} }, "importPrivateKeyParagraph": "Enter your private key below.", "@importPrivateKeyParagraph": { "description": "A paragraph that tells the user to enter their private key to the text field below", "type": "text", "placeholders": {} }, "looksLikeEncryptedKeyParagraph": "This looks like an encrypted private key, please enter the password to decrypt and import it.", "@looksLikeEncryptedKeyParagraph": { "description": "A paragraph that tells the user that the key looks like an encrypted one and it needs to be decrypted to import", "type": "text", "placeholders": {} }, "changeDaemonParagraph": "Enter an address to use a different Pascal daemon for RPC requests.", "@changeDaemonParagraph": { "description": "A paragraph that tells the user to enter a new daemon address below", "type": "text", "placeholders": {} }, "urlChangedToParagraph": "URL changed to %1", "@urlChangedToParagraph": { "description": "A paragraph that tells the user that the URL is changed to the entered URL", "type": "text", "placeholders": {} }, "backupKeyFirstParagraph": "You have 2 options for backing up your private key:", "@backupKeyFirstParagraph": { "description": "The first paragraph of the explanation for the process of backing up the private key", "type": "text", "placeholders": {} }, "backupKeySecondParagraph": "1- Encrypted, which means it is protected by a password.", "@backupKeySecondParagraph": { "description": "The second paragraph of the explanation for the process of backing up the private key", "type": "text", "placeholders": {} }, "backupKeyThirdParagraph": "2- Unencrypted, which means it is raw and not protected by a password.", "@backupKeyThirdParagraph": { "description": "The third paragraph of the explanation for the process of backing up the private key", "type": "text", "placeholders": {} }, "backupKeyFourthParagraph": "We recommend storing the unencrypted version offline, by writing it on a piece of paper. You can store the encrypted version on a password manager for your convenience.", "@backupKeyFourthParagraph": { "description": "The fourth paragraph of the explanation for the process of backing up the private key", "type": "text", "placeholders": {} }, "encryptKeyParagraph": "Create a new password to encrypt your private key.", "@encryptKeyParagraph": { "description": "A paragraph that tells the user to create a new password to encrypt their key", "type": "text", "placeholders": {} }, "backupEncryptedKeyFirstParagraph": "Below is your encrypted private key. It is protected by a password. You can store it safely on a password manager for your convenience.", "@backupEncryptedKeyFirstParagraph": { "description": "A paragraph that explains how an encrypted private key can be backed up", "type": "text", "placeholders": {} }, "backupEncryptedKeySecondParagraph": "Since it is encrypted with your password, if you lose or forget your password, you won't be able to decrypt it and access your funds.", "@backupEncryptedKeySecondParagraph": { "description": "A paragraph that gives a disclaimer about what would happen in case the password that was used to encrypt the private key is lost or forgotten", "type": "text", "placeholders": {} }, "backupUnencryptedKeyParagraph": "Below is your unencrypted private key. It is not protected by a password, which means it is crucial that you store it somewhere safe and offline. We recommend writing it on a piece of paper.", "@backupUnencryptedKeyParagraph": { "description": "A paragraph that explains the process of backing up the unencrypted private key", "type": "text", "placeholders": {} }, "publicKeyParagraph": "Below is your public key. As the name suggests, it is intended to be shared publicly and prove that a particular operation belongs to your private key.", "@publicKeyParagraph": { "description": "A paragraph that explains what a public key is", "type": "text", "placeholders": {} }, "borrowedAccountParagraph": "This is a borrowed account.\nIf you send at least %1 Pascal to it in the next %2 days, %3 hours, and %4 minutes, it’ll be yours.", "@borrowedAccountParagraph": { "description": "A paragraph that explains what a borrowed account is", "type": "text", "placeholders": {} }, "borrowedAccountPaidParagraph": "Your account has been purchased!\nThe transfer is currently processing. This process usually takes about 15 minutes, in some cases it may take slightly longer.", "@borrowedAccountPaidParagraph": { "description": "A paragraph that explains that the account has been purchased and transfer is currently processing", "type": "text", "placeholders": {} }, "logoutFirstDisclaimerParagraph": "Logging out will remove your private key and all Blaise related data from this device. If your private key is not backed up, you will never be able to access your funds again. If your private key is backed up, you have nothing to worry about.", "@logoutFirstDisclaimerParagraph": { "description": "The first part of the disclaimer that is shown when the user tries to log out.", "type": "text", "placeholders": {} }, "logoutSecondDisclaimerParagraph": "Are you sure that you've backed up your private key? As long as you've backed up your private key, you have nothing to worry about.", "@logoutSecondDisclaimerParagraph": { "description": "The second part of the disclaimer that is shown when the user tries to log out.", "type": "text", "placeholders": {} }, "noResultsFound": "No results found", "@noResultsFound": { "description": "When searching for account name has returned 0 results", "type": "text", "placeholders": {} }, "sendingConfirmParagraph": "Confirm the transaction details to send.", "@sendingConfirmParagraph": { "description": "A paragraph that tells the user to confirm the info below to send", "type": "text", "placeholders": {} }, "sentParagraph": "Transaction has been sent succesfully.", "@sentParagraph": { "description": "A paragraph that informs the user that the transaction has been sent succesfully", "type": "text", "placeholders": {} }, "changeNameParagraph": "Enter a name below to change your account's name.", "@changeNameParagraph": { "description": "A paragraph that tells the user to enter a new account name below", "type": "text", "placeholders": {} }, "changingNameParagraph": "Confirm your new account name to proceed.", "@changingNameParagraph": { "description": "A paragraph that tells the user to confirm the new account name", "type": "text", "placeholders": {} }, "changedNameParagraph": "Your account name has been changed successfully.", "@changedNameParagraph": { "description": "A paragraph that informs the user that the account name has been changed successfully", "type": "text", "placeholders": {} }, "transferParagraph": "Enter a public key below to transfer the ownership of this account to it.", "@transferParagraph": { "description": "A paragraph that tells the user to enter a public key to the text field below to transfer the ownership of the account", "type": "text", "placeholders": {} }, "transferringParagraph": "Confirm the public key below to transfer the ownership of this account to it.", "@transferringParagraph": { "description": "A paragraph that tells the user to confirm the public key below to proceed with the transfer", "type": "text", "placeholders": {} }, "transferredParagraph": "Your account has been transferred successfully to the public key below.", "@transferredParagraph": { "description": "A paragraph that informs the user that the account transfer has been completed successfully", "type": "text", "placeholders": {} }, "listForSaleParagraph": "Enter a price and an account that will be receiving the payment to list this account for sale.", "@listForSaleParagraph": { "description": "A paragraph that tells the user to enter a price and a receiver account to list the account for sale", "type": "text", "placeholders": {} }, "listingForSaleParagraph": "Confirm the price and the account that will be receiving the payment.", "@listingForSaleParagraph": { "description": "A paragraph that tells the user to confirm the price and the receiver account", "type": "text", "placeholders": {} }, "listedForSaleParagraph": "Your account has been successfully listed for sale. We’ll let you know if someone buys it.", "@listedForSaleParagraph": { "description": "A paragraph that informs the user that the account has been listed for sale successfully", "type": "text", "placeholders": {} }, "createPrivateSaleParagraph": "Enter a price, a receiving account, and a public key below to create a private sale for this account.", "@createPrivateSaleParagraph": { "description": "A paragraph that tells the user to enter a price, a receiver account, and a public key to create a private sale for the account", "type": "text", "placeholders": {} }, "creatingPrivateSaleParagraph": "Confirm the information below.", "@creatingPrivateSaleParagraph": { "description": "A paragraph that tells the user to confirm the information below.", "type": "text", "placeholders": {} }, "createdPrivateSaleParagraph": "The private sale has been created successfully. We’ll let you know if it is bought.", "@createdPrivateSaleParagraph": { "description": "A paragraph that informs the user that the private sale has been created successfully", "type": "text", "placeholders": {} }, "delistFromSaleParagraph": "Confirm that you would like to delist this account from sale.", "@delistFromSaleParagraph": { "description": "A paragraph that tells the users to confirm that they would like to delist the account from sale.", "type": "text", "placeholders": {} }, "delistedFromSaleParagraph": "Your account has been successfully delisted from sale.", "@delistedFromSaleParagraph": { "description": "A paragraph that informs the user that the account has been delisted from sale successfully", "type": "text", "placeholders": {} }, "feeRequiredParagraph": "This operation requires a fee.", "@feeRequiredParagraph": { "description": "A paragraph to indicate that the operation requires a fee", "type": "text", "placeholders": {} }, "feeConfirmAmountParagraph": "Please confirm the addition of %1 Pascal fee to this operation to continue.", "@feeConfirmAmountParagraph": { "description": "A paragraph to tell the user to confirm the addition of a fee", "type": "text", "placeholders": {} }, "keyTypeNotSupportedParagraph": "This type of private key is not yet supported by Blaise. You may create a new private key and transfer your accounts to it using a different wallet.", "@keyTypeNotSupportedParagraph": { "description": "A paragraph to tell the user that the private key type is not supported", "type": "text", "placeholders": {} }, "enterPINToUnlockParagraph": "Enter PIN to unlock Blaise", "@enterPINToUnlockParagraph": { "description": "A paragraph that tells the user to enter the PIN to unlock the wallet", "type": "text", "placeholders": {} }, "authenticateToUnlockParagraph": "Authenticate to Unlock Blaise", "@authenticateToUnlockParagraph": { "description": "A paragraph that tells the user to authenticate to unlock the wallet", "type": "text", "placeholders": {} }, "manyFailedAttemptsParagraph": "Too many failed unlock attempts", "@manyFailedAttemptsParagraph": { "description": "A paragraph to inform the user that there was too many failed unlock attempts", "type": "text", "placeholders": {} }, "authenticateToChangeNameParagraph": "Authenticate to change account name to \"%1\"", "@authenticateToChangeNameParagraph": { "description": "A paragraph that tells the user to authenticate to change the name of the account", "type": "text", "placeholders": {} }, "authenticateToDelistParagraph": "Authenticate to delist the account from sale", "@authenticateToDelistParagraph": { "description": "A paragraph that tells the user to authenticate to delist the account from sale", "type": "text", "placeholders": {} }, "authenticateToListForSaleParagraph": "Authenticate to list account for sale", "@authenticateToListForSaleParagraph": { "description": "A paragraph that tells the user to authenticate to list the account for sale", "type": "text", "placeholders": {} }, "authenticateToCreatePrivateSaleParagraph": "Authenticate to create private sale", "@authenticateToCreatePrivateSaleParagraph": { "description": "A paragraph that tells the user to authenticate to create a private sale for the account", "type": "text", "placeholders": {} }, "authenticateToTransferParagraph": "Authenticate to transfer account", "@authenticateToTransferParagraph": { "description": "A paragraph that tells the user to authenticate to transfer the ownership of the account", "type": "text", "placeholders": {} }, "authenticateToSendParagraph": "Authenticate to send %1 Pascal", "@authenticateToSendParagraph": { "description": "A paragraph that tells the user to authenticate to send a specified amount of Pascal", "type": "text", "placeholders": {} }, "authenticateToBackUpParagraph": "Authenticate to back up private key", "@authenticateToBackUpParagraph": { "description": "A paragraph that tells the user to authenticate to back up the private key", "type": "text", "placeholders": {} }, "invalidPINParagraph": "Invalid PIN", "@invalidPINParagraph": { "description": "A paragraph that tells the user that the entered PIN is invalid", "type": "text", "placeholders": {} }, "noMatchPINParagraph": "PINs do not match", "@noMatchPINParagraph": { "description": "A paragraph that tells the user that the entered PINs do not match", "type": "text", "placeholders": {} }, "confirmPINParagraph": "Confirm your PIN", "@confirmPINParagraph": { "description": "A paragraph that tells the user to confirm the PIN", "type": "text", "placeholders": {} }, "enterPINParagraph": "Enter PIN", "@enterPINParagraph": { "description": "A paragraph that tells the user to enter the PIN", "type": "text", "placeholders": {} }, "createPINParagraph": "Create a 6-digit PIN", "@createPINParagraph": { "description": "A paragraph that tells the user to create a PIN", "type": "text", "placeholders": {} }, "addedToContactsParagraph": "%1 added to contacts", "@addedToContactsParagraph": { "description": "A paragraph that tells the user that the contact has been added to contacts", "type": "text", "placeholders": {} }, "removedFromContactsParagraph": "Removed %1 from contacts", "@removedFromContactsParagraph": { "description": "A paragraph that tells the user that the contact has been removed from contacts", "type": "text", "placeholders": {} }, "failedToRemoveFromContactsParagraph": "Failed to remove %1 from contacts", "@failedToRemoveFromContactsParagraph": { "description": "A paragraph that tells the user that the contact removel process is failed", "type": "text", "placeholders": {} }, "successfullyImportedContactsParagraph": "Successfully imported %1 contacts", "@successfullyImportedContactsParagraph": { "description": "A paragraph to tell the user that a specific number of contacts was successfully imported", "type": "text", "placeholders": {} }, "checkOutBlaiseParagraph": "Check out Blaise! Simple, sleek & secure Pascal wallet for iOS and Android: https://blaisewallet.com", "@checkOutBlaiseParagraph": { "description": "A paragraph that is shared when the user shares Blaise with others via the option in the settings", "type": "text", "placeholders": {} }, "newAccountParagraph": "This is your new account.\nOnce you receive Pascal, operations will show up like below.", "@newAccountParagraph": { "description": "A paragraph that is shown in the operations list of a new account as an explainer", "type": "text", "placeholders": {} }, "settingsHeader": "Settings", "@settingsHeader": { "description": "Header for the settings", "type": "text", "placeholders": {} }, "preferencesHeader": "Preferences", "@preferencesHeader": { "description": "Header for the preferences section", "type": "text", "placeholders": {} }, "currencyHeader": "Currency", "@currencyHeader": { "description": "Header for the currencies", "type": "text", "placeholders": {} }, "languageHeader": "Language", "@languageHeader": { "description": "Header for the languages", "type": "text", "placeholders": {} }, "languageColonHeader": "Language:", "@languageColonHeader": { "description": "Header for the language option on welcome page", "type": "text", "placeholders": {} }, "systemDefaultHeader": "System Default", "@systemDefaultHeader": { "description": "Header for system default", "type": "text", "placeholders": {} }, "themeHeader": "Theme", "@themeHeader": { "description": "Header for the themes", "type": "text", "placeholders": {} }, "themeLightHeader": "Light", "@themeLightHeader": { "description": "Header for the light theme", "type": "text", "placeholders": {} }, "themeDarkHeader": "Dark", "@themeDarkHeader": { "description": "Header for the dark theme", "type": "text", "placeholders": {} }, "themeCopperHeader": "Copper", "@themeCopperHeader": { "description": "Header for the copper theme", "type": "text", "placeholders": {} }, "notificationsHeader": "Notifications", "@notificationsHeader": { "description": "Header for the notifications", "type": "text", "placeholders": {} }, "securityHeader": "Security", "@securityHeader": { "description": "Header for the security section", "type": "text", "placeholders": {} }, "authenticationMethodHeader": "Authentication Method", "@authenticationMethodHeader": { "description": "Header for the authentication method", "type": "text", "placeholders": {} }, "authenticationPINHeader": "PIN", "@authenticationPINHeader": { "description": "Header for the PIN authentication method", "type": "text", "placeholders": {} }, "authenticationBiometricsHeader": "Biometrics", "@authenticationBiometricsHeader": { "description": "Header for the biometric authentication method", "type": "text", "placeholders": {} }, "authenticateOnLaunchHeader": "Authenticate on Launch", "@authenticateOnLaunchHeader": { "description": "Header for the authenticate on launch option", "type": "text", "placeholders": {} }, "yesHeader": "Yes", "@yesHeader": { "description": "Header for the yes option", "type": "text", "placeholders": {} }, "noHeader": "No", "@noHeader": { "description": "Header for the no option", "type": "text", "placeholders": {} }, "automaticallyLockHeader": "Automatically Lock", "@automaticallyLockHeader": { "description": "Header for the automatically lock option", "type": "text", "placeholders": {} }, "lockInstantHeader": "Instantly", "@lockInstantHeader": { "description": "Header for instantly locking option", "type": "text", "placeholders": {} }, "lock1Header": "After %1 minute", "@lock1Header": { "description": "Header for locking after 1 minute option", "type": "text", "placeholders": {} }, "lock5Header": "After %1 minutes", "@lock5Header": { "description": "Header for locking after 5 minutes option", "type": "text", "placeholders": {} }, "lock15Header": "After %1 minutes", "@lock15Header": { "description": "Header for locking after 15 minutes option", "type": "text", "placeholders": {} }, "lock30Header": "After %1 minutes", "@lock30Header": { "description": "Header for locking after 30 minutes option", "type": "text", "placeholders": {} }, "lock60Header": "After %1 minutes", "@lock60Header": { "description": "Header for locking after 60 minutes option", "type": "text", "placeholders": {} }, "daemonHeader": "Daemon", "@daemonHeader": { "description": "Header for Pascal daemon setting", "type": "text", "placeholders": {} }, "defaultHeader": "Default", "@defaultHeader": { "description": "Header for default option", "type": "text", "placeholders": {} }, "manageHeader": "Manage", "@manageHeader": { "description": "Header for the manage section", "type": "text", "placeholders": {} }, "contactsHeader": "Contacts", "@contactsHeader": { "description": "Header for the contacts section in settings", "type": "text", "placeholders": {} }, "backUpPrivateKeyHeader": "Back Up Private Key", "@backUpPrivateKeyHeader": { "description": "Header for the back up private key option in settings", "type": "text", "placeholders": {} }, "viewPublicKeyHeader": "View Public Key", "@viewPublicKeyHeader": { "description": "Header for the view public key option in settings", "type": "text", "placeholders": {} }, "shareHeader": "Share Blaise", "@shareHeader": { "description": "Header for the share Blaise option in settings", "type": "text", "placeholders": {} }, "logoutHeader": "Logout", "@logoutHeader": { "description": "Header for the logout option in settings", "type": "text", "placeholders": {} }, "privacyPolicyHeader": "Privacy Policy", "@privacyPolicyHeader": { "description": "Header for the privacy policy option in settings", "type": "text", "placeholders": {} }, "changeAccountNameHeader": "Change Account Name", "@changeAccountNameHeader": { "description": "Header for the change account name option in other operations list", "type": "text", "placeholders": {} }, "transferAccountHeader": "Transfer Account", "@transferAccountHeader": { "description": "Header for the transfer account option in other operations list", "type": "text", "placeholders": {} }, "listAccountForSaleHeader": "List Account For Sale", "@listAccountForSaleHeader": { "description": "Header for the list account for sale option in other operations list", "type": "text", "placeholders": {} }, "createPrivateSaleHeader": "Create Private Sale", "@createPrivateSaleHeader": { "description": "Header for the create private sale option in other operations list", "type": "text", "placeholders": {} }, "delistFromSaleHeader": "Delist From Sale", "@delistFromSaleHeader": { "description": "Header for the delist from sale option in other operations list", "type": "text", "placeholders": {} }, "getAccountSheetHeader": "Get Account", "@getAccountSheetHeader": { "description": "Header for the get account sheet (screen)", "type": "text", "placeholders": {} }, "freeAccountSheetHeader": "Free Account", "@freeAccountSheetHeader": { "description": "Header for the free account sheet (screen)", "type": "text", "placeholders": {} }, "buyAccountSheetHeader": "Buy Account", "@buyAccountSheetHeader": { "description": "Header for the buy account sheet (screen)", "type": "text", "placeholders": {} }, "sendSheetHeader": "Send", "@sendSheetHeader": { "description": "Header for send sheet (screen)", "type": "text", "placeholders": {} }, "sendingSheetHeader": "Sending", "@sendingSheetHeader": { "description": "Header for sending sheet (screen)", "type": "text", "placeholders": {} }, "sentSheetHeader": "Sent", "@sentSheetHeader": { "description": "Header for sent sheet (screen)", "type": "text", "placeholders": {} }, "requestSheetHeader": "Request", "@requestSheetHeader": { "description": "Header for request sheet (screen)", "type": "text", "placeholders": {} }, "changeNameSheetHeader": "Change Name", "@changeNameSheetHeader": { "description": "Header for change name sheet (screen)", "type": "text", "placeholders": {} }, "changingNameSheetHeader": "Changing", "@changingNameSheetHeader": { "description": "Header for name changing sheet (screen)", "type": "text", "placeholders": {} }, "changedNameSheetHeader": "Changed", "@changedNameSheetHeader": { "description": "Header for name changed sheet (screen)", "type": "text", "placeholders": {} }, "transferSheetHeader": "Transfer", "@transferSheetHeader": { "description": "Header for transfer sheet (screen)", "type": "text", "placeholders": {} }, "transferringSheetHeader": "Transferring", "@transferringSheetHeader": { "description": "Header for transferring sheet (screen)", "type": "text", "placeholders": {} }, "transferredSheetHeader": "Transferred", "@transferredSheetHeader": { "description": "Header for transferred sheet (screen)", "type": "text", "placeholders": {} }, "listForSaleSheetHeader": "List For Sale", "@listForSaleSheetHeader": { "description": "Header for list for sale sheet (screen)", "type": "text", "placeholders": {} }, "listingForSaleSheetHeader": "Listing", "@listingForSaleSheetHeader": { "description": "Header for listing for sale sheet (screen)", "type": "text", "placeholders": {} }, "listedForSaleSheetHeader": "Listed", "@listedForSaleSheetHeader": { "description": "Header for listed for sale sheet (screen)", "type": "text", "placeholders": {} }, "createPrivateSaleSheetHeader": "Private Sale", "@createPrivateSaleSheetHeader": { "description": "Header for create private sale sheet (screen)", "type": "text", "placeholders": {} }, "creatingPrivateSaleSheetHeader": "Creating", "@creatingPrivateSaleSheetHeader": { "description": "Header for creating private sale sheet (screen)", "type": "text", "placeholders": {} }, "createdPrivateSaleSheetHeader": "Created", "@createdPrivateSaleSheetHeader": { "description": "Header for created private sale sheet (screen)", "type": "text", "placeholders": {} }, "delistingSheetHeader": "Delisting", "@delistingSheetHeader": { "description": "Header for delisting sheet (screen)", "type": "text", "placeholders": {} }, "delistedSheetHeader": "Delisted", "@delistedSheetHeader": { "description": "Header for delisted sheet (screen)", "type": "text", "placeholders": {} }, "addContactSheetHeader": "Add Contact", "@addContactSheetHeader": { "description": "Header for add contact sheet (screen)", "type": "text", "placeholders": {} }, "contactSheetHeader": "Contact", "@contactSheetHeader": { "description": "Header for contact details sheet (screen)", "type": "text", "placeholders": {} }, "publicKeySheetHeader": "Public Key", "@publicKeySheetHeader": { "description": "Header for public key sheet (screen)", "type": "text", "placeholders": {} }, "privateKeySheetHeader": "Private Key", "@privateKeySheetHeader": { "description": "Header for private key sheet (screen)", "type": "text", "placeholders": {} }, "backUpSheetHeader": "Back Up", "@backUpSheetHeader": { "description": "Header for back up sheet (screen)", "type": "text", "placeholders": {} }, "encryptSheetHeader": "Encrypt", "@encryptSheetHeader": { "description": "Header for encrypt sheet (screen)", "type": "text", "placeholders": {} }, "changeDaemonSheetHeader": "Change Daemon", "@changeDaemonSheetHeader": { "description": "Header for change daemon sheet (screen)", "type": "text", "placeholders": {} }, "securityFirstHeader": "Security First!", "@securityFirstHeader": { "description": "Header for security first screen", "type": "text", "placeholders": {} }, "newPrivateKeyHeader": "New Private Key", "@newPrivateKeyHeader": { "description": "Header for new private key screen", "type": "text", "placeholders": {} }, "importPrivateKeyHeader": "Import Private Key", "@importPrivateKeyHeader": { "description": "Header for import private key screen", "type": "text", "placeholders": {} }, "decryptAndImportKeyHeader": "Decrypt & Import", "@decryptAndImportKeyHeader": { "description": "Header for decrypt & import private key screen", "type": "text", "placeholders": {} }, "backUpKeyHeader": "Back Up Your Key!", "@backUpKeyHeader": { "description": "Header for back up your key screen", "type": "text", "placeholders": {} }, "lockedHeader": "Locked", "@lockedHeader": { "description": "Header for locked screen", "type": "text", "placeholders": {} }, "privateKeyTextFieldHeader": "Private Key", "@privateKeyTextFieldHeader": { "description": "Header for private key text field", "type": "text", "placeholders": {} }, "passwordTextFieldHeader": "Password", "@passwordTextFieldHeader": { "description": "Header for password text field", "type": "text", "placeholders": {} }, "newPasswordTextFieldHeader": "New Password", "@newPasswordTextFieldHeader": { "description": "Header for new password text field", "type": "text", "placeholders": {} }, "confirmPasswordTextFieldHeader": "Confirm Password", "@confirmPasswordTextFieldHeader": { "description": "Header for confirm password text field", "type": "text", "placeholders": {} }, "confirmTextFieldHeader": "Confirm", "@confirmTextFieldHeader": { "description": "Header for confirm text field", "type": "text", "placeholders": {} }, "countryCodeTextFieldHeader": "Country Code", "@countryCodeTextFieldHeader": { "description": "Header for country code text field", "type": "text", "placeholders": {} }, "phoneNumberTextFieldHeader": "Phone Number", "@phoneNumberTextFieldHeader": { "description": "Header for phone number text field", "type": "text", "placeholders": {} }, "confirmationCodeTextFieldHeader": "Confirmation Code", "@confirmationCodeTextFieldHeader": { "description": "Header for confirmation code text field", "type": "text", "placeholders": {} }, "accountTextFieldHeader": "Account", "@accountTextFieldHeader": { "description": "Header for account text field", "type": "text", "placeholders": {} }, "addressTextFieldHeader": "Address", "@addressTextFieldHeader": { "description": "Header for address text field", "type": "text", "placeholders": {} }, "contactNameTextFieldHeader": "Contact Name", "@contactNameTextFieldHeader": { "description": "Header for contact name text field", "type": "text", "placeholders": {} }, "amountTextFieldHeader": "Amount", "@amountTextFieldHeader": { "description": "Header for amount text field", "type": "text", "placeholders": {} }, "payloadTextFieldHeader": "Payload", "@payloadTextFieldHeader": { "description": "Header for payload text field", "type": "text", "placeholders": {} }, "nameTextFieldHeader": "Name", "@nameTextFieldHeader": { "description": "Header for name text field", "type": "text", "placeholders": {} }, "newAccountNameTextFieldHeader": "New Account Name", "@newAccountNameTextFieldHeader": { "description": "Header for new account name text field", "type": "text", "placeholders": {} }, "publicKeyTextFieldHeader": "Public Key", "@publicKeyTextFieldHeader": { "description": "Header for public key text field", "type": "text", "placeholders": {} }, "priceTextFieldHeader": "Price", "@priceTextFieldHeader": { "description": "Header for price text field", "type": "text", "placeholders": {} }, "receivingAccountTextFieldHeader": "Receiving Account", "@receivingAccountTextFieldHeader": { "description": "Header for receiving account text field", "type": "text", "placeholders": {} }, "durationTextFieldHeader": "Duration", "@durationTextFieldHeader": { "description": "Header for duration text field", "type": "text", "placeholders": {} }, "feeTextFieldHeader": "Fee", "@feeTextFieldHeader": { "description": "Header for fee text field", "type": "text", "placeholders": {} }, "otherOperationsHeader": "Other Operations", "@otherOperationsHeader": { "description": "Header for other operations dialog", "type": "text", "placeholders": {} }, "warningHeader": "Warning", "@warningHeader": { "description": "Header for warning dialog", "type": "text", "placeholders": {} }, "areYouSureHeader": "Are You Sure?", "@areYouSureHeader": { "description": "Header for are you sure dialog", "type": "text", "placeholders": {} }, "addFeeHeader": "Add Fee", "@addFeeHeader": { "description": "Header for add fee dialog", "type": "text", "placeholders": {} }, "keyTypeNotSupportedHeader": "Key Not Supported", "@keyTypeNotSupportedHeader": { "description": "Header for key not supported dialog", "type": "text", "placeholders": {} }, "accountToSendFromHeader": "Account to Send From", "@accountToSendFromHeader": { "description": "Header for account to send from dialog", "type": "text", "placeholders": {} }, "sentHeader": "Sent", "@sentHeader": { "description": "Header for sent type operation list item", "type": "text", "placeholders": {} }, "receivedHeader": "Received", "@receivedHeader": { "description": "Header for received type operation list item", "type": "text", "placeholders": {} }, "nameChangedHeader": "Name Changed", "@nameChangedHeader": { "description": "Header for listed for sale type operation list item", "type": "text", "placeholders": {} }, "listedForSaleHeader": "Listed For Sale", "@listedForSaleHeader": { "description": "Header for listed for sale type operation list item", "type": "text", "placeholders": {} }, "privateSaleHeader": "Private Sale", "@privateSaleHeader": { "description": "Header for private sale type operation list item", "type": "text", "placeholders": {} }, "delistedFromSaleHeader": "Delisted From Sale", "@delistedFromSaleHeader": { "description": "Header for delisted from sale type operation list item", "type": "text", "placeholders": {} }, "delistedHeader": "Delisted", "@delistedHeader": { "description": "Header for delisted type operation list item", "type": "text", "placeholders": {} }, "undefinedHeader": "Undefined", "@undefinedHeader": { "description": "Header for undefined type operation list item", "type": "text", "placeholders": {} }, "transferredHeader": "Transferred", "@transferredHeader": { "description": "Header for transferred type operation list item", "type": "text", "placeholders": {} }, "connectingHeader": "Connecting", "@connectingHeader": { "description": "A header to let the user now that Blaise is currently connecting to (or loading) live chat.", "type": "text", "placeholders": {} }, "balanceHeader": "Balance", "@balanceHeader": { "description": "Header for balance", "type": "text", "placeholders": {} }, "totalBalanceHeader": "Total Balance", "@totalBalanceHeader": { "description": "Header for total balance", "type": "text", "placeholders": {} }, "accountBalanceHeader": "Account Balance", "@accountBalanceHeader": { "description": "Header for account balance", "type": "text", "placeholders": {} }, "accountsHeader": "Accounts", "@accountsHeader": { "description": "Header for accounts", "type": "text", "placeholders": {} }, "operationsHeader": "Operations", "@operationsHeader": { "description": "Header for operations", "type": "text", "placeholders": {} }, "encryptThePayloadHeader": "Encrypt the Payload", "@encryptThePayloadHeader": { "description": "Header for encrypt the payload switch", "type": "text", "placeholders": {} }, "encryptPayloadHeader": "Encrypt Payload", "@encryptPayloadHeader": { "description": "Header for encrypt payload switch", "type": "text", "placeholders": {} }, "forSaleHeader": "For Sale", "@forSaleHeader": { "description": "Header of for sale tag", "type": "text", "placeholders": {} }, "borrowedHeader": "Borrowed", "@borrowedHeader": { "description": "Header for borrowed tag", "type": "text", "placeholders": {} }, "borrowedTransferredHeader": "Transfer Pending", "@borrowedTransferredHeader": { "description": "Header for borrowed tag, after account is transferred but not confirmed", "type": "text", "placeholders": {} }, "borrowedAccountHeader": "Borrowed Account", "@borrowedAccountHeader": { "description": "Header for borrowed account tag", "type": "text", "placeholders": {} }, "feeColonHeader": "Fee:", "@feeColonHeader": { "description": "Header for fee amount", "type": "text", "placeholders": {} }, "pendingHeader": "Pending", "@pendingHeader": { "description": "Header to indicate that an operation is pending", "type": "text", "placeholders": {} }, "onHeader": "On", "@onHeader": { "description": "A header to indicate that something is on", "type": "text", "placeholders": {} }, "offHeader": "Off", "@offHeader": { "description": "A header to indicate that something is off", "type": "text", "placeholders": {} }, "priceRequiredError": "Price is required", "@priceRequiredError": { "description": "Error that tells the user that the price is required", "type": "text", "placeholders": {} }, "amountRequiredError": "Amount is required", "@amountRequiredError": { "description": "Error that tells the user that the amount is required", "type": "text", "placeholders": {} }, "nameRequiredError": "Name is required", "@nameRequiredError": { "description": "Error that tells the user that the name is required", "type": "text", "placeholders": {} }, "zeroPriceError": "Price can't be 0", "@zeroPriceError": { "description": "Error that tells the user that the price cant be zero", "type": "text", "placeholders": {} }, "zeroAmountError": "Amount can't be 0", "@zeroAmountError": { "description": "Error that tells the user that the amount cant be zero", "type": "text", "placeholders": {} }, "invalidAccountNameError": "Invalid account name", "@invalidAccountNameError": { "description": "Error that tells the user that the account name is invalid", "type": "text", "placeholders": {} }, "invalidReceivingAccountError": "Invalid receiving account", "@invalidReceivingAccountError": { "description": "Error that tells the user that the receiving account is invalid", "type": "text", "placeholders": {} }, "invalidPublicKeyError": "Invalid public key", "@invalidPublicKeyError": { "description": "Error that tells the user that the public key is invalid", "type": "text", "placeholders": {} }, "invalidPrivateKeyError": "Invalid private key", "@invalidPrivateKeyError": { "description": "Error that tells the user that the private key is invalid", "type": "text", "placeholders": {} }, "invalidAddressError": "Invalid address", "@invalidAddressError": { "description": "Error that tells the user that the address is invalid", "type": "text", "placeholders": {} }, "invalidAccountError": "Invalid account", "@invalidAccountError": { "description": "Error that tells the user that the account is invalid", "type": "text", "placeholders": {} }, "invalidDestinationError": "Invalid destination", "@invalidDestinationError": { "description": "Error that tells the user that the destination is invalid", "type": "text", "placeholders": {} }, "insufficientBalanceError": "Insufficient balance", "@insufficientBalanceError": { "description": "Error that tells the user that the balance is insufficient", "type": "text", "placeholders": {} }, "threeCharacterNameError": "Must be at least 3 characters", "@threeCharacterNameError": { "description": "Error that tells the user that the account name cant be shorter than 3 characters", "type": "text", "placeholders": {} }, "contactDoesntExistError": "Contact doesn't exist", "@contactDoesntExistError": { "description": "Error that tells the user that the contact doesnt exist", "type": "text", "placeholders": {} }, "contactAlreadyExistsError": "Contact already exists", "@contactAlreadyExistsError": { "description": "Error that tells the user that the contact already exists", "type": "text", "placeholders": {} }, "cantSendToYourselfError": "Can't send to yourself", "@cantSendToYourselfError": { "description": "Error that tells the user that you cant send to yourself", "type": "text", "placeholders": {} }, "somethingWentWrongError": "Something went wrong, please try again later", "@somethingWentWrongError": { "description": "Error that tells the user that something went wrong", "type": "text", "placeholders": {} }, "failedToEncryptPayloadError": "Failed to encrypt the payload", "@failedToEncryptPayloadError": { "description": "Error that tells the user that payload encrypt is failed", "type": "text", "placeholders": {} }, "emptyPasswordError": "Password can't be empty", "@emptyPasswordError": { "description": "Error that tells the user that the password cant be empty", "type": "text", "placeholders": {} }, "noMatchPasswordError": "Passwords don't match", "@noMatchPasswordError": { "description": "Error that tells the user that the passwords dont match", "type": "text", "placeholders": {} }, "invalidPasswordError": "Invalid password", "@invalidPasswordError": { "description": "Error that tells the user that the password is invalid", "type": "text", "placeholders": {} }, "didNotGetResponseError": "Did not get a response from server", "@didNotGetResponseError": { "description": "Error that tells the user that there is no response from the server", "type": "text", "placeholders": {} }, "noContactsToExportError": "No contacts to export", "@noContactsToExportError": { "description": "Error that tells the user that there is no contacts to export", "type": "text", "placeholders": {} }, "noContactsToImportError": "No contacts to import", "@noContactsToImportError": { "description": "Error that tells the user that there is no contacts to import", "type": "text", "placeholders": {} }, "failedToImportContactsError": "Failed to import contacts", "@failedToImportContactsError": { "description": "Error that tells the user that there is no contacts to export", "type": "text", "placeholders": {} }, "blockchainRewardOPDetails": "Blockchain Reward (%1)", "@blockchainRewardOPDetails": { "description": "Operation details header for blockchain reward", "type": "text", "placeholders": {} }, "transactionOPDetails": "Transaction (%1)", "@transactionOPDetails": { "description": "Operation details header for transaction", "type": "text", "placeholders": {} }, "changeKeyOPDetails": "Change key (%1)", "@changeKeyOPDetails": { "description": "Operation details header for change key", "type": "text", "placeholders": {} }, "recoverFundsOPDetails": "Recover Funds (%1)", "@recoverFundsOPDetails": { "description": "Operation details header for recover funds", "type": "text", "placeholders": {} }, "listAccountForSaleOPDetails": "List Account for Sale (%1)", "@listAccountForSaleOPDetails": { "description": "Operation details header for list account for sale", "type": "text", "placeholders": {} }, "delistAccountOPDetails": "Delist Account (%1)", "@delistAccountOPDetails": { "description": "Operation details header for delist account", "type": "text", "placeholders": {} }, "buyAccountOPDetails": "Buy Account (%1)", "@buyAccountOPDetails": { "description": "Operation details header for buy account", "type": "text", "placeholders": {} }, "changeKeySignedOPDetails": "Change Key Signed (%1)", "@changeKeySignedOPDetails": { "description": "Operation details header for change key signed", "type": "text", "placeholders": {} }, "changeAccountInfoOPDetails": "Change Account Info (%1)", "@changeAccountInfoOPDetails": { "description": "Operation details header for change account info", "type": "text", "placeholders": {} }, "multioperationOPDetails": "Multioperation (%1)", "@multioperationOPDetails": { "description": "Operation details header for multioperation", "type": "text", "placeholders": {} }, "unknownOPDetails": "Unknown (%1)", "@unknownOPDetails": { "description": "Operation details header for unknown", "type": "text", "placeholders": {} }, "sendingAccountOPDetails": "Sending Account", "@sendingAccountOPDetails": { "description": "Operation details header for sending account", "type": "text", "placeholders": {} }, "receivingAccountOPDetails": "Receiving Account", "@receivingAccountOPDetails": { "description": "Operation details header for receiving account", "type": "text", "placeholders": {} }, "changingAccountOPDetails": "Changing Account", "@changingAccountOPDetails": { "description": "Operation details header for changing account", "type": "text", "placeholders": {} }, "sendAmountOPDetails": "Send Amount", "@sendAmountOPDetails": { "description": "Operation details header for send amount", "type": "text", "placeholders": {} }, "payloadOPDetails": "Payload", "@payloadOPDetails": { "description": "Operation details header for payload", "type": "text", "placeholders": {} }, "newPublicKeyOPDetails": "New Public Key", "@newPublicKeyOPDetails": { "description": "Operation details header for new public key", "type": "text", "placeholders": {} }, "newNameOPDetails": "New Name", "@newNameOPDetails": { "description": "Operation details header for new name", "type": "text", "placeholders": {} }, "sellerAccountOPDetails": "Seller Account", "@sellerAccountOPDetails": { "description": "Operation details header for seller account", "type": "text", "placeholders": {} }, "accountPriceOPDetails": "Account Price", "@accountPriceOPDetails": { "description": "Operation details header for account price", "type": "text", "placeholders": {} }, "lockedUntilBlockOPDetails": "Locked Until Block", "@lockedUntilBlockOPDetails": { "description": "Operation details header for locked until block", "type": "text", "placeholders": {} }, "blockOPDetails": "block", "@blockOPDetails": { "description": "Operation details header for block", "type": "text", "placeholders": {} }, "optxtOPDetails": "optxt", "@optxtOPDetails": { "description": "Operation details header for optxt", "type": "text", "placeholders": {} }, "timeOPDetails": "time", "@timeOPDetails": { "description": "Operation details header for time", "type": "text", "placeholders": {} }, "naOPDetails": "N/A", "@naOPDetails": { "description": "Operation details header for N/A", "type": "text", "placeholders": {} }, "ophashOPDetails": "ophash", "@ophashOPDetails": { "description": "Operation details header for ophash", "type": "text", "placeholders": {} }, "optypeOPDetails": "optype", "@optypeOPDetails": { "description": "Operation details header for optype", "type": "text", "placeholders": {} }, "maturationOPDetails": "maturation", "@maturationOPDetails": { "description": "Operation details header for maturation", "type": "text", "placeholders": {} }, "nullOPDetails": "null", "@nullOPDetails": { "description": "Operation details header for null", "type": "text", "placeholders": {} }, "feeOPDetails": "fee", "@feeOPDetails": { "description": "Operation details header for fee", "type": "text", "placeholders": {} }, "opblockOPDetails": "opblock", "@opblockOPDetails": { "description": "Operation details header for opblock", "type": "text", "placeholders": {} }, "noperationOPDetails": "n_operation", "@noperationOPDetails": { "description": "Operation details header for n_operation", "type": "text", "placeholders": {} }, "accountOPDetails": "account", "@accountOPDetails": { "description": "Operation details header for account", "type": "text", "placeholders": {} }, "signeraccountOPDetails": "signer_account", "@signeraccountOPDetails": { "description": "Operation details header for signer_account", "type": "text", "placeholders": {} } } ================================================ FILE: lib/l10n/intl_es.arb ================================================ { "newPrivateKeyButton": "Nueva clave privada", "importPrivateKeyButton": "Importar clave privada", "gotItButton": "¡De acuerdo!", "goBackButton": "Volver", "copyButton": "Copiar", "copiedButton": "Copiado", "keyCopiedButton": "Clave Copiada", "iHaveBackedItUpButton": "Lo he respaldado", "yesImSureButton": "Si, estoy seguro", "noGoBackButton": "No, volver", "getAnAccountButton": "Obtener una cuenta", "getAFreeAccountButton": "Obtener una cuenta gratuita", "buyAnAccountButton": "Comprar una cuenta", "sendConfirmationButton": "Enviar confirmación", "confirmButton": "Confirmar", "borrowAnAccountButton": "Pedir prestada una cuenta", "importButton": "Importar", "receiveButton": "Recibir", "sendButton": "Enviar", "copyAddressButton": "Copiar dirección", "copiedAddressButton": "Dirección copiada", "addToContactsButton": "Agregar a contactos", "operationDetailsButton": "Detalles de la operación", "openInExplorerButton": "Abrir en el explorador", "requestButton": "Solicitar", "addAPayloadButton": "+ Agregar un Payload", "addADurationButton": "+ Agregar una duración", "scanQRCodeButton": "Escanear código QR", "cancelButton": "Cancelar", "closeButton": "Cerrar", "changeNameButton": "Cambiar nombre", "transferButton": "Transferir", "listForSaleButton": "Publicar a la venta", "createPrivateSaleButton": "Crear venta privada", "yesAddFeeButton": "Si, agregar tarifa", "unlockButton": "Desbloquear", "unlockWithBiometricsButton": "Desbloquear con datos biométricos", "unlockWithPINButton": "Desbloquear con PIN", "setToDefaultButton": "Establecer predeterminado", "changeDaemonButton": "Cambiar Daemon", "addContactButton": "Agregar contacto", "encryptedKeyButton": "Clave encriptada", "unencryptedKeyButton": "Clave no encriptada", "encryptButton": "Encriptar", "showButton": "Mostrar", "hideButton": "Ocultar", "copyEncryptedKeyButton": "Copiar clave encriptada", "copyUnencryptedKeyButton": "Copiar clave no encriptada", "copyKeyButton": "Copiar clave", "copyPublicKeyButton": "Copiar clave pública", "deletePrivateKeyAndLogoutButton": "Borrar clave privada\ny cerrar sesión", "searchForNameButton": "Buscar por nombre", "searchAccountNameButton": "Buscar por nombre de cuenta", "searchNameButton": "Buscar nombre", "okayGoBackButton": "Ok, volver", "nextButton": "Siguiente", "welcomeParagraph": "Bienvenido a Blaise Wallet. Para empezar, puede crear una nueva clave privada o importar una.", "newKeySecurityParagraph": "En la siguiente pantalla, verá su nueva clave privada. Es una contraseña para acceder a sus fondos. Es crucial que la respalde y nunca la comparta con nadie.", "uninstallDisclaimerParagraph": "Si pierde su dispositivo o desinstala Blaise Wallet, necesitará su clave privada para recuperar sus fondos.", "newPrivateKeyParagraph": "A continuación se muestra la clave privada de su nueva billetera. Es crucial que haga una copia de seguridad de su clave privada y nunca la almacene como texto plano o como una captura de pantalla. Recomendamos escribirla en una hoja de papel y almacenarla sin conexión.", "newKeyBackUpConfirmParagraph": "¿Estás seguro de que has respaldado la clave privada de tu nueva billetera?", "newWalletGreetingParagraph": "Bienvenido a Blaise Wallet.\nPuede comenzar obteniendo una cuenta.", "getAccountFirstParagraph": "Existen dos opciones para obtener su primera cuenta:", "getAccountSecondParagraph": "1- Puede obtener una cuenta gratuita usando su número telefónico.\nSe permite una sola cuenta por número telefónico.", "getAccountThirdParagraph": "2- Puede comprar tantas cuentas como quiera por %1 Pascal (%2).", "enterPhoneNumberParagraph": "Ingrese su número telefónico a continuación.", "enterConfirmationCodeParagraph": "Hemos enviado un código de confirmación, por favor ingréselo a continuación.", "borrowStarted": "Compra iniciada por %1", "borrowAccountParagraph": "Para comprar una cuenta, primero deberá pedir prestada una de forma gratuita. Si envía al menos %1 Pascal (%2) a la cuenta dentro de %3 días, la cuenta será suya y %1 Pascal se deducirá de su saldo automáticamente. \nDe lo contrario, volverá a nosotros al final de %3 días y ya no pertenecerá a su billetera.\nSe recomienda solo enviar una pequeña cantidad de monedas hasta que posea la cuenta.", "importPrivateKeyParagraph": "Ingrese su clave privada a continuación.", "looksLikeEncryptedKeyParagraph": "Esta parece una clave privada encriptada, ingrese la contraseña para descifrarla e importarla.", "changeDaemonParagraph": "Ingrese una dirección para usar un daemon Pascal diferente para solicitudes RPC.", "urlChangedToParagraph": "URL cambiada a %1", "backupKeyFirstParagraph": "Tiene dos opciones para respaldar su clave privada:", "backupKeySecondParagraph": "1- Encriptada, significa que está protegida por una contraseña.", "backupKeyThirdParagraph": "2- Sin encriptar, lo que significa que se encuentra sin formato y no está protegida por una contraseña.", "backupKeyFourthParagraph": "Recomendamos almacenar la versión sin encriptar sin conexión, escribiéndola en una hoja de papel. Puede almacenar la versión encriptada en un administrador de contraseñas para su conveniencia.", "encryptKeyParagraph": "Crear una nueva contraseña para encriptar su clave privada.", "backupEncryptedKeyFirstParagraph": "A continuación se muestra su clave privada encriptada. Está protegida por una contraseña. Puede almacenarla de forma segura en un administrador de contraseñas para su conveniencia.", "backupEncryptedKeySecondParagraph": "Como está encriptada con su contraseña, si la pierde o la olvida, no podrá descifrarla ni acceder a sus fondos.", "backupUnencryptedKeyParagraph": "A continuación se muestra su clave privada sin encriptar. No está protegida por una contraseña, lo que significa que es crucial que la guarde en un lugar seguro y sin conexión. Recomendamos escribirla en una hoja de papel.", "publicKeyParagraph": "A continuación se muestra su clave pública. Como su nombre lo indica, está destinada a ser compartida públicamente y probar que una operación particular pertenece a su clave privada.", "borrowedAccountParagraph": "Esta es una cuenta prestada .\nSi le envía al menos %1 Pascal en los próximos %2 días,%3 horas y %4 minutos, será suya.", "borrowedAccountPaidParagraph": "¡Su cuenta ha sido comprada!\nLa transferencia se está procesando actualmente. Este proceso generalmente toma unos 15 minutos, en algunos casos puede tardar un poco más.", "logoutFirstDisclaimerParagraph": "Cerrar sesión eliminará su clave privada y todos los datos relacionados con Blaise de este dispositivo. Si no se realiza una copia de seguridad de su clave privada, nunca podrá volver a acceder a sus fondos. Si su clave privada está respaldada, no tiene nada de qué preocuparse.", "logoutSecondDisclaimerParagraph": "¿Está seguro de que ha hecho una copia de seguridad de su clave privada? Mientras haya hecho una copia de seguridad de su clave privada, no tiene nada de qué preocuparse.", "sendingConfirmParagraph": "Confirme los detalles de la transacción para enviar.", "sentParagraph": "La transacción ha sido enviada con éxito.", "changeNameParagraph": "Ingrese un nombre a continuación para modificar el nombre de su cuenta.", "changingNameParagraph": "Confirme su nuevo nombre de cuenta para continuar.", "changedNameParagraph": "Su nombre de cuenta ha sido modificado exitosamente.", "transferParagraph": "Ingrese una clave pública a continuación para transferirle la propiedad de esta cuenta.", "transferringParagraph": "Confirme la clave pública a continuación para transferirle la propiedad de esta cuenta.", "transferredParagraph": "Su cuenta ha sido transferida exitosamente a la clave pública que se muestra a continuación.", "listForSaleParagraph": "Ingrese un precio y una cuenta que recibirá el pago para publicar a la venta esta cuenta.", "listingForSaleParagraph": "Confirme el precio y la cuenta que recibirá el pago.", "listedForSaleParagraph": "Su cuenta ha sido exitosamente publicada a la venta. Le avisaremos si alguien la compra.", "createPrivateSaleParagraph": "Ingrese un precio, una cuenta receptora y una clave pública a continuación para crear una venta privada para esta cuenta.", "creatingPrivateSaleParagraph": "Confirme la información a continuación.", "createdPrivateSaleParagraph": "La venta privada se ha creado con éxito. Le haremos saber si se compra.", "delistFromSaleParagraph": "Confirme que desea retirar de la venta esta cuenta.", "delistedFromSaleParagraph": "Su cuenta ha sido retirada de la venta con éxito.", "feeRequiredParagraph": "Está operación requiere una tarifa", "feeConfirmAmountParagraph": "Confirme la adición de una comisión de %1 Pascal en operación para continuar.", "keyTypeNotSupportedParagraph": "Blaise aún no admite este tipo de clave privada. Puede crear una nueva clave privada y transferir sus cuentas a esta utilizando una billetera diferente.", "enterPINToUnlockParagraph": "Introduzca el PIN para desbloquear Blaise", "authenticateToUnlockParagraph": "Autenticar para desbloquear a Blaise", "manyFailedAttemptsParagraph": "Demasiados intentos de desbloqueo fallidos", "authenticateToChangeNameParagraph": "Autenticar para cambiar el nombre de la cuenta por \"%1\"", "authenticateToDelistParagraph": "Autenticar para quitar la cuenta de la venta", "authenticateToListForSaleParagraph": "Autenticar para agregar la cuenta a la venta", "authenticateToCreatePrivateSaleParagraph": "Autenticar para crear una venta privada", "authenticateToTransferParagraph": "Autenticar para transferir cuenta", "authenticateToSendParagraph": "Autenticar para enviar %1 Pascal", "authenticateToBackUpParagraph": "Autenticar para hacer una copia de seguridad de la clave privada", "invalidPINParagraph": "PIN inválido", "noMatchPINParagraph": "Los PIN no coinciden", "confirmPINParagraph": "Confirme su PIN", "enterPINParagraph": "Introduzca PIN", "createPINParagraph": "Crear un PIN de 6 dígitos", "addedToContactsParagraph": "%1 agregado a contactos", "removedFromContactsParagraph": "%1 removido de contactos", "failedToRemoveFromContactsParagraph": "Error al remover %1 de contactos", "successfullyImportedContactsParagraph": "Exitosamente importados %1 contactos", "checkOutBlaiseParagraph": "¡Vea Blaise! Cartera Pascal simple, elegante y segura para iOS y Android: https://blaisewallet.com", "newAccountParagraph": "Esta es su nueva cuenta.\nUna vez que reciba Pascal, las operaciones se mostrarán como a continuación.", "settingsHeader": "Configuración", "preferencesHeader": "Preferencias", "currencyHeader": "Moneda", "languageHeader": "Idioma", "languageColonHeader": "Idioma:", "systemDefaultHeader": "Sistema por defecto", "themeHeader": "Tema", "themeLightHeader": "Claro", "themeDarkHeader": "Oscuro", "themeCopperHeader": "Cobre", "notificationsHeader": "Notificaciones", "securityHeader": "Seguridad", "authenticationMethodHeader": "Método de autenticación", "authenticationPINHeader": "PIN", "authenticationBiometricsHeader": "Datos biométricos", "authenticateOnLaunchHeader": "Autenticar al iniciar", "yesHeader": "Si", "noHeader": "No", "automaticallyLockHeader": "Bloqueo automático", "lockInstantHeader": "Instantáneamente", "lock1Header": "Después de %1 minuto", "lock5Header": "Después de %1 minutos", "lock15Header": "Después de %1 minutos", "lock30Header": "Después de %1 minutos", "lock60Header": "Después de %1 minutos", "daemonHeader": "Daemon", "defaultHeader": "Por defecto", "manageHeader": "Gestionar", "contactsHeader": "Contactos", "backUpPrivateKeyHeader": "Respaldar clave privada", "viewPublicKeyHeader": "Ver clave pública", "shareHeader": "Compartir Blaise", "logoutHeader": "Cerrar sesión", "privacyPolicyHeader": "Política de privacidad", "changeAccountNameHeader": "Cambiar nombre de cuenta", "transferAccountHeader": "Transferir cuenta", "listAccountForSaleHeader": "Publicar cuenta a la venta", "createPrivateSaleHeader": "Crear venta privada", "delistFromSaleHeader": "Quitar de listado de venta", "getAccountSheetHeader": "Obtener cuenta", "freeAccountSheetHeader": "Cuenta gratuita", "buyAccountSheetHeader": "Comprar cuenta", "sendSheetHeader": "Enviar", "sendingSheetHeader": "Enviando", "sentSheetHeader": "Enviado", "requestSheetHeader": "Solicitar", "changeNameSheetHeader": "Cambiar nombre", "changingNameSheetHeader": "Cambiando", "changedNameSheetHeader": "Cambiado", "transferSheetHeader": "Transferir", "transferringSheetHeader": "Transfiriendo", "transferredSheetHeader": "Transferido", "listForSaleSheetHeader": "Publicar a la venta", "listingForSaleSheetHeader": "Publicando", "listedForSaleSheetHeader": "Publicada", "createPrivateSaleSheetHeader": "Venta privada", "creatingPrivateSaleSheetHeader": "Creando", "createdPrivateSaleSheetHeader": "Creada", "delistingSheetHeader": "Quietar de la venta", "delistedSheetHeader": "Removida de la venta", "addContactSheetHeader": "Agregar contacto", "contactSheetHeader": "Contacto", "publicKeySheetHeader": "Clave pública", "privateKeySheetHeader": "Clave privada", "backUpSheetHeader": "Respaldo", "encryptSheetHeader": "Encriptar", "changeDaemonSheetHeader": "Cambiar daemon", "securityFirstHeader": "¡La seguridad primero!", "newPrivateKeyHeader": "Nueva clave privada", "importPrivateKeyHeader": "Importar clave privada", "decryptAndImportKeyHeader": "Descifrar e importar", "backUpKeyHeader": "¡Resguarde su clave!", "lockedHeader": "Bloqueada", "privateKeyTextFieldHeader": "Clave privada", "passwordTextFieldHeader": "Contraseña", "newPasswordTextFieldHeader": "Nueva contraseña", "confirmPasswordTextFieldHeader": "Confirmar contraseña", "confirmTextFieldHeader": "Confirmar", "countryCodeTextFieldHeader": "Código de país", "phoneNumberTextFieldHeader": "Número telefónico", "confirmationCodeTextFieldHeader": "Código de confirmación", "accountTextFieldHeader": "Cuenta", "addressTextFieldHeader": "Dirección", "contactNameTextFieldHeader": "Nombre de contacto", "amountTextFieldHeader": "Monto", "payloadTextFieldHeader": "Payload", "nameTextFieldHeader": "Nombre", "newAccountNameTextFieldHeader": "Nuevo nombre de cuenta", "publicKeyTextFieldHeader": "Clave pública", "priceTextFieldHeader": "Precio", "receivingAccountTextFieldHeader": "Cuenta receptora", "durationTextFieldHeader": "Duración", "feeTextFieldHeader": "Tarifa", "otherOperationsHeader": "Otras operaciones", "warningHeader": "Advertencia", "areYouSureHeader": "¿Está seguro?", "addFeeHeader": "Agregar tarifa", "keyTypeNotSupportedHeader": "Clave no soportada", "accountToSendFromHeader": "Cuenta desde la cual enviar", "sentHeader": "Enviado", "receivedHeader": "Recibido", "nameChangedHeader": "Nombre modificado", "listedForSaleHeader": "Publicada a la venta", "privateSaleHeader": "Venta privada", "delistedFromSaleHeader": "Retirada de la venta", "delistedHeader": "Retirada de llistado", "undefinedHeader": "Indefinida", "transferredHeader": "Transferido", "balanceHeader": "Balance", "totalBalanceHeader": "Balance total", "accountBalanceHeader": "Balance de cuenta", "accountsHeader": "Cuentas", "operationsHeader": "Operaciones", "encryptThePayloadHeader": "Encriptar el Payload", "encryptPayloadHeader": "Encriptar Payload", "forSaleHeader": "A la venta", "borrowedHeader": "Prestada", "borrowedTransferredHeader": "Transferencia pendiente", "borrowedAccountHeader": "Cuenta prestada", "feeColonHeader": "Tarifa:", "pendingHeader": "Pendiente", "onHeader": "Encendido", "offHeader": "Apagado", "priceRequiredError": "El precio es obligatorio", "amountRequiredError": "El monto es obligatorio", "nameRequiredError": "El nombre es obligatorio", "zeroPriceError": "El precio no puede ser 0", "zeroAmountError": "El monto no puede ser 0", "invalidAccountNameError": "Nombre de cuenta inválido", "invalidReceivingAccountError": "Cuenta de recepción inválida", "invalidPublicKeyError": "Clave pública inválida", "invalidPrivateKeyError": "Clave privada inválida", "invalidAddressError": "Dirección inválida", "invalidAccountError": "Cuenta inválida", "invalidDestinationError": "Destino inválido", "insufficientBalanceError": "Balance insuficiente", "threeCharacterNameError": "Debe tener al menos 3 caracteres", "contactDoesntExistError": "El contacto no existe", "contactAlreadyExistsError": "El contacto ya existe", "cantSendToYourselfError": "No puede enviar a usted mismo", "somethingWentWrongError": "Algo anduvo mal, por favor intente más tarde", "failedToEncryptPayloadError": "Error al encriptar el payload", "emptyPasswordError": "La contraseña no puede estar vacía", "noMatchPasswordError": "Contraseñan no coinciden", "invalidPasswordError": "Contraseña inválida", "didNotGetResponseError": "No se recibe respuesta del servidor", "noContactsToExportError": "No hay contactos para exportar", "noContactsToImportError": "No hay contactos para importar", "failedToImportContactsError": "Error al importar contactos", "blockchainRewardOPDetails": "Recompensa Blockchain (%1)", "transactionOPDetails": "Transacción (%1)", "changeKeyOPDetails": "Cambiar clave (%1)", "recoverFundsOPDetails": "Recuperar fondos (%1)", "listAccountForSaleOPDetails": "Publicar cuenta a la venta (%1)", "delistAccountOPDetails": "Quitar cuenta de la venta (%1)", "buyAccountOPDetails": "Comprar cuenta (%1)", "changeKeySignedOPDetails": "Cambiar clave firmada (%1)", "changeAccountInfoOPDetails": "Cambiar información de la cuenta (%1)", "multioperationOPDetails": "Multioperación (%1)", "unknownOPDetails": "Desconocido (%1)", "sendingAccountOPDetails": "Cuenta de envío", "receivingAccountOPDetails": "Cuenta receptora", "changingAccountOPDetails": "Cambio de cuenta", "sendAmountOPDetails": "Cantidad a enviar", "payloadOPDetails": "Payload", "newPublicKeyOPDetails": "Nueva clave pública", "newNameOPDetails": "Nuevo nombre", "sellerAccountOPDetails": "Cuenta vendedora", "accountPriceOPDetails": "Precio de la cuenta", "lockedUntilBlockOPDetails": "Bloqueado hasta el bloque", "blockOPDetails": "bloque", "optxtOPDetails": "optxt", "timeOPDetails": "Fecha", "naOPDetails": "N/A", "ophashOPDetails": "ophash", "optypeOPDetails": "optype", "maturationOPDetails": "maduración", "nullOPDetails": "null", "feeOPDetails": "tarifa", "opblockOPDetails": "opblock", "noperationOPDetails": "n_operation", "accountOPDetails": "cuenta", "signeraccountOPDetails": "signer_account", "noResultsFound": "No se han encontrado resultados", "getAccountThirdParagraphAlternative": "2- Puede comprar una cuenta por %1 Pascal (%2). Se permite comprar solo 1 cuenta por usuario.", "getAccountThirdParagraphAlternative2": "2- Puede comprar una cuenta por %1 Pascal (%2). Puede comprar hasta %3 cuentas.", "receiveAccountButton": "Recibir cuenta", "receiveAnAccountButton": "Recibir una cuenta" } ================================================ FILE: lib/l10n/intl_messages.arb ================================================ { "@@last_modified": "2019-10-25T14:47:22.704586", "newPrivateKeyButton": "New Private Key", "@newPrivateKeyButton": { "description": "A button that creates a new private key", "type": "text", "placeholders": {} }, "importPrivateKeyButton": "Import Private Key", "@importPrivateKeyButton": { "description": "A button that imports a private key", "type": "text", "placeholders": {} }, "gotItButton": "Got It!", "@gotItButton": { "description": "A button that implies a message is understood", "type": "text", "placeholders": {} }, "goBackButton": "Go Back", "@goBackButton": { "description": "A button to go back to previous screen", "type": "text", "placeholders": {} }, "copyButton": "Copy", "@copyButton": { "description": "A button to copy something", "type": "text", "placeholders": {} }, "copiedButton": "Copied", "@copiedButton": { "description": "A button to inform the user that something has been copied", "type": "text", "placeholders": {} }, "keyCopiedButton": "Key Copied", "@keyCopiedButton": { "description": "A button to inform the user that the key has been copied", "type": "text", "placeholders": {} }, "iHaveBackedItUpButton": "I've Backed It Up", "@iHaveBackedItUpButton": { "description": "A button to confirm that something is backed up", "type": "text", "placeholders": {} }, "yesImSureButton": "Yes, I'm Sure", "@yesImSureButton": { "description": "A button to confirm if the user is sure", "type": "text", "placeholders": {} }, "noGoBackButton": "No, Go Back", "@noGoBackButton": { "description": "A button to go back to previous screen if the user didnt do what the question asks", "type": "text", "placeholders": {} }, "getAnAccountButton": "Get an Account", "@getAnAccountButton": { "description": "A button to start the process of getting an account", "type": "text", "placeholders": {} }, "getAFreeAccountButton": "Get a Free Account", "@getAFreeAccountButton": { "description": "A button to start the process of getting a free account", "type": "text", "placeholders": {} }, "buyAnAccountButton": "Buy an Account", "@buyAnAccountButton": { "description": "A button to start the process of buying an account", "type": "text", "placeholders": {} }, "sendConfirmationButton": "Send Confirmation", "@sendConfirmationButton": { "description": "A button to request a confirmation to be sent", "type": "text", "placeholders": {} }, "confirmButton": "Confirm", "@confirmButton": { "description": "A button to confirm that a process should be executed", "type": "text", "placeholders": {} }, "borrowAnAccountButton": "Borrow An Account", "@borrowAnAccountButton": { "description": "A button to borrow an account", "type": "text", "placeholders": {} }, "importButton": "Import", "@importButton": { "description": "A button to import something", "type": "text", "placeholders": {} }, "receiveButton": "Receive", "@receiveButton": { "description": "A button to receive Pascal", "type": "text", "placeholders": {} }, "sendButton": "Send", "@sendButton": { "description": "A button to send Pascal", "type": "text", "placeholders": {} }, "copyAddressButton": "Copy Address", "@copyAddressButton": { "description": "A button to copy an address", "type": "text", "placeholders": {} }, "copiedAddressButton": "Address Copied", "@copiedAddressButton": { "description": "A button to inform the user that the address has been copied", "type": "text", "placeholders": {} }, "addToContactsButton": "Add to Contacts", "@addToContactsButton": { "description": "A button to add an account to contacts", "type": "text", "placeholders": {} }, "operationDetailsButton": "Operation Details", "@operationDetailsButton": { "description": "A button to view the details of an operation", "type": "text", "placeholders": {} }, "openInExplorerButton": "Open in Explorer", "@openInExplorerButton": { "description": "A button to view the details of an operation on the Pascal explorer", "type": "text", "placeholders": {} }, "requestButton": "Request", "@requestButton": { "description": "A button to request something", "type": "text", "placeholders": {} }, "addAPayloadButton": "+ Add a Payload", "@addAPayloadButton": { "description": "A button to add a payload (note) to an operation", "type": "text", "placeholders": {} }, "addADurationButton": "+ Add a Duration", "@addADurationButton": { "description": "A button to add a duration to the sale", "type": "text", "placeholders": {} }, "scanQRCodeButton": "Scan QR Code", "@scanQRCodeButton": { "description": "A button to scan a QR Code", "type": "text", "placeholders": {} }, "cancelButton": "Cancel", "@cancelButton": { "description": "A button to cancel a process", "type": "text", "placeholders": {} }, "closeButton": "Close", "@closeButton": { "description": "A button to close a screen or a pop-up", "type": "text", "placeholders": {} }, "changeNameButton": "Change Name", "@changeNameButton": { "description": "A button to change the name of an account", "type": "text", "placeholders": {} }, "transferButton": "Transfer", "@transferButton": { "description": "A button to transfer the ownership of an account", "type": "text", "placeholders": {} }, "listForSaleButton": "List for Sale", "@listForSaleButton": { "description": "A button to list an account for sale", "type": "text", "placeholders": {} }, "createPrivateSaleButton": "Create Private Sale", "@createPrivateSaleButton": { "description": "A button to create a private sale for the account", "type": "text", "placeholders": {} }, "yesAddFeeButton": "Yes, Add Fee", "@yesAddFeeButton": { "description": "A button to confirm the addition of a fee to an operation", "type": "text", "placeholders": {} }, "unlockButton": "Unlock", "@unlockButton": { "description": "A button to unlock the wallet", "type": "text", "placeholders": {} }, "unlockWithBiometricsButton": "Unlock with Biometrics", "@unlockWithBiometricsButton": { "description": "A button to unlock the wallet using biometrics", "type": "text", "placeholders": {} }, "unlockWithPINButton": "Unlock with PIN", "@unlockWithPINButton": { "description": "A button to unlock the wallet using PIN", "type": "text", "placeholders": {} }, "setToDefaultButton": "Set to Default", "@setToDefaultButton": { "description": "A button to set something to its default", "type": "text", "placeholders": {} }, "changeDaemonButton": "Change Daemon", "@changeDaemonButton": { "description": "A button to change the Pascal daemon for RPC requests", "type": "text", "placeholders": {} }, "addContactButton": "Add Contact", "@addContactButton": { "description": "A button to add a contact", "type": "text", "placeholders": {} }, "encryptedKeyButton": "Encrypted Key", "@encryptedKeyButton": { "description": "A button to view the encrypted key", "type": "text", "placeholders": {} }, "unencryptedKeyButton": "Unencrypted Key", "@unencryptedKeyButton": { "description": "A button to view the unencrypted key", "type": "text", "placeholders": {} }, "encryptButton": "Encrypt", "@encryptButton": { "description": "A button to encrypt the private key with a password", "type": "text", "placeholders": {} }, "showButton": "Show", "@showButton": { "description": "A button to show something that is hidden", "type": "text", "placeholders": {} }, "hideButton": "Hide", "@hideButton": { "description": "A button to hide something that is shown", "type": "text", "placeholders": {} }, "copyEncryptedKeyButton": "Copy Encrypted Key", "@copyEncryptedKeyButton": { "description": "A button to copy an encrypted key", "type": "text", "placeholders": {} }, "copyUnencryptedKeyButton": "Copy Unencrypted Key", "@copyUnencryptedKeyButton": { "description": "A button to copy an unencrypted key", "type": "text", "placeholders": {} }, "copyKeyButton": "Copy Key", "@copyKeyButton": { "description": "A button to copy a key (private or public key)", "type": "text", "placeholders": {} }, "copyPublicKeyButton": "Copy Public Key", "@copyPublicKeyButton": { "description": "A button to copy a public key", "type": "text", "placeholders": {} }, "deletePrivateKeyAndLogoutButton": "Delete Private Key\nAnd Logout", "@deletePrivateKeyAndLogoutButton": { "description": "A button to delete the private key and logout", "type": "text", "placeholders": {} }, "searchForNameButton": "Search For Name", "@searchForNameButton": { "description": "A button to search for an account name", "type": "text", "placeholders": {} }, "searchAccountNameButton": "Search Account Name", "@searchAccountNameButton": { "description": "A button to search an account name", "type": "text", "placeholders": {} }, "searchNameButton": "Search Name", "@searchNameButton": { "description": "A button to search name", "type": "text", "placeholders": {} }, "okayGoBackButton": "Okay, Go Back", "@okayGoBackButton": { "description": "A button to confirm and go back", "type": "text", "placeholders": {} }, "okayButton": "Okay", "@okayButton": { "description": "A button that simply indicates a neutral action, like closing an informative dialog", "type": "text", "placeholders": {} }, "nextButton": "Next", "@nextButton": { "description": "A button to the next screen", "type": "text", "placeholders": {} }, "receiveAccountButton": "Receive Account", "@receiveAccountButton": { "description": "A button to open up the public key sheet(screen) that displays a QR code to receive an account", "type": "text", "placeholders": {} }, "receiveAnAccountButton": "Receive an Account", "@receiveAnAccountButton": { "description": "A button to open up the public key sheet(screen) that displays a QR code to receive an account", "type": "text", "placeholders": {} }, "supportButton": "Support", "@supportButton": { "description": "A button to open up the live support window", "type": "text", "placeholders": {} }, "liveSupportButton": "Support", "@liveSupportButton": { "description": "A button to open up the live support window", "type": "text", "placeholders": {} }, "welcomeParagraph": "Welcome to Blaise Wallet. To begin, you can create a new private key or import one.", "@welcomeParagraph": { "description": "A paragraph that greets the user in the initial opening", "type": "text", "placeholders": {} }, "newKeySecurityParagraph": "In the next screen, you'll see your new private key. It is a password to access your funds. It is crucial that you back it up and never share it with anyone.", "@newKeySecurityParagraph": { "description": "A paragraph that explains what users should do with their new private key", "type": "text", "placeholders": {} }, "uninstallDisclaimerParagraph": "If you lose your device or uninstall Blaise Wallet, you'll need your private key to recover your funds.", "@uninstallDisclaimerParagraph": { "description": "A paragraph that gives a security disclaimer about what happens if the wallet is uninstalled", "type": "text", "placeholders": {} }, "newPrivateKeyParagraph": "Below is your new wallet’s private key. It is crucial that you backup your private key and never store it as plaintext or a screenshot. We recommend writing it on a piece of paper and storing it offline.", "@newPrivateKeyParagraph": { "description": "A paragraph that explains what users should do with their new private key", "type": "text", "placeholders": {} }, "newKeyBackUpConfirmParagraph": "Are you sure that you have backed up your new wallet’s private key?", "@newKeyBackUpConfirmParagraph": { "description": "A paragraph to confirm if the new private key is backed up", "type": "text", "placeholders": {} }, "newWalletGreetingParagraph": "Welcome to Blaise Wallet.\nYou can start by getting an account.", "@newWalletGreetingParagraph": { "description": "A paragraph to greet the user when a new wallet is created", "type": "text", "placeholders": {} }, "getAccountFirstParagraph": "There are 2 options for getting your first account:", "@getAccountFirstParagraph": { "description": "The first paragraph of the explanation for the process of getting an account", "type": "text", "placeholders": {} }, "getAccountSecondParagraph": "1- You can get a free account using your phone number. Only 1 account per phone number is allowed.", "@getAccountSecondParagraph": { "description": "The second paragraph of the explanation for the process of getting an account", "type": "text", "placeholders": {} }, "getAccountThirdParagraph": "2- You can buy as many accounts as you want for %1 Pascal (%2).", "@getAccountThirdParagraph": { "description": "The third paragraph of the explanation for the process of getting an account", "type": "text", "placeholders": {} }, "getAccountThirdParagraphAlternative": "2- You can buy an account for %1 Pascal (%2). Buying only 1 account is allowed per user.", "@getAccountThirdParagraphAlternative": { "description": "The third paragraph of the explanation for the process of getting an account", "type": "text", "placeholders": {} }, "getAccountThirdParagraphAlternative2": "2- You can buy an account for %1 Pascal (%2). You can buy up to %3 accounts.", "@getAccountThirdParagraphAlternative2": { "description": "The third paragraph of the explanation for the process of getting an account", "type": "text", "placeholders": {} }, "enterPhoneNumberParagraph": "Enter your phone number below.", "@enterPhoneNumberParagraph": { "description": "A paragraph that tells users to enter their phone number to the text field below", "type": "text", "placeholders": {} }, "invalidPhoneNumberParagraph": "Phone number is not valid", "@invalidPhoneNumberParagraph": { "description": "User has entered an invalid phone number", "type": "text", "placeholders": {} }, "enterConfirmationCodeParagraph": "We have sent you a confirmation code, please enter it below.", "@enterConfirmationCodeParagraph": { "description": "A paragraph that tells users to enter the confirmation code to the text field below", "type": "text", "placeholders": {} }, "confirmationCodeError": "Failed to verify code, ensure you've entered it correctly", "@confirmationCodeError": { "description": "When a user enters their freepasa SMS code but it can't be verified", "type": "text", "placeholders": {} }, "freepasaComplete": "Success, your new account will be available after 1 network confirmation", "@freepasaComplete": { "description": "After the freepasa process is complete", "type": "text", "placeholders": {} }, "unconfirmedAccountHeader": "Unconfirmed Account", "@unconfirmedAccountHeader": { "description": "A user has an account in their wallet that has been transferred to them, but isnt confirmed yet. This is the info dialog header.", "type": "text", "placeholders": {} }, "unconfirmedAccountParagraph": "This is an unconfirmed account. It has been transferred to you, but there needs to be 1 network confirmation before you can use it. This usually takes about 5 minutes, once it's complete you'll be able to use this account.", "@unconfirmedAccountParagraph": { "description": "Explaining that an account can't be used until 1 network confirmation to the user.", "type": "text", "placeholders": {} }, "borrowStarted": "Purchase Started for %1", "@borrowStarted": { "description": "Users may see this after starting the account purchase process", "type": "text", "placeholders": {} }, "borrowAccountParagraph": "To buy an account, first you’ll need to borrow one for free. If you send at least %1 Pascal (%2) to the account within %3 days, the account will be yours and %1 Pascal will be deducted from your balance automatically.\nOtherwise, it’ll return back to us at the end of %3 days and won’t belong to your wallet anymore.\nIt is recommended you only send a small amount of coins until you own the account.", "@borrowAccountParagraph": { "description": "A paragraph that explains the process of borrowing & buying an account", "type": "text", "placeholders": {} }, "importPrivateKeyParagraph": "Enter your private key below.", "@importPrivateKeyParagraph": { "description": "A paragraph that tells the user to enter their private key to the text field below", "type": "text", "placeholders": {} }, "looksLikeEncryptedKeyParagraph": "This looks like an encrypted private key, please enter the password to decrypt and import it.", "@looksLikeEncryptedKeyParagraph": { "description": "A paragraph that tells the user that the key looks like an encrypted one and it needs to be decrypted to import", "type": "text", "placeholders": {} }, "changeDaemonParagraph": "Enter an address to use a different Pascal daemon for RPC requests.", "@changeDaemonParagraph": { "description": "A paragraph that tells the user to enter a new daemon address below", "type": "text", "placeholders": {} }, "urlChangedToParagraph": "URL changed to %1", "@urlChangedToParagraph": { "description": "A paragraph that tells the user that the URL is changed to the entered URL", "type": "text", "placeholders": {} }, "backupKeyFirstParagraph": "You have 2 options for backing up your private key:", "@backupKeyFirstParagraph": { "description": "The first paragraph of the explanation for the process of backing up the private key", "type": "text", "placeholders": {} }, "backupKeySecondParagraph": "1- Encrypted, which means it is protected by a password.", "@backupKeySecondParagraph": { "description": "The second paragraph of the explanation for the process of backing up the private key", "type": "text", "placeholders": {} }, "backupKeyThirdParagraph": "2- Unencrypted, which means it is raw and not protected by a password.", "@backupKeyThirdParagraph": { "description": "The third paragraph of the explanation for the process of backing up the private key", "type": "text", "placeholders": {} }, "backupKeyFourthParagraph": "We recommend storing the unencrypted version offline, by writing it on a piece of paper. You can store the encrypted version on a password manager for your convenience.", "@backupKeyFourthParagraph": { "description": "The fourth paragraph of the explanation for the process of backing up the private key", "type": "text", "placeholders": {} }, "encryptKeyParagraph": "Create a new password to encrypt your private key.", "@encryptKeyParagraph": { "description": "A paragraph that tells the user to create a new password to encrypt their key", "type": "text", "placeholders": {} }, "backupEncryptedKeyFirstParagraph": "Below is your encrypted private key. It is protected by a password. You can store it safely on a password manager for your convenience.", "@backupEncryptedKeyFirstParagraph": { "description": "A paragraph that explains how an encrypted private key can be backed up", "type": "text", "placeholders": {} }, "backupEncryptedKeySecondParagraph": "Since it is encrypted with your password, if you lose or forget your password, you won't be able to decrypt it and access your funds.", "@backupEncryptedKeySecondParagraph": { "description": "A paragraph that gives a disclaimer about what would happen in case the password that was used to encrypt the private key is lost or forgotten", "type": "text", "placeholders": {} }, "backupUnencryptedKeyParagraph": "Below is your unencrypted private key. It is not protected by a password, which means it is crucial that you store it somewhere safe and offline. We recommend writing it on a piece of paper.", "@backupUnencryptedKeyParagraph": { "description": "A paragraph that explains the process of backing up the unencrypted private key", "type": "text", "placeholders": {} }, "publicKeyParagraph": "Below is your public key. As the name suggests, it is intended to be shared publicly and prove that a particular operation belongs to your private key.", "@publicKeyParagraph": { "description": "A paragraph that explains what a public key is", "type": "text", "placeholders": {} }, "borrowedAccountParagraph": "This is a borrowed account.\nIf you send at least %1 Pascal to it in the next %2 days, %3 hours, and %4 minutes, it’ll be yours.", "@borrowedAccountParagraph": { "description": "A paragraph that explains what a borrowed account is", "type": "text", "placeholders": {} }, "borrowedAccountPaidParagraph": "Your account has been purchased!\nThe transfer is currently processing. This process usually takes about 15 minutes, in some cases it may take slightly longer.", "@borrowedAccountPaidParagraph": { "description": "A paragraph that explains that the account has been purchased and transfer is currently processing", "type": "text", "placeholders": {} }, "logoutFirstDisclaimerParagraph": "Logging out will remove your private key and all Blaise related data from this device. If your private key is not backed up, you will never be able to access your funds again. If your private key is backed up, you have nothing to worry about.", "@logoutFirstDisclaimerParagraph": { "description": "The first part of the disclaimer that is shown when the user tries to log out.", "type": "text", "placeholders": {} }, "logoutSecondDisclaimerParagraph": "Are you sure that you've backed up your private key? As long as you've backed up your private key, you have nothing to worry about.", "@logoutSecondDisclaimerParagraph": { "description": "The second part of the disclaimer that is shown when the user tries to log out.", "type": "text", "placeholders": {} }, "noResultsFound": "No results found", "@noResultsFound": { "description": "When searching for account name has returned 0 results", "type": "text", "placeholders": {} }, "sendingConfirmParagraph": "Confirm the transaction details to send.", "@sendingConfirmParagraph": { "description": "A paragraph that tells the user to confirm the info below to send", "type": "text", "placeholders": {} }, "sentParagraph": "Transaction has been sent succesfully.", "@sentParagraph": { "description": "A paragraph that informs the user that the transaction has been sent succesfully", "type": "text", "placeholders": {} }, "changeNameParagraph": "Enter a name below to change your account's name.", "@changeNameParagraph": { "description": "A paragraph that tells the user to enter a new account name below", "type": "text", "placeholders": {} }, "changingNameParagraph": "Confirm your new account name to proceed.", "@changingNameParagraph": { "description": "A paragraph that tells the user to confirm the new account name", "type": "text", "placeholders": {} }, "changedNameParagraph": "Your account name has been changed successfully.", "@changedNameParagraph": { "description": "A paragraph that informs the user that the account name has been changed successfully", "type": "text", "placeholders": {} }, "transferParagraph": "Enter a public key below to transfer the ownership of this account to it.", "@transferParagraph": { "description": "A paragraph that tells the user to enter a public key to the text field below to transfer the ownership of the account", "type": "text", "placeholders": {} }, "transferringParagraph": "Confirm the public key below to transfer the ownership of this account to it.", "@transferringParagraph": { "description": "A paragraph that tells the user to confirm the public key below to proceed with the transfer", "type": "text", "placeholders": {} }, "transferredParagraph": "Your account has been transferred successfully to the public key below.", "@transferredParagraph": { "description": "A paragraph that informs the user that the account transfer has been completed successfully", "type": "text", "placeholders": {} }, "listForSaleParagraph": "Enter a price and an account that will be receiving the payment to list this account for sale.", "@listForSaleParagraph": { "description": "A paragraph that tells the user to enter a price and a receiver account to list the account for sale", "type": "text", "placeholders": {} }, "listingForSaleParagraph": "Confirm the price and the account that will be receiving the payment.", "@listingForSaleParagraph": { "description": "A paragraph that tells the user to confirm the price and the receiver account", "type": "text", "placeholders": {} }, "listedForSaleParagraph": "Your account has been successfully listed for sale. We’ll let you know if someone buys it.", "@listedForSaleParagraph": { "description": "A paragraph that informs the user that the account has been listed for sale successfully", "type": "text", "placeholders": {} }, "createPrivateSaleParagraph": "Enter a price, a receiving account, and a public key below to create a private sale for this account.", "@createPrivateSaleParagraph": { "description": "A paragraph that tells the user to enter a price, a receiver account, and a public key to create a private sale for the account", "type": "text", "placeholders": {} }, "creatingPrivateSaleParagraph": "Confirm the information below.", "@creatingPrivateSaleParagraph": { "description": "A paragraph that tells the user to confirm the information below.", "type": "text", "placeholders": {} }, "createdPrivateSaleParagraph": "The private sale has been created successfully. We’ll let you know if it is bought.", "@createdPrivateSaleParagraph": { "description": "A paragraph that informs the user that the private sale has been created successfully", "type": "text", "placeholders": {} }, "delistFromSaleParagraph": "Confirm that you would like to delist this account from sale.", "@delistFromSaleParagraph": { "description": "A paragraph that tells the users to confirm that they would like to delist the account from sale.", "type": "text", "placeholders": {} }, "delistedFromSaleParagraph": "Your account has been successfully delisted from sale.", "@delistedFromSaleParagraph": { "description": "A paragraph that informs the user that the account has been delisted from sale successfully", "type": "text", "placeholders": {} }, "feeRequiredParagraph": "This operation requires a fee.", "@feeRequiredParagraph": { "description": "A paragraph to indicate that the operation requires a fee", "type": "text", "placeholders": {} }, "feeConfirmAmountParagraph": "Please confirm the addition of %1 Pascal fee to this operation to continue.", "@feeConfirmAmountParagraph": { "description": "A paragraph to tell the user to confirm the addition of a fee", "type": "text", "placeholders": {} }, "keyTypeNotSupportedParagraph": "This type of private key is not yet supported by Blaise. You may create a new private key and transfer your accounts to it using a different wallet.", "@keyTypeNotSupportedParagraph": { "description": "A paragraph to tell the user that the private key type is not supported", "type": "text", "placeholders": {} }, "enterPINToUnlockParagraph": "Enter PIN to unlock Blaise", "@enterPINToUnlockParagraph": { "description": "A paragraph that tells the user to enter the PIN to unlock the wallet", "type": "text", "placeholders": {} }, "authenticateToUnlockParagraph": "Authenticate to Unlock Blaise", "@authenticateToUnlockParagraph": { "description": "A paragraph that tells the user to authenticate to unlock the wallet", "type": "text", "placeholders": {} }, "manyFailedAttemptsParagraph": "Too many failed unlock attempts", "@manyFailedAttemptsParagraph": { "description": "A paragraph to inform the user that there was too many failed unlock attempts", "type": "text", "placeholders": {} }, "authenticateToChangeNameParagraph": "Authenticate to change account name to \"%1\"", "@authenticateToChangeNameParagraph": { "description": "A paragraph that tells the user to authenticate to change the name of the account", "type": "text", "placeholders": {} }, "authenticateToDelistParagraph": "Authenticate to delist the account from sale", "@authenticateToDelistParagraph": { "description": "A paragraph that tells the user to authenticate to delist the account from sale", "type": "text", "placeholders": {} }, "authenticateToListForSaleParagraph": "Authenticate to list account for sale", "@authenticateToListForSaleParagraph": { "description": "A paragraph that tells the user to authenticate to list the account for sale", "type": "text", "placeholders": {} }, "authenticateToCreatePrivateSaleParagraph": "Authenticate to create private sale", "@authenticateToCreatePrivateSaleParagraph": { "description": "A paragraph that tells the user to authenticate to create a private sale for the account", "type": "text", "placeholders": {} }, "authenticateToTransferParagraph": "Authenticate to transfer account", "@authenticateToTransferParagraph": { "description": "A paragraph that tells the user to authenticate to transfer the ownership of the account", "type": "text", "placeholders": {} }, "authenticateToSendParagraph": "Authenticate to send %1 Pascal", "@authenticateToSendParagraph": { "description": "A paragraph that tells the user to authenticate to send a specified amount of Pascal", "type": "text", "placeholders": {} }, "authenticateToBackUpParagraph": "Authenticate to back up private key", "@authenticateToBackUpParagraph": { "description": "A paragraph that tells the user to authenticate to back up the private key", "type": "text", "placeholders": {} }, "invalidPINParagraph": "Invalid PIN", "@invalidPINParagraph": { "description": "A paragraph that tells the user that the entered PIN is invalid", "type": "text", "placeholders": {} }, "noMatchPINParagraph": "PINs do not match", "@noMatchPINParagraph": { "description": "A paragraph that tells the user that the entered PINs do not match", "type": "text", "placeholders": {} }, "confirmPINParagraph": "Confirm your PIN", "@confirmPINParagraph": { "description": "A paragraph that tells the user to confirm the PIN", "type": "text", "placeholders": {} }, "enterPINParagraph": "Enter PIN", "@enterPINParagraph": { "description": "A paragraph that tells the user to enter the PIN", "type": "text", "placeholders": {} }, "createPINParagraph": "Create a 6-digit PIN", "@createPINParagraph": { "description": "A paragraph that tells the user to create a PIN", "type": "text", "placeholders": {} }, "addedToContactsParagraph": "%1 added to contacts", "@addedToContactsParagraph": { "description": "A paragraph that tells the user that the contact has been added to contacts", "type": "text", "placeholders": {} }, "removedFromContactsParagraph": "Removed %1 from contacts", "@removedFromContactsParagraph": { "description": "A paragraph that tells the user that the contact has been removed from contacts", "type": "text", "placeholders": {} }, "failedToRemoveFromContactsParagraph": "Failed to remove %1 from contacts", "@failedToRemoveFromContactsParagraph": { "description": "A paragraph that tells the user that the contact removel process is failed", "type": "text", "placeholders": {} }, "successfullyImportedContactsParagraph": "Successfully imported %1 contacts", "@successfullyImportedContactsParagraph": { "description": "A paragraph to tell the user that a specific number of contacts was successfully imported", "type": "text", "placeholders": {} }, "checkOutBlaiseParagraph": "Check out Blaise! Simple, sleek & secure Pascal wallet for iOS and Android: https://blaisewallet.com", "@checkOutBlaiseParagraph": { "description": "A paragraph that is shared when the user shares Blaise with others via the option in the settings", "type": "text", "placeholders": {} }, "newAccountParagraph": "This is your new account.\nOnce you receive Pascal, operations will show up like below.", "@newAccountParagraph": { "description": "A paragraph that is shown in the operations list of a new account as an explainer", "type": "text", "placeholders": {} }, "settingsHeader": "Settings", "@settingsHeader": { "description": "Header for the settings", "type": "text", "placeholders": {} }, "preferencesHeader": "Preferences", "@preferencesHeader": { "description": "Header for the preferences section", "type": "text", "placeholders": {} }, "currencyHeader": "Currency", "@currencyHeader": { "description": "Header for the currencies", "type": "text", "placeholders": {} }, "languageHeader": "Language", "@languageHeader": { "description": "Header for the languages", "type": "text", "placeholders": {} }, "languageColonHeader": "Language:", "@languageColonHeader": { "description": "Header for the language option on welcome page", "type": "text", "placeholders": {} }, "systemDefaultHeader": "System Default", "@systemDefaultHeader": { "description": "Header for system default", "type": "text", "placeholders": {} }, "themeHeader": "Theme", "@themeHeader": { "description": "Header for the themes", "type": "text", "placeholders": {} }, "themeLightHeader": "Light", "@themeLightHeader": { "description": "Header for the light theme", "type": "text", "placeholders": {} }, "themeDarkHeader": "Dark", "@themeDarkHeader": { "description": "Header for the dark theme", "type": "text", "placeholders": {} }, "themeCopperHeader": "Copper", "@themeCopperHeader": { "description": "Header for the copper theme", "type": "text", "placeholders": {} }, "notificationsHeader": "Notifications", "@notificationsHeader": { "description": "Header for the notifications", "type": "text", "placeholders": {} }, "securityHeader": "Security", "@securityHeader": { "description": "Header for the security section", "type": "text", "placeholders": {} }, "authenticationMethodHeader": "Authentication Method", "@authenticationMethodHeader": { "description": "Header for the authentication method", "type": "text", "placeholders": {} }, "authenticationPINHeader": "PIN", "@authenticationPINHeader": { "description": "Header for the PIN authentication method", "type": "text", "placeholders": {} }, "authenticationBiometricsHeader": "Biometrics", "@authenticationBiometricsHeader": { "description": "Header for the biometric authentication method", "type": "text", "placeholders": {} }, "authenticateOnLaunchHeader": "Authenticate on Launch", "@authenticateOnLaunchHeader": { "description": "Header for the authenticate on launch option", "type": "text", "placeholders": {} }, "yesHeader": "Yes", "@yesHeader": { "description": "Header for the yes option", "type": "text", "placeholders": {} }, "noHeader": "No", "@noHeader": { "description": "Header for the no option", "type": "text", "placeholders": {} }, "automaticallyLockHeader": "Automatically Lock", "@automaticallyLockHeader": { "description": "Header for the automatically lock option", "type": "text", "placeholders": {} }, "lockInstantHeader": "Instantly", "@lockInstantHeader": { "description": "Header for instantly locking option", "type": "text", "placeholders": {} }, "lock1Header": "After %1 minute", "@lock1Header": { "description": "Header for locking after 1 minute option", "type": "text", "placeholders": {} }, "lock5Header": "After %1 minutes", "@lock5Header": { "description": "Header for locking after 5 minutes option", "type": "text", "placeholders": {} }, "lock15Header": "After %1 minutes", "@lock15Header": { "description": "Header for locking after 15 minutes option", "type": "text", "placeholders": {} }, "lock30Header": "After %1 minutes", "@lock30Header": { "description": "Header for locking after 30 minutes option", "type": "text", "placeholders": {} }, "lock60Header": "After %1 minutes", "@lock60Header": { "description": "Header for locking after 60 minutes option", "type": "text", "placeholders": {} }, "daemonHeader": "Daemon", "@daemonHeader": { "description": "Header for Pascal daemon setting", "type": "text", "placeholders": {} }, "defaultHeader": "Default", "@defaultHeader": { "description": "Header for default option", "type": "text", "placeholders": {} }, "manageHeader": "Manage", "@manageHeader": { "description": "Header for the manage section", "type": "text", "placeholders": {} }, "contactsHeader": "Contacts", "@contactsHeader": { "description": "Header for the contacts section in settings", "type": "text", "placeholders": {} }, "backUpPrivateKeyHeader": "Back Up Private Key", "@backUpPrivateKeyHeader": { "description": "Header for the back up private key option in settings", "type": "text", "placeholders": {} }, "viewPublicKeyHeader": "View Public Key", "@viewPublicKeyHeader": { "description": "Header for the view public key option in settings", "type": "text", "placeholders": {} }, "shareHeader": "Share Blaise", "@shareHeader": { "description": "Header for the share Blaise option in settings", "type": "text", "placeholders": {} }, "logoutHeader": "Logout", "@logoutHeader": { "description": "Header for the logout option in settings", "type": "text", "placeholders": {} }, "privacyPolicyHeader": "Privacy Policy", "@privacyPolicyHeader": { "description": "Header for the privacy policy option in settings", "type": "text", "placeholders": {} }, "changeAccountNameHeader": "Change Account Name", "@changeAccountNameHeader": { "description": "Header for the change account name option in other operations list", "type": "text", "placeholders": {} }, "transferAccountHeader": "Transfer Account", "@transferAccountHeader": { "description": "Header for the transfer account option in other operations list", "type": "text", "placeholders": {} }, "listAccountForSaleHeader": "List Account For Sale", "@listAccountForSaleHeader": { "description": "Header for the list account for sale option in other operations list", "type": "text", "placeholders": {} }, "createPrivateSaleHeader": "Create Private Sale", "@createPrivateSaleHeader": { "description": "Header for the create private sale option in other operations list", "type": "text", "placeholders": {} }, "delistFromSaleHeader": "Delist From Sale", "@delistFromSaleHeader": { "description": "Header for the delist from sale option in other operations list", "type": "text", "placeholders": {} }, "getAccountSheetHeader": "Get Account", "@getAccountSheetHeader": { "description": "Header for the get account sheet (screen)", "type": "text", "placeholders": {} }, "freeAccountSheetHeader": "Free Account", "@freeAccountSheetHeader": { "description": "Header for the free account sheet (screen)", "type": "text", "placeholders": {} }, "buyAccountSheetHeader": "Buy Account", "@buyAccountSheetHeader": { "description": "Header for the buy account sheet (screen)", "type": "text", "placeholders": {} }, "sendSheetHeader": "Send", "@sendSheetHeader": { "description": "Header for send sheet (screen)", "type": "text", "placeholders": {} }, "sendingSheetHeader": "Sending", "@sendingSheetHeader": { "description": "Header for sending sheet (screen)", "type": "text", "placeholders": {} }, "sentSheetHeader": "Sent", "@sentSheetHeader": { "description": "Header for sent sheet (screen)", "type": "text", "placeholders": {} }, "requestSheetHeader": "Request", "@requestSheetHeader": { "description": "Header for request sheet (screen)", "type": "text", "placeholders": {} }, "changeNameSheetHeader": "Change Name", "@changeNameSheetHeader": { "description": "Header for change name sheet (screen)", "type": "text", "placeholders": {} }, "changingNameSheetHeader": "Changing", "@changingNameSheetHeader": { "description": "Header for name changing sheet (screen)", "type": "text", "placeholders": {} }, "changedNameSheetHeader": "Changed", "@changedNameSheetHeader": { "description": "Header for name changed sheet (screen)", "type": "text", "placeholders": {} }, "transferSheetHeader": "Transfer", "@transferSheetHeader": { "description": "Header for transfer sheet (screen)", "type": "text", "placeholders": {} }, "transferringSheetHeader": "Transferring", "@transferringSheetHeader": { "description": "Header for transferring sheet (screen)", "type": "text", "placeholders": {} }, "transferredSheetHeader": "Transferred", "@transferredSheetHeader": { "description": "Header for transferred sheet (screen)", "type": "text", "placeholders": {} }, "listForSaleSheetHeader": "List For Sale", "@listForSaleSheetHeader": { "description": "Header for list for sale sheet (screen)", "type": "text", "placeholders": {} }, "listingForSaleSheetHeader": "Listing", "@listingForSaleSheetHeader": { "description": "Header for listing for sale sheet (screen)", "type": "text", "placeholders": {} }, "listedForSaleSheetHeader": "Listed", "@listedForSaleSheetHeader": { "description": "Header for listed for sale sheet (screen)", "type": "text", "placeholders": {} }, "createPrivateSaleSheetHeader": "Private Sale", "@createPrivateSaleSheetHeader": { "description": "Header for create private sale sheet (screen)", "type": "text", "placeholders": {} }, "creatingPrivateSaleSheetHeader": "Creating", "@creatingPrivateSaleSheetHeader": { "description": "Header for creating private sale sheet (screen)", "type": "text", "placeholders": {} }, "createdPrivateSaleSheetHeader": "Created", "@createdPrivateSaleSheetHeader": { "description": "Header for created private sale sheet (screen)", "type": "text", "placeholders": {} }, "delistingSheetHeader": "Delisting", "@delistingSheetHeader": { "description": "Header for delisting sheet (screen)", "type": "text", "placeholders": {} }, "delistedSheetHeader": "Delisted", "@delistedSheetHeader": { "description": "Header for delisted sheet (screen)", "type": "text", "placeholders": {} }, "addContactSheetHeader": "Add Contact", "@addContactSheetHeader": { "description": "Header for add contact sheet (screen)", "type": "text", "placeholders": {} }, "contactSheetHeader": "Contact", "@contactSheetHeader": { "description": "Header for contact details sheet (screen)", "type": "text", "placeholders": {} }, "publicKeySheetHeader": "Public Key", "@publicKeySheetHeader": { "description": "Header for public key sheet (screen)", "type": "text", "placeholders": {} }, "privateKeySheetHeader": "Private Key", "@privateKeySheetHeader": { "description": "Header for private key sheet (screen)", "type": "text", "placeholders": {} }, "backUpSheetHeader": "Back Up", "@backUpSheetHeader": { "description": "Header for back up sheet (screen)", "type": "text", "placeholders": {} }, "encryptSheetHeader": "Encrypt", "@encryptSheetHeader": { "description": "Header for encrypt sheet (screen)", "type": "text", "placeholders": {} }, "changeDaemonSheetHeader": "Change Daemon", "@changeDaemonSheetHeader": { "description": "Header for change daemon sheet (screen)", "type": "text", "placeholders": {} }, "securityFirstHeader": "Security First!", "@securityFirstHeader": { "description": "Header for security first screen", "type": "text", "placeholders": {} }, "newPrivateKeyHeader": "New Private Key", "@newPrivateKeyHeader": { "description": "Header for new private key screen", "type": "text", "placeholders": {} }, "importPrivateKeyHeader": "Import Private Key", "@importPrivateKeyHeader": { "description": "Header for import private key screen", "type": "text", "placeholders": {} }, "decryptAndImportKeyHeader": "Decrypt & Import", "@decryptAndImportKeyHeader": { "description": "Header for decrypt & import private key screen", "type": "text", "placeholders": {} }, "backUpKeyHeader": "Back Up Your Key!", "@backUpKeyHeader": { "description": "Header for back up your key screen", "type": "text", "placeholders": {} }, "lockedHeader": "Locked", "@lockedHeader": { "description": "Header for locked screen", "type": "text", "placeholders": {} }, "privateKeyTextFieldHeader": "Private Key", "@privateKeyTextFieldHeader": { "description": "Header for private key text field", "type": "text", "placeholders": {} }, "passwordTextFieldHeader": "Password", "@passwordTextFieldHeader": { "description": "Header for password text field", "type": "text", "placeholders": {} }, "newPasswordTextFieldHeader": "New Password", "@newPasswordTextFieldHeader": { "description": "Header for new password text field", "type": "text", "placeholders": {} }, "confirmPasswordTextFieldHeader": "Confirm Password", "@confirmPasswordTextFieldHeader": { "description": "Header for confirm password text field", "type": "text", "placeholders": {} }, "confirmTextFieldHeader": "Confirm", "@confirmTextFieldHeader": { "description": "Header for confirm text field", "type": "text", "placeholders": {} }, "countryCodeTextFieldHeader": "Country Code", "@countryCodeTextFieldHeader": { "description": "Header for country code text field", "type": "text", "placeholders": {} }, "phoneNumberTextFieldHeader": "Phone Number", "@phoneNumberTextFieldHeader": { "description": "Header for phone number text field", "type": "text", "placeholders": {} }, "confirmationCodeTextFieldHeader": "Confirmation Code", "@confirmationCodeTextFieldHeader": { "description": "Header for confirmation code text field", "type": "text", "placeholders": {} }, "accountTextFieldHeader": "Account", "@accountTextFieldHeader": { "description": "Header for account text field", "type": "text", "placeholders": {} }, "addressTextFieldHeader": "Address", "@addressTextFieldHeader": { "description": "Header for address text field", "type": "text", "placeholders": {} }, "contactNameTextFieldHeader": "Contact Name", "@contactNameTextFieldHeader": { "description": "Header for contact name text field", "type": "text", "placeholders": {} }, "amountTextFieldHeader": "Amount", "@amountTextFieldHeader": { "description": "Header for amount text field", "type": "text", "placeholders": {} }, "payloadTextFieldHeader": "Payload", "@payloadTextFieldHeader": { "description": "Header for payload text field", "type": "text", "placeholders": {} }, "nameTextFieldHeader": "Name", "@nameTextFieldHeader": { "description": "Header for name text field", "type": "text", "placeholders": {} }, "newAccountNameTextFieldHeader": "New Account Name", "@newAccountNameTextFieldHeader": { "description": "Header for new account name text field", "type": "text", "placeholders": {} }, "publicKeyTextFieldHeader": "Public Key", "@publicKeyTextFieldHeader": { "description": "Header for public key text field", "type": "text", "placeholders": {} }, "priceTextFieldHeader": "Price", "@priceTextFieldHeader": { "description": "Header for price text field", "type": "text", "placeholders": {} }, "receivingAccountTextFieldHeader": "Receiving Account", "@receivingAccountTextFieldHeader": { "description": "Header for receiving account text field", "type": "text", "placeholders": {} }, "durationTextFieldHeader": "Duration", "@durationTextFieldHeader": { "description": "Header for duration text field", "type": "text", "placeholders": {} }, "feeTextFieldHeader": "Fee", "@feeTextFieldHeader": { "description": "Header for fee text field", "type": "text", "placeholders": {} }, "otherOperationsHeader": "Other Operations", "@otherOperationsHeader": { "description": "Header for other operations dialog", "type": "text", "placeholders": {} }, "warningHeader": "Warning", "@warningHeader": { "description": "Header for warning dialog", "type": "text", "placeholders": {} }, "areYouSureHeader": "Are You Sure?", "@areYouSureHeader": { "description": "Header for are you sure dialog", "type": "text", "placeholders": {} }, "addFeeHeader": "Add Fee", "@addFeeHeader": { "description": "Header for add fee dialog", "type": "text", "placeholders": {} }, "keyTypeNotSupportedHeader": "Key Not Supported", "@keyTypeNotSupportedHeader": { "description": "Header for key not supported dialog", "type": "text", "placeholders": {} }, "accountToSendFromHeader": "Account to Send From", "@accountToSendFromHeader": { "description": "Header for account to send from dialog", "type": "text", "placeholders": {} }, "sentHeader": "Sent", "@sentHeader": { "description": "Header for sent type operation list item", "type": "text", "placeholders": {} }, "receivedHeader": "Received", "@receivedHeader": { "description": "Header for received type operation list item", "type": "text", "placeholders": {} }, "nameChangedHeader": "Name Changed", "@nameChangedHeader": { "description": "Header for listed for sale type operation list item", "type": "text", "placeholders": {} }, "listedForSaleHeader": "Listed For Sale", "@listedForSaleHeader": { "description": "Header for listed for sale type operation list item", "type": "text", "placeholders": {} }, "privateSaleHeader": "Private Sale", "@privateSaleHeader": { "description": "Header for private sale type operation list item", "type": "text", "placeholders": {} }, "delistedFromSaleHeader": "Delisted From Sale", "@delistedFromSaleHeader": { "description": "Header for delisted from sale type operation list item", "type": "text", "placeholders": {} }, "delistedHeader": "Delisted", "@delistedHeader": { "description": "Header for delisted type operation list item", "type": "text", "placeholders": {} }, "undefinedHeader": "Undefined", "@undefinedHeader": { "description": "Header for undefined type operation list item", "type": "text", "placeholders": {} }, "transferredHeader": "Transferred", "@transferredHeader": { "description": "Header for transferred type operation list item", "type": "text", "placeholders": {} }, "connectingHeader": "Connecting", "@connectingHeader": { "description": "A header to let the user now that Blaise is currently connecting to (or loading) live chat.", "type": "text", "placeholders": {} }, "balanceHeader": "Balance", "@balanceHeader": { "description": "Header for balance", "type": "text", "placeholders": {} }, "totalBalanceHeader": "Total Balance", "@totalBalanceHeader": { "description": "Header for total balance", "type": "text", "placeholders": {} }, "accountBalanceHeader": "Account Balance", "@accountBalanceHeader": { "description": "Header for account balance", "type": "text", "placeholders": {} }, "accountsHeader": "Accounts", "@accountsHeader": { "description": "Header for accounts", "type": "text", "placeholders": {} }, "operationsHeader": "Operations", "@operationsHeader": { "description": "Header for operations", "type": "text", "placeholders": {} }, "encryptThePayloadHeader": "Encrypt the Payload", "@encryptThePayloadHeader": { "description": "Header for encrypt the payload switch", "type": "text", "placeholders": {} }, "encryptPayloadHeader": "Encrypt Payload", "@encryptPayloadHeader": { "description": "Header for encrypt payload switch", "type": "text", "placeholders": {} }, "forSaleHeader": "For Sale", "@forSaleHeader": { "description": "Header of for sale tag", "type": "text", "placeholders": {} }, "borrowedHeader": "Borrowed", "@borrowedHeader": { "description": "Header for borrowed tag", "type": "text", "placeholders": {} }, "borrowedTransferredHeader": "Transfer Pending", "@borrowedTransferredHeader": { "description": "Header for borrowed tag, after account is transferred but not confirmed", "type": "text", "placeholders": {} }, "borrowedAccountHeader": "Borrowed Account", "@borrowedAccountHeader": { "description": "Header for borrowed account tag", "type": "text", "placeholders": {} }, "feeColonHeader": "Fee:", "@feeColonHeader": { "description": "Header for fee amount", "type": "text", "placeholders": {} }, "pendingHeader": "Pending", "@pendingHeader": { "description": "Header to indicate that an operation is pending", "type": "text", "placeholders": {} }, "onHeader": "On", "@onHeader": { "description": "A header to indicate that something is on", "type": "text", "placeholders": {} }, "offHeader": "Off", "@offHeader": { "description": "A header to indicate that something is off", "type": "text", "placeholders": {} }, "priceRequiredError": "Price is required", "@priceRequiredError": { "description": "Error that tells the user that the price is required", "type": "text", "placeholders": {} }, "amountRequiredError": "Amount is required", "@amountRequiredError": { "description": "Error that tells the user that the amount is required", "type": "text", "placeholders": {} }, "nameRequiredError": "Name is required", "@nameRequiredError": { "description": "Error that tells the user that the name is required", "type": "text", "placeholders": {} }, "zeroPriceError": "Price can't be 0", "@zeroPriceError": { "description": "Error that tells the user that the price cant be zero", "type": "text", "placeholders": {} }, "zeroAmountError": "Amount can't be 0", "@zeroAmountError": { "description": "Error that tells the user that the amount cant be zero", "type": "text", "placeholders": {} }, "invalidAccountNameError": "Invalid account name", "@invalidAccountNameError": { "description": "Error that tells the user that the account name is invalid", "type": "text", "placeholders": {} }, "invalidReceivingAccountError": "Invalid receiving account", "@invalidReceivingAccountError": { "description": "Error that tells the user that the receiving account is invalid", "type": "text", "placeholders": {} }, "invalidPublicKeyError": "Invalid public key", "@invalidPublicKeyError": { "description": "Error that tells the user that the public key is invalid", "type": "text", "placeholders": {} }, "invalidPrivateKeyError": "Invalid private key", "@invalidPrivateKeyError": { "description": "Error that tells the user that the private key is invalid", "type": "text", "placeholders": {} }, "invalidAddressError": "Invalid address", "@invalidAddressError": { "description": "Error that tells the user that the address is invalid", "type": "text", "placeholders": {} }, "invalidAccountError": "Invalid account", "@invalidAccountError": { "description": "Error that tells the user that the account is invalid", "type": "text", "placeholders": {} }, "invalidDestinationError": "Invalid destination", "@invalidDestinationError": { "description": "Error that tells the user that the destination is invalid", "type": "text", "placeholders": {} }, "insufficientBalanceError": "Insufficient balance", "@insufficientBalanceError": { "description": "Error that tells the user that the balance is insufficient", "type": "text", "placeholders": {} }, "threeCharacterNameError": "Must be at least 3 characters", "@threeCharacterNameError": { "description": "Error that tells the user that the account name cant be shorter than 3 characters", "type": "text", "placeholders": {} }, "contactDoesntExistError": "Contact doesn't exist", "@contactDoesntExistError": { "description": "Error that tells the user that the contact doesnt exist", "type": "text", "placeholders": {} }, "contactAlreadyExistsError": "Contact already exists", "@contactAlreadyExistsError": { "description": "Error that tells the user that the contact already exists", "type": "text", "placeholders": {} }, "cantSendToYourselfError": "Can't send to yourself", "@cantSendToYourselfError": { "description": "Error that tells the user that you cant send to yourself", "type": "text", "placeholders": {} }, "somethingWentWrongError": "Something went wrong, please try again later", "@somethingWentWrongError": { "description": "Error that tells the user that something went wrong", "type": "text", "placeholders": {} }, "failedToEncryptPayloadError": "Failed to encrypt the payload", "@failedToEncryptPayloadError": { "description": "Error that tells the user that payload encrypt is failed", "type": "text", "placeholders": {} }, "emptyPasswordError": "Password can't be empty", "@emptyPasswordError": { "description": "Error that tells the user that the password cant be empty", "type": "text", "placeholders": {} }, "noMatchPasswordError": "Passwords don't match", "@noMatchPasswordError": { "description": "Error that tells the user that the passwords dont match", "type": "text", "placeholders": {} }, "invalidPasswordError": "Invalid password", "@invalidPasswordError": { "description": "Error that tells the user that the password is invalid", "type": "text", "placeholders": {} }, "didNotGetResponseError": "Did not get a response from server", "@didNotGetResponseError": { "description": "Error that tells the user that there is no response from the server", "type": "text", "placeholders": {} }, "noContactsToExportError": "No contacts to export", "@noContactsToExportError": { "description": "Error that tells the user that there is no contacts to export", "type": "text", "placeholders": {} }, "noContactsToImportError": "No contacts to import", "@noContactsToImportError": { "description": "Error that tells the user that there is no contacts to import", "type": "text", "placeholders": {} }, "failedToImportContactsError": "Failed to import contacts", "@failedToImportContactsError": { "description": "Error that tells the user that there is no contacts to export", "type": "text", "placeholders": {} }, "blockchainRewardOPDetails": "Blockchain Reward (%1)", "@blockchainRewardOPDetails": { "description": "Operation details header for blockchain reward", "type": "text", "placeholders": {} }, "transactionOPDetails": "Transaction (%1)", "@transactionOPDetails": { "description": "Operation details header for transaction", "type": "text", "placeholders": {} }, "changeKeyOPDetails": "Change key (%1)", "@changeKeyOPDetails": { "description": "Operation details header for change key", "type": "text", "placeholders": {} }, "recoverFundsOPDetails": "Recover Funds (%1)", "@recoverFundsOPDetails": { "description": "Operation details header for recover funds", "type": "text", "placeholders": {} }, "listAccountForSaleOPDetails": "List Account for Sale (%1)", "@listAccountForSaleOPDetails": { "description": "Operation details header for list account for sale", "type": "text", "placeholders": {} }, "delistAccountOPDetails": "Delist Account (%1)", "@delistAccountOPDetails": { "description": "Operation details header for delist account", "type": "text", "placeholders": {} }, "buyAccountOPDetails": "Buy Account (%1)", "@buyAccountOPDetails": { "description": "Operation details header for buy account", "type": "text", "placeholders": {} }, "changeKeySignedOPDetails": "Change Key Signed (%1)", "@changeKeySignedOPDetails": { "description": "Operation details header for change key signed", "type": "text", "placeholders": {} }, "changeAccountInfoOPDetails": "Change Account Info (%1)", "@changeAccountInfoOPDetails": { "description": "Operation details header for change account info", "type": "text", "placeholders": {} }, "multioperationOPDetails": "Multioperation (%1)", "@multioperationOPDetails": { "description": "Operation details header for multioperation", "type": "text", "placeholders": {} }, "unknownOPDetails": "Unknown (%1)", "@unknownOPDetails": { "description": "Operation details header for unknown", "type": "text", "placeholders": {} }, "sendingAccountOPDetails": "Sending Account", "@sendingAccountOPDetails": { "description": "Operation details header for sending account", "type": "text", "placeholders": {} }, "receivingAccountOPDetails": "Receiving Account", "@receivingAccountOPDetails": { "description": "Operation details header for receiving account", "type": "text", "placeholders": {} }, "changingAccountOPDetails": "Changing Account", "@changingAccountOPDetails": { "description": "Operation details header for changing account", "type": "text", "placeholders": {} }, "sendAmountOPDetails": "Send Amount", "@sendAmountOPDetails": { "description": "Operation details header for send amount", "type": "text", "placeholders": {} }, "payloadOPDetails": "Payload", "@payloadOPDetails": { "description": "Operation details header for payload", "type": "text", "placeholders": {} }, "newPublicKeyOPDetails": "New Public Key", "@newPublicKeyOPDetails": { "description": "Operation details header for new public key", "type": "text", "placeholders": {} }, "newNameOPDetails": "New Name", "@newNameOPDetails": { "description": "Operation details header for new name", "type": "text", "placeholders": {} }, "sellerAccountOPDetails": "Seller Account", "@sellerAccountOPDetails": { "description": "Operation details header for seller account", "type": "text", "placeholders": {} }, "accountPriceOPDetails": "Account Price", "@accountPriceOPDetails": { "description": "Operation details header for account price", "type": "text", "placeholders": {} }, "lockedUntilBlockOPDetails": "Locked Until Block", "@lockedUntilBlockOPDetails": { "description": "Operation details header for locked until block", "type": "text", "placeholders": {} }, "blockOPDetails": "block", "@blockOPDetails": { "description": "Operation details header for block", "type": "text", "placeholders": {} }, "optxtOPDetails": "optxt", "@optxtOPDetails": { "description": "Operation details header for optxt", "type": "text", "placeholders": {} }, "timeOPDetails": "time", "@timeOPDetails": { "description": "Operation details header for time", "type": "text", "placeholders": {} }, "naOPDetails": "N/A", "@naOPDetails": { "description": "Operation details header for N/A", "type": "text", "placeholders": {} }, "ophashOPDetails": "ophash", "@ophashOPDetails": { "description": "Operation details header for ophash", "type": "text", "placeholders": {} }, "optypeOPDetails": "optype", "@optypeOPDetails": { "description": "Operation details header for optype", "type": "text", "placeholders": {} }, "maturationOPDetails": "maturation", "@maturationOPDetails": { "description": "Operation details header for maturation", "type": "text", "placeholders": {} }, "nullOPDetails": "null", "@nullOPDetails": { "description": "Operation details header for null", "type": "text", "placeholders": {} }, "feeOPDetails": "fee", "@feeOPDetails": { "description": "Operation details header for fee", "type": "text", "placeholders": {} }, "opblockOPDetails": "opblock", "@opblockOPDetails": { "description": "Operation details header for opblock", "type": "text", "placeholders": {} }, "noperationOPDetails": "n_operation", "@noperationOPDetails": { "description": "Operation details header for n_operation", "type": "text", "placeholders": {} }, "accountOPDetails": "account", "@accountOPDetails": { "description": "Operation details header for account", "type": "text", "placeholders": {} }, "signeraccountOPDetails": "signer_account", "@signeraccountOPDetails": { "description": "Operation details header for signer_account", "type": "text", "placeholders": {} } } ================================================ FILE: lib/l10n/intl_tr.arb ================================================ { "newPrivateKeyButton": "Yeni Gizli Anahtar", "importPrivateKeyButton": "Anahtarı İçe Aktar", "gotItButton": "Tamamdır!", "goBackButton": "Geri Dön", "copyButton": "Kopyala", "copiedButton": "Kopyalandı", "keyCopiedButton": "Anahtar Kopyalandı", "iHaveBackedItUpButton": "Yedekledim", "yesImSureButton": "Evet, Eminim", "noGoBackButton": "Hayır, Geri Dön", "getAnAccountButton": "Hesap Al", "getAFreeAccountButton": "Ücretsiz Hesap Al", "buyAnAccountButton": "Hesap Satın Al", "sendConfirmationButton": "Onay Kodu Gönder", "confirmButton": "Onayla", "borrowAnAccountButton": "Ödünç Hesap Al", "importButton": "İçe Aktar", "receiveButton": "İste", "sendButton": "Gönder", "copyAddressButton": "Adresi Kopyala", "copiedAddressButton": "Adres Kopyalandı", "addToContactsButton": "Rehbere Ekle", "operationDetailsButton": "Operasyon detayları", "openInExplorerButton": "Tarayıcıda Aç", "requestButton": "İste", "addAPayloadButton": "+ Not Ekle", "addADurationButton": "+ Süre Ekle", "scanQRCodeButton": "QR Kodu Tara", "cancelButton": "İptal", "closeButton": "Kapat", "changeNameButton": "Adı Değiştir", "transferButton": "Transfer Et", "listForSaleButton": "Şatışa Çıkar", "createPrivateSaleButton": "Özel Satış Oluştur", "yesAddFeeButton": "Evet, Ücreti Ekle", "unlockButton": "Kilidi Aç", "unlockWithBiometricsButton": "Biometrik ile Aç", "unlockWithPINButton": "PIN ile Aç", "setToDefaultButton": "Varsayılan", "changeDaemonButton": "Değiştir", "addContactButton": "Kişi Ekle", "encryptedKeyButton": "Şifreli Anahtar", "unencryptedKeyButton": "Ham Anahtar", "encryptButton": "Şifrele", "showButton": "Göster", "hideButton": "Gizle", "copyEncryptedKeyButton": "Şifreli Anahtarı Kopyala", "copyUnencryptedKeyButton": "Ham Anahtarı Kopyala", "copyKeyButton": "Anahtarı Kopyala", "copyPublicKeyButton": "Açık Anahtarı Kopyala", "deletePrivateKeyAndLogoutButton": "Gizli Anahtarı Sil\nve Çık", "searchForNameButton": "Adı Ara", "searchAccountNameButton": "Hesap Adı Ara", "searchNameButton": "Adı Ara", "okayGoBackButton": "Tamamdır, Geri Dön", "nextButton": "İleri", "welcomeParagraph": "Blaise'e hoşgeldin. Başlamak için yeni bir gizli anahtar oluştur veya zaten bir gizli anahtarın varsa içe aktar.", "newKeySecurityParagraph": "Bir sonraki ekranda yeni gizli anahtarını göreceksin. Bu, cüzdanına ve bakiyene ulaşmanı sağlayan bir çeşit şifre. Anahtarı yedeklemen ve kimseyle paylaşmaman çok önemli.", "uninstallDisclaimerParagraph": "Cihazını kaybedersen veya Blaise'i silersen, bakiyene ve cüzdanına tekrar ulaşmak için gizli anahtarına ihtiyaç duyacaksın.", "newPrivateKeyParagraph": "Aşağıda cüzdanın yeni gizli anahtarını görebilirsin. Anahtarını yedeklemen çok önemli. Anahtarı kesinlikle ekran görüntüsü olarak saklamamalısın. Anahtarı bir kağıda yazmanı ve çevrim dışı olarak saklamanı tavsiye ediyoruz.", "newKeyBackUpConfirmParagraph": "Yeni cüzdanının gizli anahtarını yedeklediğine emin misin?", "newWalletGreetingParagraph": "Blaise'e hoşgeldin.\nBir hesap alarak başlayabilirsin.", "getAccountFirstParagraph": "İlk hesabını almak için 2 seçeneğin var:", "getAccountSecondParagraph": "1- Telefon numaranı kullanarak ücretsiz olarak bir hesap alabilirsin. Her telefon numarası için en fazla 1 hesap alabilirsin.", "getAccountThirdParagraph": "2- %1 Pascal (%2) karşılığında istediğin kadar hesap satın alabilirsin.", "enterPhoneNumberParagraph": "Telefon numaranı aşağıya gir.", "enterConfirmationCodeParagraph": "Sana bir onay kodu yolladık, lütfen kodu aşağıya gir.", "borrowStarted": "%1 için alım işlemi başladı", "borrowAccountParagraph": "Bir hesap satın almak için, öncelikle bu hesabı ödünç alman gerekiyor. Ödünç aldığın hesaba %3 gün içinde %1 Pascal (%2) gönderirsen, hesap tamamen senin olacak ve hesaptan otomatik olarak %1 Pascal ücret kesilecek.\nAksi takdirde hesap %3 günün sonunda bize geri dönecek.\nHesap tamamen senin olmadan, hesaba fazla miktarda gönderi yapmamanı tavsiye ediyoruz.", "importPrivateKeyParagraph": "Gizli anahtarını aşağıya gir.", "looksLikeEncryptedKeyParagraph": "Bu, şifreli bir gizli anahtara benziyor. Şifrelemeyi çözmek ve anahtarı içe aktarmak için şifreni gir.", "changeDaemonParagraph": "Pascal erişim noktasını değiştirmek için yeni bir erişim noktası gir.", "urlChangedToParagraph": "URL %1 olarak değiştirildi", "backupKeyFirstParagraph": "Gizli anahtarını yedeklemek için iki seçeneğin var:", "backupKeySecondParagraph": "2- Şifreli, yani bir şifre ile korunan.", "backupKeyThirdParagraph": "2- Ham, yani bir şifre ile korunmayan.", "backupKeyFourthParagraph": "Anahtarın ham halini bir kağıda yazıp çevrim dışı olarak saklamanı, şifreli halini de kolayca erişebilmen için bir şifre yöneticisinde saklamanı tavsiye ediyoruz.", "encryptKeyParagraph": "Gizli anahtarını şifrelemek için yeni bir parola oluştur.", "backupEncryptedKeyFirstParagraph": "Şifreli gizli anahtarını aşağıda görebilirsin. Bir şifre ile korunduğu için, anahtarını güvenle bir şifre yöneticisinde saklayabilirsin.", "backupEncryptedKeySecondParagraph": "Anahtar şifreli olduğu için, şifreni unutman veya kaybetmen durumunda, şifrelemeyi açıp cüzdanına erişmen mümkün olmayacak.", "backupUnencryptedKeyParagraph": "Ham gizli anahtarını aşağıda görebilirsin. Bir şifre ile korunmadığı için, ham anahtarını güvenli ve çevrim dışı bir yerde saklaman çok önemli. Anahtarını bir kağıda yazarak saklamanı tavsiye ediyoruz.", "publicKeyParagraph": "Açık anahtarını aşağıda görebilirsin. Bu anahtar, herkese açık şekilde paylaşılmak ve bir operasyonun senin gizli anahtarına ait olduğu kanıtlamak için var.", "borrowedAccountParagraph": "Bu bir ödünç hesap.\n%2 gün, %3 saat, %4 dakika içinde bu hesaba en az %1 Pascal gönderirsen, hesap senin olacak.", "borrowedAccountPaidParagraph": "Hesap satın alındı!\nTransfer işlemi şuan devam ediyor. Transfer genellikle 15 dakika, bazen ise biraz daha uzun sürebilir.", "logoutFirstDisclaimerParagraph": "Çıkış yaparsan, gizli anahtarın ve Blaise ile alakalı tüm veriler bu cihazdan silinecek. Gizli anahtarını yedeklemediysen, cüzdanına ve bakiyene bir daha ulaşamazsın. Yedeklediysen, endişelenecek hiçbir şey yok.", "logoutSecondDisclaimerParagraph": "Gizli anahtarını yedeklediğine emin misin? Yedeklediysen endişelenecek bir şey yok.", "sendingConfirmParagraph": "Göndermek için işlem detaylarını onayla.", "sentParagraph": "Gönderme işlemi başarıyla tamamlandı.", "changeNameParagraph": "Hesap adını değiştirmek için aşağıya yeni bir hesap adı gir.", "changingNameParagraph": "İlerlemek için hesabın yeni adını onayla.", "changedNameParagraph": "Hesap adı başarıyla değiştirildi.", "transferParagraph": "Bu hesabı transfer etmek istediğin açık anahtarı aşağıya gir.", "transferringParagraph": "Bu hesabı transfer etmek istediğin açık anahtarı onayla.", "transferredParagraph": "Hesabın aşağıdaki açık anahtara başarıyla transfer edildi.", "listForSaleParagraph": "Bu hesabı satışa çıkarmak için aşağıya bir fiyat ve ödemeyi alacak hesabı gir.", "listingForSaleParagraph": "Fiyatı ve ödemeyi alacak hesabı onayla.", "listedForSaleParagraph": "Hesabın satışa çıkarıldı. Satın alınırsa seni haberdar ederiz.", "createPrivateSaleParagraph": "Özel satış oluşturmak için fiyatı, ödemeyi alacak hesabı ve müşterinin açık anahtarını gir.", "creatingPrivateSaleParagraph": "Aşağıdaki bilgileri doğrula.", "createdPrivateSaleParagraph": "Özel satış başarıyla oluşturuldu. Hesap satın alınırsa seni haberdar ederiz.", "delistFromSaleParagraph": "Hesabı satıştan kaldırmak istediğini onayla.", "delistedFromSaleParagraph": "Hesap satıştan başarıyla kaldırıldı.", "feeRequiredParagraph": "Bu operasyon bir ücret gerektiriyor.", "feeConfirmAmountParagraph": "Devam etmek için %1 Pascal tutarında bir ücret eklenmesini onayla.", "keyTypeNotSupportedParagraph": "Blaise henüz bu türdeki gizli anahtarları desteklemiyor. Yeni bir gizli anahtar oluşturup, hesaplarını başka bir cüzdan kullanarak yeni gizli anahtarına transfer edebilirsin.", "enterPINToUnlockParagraph": "Blaise'i açmak için PIN'i gir", "authenticateToUnlockParagraph": "Blaise'i açmak için doğrula", "manyFailedAttemptsParagraph": "Çok fazla başarısız giriş denemesi yapıldı", "authenticateToChangeNameParagraph": "Hesap adını \"%1\" olarak değiştirmek için doğrula", "authenticateToDelistParagraph": "Hesabı satıştan kaldırmak için doğrula", "authenticateToListForSaleParagraph": "Hesabı satışa çıkarmak için doğrula", "authenticateToCreatePrivateSaleParagraph": "Özel satış oluşturmak için doğrula", "authenticateToTransferParagraph": "Hesabı transfer etmek için doğrula", "authenticateToSendParagraph": "%1 Pascal göndermek için doğrula", "authenticateToBackUpParagraph": "Gizli anahtarını yedeklemek için doğrula", "invalidPINParagraph": "Geçersiz PIN", "noMatchPINParagraph": "PIN'ler uyuşmuyor", "confirmPINParagraph": "PIN'i onayla", "enterPINParagraph": "PIN'i gir", "createPINParagraph": "6 haneli bir PIN oluştur", "addedToContactsParagraph": "%1 rehbere eklendi", "removedFromContactsParagraph": "%1 rehberden silindi", "failedToRemoveFromContactsParagraph": "%1 rehberden silinemedi", "successfullyImportedContactsParagraph": "%1 kişi rehbere başarıyla eklendi", "checkOutBlaiseParagraph": "Blaise'i Dene! Sade, şık & güvenli bir Pascal cüzdanı. Hem iOS, hem Android'de: https://blaisewallet.com", "newAccountParagraph": "Yeni hesabına hoşgeldin.\nPascal aldığında veya gönderdiğinde, operasyonların aşağıdaki gibi görünecek.", "settingsHeader": "Ayarlar", "preferencesHeader": "Tercihler", "currencyHeader": "Para Birimi", "languageHeader": "Dil", "languageColonHeader": "Dil:", "systemDefaultHeader": "Sistem Varsayılanı", "themeHeader": "Tema", "themeLightHeader": "Açık", "themeDarkHeader": "Koyu", "themeCopperHeader": "Bakır", "notificationsHeader": "Bildirimler", "securityHeader": "Güvenlik", "authenticationMethodHeader": "Doğrulama Metodu", "authenticationPINHeader": "PIN", "authenticationBiometricsHeader": "Biometrik", "authenticateOnLaunchHeader": "Başlangıçta Doğrula", "yesHeader": "Evet", "noHeader": "Hayır", "automaticallyLockHeader": "Otomatik Kilitle", "lockInstantHeader": "Hemen", "lock1Header": "%1 dakika sonra", "lock5Header": "%1 dakika sonra", "lock15Header": "%1 dakika sonra", "lock30Header": "%1 dakika sonra", "lock60Header": "%1 dakika sonra", "daemonHeader": "Erişim Noktası", "defaultHeader": "Varsayılan", "manageHeader": "Yönet", "contactsHeader": "Rehber", "backUpPrivateKeyHeader": "Gizli Anahtarı Yedekle", "viewPublicKeyHeader": "Açık Anahtarı Görüntüle", "shareHeader": "Blaise'i Paylaş", "logoutHeader": "Çıkış", "privacyPolicyHeader": "Gizlilik Politikası", "changeAccountNameHeader": "Hesap Adı Değiştir", "transferAccountHeader": "Hesabı Transfer Et", "listAccountForSaleHeader": "Hesabı Satışa Çıkar", "createPrivateSaleHeader": "Özel Satış Oluştur", "delistFromSaleHeader": "Satıştan Kaldır", "getAccountSheetHeader": "Hesap Al", "freeAccountSheetHeader": "Ücretsiz Hesap", "buyAccountSheetHeader": "Hesap Al", "sendSheetHeader": "Gönder", "sendingSheetHeader": "Gönderiliyor", "sentSheetHeader": "Gönderildi", "requestSheetHeader": "İste", "changeNameSheetHeader": "Ad Değiştir", "changingNameSheetHeader": "Değiştiriliyor", "changedNameSheetHeader": "Değiştirildi", "transferSheetHeader": "Transfer Et", "transferringSheetHeader": "Ediliyor", "transferredSheetHeader": "Edildi", "listForSaleSheetHeader": "Satış", "listingForSaleSheetHeader": "Listeleniyor", "listedForSaleSheetHeader": "Listelendi", "createPrivateSaleSheetHeader": "Özel Satış", "creatingPrivateSaleSheetHeader": "Oluşturuluyor", "createdPrivateSaleSheetHeader": "Oluşturuldu", "delistingSheetHeader": "Kaldırılıyor", "delistedSheetHeader": "Kaldırıldı", "addContactSheetHeader": "Kişi Ekle", "contactSheetHeader": "Kişi", "publicKeySheetHeader": "Açık Anahtar", "privateKeySheetHeader": "Gizli Anahtar", "backUpSheetHeader": "Yedekle", "encryptSheetHeader": "Şifrele", "changeDaemonSheetHeader": "Erişim Noktası", "securityFirstHeader": "Önce Güvenlik!", "newPrivateKeyHeader": "Yeni Gizli Anahtar", "importPrivateKeyHeader": "Gizli Anahtarı İçe Aktar", "decryptAndImportKeyHeader": "Şifreyi Aç & İçe Aktar", "backUpKeyHeader": "Anahtarını Yedekle!", "lockedHeader": "Kilitli", "privateKeyTextFieldHeader": "Gizli Anahtar", "passwordTextFieldHeader": "Şifre", "newPasswordTextFieldHeader": "Yeni Şifre", "confirmPasswordTextFieldHeader": "Şifreyi Onayla", "confirmTextFieldHeader": "Onayla", "countryCodeTextFieldHeader": "Ülke Kodu", "phoneNumberTextFieldHeader": "Telefon Numarası", "confirmationCodeTextFieldHeader": "Onay Kodu", "accountTextFieldHeader": "Hesap", "addressTextFieldHeader": "Adres", "contactNameTextFieldHeader": "Kişi Adı", "amountTextFieldHeader": "Miktar", "payloadTextFieldHeader": "Not", "nameTextFieldHeader": "Ad", "newAccountNameTextFieldHeader": "Yeni Hesap Adı", "publicKeyTextFieldHeader": "Açık Anahtar", "priceTextFieldHeader": "Fiyat", "receivingAccountTextFieldHeader": "Ödemeyi Alacak Hesap", "durationTextFieldHeader": "Süre", "feeTextFieldHeader": "Ücret", "otherOperationsHeader": "Diğer Operasyonlar", "warningHeader": "Uyarı", "areYouSureHeader": "Emin Misin?", "addFeeHeader": "Ücret Ekle", "keyTypeNotSupportedHeader": "Anahtar Desteklenmiyor", "accountToSendFromHeader": "Gönderilecek Hesap", "sentHeader": "Gönderildi", "receivedHeader": "Alındı", "nameChangedHeader": "Ad Değiştirildi", "listedForSaleHeader": "Satışa Çıkarıldı", "privateSaleHeader": "Özel Satış", "delistedFromSaleHeader": "Satıştan Kaldırıldı", "delistedHeader": "Satıştan Kaldırıldı", "undefinedHeader": "Tanımsız", "transferredHeader": "Transfer Edildi", "balanceHeader": "Bakiye", "totalBalanceHeader": "Genel Bakiye", "accountBalanceHeader": "Hesap Bakiyesi", "accountsHeader": "Hesaplar", "operationsHeader": "Operasyonlar", "encryptThePayloadHeader": "Notu Şifrele", "encryptPayloadHeader": "Notu Şifrele", "forSaleHeader": "Satılık", "borrowedHeader": "Ödünç", "borrowedTransferredHeader": "Transfer Bekleniyor", "borrowedAccountHeader": "Ödünç Hesap", "feeColonHeader": "Ücret:", "pendingHeader": "Bekleniyor", "onHeader": "Açık", "offHeader": "Kapalı", "priceRequiredError": "Fiyat gerekli", "amountRequiredError": "Miktar gerekli", "nameRequiredError": "Ad gerekli", "zeroPriceError": "Fiyat sıfır olamaz", "zeroAmountError": "Miktar sıfır olamaz", "invalidAccountNameError": "Geçersiz hesap adı", "invalidReceivingAccountError": "Geçersiz alıcı hesap", "invalidPublicKeyError": "Geçersiz açık anahtar", "invalidPrivateKeyError": "Geçersiz gizli anahtar", "invalidAddressError": "Geçersiz adres", "invalidAccountError": "Geçersiz hesap", "invalidDestinationError": "Geçersiz varış noktası", "insufficientBalanceError": "Bakiye yetersiz", "threeCharacterNameError": "En az 3 karakter olmalı", "contactDoesntExistError": "Kişi bulunamadı", "contactAlreadyExistsError": "Kişi zaten mevcut", "cantSendToYourselfError": "Kendine gönderemezsin", "somethingWentWrongError": "Bir şeyler ters gitti, daha sonra tekrar dene", "failedToEncryptPayloadError": "Not şifreleme başarısız", "emptyPasswordError": "Şifre alanı boş olamaz", "noMatchPasswordError": "Şifreler uyuşmuyor", "invalidPasswordError": "Geçersiz şifre", "didNotGetResponseError": "Sunucudan cevap alınamadı", "noContactsToExportError": "Dışa aktarılacak kişi bulunamadı", "noContactsToImportError": "İçe aktarılacak kişi bulunamadı", "failedToImportContactsError": "Kişileri içe aktarma işlemi başarısız", "blockchainRewardOPDetails": "Blokzinciri Ödülü (%1)", "transactionOPDetails": "İşlem (%1)", "changeKeyOPDetails": "Anahtar Değişimi (%1)", "recoverFundsOPDetails": "Bakiye Kurtarma (%1)", "listAccountForSaleOPDetails": "Hesap Satışı (%1)", "delistAccountOPDetails": "Satıştan Kaldırma (%1)", "buyAccountOPDetails": "Hesap Satın Alımı (%1)", "changeKeySignedOPDetails": "Anahtar Değişimi (%1)", "changeAccountInfoOPDetails": "Hesap Bilgisi Değişimi (%1)", "multioperationOPDetails": "Çoklu Operasyon (%1)", "unknownOPDetails": "Bilinmeyen (%1)", "sendingAccountOPDetails": "Gönderici Hesap", "receivingAccountOPDetails": "Alıcı Hesap", "changingAccountOPDetails": "Değişen Hesap", "sendAmountOPDetails": "Gönderi Miktarı", "payloadOPDetails": "Not", "newPublicKeyOPDetails": "Yeni Açık Anahtar", "newNameOPDetails": "Yeni Ad", "sellerAccountOPDetails": "Satıcı Hesap", "accountPriceOPDetails": "Hesap Fiyatı", "lockedUntilBlockOPDetails": "Bu Bloğa Kadar Kilitli", "blockOPDetails": "Blok", "optxtOPDetails": "Operasyon Notu", "timeOPDetails": "Tarih", "naOPDetails": "Mevcut Değil", "ophashOPDetails": "Operasyon Hash'i", "optypeOPDetails": "Operasyon Tipi", "maturationOPDetails": "Olgunluk", "nullOPDetails": "Belirsiz", "feeOPDetails": "Ücret", "opblockOPDetails": "Bloktaki Sırası", "noperationOPDetails": "Toplam Operasyon", "accountOPDetails": "Hesap", "signeraccountOPDetails": "İmzalayan Hesap", "noResultsFound": "Sonuç bulunamadı", "getAccountThirdParagraphAlternative": "%1 Pascal (%2) karşılığında bir hesap satın alabilirsin. Her kullanıcı en fazla 1 adet hesap satın alabilir.", "getAccountThirdParagraphAlternative2": "%1 Pascal (%2) karşılığında bir hesap satın alabilirsin. Her kullanıcı en fazla %3 adet hesap satın alabilir.", "receiveAccountButton": "Hesap İste", "receiveAnAccountButton": "Hesap İste" } ================================================ FILE: lib/l10n/intl_zh-Hans.arb ================================================ { "newPrivateKeyButton": "新建私钥", "importPrivateKeyButton": "导入私钥", "gotItButton": "确认", "goBackButton": "返回", "copyButton": "复制", "copiedButton": "复制成功", "keyCopiedButton": "成功复制密钥", "iHaveBackedItUpButton": "我已做好备份", "yesImSureButton": "确定", "noGoBackButton": "返回", "getAnAccountButton": "获取账号", "getAFreeAccountButton": "获取免费账号", "buyAnAccountButton": "购买账号", "sendConfirmationButton": "确认发送", "confirmButton": "确认", "borrowAnAccountButton": "借用账号", "importButton": "导入", "receiveButton": "接收", "sendButton": "发送", "copyAddressButton": "复制地址", "copiedAddressButton": "地址复制成功", "addToContactsButton": "添加到联系人", "operationDetailsButton": "操作细节", "openInExplorerButton": "在浏览器中打开", "requestButton": "请求", "addAPayloadButton": "+ 添加负载", "addADurationButton": "+ 添加时长", "scanQRCodeButton": "扫描二维码", "cancelButton": "取消", "closeButton": "关闭", "changeNameButton": "修改账号名", "transferButton": "转让账号", "listForSaleButton": "挂卖账号", "createPrivateSaleButton": "创建非公开挂卖", "yesAddFeeButton": "确认添加手续费", "unlockButton": "解锁", "unlockWithBiometricsButton": "生物特征解锁", "unlockWithPINButton": "用识别码解锁", "setToDefaultButton": "设为默认", "changeDaemonButton": "更改后台", "addContactButton": "添加联系人", "encryptedKeyButton": "显示已加密私钥", "unencryptedKeyButton": "显示未加密私钥", "encryptButton": "加密私钥", "showButton": "显示", "hideButton": "隐藏", "copyEncryptedKeyButton": "复制已加密私钥", "copyUnencryptedKeyButton": "复制原始私钥", "copyKeyButton": "复制密钥", "copyPublicKeyButton": "复制公钥", "deletePrivateKeyAndLogoutButton": "删去私钥\n并登出", "searchForNameButton": "搜索名字", "searchAccountNameButton": "搜索账号名", "searchNameButton": "搜索名字", "okayGoBackButton": "返回", "nextButton": "继续", "welcomeParagraph": "欢迎使用Blaise钱包。下一步,您可以创建新私钥或导入现有私钥。", "newKeySecurityParagraph": "接下来您将看到您新建的私钥,请务必将其备份且不要泄露给任何人。", "uninstallDisclaimerParagraph": "如果您丢失了该设备或者卸载了Blaise钱包,您需要使用私钥来恢复资金。", "newPrivateKeyParagraph": "以下是您的新钱包私钥。请注意,您必须备份私钥但不要将其备份为纯文本或屏幕截图,我们建议您用笔将其抄写到纸上。", "newKeyBackUpConfirmParagraph": "您确定已经将新钱包的私钥备份好了吗?", "newWalletGreetingParagraph": "欢迎使用Blaise钱包。\n您可以从获取一个账号开始", "getAccountFirstParagraph": "您可以通过两种方式获得账号:", "getAccountSecondParagraph": "1- 通过手机号码获得账号,每个手机号码只能获得一个。", "getAccountThirdParagraph": "2- 您可以以单价%1 Pascal (%2)购买任意数量的账号。", "enterPhoneNumberParagraph": "在下面输入您的手机号码", "enterConfirmationCodeParagraph": "请输入您收到的验证码。", "borrowStarted": "开始购买账号 %1", "borrowAccountParagraph": "要购买一个帐户,您首先需要免费借用一个。如果您在%3天内至少存入%1 Pascal (%2)到该账号,该账号将是您的,并且%1 Pascal将自动从您的余额中扣除。\n否则,该账号将在%3天结束时返回给我们,不再属于您的钱包。\n建议您在完全拥有该帐号前只存入少量的Pascal。", "importPrivateKeyParagraph": "请在下方输入您的私钥:", "looksLikeEncryptedKeyParagraph": "这看起来像是一个经过加密的私钥,请输入密码以解密并导入它。", "changeDaemonParagraph": "请输入一个新的后台地址以便进行RPC请求。", "urlChangedToParagraph": "将URL变更为 %1", "backupKeyFirstParagraph": "您有两种可选方式来备份私钥:", "backupKeySecondParagraph": "1- 加密方式。这意味着你的私钥将被密码保护。", "backupKeyThirdParagraph": "2- 非加密方式。以原始明文方式备份,不通过密码保护。", "backupKeyFourthParagraph": "我们推荐以非加密方式将私钥抄写到纸上,离线保存。您也可以选择以加密方式将私钥保存到密码管理器中。", "encryptKeyParagraph": "创建一个密码,用来加密你的私钥。", "backupEncryptedKeyFirstParagraph": "以下是经过您的密码加密后的私钥。您可以选择将它保存到密码管理器中。", "backupEncryptedKeySecondParagraph": "由于私钥经过了您的密码加密,如果您丢失或忘记了密码,那么您将无法再访问您的资金。", "backupUnencryptedKeyParagraph": "以下是您未经加密的原始私钥。该私钥没有使用密码保护,请务必将其备份到安全且离线的地方。 我们推荐您将其抄写到纸上。", "publicKeyParagraph": "以下是您的公钥。顾名思义,它可用作公开共享,并证明某个特定操作属于您的私钥。", "borrowedAccountParagraph": "这是一个借用的账号。\n如果您在接下来的%2天 %3时 %4分内存入%1 Pascal到该账号中,您将永远拥有它。", "borrowedAccountPaidParagraph": "您已成功购买该账号!\n网络正在处理该操作,这个过程大约需要15分钟, 在某些情况下,可能需要稍长时间。", "logoutFirstDisclaimerParagraph": "登出将会从这个设备删除您的私钥和所有与Blaise钱包相关的数据。如果您的私钥还未备份,您将永远无法再访问您的资金。如果您的私钥已备份,则无需担心。", "logoutSecondDisclaimerParagraph": "您确定已经备份了您的私钥吗?只要您备份了私钥,就没有什么好担心的.", "sendingConfirmParagraph": "确认您的交易信息以发送。", "sentParagraph": "成功发送交易。", "changeNameParagraph": "在下方输入一个名字来更改您的账号名。", "changingNameParagraph": "请确认您的新账号名。", "changedNameParagraph": "您已成功更改账号名。", "transferParagraph": "在下方输入公钥,将此账号的所有权转移给它。", "transferringParagraph": "请确认以下公钥,此账号将转移给它。", "transferredParagraph": "您的帐号已成功转移给下方的公钥。", "listForSaleParagraph": "请输入该账号的售价和一个用来接收付款的账号。", "listingForSaleParagraph": "请确认售价和收款账号。", "listedForSaleParagraph": "您的帐号正式挂牌出售。如果有人买了,我们将会通知您。", "createPrivateSaleParagraph": "输入售价、收款账号和公钥,以对该账号进行对私出售。", "creatingPrivateSaleParagraph": "请确认以下信息。", "createdPrivateSaleParagraph": "对私出售已经成功创建。如果账号被购买了,我们将会通知您。", "delistFromSaleParagraph": "请确认您想将此帐户从销售列表中下架。", "delistedFromSaleParagraph": "您的帐户已成功从销售列表中下架。", "feeRequiredParagraph": "该操作需要手续费。", "feeConfirmAmountParagraph": "请确认将%1Pascal作为费用以继续此操作。", "keyTypeNotSupportedParagraph": "Blaise钱包目前还不支持这种类型的私钥。您可以创建一个新的私钥,并使用其它钱包将您的帐户转移给该私钥。", "enterPINToUnlockParagraph": "输入识别码以解锁", "authenticateToUnlockParagraph": "验证以解锁钱包", "manyFailedAttemptsParagraph": "解锁尝试失败次数太多", "authenticateToChangeNameParagraph": "验证将帐户名称更改为\"%1\"", "authenticateToDelistParagraph": "验证以下架正在出售的账号", "authenticateToListForSaleParagraph": "验证以上架出售账号", "authenticateToCreatePrivateSaleParagraph": "验证以创建新的私钥", "authenticateToTransferParagraph": "验证以转移账号", "authenticateToSendParagraph": "验证以发送%1Pascal", "authenticateToBackUpParagraph": "验证以备份私钥", "invalidPINParagraph": "无效的识别码", "noMatchPINParagraph": "识别码不匹配", "confirmPINParagraph": "确认您的识别码", "enterPINParagraph": "请输入输入识别码", "createPINParagraph": "创建一个6位数的识别码", "addedToContactsParagraph": "已将%1添加到联系人", "removedFromContactsParagraph": "已将%1从联系人中删除", "failedToRemoveFromContactsParagraph": "未能从联系人中删除%1", "successfullyImportedContactsParagraph": "成功导入%1个联系人", "checkOutBlaiseParagraph": "了解Blaise,简单、安全又好用的Pascal钱包:https://blaisewallet.com", "newAccountParagraph": "这是您的新账号。\n一旦您接收到Pascal,操作将如下所示。", "settingsHeader": "设置", "preferencesHeader": "偏好", "currencyHeader": "货币单位", "languageHeader": "语言", "languageColonHeader": "语言:", "systemDefaultHeader": "系统默认", "themeHeader": "主题", "themeLightHeader": "高亮", "themeDarkHeader": "暗色", "themeCopperHeader": "铜色", "notificationsHeader": "通知", "securityHeader": "安全设置", "authenticationMethodHeader": "验证方式", "authenticationPINHeader": "识别码", "authenticationBiometricsHeader": "生物验证", "authenticateOnLaunchHeader": "启动时要求验证", "yesHeader": "是", "noHeader": "否", "automaticallyLockHeader": "自动锁定", "lockInstantHeader": "立刻", "lock1Header": "%1 分钟后", "lock5Header": "%1 分钟后", "lock15Header": "%1 分钟后", "lock30Header": "%1 分钟后", "lock60Header": "%1 分钟后", "daemonHeader": "后台设置", "defaultHeader": "默认", "manageHeader": "管理", "contactsHeader": "联系人", "backUpPrivateKeyHeader": "备份私钥", "viewPublicKeyHeader": "查看公钥", "shareHeader": "分享Blaise", "logoutHeader": "登出", "privacyPolicyHeader": "隐私政策", "changeAccountNameHeader": "更改账号名", "transferAccountHeader": "转让账号", "listAccountForSaleHeader": "上架出售账号", "createPrivateSaleHeader": "对私出售该账号", "delistFromSaleHeader": "取消出售", "getAccountSheetHeader": "获取一个账号", "freeAccountSheetHeader": "免费账号", "buyAccountSheetHeader": "购买账号", "sendSheetHeader": "发送", "sendingSheetHeader": "正在发送", "sentSheetHeader": "已发送", "requestSheetHeader": "请求", "changeNameSheetHeader": "更名", "changingNameSheetHeader": "正在更改", "changedNameSheetHeader": "已更改", "transferSheetHeader": "转让", "transferringSheetHeader": "正在转让", "transferredSheetHeader": "已转让", "listForSaleSheetHeader": "上架出售", "listingForSaleSheetHeader": "上架明细", "listedForSaleSheetHeader": "成功上架", "createPrivateSaleSheetHeader": "对私出售", "creatingPrivateSaleSheetHeader": "创建", "createdPrivateSaleSheetHeader": "成功创建", "delistingSheetHeader": "下架", "delistedSheetHeader": "成功下架", "addContactSheetHeader": "添加联系人", "contactSheetHeader": "联系人", "publicKeySheetHeader": "公钥", "privateKeySheetHeader": "私钥", "backUpSheetHeader": "备份", "encryptSheetHeader": "加密", "changeDaemonSheetHeader": "更改后台", "securityFirstHeader": "安全第一", "newPrivateKeyHeader": "新建私钥", "importPrivateKeyHeader": "导入私钥", "decryptAndImportKeyHeader": "解密并导入", "backUpKeyHeader": "备份您的私钥!", "lockedHeader": "已锁定", "privateKeyTextFieldHeader": "私钥", "passwordTextFieldHeader": "密码", "newPasswordTextFieldHeader": "新密码", "confirmPasswordTextFieldHeader": "确认密码", "confirmTextFieldHeader": "确认", "countryCodeTextFieldHeader": "国家代码", "phoneNumberTextFieldHeader": "手机号码", "confirmationCodeTextFieldHeader": "验证码", "accountTextFieldHeader": "账号", "addressTextFieldHeader": "地址", "contactNameTextFieldHeader": "联系人名字", "amountTextFieldHeader": "金额", "payloadTextFieldHeader": "负载", "nameTextFieldHeader": "名字", "newAccountNameTextFieldHeader": "新账号名", "publicKeyTextFieldHeader": "公钥", "priceTextFieldHeader": "价格", "receivingAccountTextFieldHeader": "收款账号", "durationTextFieldHeader": "时长", "feeTextFieldHeader": "手续费", "otherOperationsHeader": "其它操作", "warningHeader": "警告", "areYouSureHeader": "您确定吗?", "addFeeHeader": "添加手续费", "keyTypeNotSupportedHeader": "不被支持的私钥", "accountToSendFromHeader": "发送账号", "sentHeader": "已发送", "receivedHeader": "已收到", "nameChangedHeader": "更名成功", "listedForSaleHeader": "上架出售", "privateSaleHeader": "对私出售", "delistedFromSaleHeader": "已下架", "delistedHeader": "已下架", "undefinedHeader": "未定义操作", "transferredHeader": "已转让", "balanceHeader": "余额", "totalBalanceHeader": "总余额", "accountBalanceHeader": "账号余额", "accountsHeader": "账号列表", "operationsHeader": "操作记录", "encryptThePayloadHeader": "对负载加密", "encryptPayloadHeader": "对负载加密", "forSaleHeader": "代售", "borrowedHeader": "借用中", "borrowedTransferredHeader": "处理中的转让", "borrowedAccountHeader": "借用的账号", "feeColonHeader": "手续费:", "pendingHeader": "处理中", "onHeader": "启用", "offHeader": "禁用", "priceRequiredError": "请输入价格", "amountRequiredError": "请输入金额", "nameRequiredError": "请输入名字", "zeroPriceError": "价格不能为0", "zeroAmountError": "金额不能为0", "invalidAccountNameError": "无效的账号名", "invalidReceivingAccountError": "无效的收款账号", "invalidPublicKeyError": "无效的公钥", "invalidPrivateKeyError": "无效的私钥", "invalidAddressError": "无效的地址", "invalidAccountError": "无效的账号", "invalidDestinationError": "无效的目标", "insufficientBalanceError": "余额不足", "threeCharacterNameError": "账号名最少要三个字符", "contactDoesntExistError": "联系人不存在", "contactAlreadyExistsError": "联系人已存在", "cantSendToYourselfError": "无法发送给自己", "somethingWentWrongError": "出错了,请稍后再试", "failedToEncryptPayloadError": "未能成功加密负载", "emptyPasswordError": "密码不能为空", "noMatchPasswordError": "密码不匹配", "invalidPasswordError": "密码无效", "didNotGetResponseError": "服务器无响应", "noContactsToExportError": "没有联系人可以导出", "noContactsToImportError": "没有联系人可以导入", "failedToImportContactsError": "导入联系人失败", "blockchainRewardOPDetails": "区块奖励 (%1)", "transactionOPDetails": "交易 (%1)", "changeKeyOPDetails": "更改密钥 (%1)", "recoverFundsOPDetails": "恢复资金 (%1)", "listAccountForSaleOPDetails": "上架出售账号 (%1)", "delistAccountOPDetails": "下架账号 (%1)", "buyAccountOPDetails": "购买账号 (%1)", "changeKeySignedOPDetails": "签署更改密钥 (%1)", "changeAccountInfoOPDetails": "更改账号信息 (%1)", "multioperationOPDetails": "多重操作 (%1)", "unknownOPDetails": "未知操作 (%1)", "sendingAccountOPDetails": "发送方账号", "receivingAccountOPDetails": "收款方账号", "changingAccountOPDetails": "更改账号", "sendAmountOPDetails": "发送金额", "payloadOPDetails": "负载", "newPublicKeyOPDetails": "新的公钥", "newNameOPDetails": "新的账号名", "sellerAccountOPDetails": "卖方账号", "accountPriceOPDetails": "账号价格", "lockedUntilBlockOPDetails": "解锁区块", "blockOPDetails": "区块", "optxtOPDetails": "操作说明", "timeOPDetails": "时间戳", "naOPDetails": "N/A", "ophashOPDetails": "操作哈希", "optypeOPDetails": "操作类型", "maturationOPDetails": "成熟区块", "nullOPDetails": "空", "feeOPDetails": "手续费", "opblockOPDetails": "区块的第几笔操作", "noperationOPDetails": "操作数", "accountOPDetails": "账号", "signeraccountOPDetails": "签署账号", "noResultsFound": "未找到结果", "getAccountThirdParagraphAlternative": "2- 您可以花费%1 Pascal (%2)购买一个账号。 每个用户只允许购买一个账号。", "getAccountThirdParagraphAlternative2": "2- 您可以花费%1 Pascal (%2)购买一个账号。 您最多可以购买%3个账号。", "receiveAccountButton": "接收账号", "receiveAnAccountButton": "接收一个账号" } ================================================ FILE: lib/l10n/messages_all.dart ================================================ // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart // This is a library that looks up messages for specific locales by // delegating to the appropriate library. // Ignore issues from commonly used lints in this file. // ignore_for_file:implementation_imports, file_names, unnecessary_new // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering // ignore_for_file:argument_type_not_assignable, invalid_assignment // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases // ignore_for_file:comment_references import 'dart:async'; import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; import 'package:intl/src/intl_helpers.dart'; import 'messages_ar.dart' as messages_ar; import 'messages_ca.dart' as messages_ca; import 'messages_de.dart' as messages_de; import 'messages_en.dart' as messages_en; import 'messages_es.dart' as messages_es; import 'messages_messages.dart' as messages_messages; import 'messages_tr.dart' as messages_tr; import 'messages_zh-Hans.dart' as messages_zh_hans; typedef Future LibraryLoader(); Map _deferredLibraries = { 'ar': () => new Future.value(null), 'ca': () => new Future.value(null), 'de': () => new Future.value(null), 'en': () => new Future.value(null), 'es': () => new Future.value(null), 'messages': () => new Future.value(null), 'tr': () => new Future.value(null), 'zh_Hans': () => new Future.value(null), }; MessageLookupByLibrary _findExact(String localeName) { switch (localeName) { case 'ar': return messages_ar.messages; case 'ca': return messages_ca.messages; case 'de': return messages_de.messages; case 'en': return messages_en.messages; case 'es': return messages_es.messages; case 'messages': return messages_messages.messages; case 'tr': return messages_tr.messages; case 'zh_Hans': return messages_zh_hans.messages; default: return null; } } /// User programs should call this before using [localeName] for messages. Future initializeMessages(String localeName) async { var availableLocale = Intl.verifiedLocale( localeName, (locale) => _deferredLibraries[locale] != null, onFailure: (_) => null); if (availableLocale == null) { return new Future.value(false); } var lib = _deferredLibraries[availableLocale]; await (lib == null ? new Future.value(false) : lib()); initializeInternalMessageLookup(() => new CompositeMessageLookup()); messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); return new Future.value(true); } bool _messagesExistFor(String locale) { try { return _findExact(locale) != null; } catch (e) { return false; } } MessageLookupByLibrary _findGeneratedMessagesFor(String locale) { var actualLocale = Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); if (actualLocale == null) return null; return _findExact(actualLocale); } ================================================ FILE: lib/l10n/messages_ar.dart ================================================ // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart // This is a library that provides messages for a ar locale. All the // messages from the main program should be duplicated here with the same // function name. // Ignore issues from commonly used lints in this file. // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases // ignore_for_file:unused_import, file_names import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; final messages = new MessageLookup(); typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'ar'; final messages = _notInlinedMessages(_notInlinedMessages); static _notInlinedMessages(_) => { "accountBalanceHeader" : MessageLookupByLibrary.simpleMessage("رصيد الحساب"), "accountOPDetails" : MessageLookupByLibrary.simpleMessage("حساب"), "accountPriceOPDetails" : MessageLookupByLibrary.simpleMessage("سعر الحساب"), "accountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("حساب"), "accountToSendFromHeader" : MessageLookupByLibrary.simpleMessage("حساب لإرسال من"), "accountsHeader" : MessageLookupByLibrary.simpleMessage("الحسابات"), "addADurationButton" : MessageLookupByLibrary.simpleMessage("+ أضف مدة"), "addAPayloadButton" : MessageLookupByLibrary.simpleMessage("+ أضف ملاحظة (Payload)"), "addContactButton" : MessageLookupByLibrary.simpleMessage("أضف جهة إتصال"), "addContactSheetHeader" : MessageLookupByLibrary.simpleMessage("إضافة جهة إتصال"), "addFeeHeader" : MessageLookupByLibrary.simpleMessage("إضافة رسوم"), "addToContactsButton" : MessageLookupByLibrary.simpleMessage("أضف إلى جهات الإتصال"), "addedToContactsParagraph" : MessageLookupByLibrary.simpleMessage("%1 تم إضافته إلى جهات الإتصال"), "addressTextFieldHeader" : MessageLookupByLibrary.simpleMessage("رقم الحساب"), "amountRequiredError" : MessageLookupByLibrary.simpleMessage("المبلغ مطلوب"), "amountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("المبلغ"), "areYouSureHeader" : MessageLookupByLibrary.simpleMessage("هل أنت متأكد؟"), "authenticateOnLaunchHeader" : MessageLookupByLibrary.simpleMessage("المصادقة عند التشغيل"), "authenticateToBackUpParagraph" : MessageLookupByLibrary.simpleMessage("المصادقة لإجراء نسخ احتياطي للمفتاح الخاص"), "authenticateToChangeNameParagraph" : MessageLookupByLibrary.simpleMessage("المصادقة لتغيير اسم الحساب إلى \"%1\""), "authenticateToCreatePrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("المصادقة لإنشاء بيع خاص"), "authenticateToDelistParagraph" : MessageLookupByLibrary.simpleMessage("المصادقة لحذف الحساب من البيع"), "authenticateToListForSaleParagraph" : MessageLookupByLibrary.simpleMessage("المصادقة لإدراج الحساب للبيع"), "authenticateToSendParagraph" : MessageLookupByLibrary.simpleMessage("المصادقة لإرسال %1 باسكال"), "authenticateToTransferParagraph" : MessageLookupByLibrary.simpleMessage("المصادقة لنقل الحساب"), "authenticateToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("المصادقة لإلغاء القفل"), "authenticationBiometricsHeader" : MessageLookupByLibrary.simpleMessage("البصمة"), "authenticationMethodHeader" : MessageLookupByLibrary.simpleMessage("طريقة المصادقة"), "authenticationPINHeader" : MessageLookupByLibrary.simpleMessage("رمز المرور"), "automaticallyLockHeader" : MessageLookupByLibrary.simpleMessage("قفل تلقائي"), "backUpKeyHeader" : MessageLookupByLibrary.simpleMessage("نسخ إحتياطي لمفاتيحك"), "backUpPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("نسخ إحتياطي للمفتاح الخاص"), "backUpSheetHeader" : MessageLookupByLibrary.simpleMessage("نسخ إحتياطي"), "backupEncryptedKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("أدناه هو مفتاحك الخاص المشفر. محمي بكلمة مرور. يمكنك حفظه بأمان على مدير كلمات المرور (باسورد مانجر) لراحتك."), "backupEncryptedKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("نظرًا لأنه مشفر بكلمة مرور، إذا فقدت أو نسيت كلمة المرور، فلن تتمكن من فك تشفيره والوصول إلى أموالك."), "backupKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("لديك خياران لإجراء نسخ احتياطي للمفتاح الخاص:"), "backupKeyFourthParagraph" : MessageLookupByLibrary.simpleMessage("نوصي بحفظ النسخة غير المشفرة في وضع عدم الاتصال بالإنترنت (أوفلاين)، عن طريق كتابتها على ورقة، و يمكنك حفظ النسخة المشفرة على مدير كلمات المرور (باسورد مانجر) لراحتك."), "backupKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("1. مشفر، مما يعني أنه محمي بكلمة مرور."), "backupKeyThirdParagraph" : MessageLookupByLibrary.simpleMessage("2. غير مشفر، مما يعني أنه خام وغير محمي بكلمة مرور."), "backupUnencryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("يوجد أدناه مفتاحك الخاص غير المشفر. ليس محمي بكلمة مرور، مما يعني أنه من الأهمية حفظه في مكان آمن وغير متصل بالإنترنت. نوصي بكتابته على ورقة."), "balanceHeader" : MessageLookupByLibrary.simpleMessage("الرصيد"), "blockOPDetails" : MessageLookupByLibrary.simpleMessage("كتلة"), "blockchainRewardOPDetails" : MessageLookupByLibrary.simpleMessage("مكافأة البلوكشين (%1)"), "borrowAccountParagraph" : MessageLookupByLibrary.simpleMessage("لشراء حساب، أولا سوف نعطيك واحد (على سبيل الإعارة)، إذا أرسلت على الأقل %1 باسكال(%2) إلى هذا الحساب في خلال %3 أيام, الحساب سوف يكون ملكك و %1 باسكال سوف تخصم من رصيدك تلقائياً.\nغير ذلك، سوف نسترد الحساب بعد نهاية %3 أيام ولن يكون ملكك.\nلذلك من المستحسن أولا إرسال مبلغ صغير قبل أن تمتلك الحساب."), "borrowAnAccountButton" : MessageLookupByLibrary.simpleMessage("استعر حساباً"), "borrowStarted" : MessageLookupByLibrary.simpleMessage("بدأت عملية الشراء لـ %1"), "borrowedAccountHeader" : MessageLookupByLibrary.simpleMessage("حساب معار"), "borrowedAccountPaidParagraph" : MessageLookupByLibrary.simpleMessage("تم شراء الحساب!\nنقله إليك قيد المعالجة حاليًا. تستغرق هذه العملية عادةً 15 دقيقة, في بعض الحالات قد تستغرق وقتا أطول قليلا."), "borrowedAccountParagraph" : MessageLookupByLibrary.simpleMessage("هذا هو حساب معار إليك.\nإذا أرسلت على الأقل %1 باسكال إلى هذا الحساب في خلال %2 أيام, %3 ساعات, و %4 دقائق, سوف يكون ملكك."), "borrowedHeader" : MessageLookupByLibrary.simpleMessage("حساب معار"), "borrowedTransferredHeader" : MessageLookupByLibrary.simpleMessage("جارٍ النقل"), "buyAccountOPDetails" : MessageLookupByLibrary.simpleMessage("شراء حساب (%1)"), "buyAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("شراء حساب"), "buyAnAccountButton" : MessageLookupByLibrary.simpleMessage("إشتري حساب"), "cancelButton" : MessageLookupByLibrary.simpleMessage("إلغاء"), "cantSendToYourselfError" : MessageLookupByLibrary.simpleMessage("لا يمكن أن ترسل إلى نفسك"), "changeAccountInfoOPDetails" : MessageLookupByLibrary.simpleMessage("تغيير معلومات الحساب (%1)"), "changeAccountNameHeader" : MessageLookupByLibrary.simpleMessage("تغيير اسم الحساب"), "changeDaemonSheetHeader" : MessageLookupByLibrary.simpleMessage("تغيير Daemon"), "changeKeyOPDetails" : MessageLookupByLibrary.simpleMessage("تغيير المفتاح (%1)"), "changeKeySignedOPDetails" : MessageLookupByLibrary.simpleMessage("تغيير مفتاح التوقيع (%1)"), "changeNameButton" : MessageLookupByLibrary.simpleMessage("تغيير الإسم"), "changeNameParagraph" : MessageLookupByLibrary.simpleMessage("أدخل اسمًا أدناه لتغيير اسم الحساب."), "changeNameSheetHeader" : MessageLookupByLibrary.simpleMessage("تغيير الإسم"), "changedNameParagraph" : MessageLookupByLibrary.simpleMessage("تم تغيير اسم حسابك بنجاح."), "changedNameSheetHeader" : MessageLookupByLibrary.simpleMessage("تم التغيير"), "changingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("تغيير الحساب"), "changingNameParagraph" : MessageLookupByLibrary.simpleMessage("تأكيد اسم حسابك الجديد للمتابعة."), "changingNameSheetHeader" : MessageLookupByLibrary.simpleMessage("جارٍ التغيير"), "checkOutBlaiseParagraph" : MessageLookupByLibrary.simpleMessage("أنظر، بليز! محفظة \"باسكال\" بسيطة وأنيقة وآمنة لنظامي أندرويد و آي أو إس: https://blaisewallet.com"), "closeButton" : MessageLookupByLibrary.simpleMessage("إغلاق"), "confirmButton" : MessageLookupByLibrary.simpleMessage("تأكيد"), "confirmPINParagraph" : MessageLookupByLibrary.simpleMessage("تأكيد رمز المرور"), "confirmPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("تأكيد كلمة المرور"), "confirmTextFieldHeader" : MessageLookupByLibrary.simpleMessage("تأكيد"), "confirmationCodeError" : MessageLookupByLibrary.simpleMessage("فشل التحقق، تأكد من كتابة الرمز المرسل إليك بشكل صحيح"), "confirmationCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("كود التفعيل"), "connectingHeader" : MessageLookupByLibrary.simpleMessage("جارٍ الإتصال"), "contactAlreadyExistsError" : MessageLookupByLibrary.simpleMessage("جهة الاتصال موجود بالفعل"), "contactDoesntExistError" : MessageLookupByLibrary.simpleMessage("جهة الاتصال غير موجودة"), "contactNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("أسم جهة الإتصال"), "contactSheetHeader" : MessageLookupByLibrary.simpleMessage("جهة إتصال"), "contactsHeader" : MessageLookupByLibrary.simpleMessage("جهات الإتصال"), "copiedAddressButton" : MessageLookupByLibrary.simpleMessage("تم نسخ الحساب"), "copiedButton" : MessageLookupByLibrary.simpleMessage("تم النسخ"), "copyAddressButton" : MessageLookupByLibrary.simpleMessage("نسخ الحساب"), "copyButton" : MessageLookupByLibrary.simpleMessage("نسخ"), "copyEncryptedKeyButton" : MessageLookupByLibrary.simpleMessage("نسخ مفتاح مشفر"), "copyKeyButton" : MessageLookupByLibrary.simpleMessage("نسخ مفتاح"), "copyPublicKeyButton" : MessageLookupByLibrary.simpleMessage("نسخ المفتاح عام"), "copyUnencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("نسخ مفتاح غير مشفر"), "countryCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("كود البلد"), "createPINParagraph" : MessageLookupByLibrary.simpleMessage("إنشاء 6 أرقام رمز مرور"), "createPrivateSaleButton" : MessageLookupByLibrary.simpleMessage("إنشاء بيع خاص"), "createPrivateSaleHeader" : MessageLookupByLibrary.simpleMessage("إدارج الحساب للبيع الخاص"), "createPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("أدخل السعر, الحساب الذي سيتلقى الدفع, والمفتاح العام أدناه لإنشاء بيع خاص لهذا الحساب."), "createPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("بيع خاص"), "createdPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("تم إنشاء البيع الخاص بنجاح. سنخبرك إذا تم شراؤه."), "createdPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("تم الإنشاء"), "creatingPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("تأكد من المعلومات أدناه."), "creatingPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("إنشاء"), "currencyHeader" : MessageLookupByLibrary.simpleMessage("العملة"), "decryptAndImportKeyHeader" : MessageLookupByLibrary.simpleMessage("فك تشفير & إستيراد"), "defaultHeader" : MessageLookupByLibrary.simpleMessage("إفتراضي"), "deletePrivateKeyAndLogoutButton" : MessageLookupByLibrary.simpleMessage("احذف المفتاح الخاص\nوتسجيل خروج"), "delistAccountOPDetails" : MessageLookupByLibrary.simpleMessage("إلغاء إدراج الحساب (%1)"), "delistFromSaleHeader" : MessageLookupByLibrary.simpleMessage("حذف الحساب من البيع"), "delistFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("تأكد من رغبتك في حذف هذا الحساب من البيع."), "delistedFromSaleHeader" : MessageLookupByLibrary.simpleMessage("تم حذفه من البيع"), "delistedFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("تم حذف حسابك من البيع بنجاح."), "delistedHeader" : MessageLookupByLibrary.simpleMessage("تم حذفه"), "delistedSheetHeader" : MessageLookupByLibrary.simpleMessage("تم الحذف"), "delistingSheetHeader" : MessageLookupByLibrary.simpleMessage("جارٍ الحذف"), "didNotGetResponseError" : MessageLookupByLibrary.simpleMessage("لم نحصل على استجابة من الخادم\n"), "durationTextFieldHeader" : MessageLookupByLibrary.simpleMessage("المدة"), "emptyPasswordError" : MessageLookupByLibrary.simpleMessage("كلمة المرور لا يمكن أن تكون فارغة"), "encryptButton" : MessageLookupByLibrary.simpleMessage("تشفير"), "encryptKeyParagraph" : MessageLookupByLibrary.simpleMessage("قم بإنشاء كلمة مرور جديدة لتشفير مفتاحك الخاص."), "encryptPayloadHeader" : MessageLookupByLibrary.simpleMessage("تشفير الملاحظة (Payload)"), "encryptSheetHeader" : MessageLookupByLibrary.simpleMessage("تشفير"), "encryptThePayloadHeader" : MessageLookupByLibrary.simpleMessage("تشفير الملاحظة (Payload)"), "encryptedKeyButton" : MessageLookupByLibrary.simpleMessage("مفتاح مشفر"), "enterConfirmationCodeParagraph" : MessageLookupByLibrary.simpleMessage("لقد أرسلنا لك رمز التأكيد، يرجى إدخاله أدناه."), "enterPINParagraph" : MessageLookupByLibrary.simpleMessage("أدخل رمز المرور"), "enterPINToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("أدخل رمز المرور لإلغاء القفل"), "enterPhoneNumberParagraph" : MessageLookupByLibrary.simpleMessage("أدخل رقم تليفونك أدناه."), "failedToEncryptPayloadError" : MessageLookupByLibrary.simpleMessage("فشل في تشفير الملاحظة (Payload)"), "failedToImportContactsError" : MessageLookupByLibrary.simpleMessage("فشل في استيراد جهات الاتصال"), "failedToRemoveFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("فشل في إزالة %1 من جهات الإتصال"), "feeColonHeader" : MessageLookupByLibrary.simpleMessage("رسوم:"), "feeConfirmAmountParagraph" : MessageLookupByLibrary.simpleMessage("يرجى تأكيد إضافة رسوم %1 باسكال لهذه العملية للمتابعة.\n."), "feeOPDetails" : MessageLookupByLibrary.simpleMessage("رسوم"), "feeRequiredParagraph" : MessageLookupByLibrary.simpleMessage("هذه المعاملة تتطلب رسوم."), "feeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("رسوم"), "forSaleHeader" : MessageLookupByLibrary.simpleMessage("للبيع"), "freeAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("حساب مجاني"), "freepasaComplete" : MessageLookupByLibrary.simpleMessage("تم بنجاح، حسابك الجديد سيظهر بعد 1 تأكيد من الشبكة"), "getAFreeAccountButton" : MessageLookupByLibrary.simpleMessage("أحصل على حساب بالمجان"), "getAccountFirstParagraph" : MessageLookupByLibrary.simpleMessage("هناك خيارين للحصول على حسابك الأول:"), "getAccountSecondParagraph" : MessageLookupByLibrary.simpleMessage("1. يمكنك الحصول على حساب مجاني بإستخدام رقم تليفونك، فقط حساب واحد لكل رقم تليفون"), "getAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("الحصول على حساب"), "getAccountThirdParagraph" : MessageLookupByLibrary.simpleMessage("2. يمكنك شراء حساب/حسابات مقابل %1 باسكال (%2)."), "getAccountThirdParagraphAlternative" : MessageLookupByLibrary.simpleMessage("2. يمكنك شراء حساب واحد مقابل %1 باسكال (%2). مسموح فقط بـ \"حساب\" واحد لكل مستخدم"), "getAccountThirdParagraphAlternative2" : MessageLookupByLibrary.simpleMessage("2. يمكنك شراء \"حساب\" مقابل %1 باسكال (%2). يمكنك شراء حتى %3 حساب."), "getAnAccountButton" : MessageLookupByLibrary.simpleMessage("أحصل على حساب"), "goBackButton" : MessageLookupByLibrary.simpleMessage("رجوع"), "gotItButton" : MessageLookupByLibrary.simpleMessage("حصلت عليه!"), "hideButton" : MessageLookupByLibrary.simpleMessage("إخفي"), "iHaveBackedItUpButton" : MessageLookupByLibrary.simpleMessage("قمت بالنسخ الإحتياطي"), "importButton" : MessageLookupByLibrary.simpleMessage("إستيراد"), "importPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("استيراد مفتاح خاص"), "importPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("إستيراد مفتاح خاص"), "importPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("أدخل مفتاحك الخاص أدناه."), "insufficientBalanceError" : MessageLookupByLibrary.simpleMessage("رصيد غير كافي"), "invalidAccountError" : MessageLookupByLibrary.simpleMessage("حساب غير صحيح"), "invalidAccountNameError" : MessageLookupByLibrary.simpleMessage("اسم حساب غير صحيح"), "invalidAddressError" : MessageLookupByLibrary.simpleMessage("حساب غير صحيح"), "invalidDestinationError" : MessageLookupByLibrary.simpleMessage("وجهة غير صالحة"), "invalidPINParagraph" : MessageLookupByLibrary.simpleMessage("رمز المرور غير صحيح"), "invalidPasswordError" : MessageLookupByLibrary.simpleMessage("كلمة مرور خاطئة"), "invalidPhoneNumberParagraph" : MessageLookupByLibrary.simpleMessage("رقم التليفون غير صالح"), "invalidPrivateKeyError" : MessageLookupByLibrary.simpleMessage("مفتاح خاص غير صالح"), "invalidPublicKeyError" : MessageLookupByLibrary.simpleMessage("مفتاح عام غير صالح"), "invalidReceivingAccountError" : MessageLookupByLibrary.simpleMessage("حساب المستلم غير صحيح"), "keyCopiedButton" : MessageLookupByLibrary.simpleMessage("تم نسخ المفتاح"), "keyTypeNotSupportedHeader" : MessageLookupByLibrary.simpleMessage("المفتاح غير مدعوم"), "keyTypeNotSupportedParagraph" : MessageLookupByLibrary.simpleMessage("لا تدعم محفظة بليز هذا النوع من المفاتيح الخاصة. يمكنك إنشاء مفتاح خاص جديد ونقل حساباتك إليه باستخدام محفظة أخرى."), "languageColonHeader" : MessageLookupByLibrary.simpleMessage("اللغة:"), "languageHeader" : MessageLookupByLibrary.simpleMessage("اللغة"), "listAccountForSaleHeader" : MessageLookupByLibrary.simpleMessage("إدراج الحساب للبيع"), "listAccountForSaleOPDetails" : MessageLookupByLibrary.simpleMessage("إدراج الحساب للبيع (%1)"), "listForSaleButton" : MessageLookupByLibrary.simpleMessage("إدراج للبيع"), "listForSaleParagraph" : MessageLookupByLibrary.simpleMessage("لعرض هذا الحساب للبيع، أدخل سعرًا و\"الحساب\" الذي سيتلقى الدفع."), "listForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("إدراج للبيع"), "listedForSaleHeader" : MessageLookupByLibrary.simpleMessage("تم إدراجه للبيع"), "listedForSaleParagraph" : MessageLookupByLibrary.simpleMessage("تم إدراج حسابك للبيع بنجاح. سنخبرك إذا ما قام شخص ما بشرئه."), "listedForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("تم الإدراج"), "listingForSaleParagraph" : MessageLookupByLibrary.simpleMessage("برجاء تأكيد السعر والحساب الذي سيتلقى الدفع."), "listingForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("جارٍ الإدراج"), "liveSupportButton" : MessageLookupByLibrary.simpleMessage("مساعدة؟"), "lock15Header" : MessageLookupByLibrary.simpleMessage("بعد %1 دقائق"), "lock1Header" : MessageLookupByLibrary.simpleMessage("بعد %1 دقيقة"), "lock30Header" : MessageLookupByLibrary.simpleMessage("بعد %1 دقائق"), "lock5Header" : MessageLookupByLibrary.simpleMessage("بعد %1 دقائق"), "lock60Header" : MessageLookupByLibrary.simpleMessage("بعد %1 دقائق"), "lockInstantHeader" : MessageLookupByLibrary.simpleMessage("فوراً"), "lockedHeader" : MessageLookupByLibrary.simpleMessage("مقفل"), "lockedUntilBlockOPDetails" : MessageLookupByLibrary.simpleMessage("مغلق حتى الكتلة"), "logoutFirstDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("سيؤدي تسجيل الخروج إلى إزالة المفتاح الخاص وكافة البيانات المتعلقة بمحفظتك من على هذا الجهاز. إذا لم يتم عمل نسخة احتياطية من المفتاح الخاص، فلن تتمكن من الوصول إلى أموالك مرة أخرى. إذا قمت بعمل نسخة إحتياطية من مفتاح الخاص، فلا داعي للقلق."), "logoutHeader" : MessageLookupByLibrary.simpleMessage("تسجيل خروج"), "logoutSecondDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("هل أنت متأكد من أنك قمت بعمل نسخة احتياطية من المفتاح الخاص؟ إذا قمت بعمل نسخة إحتياطية من مفتاحك الخاص، فلا داعي للقلق بشأنه."), "looksLikeEncryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("يبدو أن هذا المفتاح الخاص مشفراً، يرجى إدخال كلمة المرور لفك تشفيره واستيراده."), "manageHeader" : MessageLookupByLibrary.simpleMessage("إدارة"), "manyFailedAttemptsParagraph" : MessageLookupByLibrary.simpleMessage("محاولات كثيرة فاشلة لإلغاء القفل"), "multioperationOPDetails" : MessageLookupByLibrary.simpleMessage("عملية متعددة (%1)"), "naOPDetails" : MessageLookupByLibrary.simpleMessage("غير موجود"), "nameChangedHeader" : MessageLookupByLibrary.simpleMessage("تم تغيير الإسم"), "nameRequiredError" : MessageLookupByLibrary.simpleMessage("الإسم مطلوب"), "nameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("إسم"), "newAccountNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("إسم الحساب الجديد"), "newAccountParagraph" : MessageLookupByLibrary.simpleMessage("هذا هو حسابك الجديد.\nبمجرد أن تستقبل باسكال, سوف تظهر العمليات كما موضح أدناه."), "newKeyBackUpConfirmParagraph" : MessageLookupByLibrary.simpleMessage("هل متأكد من أنك قمت بعمل نسخة إحتياطية من مفتاحك الخاص في مكان أمن؟"), "newKeySecurityParagraph" : MessageLookupByLibrary.simpleMessage("في الشاشة التالية، سوف ترى مفتاحك الخاص الجديد، أنه يمثل كل شئ، بدونه أنت لا تملك أموالك، لذلك من الضروري أن تحفظه في مكان أمن، ولا تشاركة مع أحد."), "newNameOPDetails" : MessageLookupByLibrary.simpleMessage("اسم جديد"), "newPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("كلمة مرور جديدة"), "newPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("مفتاح خاص جديد"), "newPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("مفتاح خاص جديد"), "newPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("يوجد أدناه المفتاح الخاص لمحفظتك الجديدة، من الضروري أن تحفظه في مكان أمن ولا تشاركه مع أحد، ولا تقم بأخذ لقطة للشاشة أو تركه في ملف على جهازك، يستحسن أن تكتبه بيدك على ورقة وتحفظة في مكان أمن."), "newPublicKeyOPDetails" : MessageLookupByLibrary.simpleMessage("مفتاح عام جديد"), "newWalletGreetingParagraph" : MessageLookupByLibrary.simpleMessage("مرحباً بك في محفظة بليز.\nيمكنك البدء بالحصول على حساب جديد."), "nextButton" : MessageLookupByLibrary.simpleMessage("التالي"), "noContactsToExportError" : MessageLookupByLibrary.simpleMessage("لا جهات اتصال للتصدير"), "noContactsToImportError" : MessageLookupByLibrary.simpleMessage("لا يوجد جهات اتصال لاستيرادها"), "noGoBackButton" : MessageLookupByLibrary.simpleMessage("لا، رجوع"), "noHeader" : MessageLookupByLibrary.simpleMessage("لا"), "noMatchPINParagraph" : MessageLookupByLibrary.simpleMessage("رمز المرور غير متطابق"), "noMatchPasswordError" : MessageLookupByLibrary.simpleMessage("كلمات المرور غير متطابقة"), "noResultsFound" : MessageLookupByLibrary.simpleMessage("لا توجد نتائج"), "noperationOPDetails" : MessageLookupByLibrary.simpleMessage("المعاملات المرسلة"), "notificationsHeader" : MessageLookupByLibrary.simpleMessage("الإشعارات"), "offHeader" : MessageLookupByLibrary.simpleMessage("لا تعمل"), "okayButton" : MessageLookupByLibrary.simpleMessage("موافق"), "okayGoBackButton" : MessageLookupByLibrary.simpleMessage("موافق، رجوع"), "onHeader" : MessageLookupByLibrary.simpleMessage("تعمل"), "openInExplorerButton" : MessageLookupByLibrary.simpleMessage("افتح في المستكشف"), "operationDetailsButton" : MessageLookupByLibrary.simpleMessage("تفاصيل العملية"), "operationsHeader" : MessageLookupByLibrary.simpleMessage("العمليات"), "ophashOPDetails" : MessageLookupByLibrary.simpleMessage("معرف المعاملة"), "otherOperationsHeader" : MessageLookupByLibrary.simpleMessage("معاملات أخرى"), "passwordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("كلمه مرور"), "payloadOPDetails" : MessageLookupByLibrary.simpleMessage("ملاحظة (Payload)"), "payloadTextFieldHeader" : MessageLookupByLibrary.simpleMessage("ملاحظة (Payload)"), "pendingHeader" : MessageLookupByLibrary.simpleMessage("قيد الانتظار"), "phoneNumberTextFieldHeader" : MessageLookupByLibrary.simpleMessage("رقم التليفون"), "preferencesHeader" : MessageLookupByLibrary.simpleMessage("تفضيلات"), "priceRequiredError" : MessageLookupByLibrary.simpleMessage("السعر مطلوب"), "priceTextFieldHeader" : MessageLookupByLibrary.simpleMessage("السعر"), "privacyPolicyHeader" : MessageLookupByLibrary.simpleMessage("سياسة الخصوصية"), "privateKeySheetHeader" : MessageLookupByLibrary.simpleMessage("مفتاح خاص"), "privateKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("مفتاح خاص"), "privateSaleHeader" : MessageLookupByLibrary.simpleMessage("بيع خاص"), "publicKeyParagraph" : MessageLookupByLibrary.simpleMessage("أدناه هو المفتاح العام الخاص بك. كما يوحي الاسم، الغرض منه هو المشاركة بشكل عام لإستقبال الحسابات عليه وانتقال ملكيتها للمفتاح الخاص التابع لهذا المفتاح العام."), "publicKeySheetHeader" : MessageLookupByLibrary.simpleMessage("مفتاح عام"), "publicKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("مفتاح عام"), "receiveAccountButton" : MessageLookupByLibrary.simpleMessage("استلام حساب"), "receiveAnAccountButton" : MessageLookupByLibrary.simpleMessage("استلام حساب"), "receiveButton" : MessageLookupByLibrary.simpleMessage("استلام"), "receivedHeader" : MessageLookupByLibrary.simpleMessage("وأرد"), "receivingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("الحساب المستقبِل"), "receivingAccountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("الحساب متلقي الدفع"), "recoverFundsOPDetails" : MessageLookupByLibrary.simpleMessage("إسترداد الأموال (%1)"), "removedFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("تم إزالة %1 من جهات الإتصال"), "requestButton" : MessageLookupByLibrary.simpleMessage("طلب"), "requestSheetHeader" : MessageLookupByLibrary.simpleMessage("طلب"), "scanQRCodeButton" : MessageLookupByLibrary.simpleMessage("مسح رمز QR"), "searchAccountNameButton" : MessageLookupByLibrary.simpleMessage("ابحث باسم الحساب"), "searchForNameButton" : MessageLookupByLibrary.simpleMessage("بحث عن اسم"), "searchNameButton" : MessageLookupByLibrary.simpleMessage("إبحث عن إسم"), "securityFirstHeader" : MessageLookupByLibrary.simpleMessage("الأمان أولا!"), "securityHeader" : MessageLookupByLibrary.simpleMessage("الأمان"), "sellerAccountOPDetails" : MessageLookupByLibrary.simpleMessage("حساب البائع"), "sendAmountOPDetails" : MessageLookupByLibrary.simpleMessage("إرسال مبلغ"), "sendButton" : MessageLookupByLibrary.simpleMessage("إرسال"), "sendConfirmationButton" : MessageLookupByLibrary.simpleMessage("ارسل تأكيد"), "sendSheetHeader" : MessageLookupByLibrary.simpleMessage("إرسال"), "sendingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("الحساب المُرسِل"), "sendingConfirmParagraph" : MessageLookupByLibrary.simpleMessage("برجاء مراجعة تفاصيل العملية قبل الإرسال."), "sendingSheetHeader" : MessageLookupByLibrary.simpleMessage("جارٍ الإرسال"), "sentHeader" : MessageLookupByLibrary.simpleMessage("صادر"), "sentParagraph" : MessageLookupByLibrary.simpleMessage("تم إرسال العملية بنجاح."), "sentSheetHeader" : MessageLookupByLibrary.simpleMessage("تم الإرسال"), "setToDefaultButton" : MessageLookupByLibrary.simpleMessage("اجعله افتراضيا"), "settingsHeader" : MessageLookupByLibrary.simpleMessage("إعدادات"), "shareHeader" : MessageLookupByLibrary.simpleMessage("مشاركة بليز"), "showButton" : MessageLookupByLibrary.simpleMessage("اظهر"), "somethingWentWrongError" : MessageLookupByLibrary.simpleMessage("هناك شئ خاطئ، برجاء المحاولة فى وقت لاحق"), "successfullyImportedContactsParagraph" : MessageLookupByLibrary.simpleMessage("تم إستيراد %1 جهات إتصال بنجاح"), "systemDefaultHeader" : MessageLookupByLibrary.simpleMessage("إفتراضي النظام"), "themeCopperHeader" : MessageLookupByLibrary.simpleMessage("نحاسي"), "themeDarkHeader" : MessageLookupByLibrary.simpleMessage("مظلم"), "themeHeader" : MessageLookupByLibrary.simpleMessage("المظهر"), "themeLightHeader" : MessageLookupByLibrary.simpleMessage("فاتح"), "threeCharacterNameError" : MessageLookupByLibrary.simpleMessage("يجب أن يكون 3 أحرف على الأقل"), "timeOPDetails" : MessageLookupByLibrary.simpleMessage("الوقت"), "totalBalanceHeader" : MessageLookupByLibrary.simpleMessage("إجمالي الرصيد"), "transactionOPDetails" : MessageLookupByLibrary.simpleMessage("العملية (%1)"), "transferAccountHeader" : MessageLookupByLibrary.simpleMessage("نقل ملكية الحساب"), "transferButton" : MessageLookupByLibrary.simpleMessage("نقل"), "transferParagraph" : MessageLookupByLibrary.simpleMessage("أدخل \"مفتاح عام\" أدناه لنقل ملكية هذا الحساب إليه."), "transferSheetHeader" : MessageLookupByLibrary.simpleMessage("نقل"), "transferredHeader" : MessageLookupByLibrary.simpleMessage("تم نقله"), "transferredParagraph" : MessageLookupByLibrary.simpleMessage("تم نقل حسابك بنجاح إلى \"المفتاح العام\" أدناه."), "transferredSheetHeader" : MessageLookupByLibrary.simpleMessage("تم النقل"), "transferringParagraph" : MessageLookupByLibrary.simpleMessage("قم بتأكيد \"المفتاح العام\" أدناه لنقل ملكية هذا الحساب إليه."), "transferringSheetHeader" : MessageLookupByLibrary.simpleMessage("جارٍ النقل"), "unconfirmedAccountHeader" : MessageLookupByLibrary.simpleMessage("حساب (لم يتم تأكيده)"), "unconfirmedAccountParagraph" : MessageLookupByLibrary.simpleMessage("هذا حساب غير مؤكد. تم نقله إليك، ولكن بإنتظار 1 تأكيد من الشبكة قبل ان تتمكن نم إستخدامه. قد تستغرق هذه العملية 5 دقائق، وعندما تكتمل سيكون بإمكانك استخدام الحساب."), "undefinedHeader" : MessageLookupByLibrary.simpleMessage("غير محدد"), "unencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("مفتاح غير مشفر"), "uninstallDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("إذا فقدت جهازك أو الغيت تسطيب محفظة بليز، سوف تحتاج للـ \"مفتاح الخاص\" لكي تستعيد أموالك."), "unknownOPDetails" : MessageLookupByLibrary.simpleMessage("غير معروف (%1)"), "unlockButton" : MessageLookupByLibrary.simpleMessage("إلغاء قفل"), "unlockWithBiometricsButton" : MessageLookupByLibrary.simpleMessage("إلغاء قفل بالبصمة"), "unlockWithPINButton" : MessageLookupByLibrary.simpleMessage("إلغاء القفل برقم سري"), "urlChangedToParagraph" : MessageLookupByLibrary.simpleMessage("URL تم تغييره إلى %1"), "viewPublicKeyHeader" : MessageLookupByLibrary.simpleMessage("عرض المفتاح العام"), "warningHeader" : MessageLookupByLibrary.simpleMessage("تحذير"), "welcomeParagraph" : MessageLookupByLibrary.simpleMessage("مرحبا بك في محفظة بليز، للبدء، يمكنك إنشاء مفتاح خاص جديد أو إستيراد مفتاح موجود"), "yesAddFeeButton" : MessageLookupByLibrary.simpleMessage("نعم، أضف رسوم"), "yesHeader" : MessageLookupByLibrary.simpleMessage("نعم"), "yesImSureButton" : MessageLookupByLibrary.simpleMessage("نعم، أنا متأكد"), "zeroAmountError" : MessageLookupByLibrary.simpleMessage("لا يمكن أن يكون المبلغ 0"), "zeroPriceError" : MessageLookupByLibrary.simpleMessage("لا يمكن أن يكون السعر 0") }; } ================================================ FILE: lib/l10n/messages_ca.dart ================================================ // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart // This is a library that provides messages for a ca locale. All the // messages from the main program should be duplicated here with the same // function name. // Ignore issues from commonly used lints in this file. // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases // ignore_for_file:unused_import, file_names import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; final messages = new MessageLookup(); typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'ca'; final messages = _notInlinedMessages(_notInlinedMessages); static _notInlinedMessages(_) => { "accountBalanceHeader" : MessageLookupByLibrary.simpleMessage("Saldo del compte"), "accountOPDetails" : MessageLookupByLibrary.simpleMessage("compte"), "accountPriceOPDetails" : MessageLookupByLibrary.simpleMessage("Preu del compte"), "accountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Compte"), "accountToSendFromHeader" : MessageLookupByLibrary.simpleMessage("Compte per enviar des"), "accountsHeader" : MessageLookupByLibrary.simpleMessage("Comptes"), "addADurationButton" : MessageLookupByLibrary.simpleMessage("+ Afegir una Durada"), "addAPayloadButton" : MessageLookupByLibrary.simpleMessage("+ Afegir un concepte (Payload)"), "addContactButton" : MessageLookupByLibrary.simpleMessage("Afegeix contacte"), "addContactSheetHeader" : MessageLookupByLibrary.simpleMessage("Afegeix contacte"), "addFeeHeader" : MessageLookupByLibrary.simpleMessage("Afegir tarifa"), "addToContactsButton" : MessageLookupByLibrary.simpleMessage("Afegir a contactes"), "addedToContactsParagraph" : MessageLookupByLibrary.simpleMessage("% 1 afegit als contactes"), "addressTextFieldHeader" : MessageLookupByLibrary.simpleMessage("adreça"), "amountRequiredError" : MessageLookupByLibrary.simpleMessage("Quantitat obligatòria"), "amountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Import"), "areYouSureHeader" : MessageLookupByLibrary.simpleMessage("Estàs segur?"), "authenticateOnLaunchHeader" : MessageLookupByLibrary.simpleMessage("Autentica\'t a Launch"), "authenticateToBackUpParagraph" : MessageLookupByLibrary.simpleMessage("Autentiqueu la còpia de seguretat de clau privada"), "authenticateToChangeNameParagraph" : MessageLookupByLibrary.simpleMessage("Autentica per canviar el nom del compte a \"% 1\""), "authenticateToCreatePrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Autentiqueu-vos per crear venda privada"), "authenticateToDelistParagraph" : MessageLookupByLibrary.simpleMessage("Autentifica\'t per eliminar la compte des de la venda"), "authenticateToListForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Autentica la llista del compte en venda"), "authenticateToSendParagraph" : MessageLookupByLibrary.simpleMessage("Autentica per enviar% 1 Pascal"), "authenticateToTransferParagraph" : MessageLookupByLibrary.simpleMessage("Autentica la transferència del compte"), "authenticateToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("Autentica per desbloquejar Blaise"), "authenticationBiometricsHeader" : MessageLookupByLibrary.simpleMessage("Biometria"), "authenticationMethodHeader" : MessageLookupByLibrary.simpleMessage("Mètode d\'autenticació"), "authenticationPINHeader" : MessageLookupByLibrary.simpleMessage("PIN"), "automaticallyLockHeader" : MessageLookupByLibrary.simpleMessage("Bloquejar automàticament"), "backUpKeyHeader" : MessageLookupByLibrary.simpleMessage("Fes una còpia de seguretat de la clau"), "backUpPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Còpia de seguretat de clau privada"), "backUpSheetHeader" : MessageLookupByLibrary.simpleMessage("Còpia de seguretat"), "backupEncryptedKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("A continuació, trobareu la vostra clau privada xifrada. Està protegit per una contrasenya. Podeu emmagatzemar-lo de forma segura en un gestor de contrasenyes per a la vostra comoditat."), "backupEncryptedKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("Com que està xifrada amb la vostra contrasenya, si perds o oblides la teva contrasenya, no podràs desxifrar-la ni accedir als teus fons."), "backupKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("Teniu dues opcions per fer una còpia de seguretat de la vostra clau privada:"), "backupKeyFourthParagraph" : MessageLookupByLibrary.simpleMessage("Recomanem guardar fora de línia la versió no xifrada, escrivint-la en un tros de paper. Podeu emmagatzemar la versió xifrada en un gestor de contrasenyes per a la vostra comoditat."), "backupKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("1- Encriptat, el que significa que està protegit amb una contrasenya."), "backupKeyThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- No xifrat, cosa que significa que és brut i no protegit per una contrasenya."), "backupUnencryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("A continuació, trobareu la vostra clau privada sense xifrar. No està protegit per una contrasenya, cosa que significa que és important guardar-la en algun lloc segur i fora de línia. Recomanem escriure-la en un tros de paper."), "balanceHeader" : MessageLookupByLibrary.simpleMessage("Equilibri"), "blockOPDetails" : MessageLookupByLibrary.simpleMessage("bloc"), "blockchainRewardOPDetails" : MessageLookupByLibrary.simpleMessage("Recompensa Blockchain (% 1)"), "borrowAccountParagraph" : MessageLookupByLibrary.simpleMessage("Per comprar un compte, primer haurà de demanar un préstec gratuït. Si envieu almenys % 1 Pascal (% 2) al compte dins de % 3 dies , el compte serà vostre i % 1 Pascal es descomptarà automàticament del vostre saldo. \n En cas contrari, ens retornarà al final de % 3 dies i ja no pertanyrà a la vostra cartera. \n Es recomana només envieu una petita quantitat de monedes fins que no teniu el compte."), "borrowAnAccountButton" : MessageLookupByLibrary.simpleMessage("Elimina un compte"), "borrowStarted" : MessageLookupByLibrary.simpleMessage("S\'ha iniciat la compra per a% 1"), "borrowedAccountHeader" : MessageLookupByLibrary.simpleMessage("Compte prestat"), "borrowedAccountPaidParagraph" : MessageLookupByLibrary.simpleMessage(" El vostre compte s\'ha comprat! \n La transferència es processa actualment. Aquest procés sol durar uns 15 minuts , en alguns casos pot trigar una mica més."), "borrowedAccountParagraph" : MessageLookupByLibrary.simpleMessage("Aquest és un compte prestat . \n Si li envieu almenys % 1 Pascal en els següents % 2 dies,% 3 hores i% 4 minuts , ja serà teu."), "borrowedHeader" : MessageLookupByLibrary.simpleMessage("Prestat"), "borrowedTransferredHeader" : MessageLookupByLibrary.simpleMessage("Transferència pendent"), "buyAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Compra del compte (% 1)"), "buyAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Compra el compte"), "buyAnAccountButton" : MessageLookupByLibrary.simpleMessage("Comprar un compte"), "cancelButton" : MessageLookupByLibrary.simpleMessage("Cancel·lar"), "cantSendToYourselfError" : MessageLookupByLibrary.simpleMessage("No es pot enviar a tu mateix"), "changeAccountInfoOPDetails" : MessageLookupByLibrary.simpleMessage("Canvia la informació del compte (% 1)"), "changeAccountNameHeader" : MessageLookupByLibrary.simpleMessage("Canvieu el nom del compte"), "changeDaemonButton" : MessageLookupByLibrary.simpleMessage("Canvia de servidor"), "changeDaemonParagraph" : MessageLookupByLibrary.simpleMessage("Introduïu una adreça per utilitzar un dimoni Pascal diferent per a sol·licituds RPC."), "changeDaemonSheetHeader" : MessageLookupByLibrary.simpleMessage("Canvia de dimoni"), "changeKeyOPDetails" : MessageLookupByLibrary.simpleMessage("Canvia la clau (% 1)"), "changeKeySignedOPDetails" : MessageLookupByLibrary.simpleMessage("Canvia la signada amb clau (% 1)"), "changeNameButton" : MessageLookupByLibrary.simpleMessage("Canviar el nom"), "changeNameParagraph" : MessageLookupByLibrary.simpleMessage("Introduïu un nom a continuació per canviar el nom del vostre compte."), "changeNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Canvieu el nom"), "changedNameParagraph" : MessageLookupByLibrary.simpleMessage("El vostre nom del compte s\'ha canviat correctament."), "changedNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Canviat"), "changingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Canvi de compte"), "changingNameParagraph" : MessageLookupByLibrary.simpleMessage("Confirmeu el nom del compte nou per continuar."), "changingNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Canviant"), "checkOutBlaiseParagraph" : MessageLookupByLibrary.simpleMessage("Fes una ullada a Blaise! Cartera de Pascal senzilla, elegant i segura per a iOS i Android: https://blaisewallet.com"), "closeButton" : MessageLookupByLibrary.simpleMessage("Tanca"), "confirmButton" : MessageLookupByLibrary.simpleMessage("Confirmar"), "confirmPINParagraph" : MessageLookupByLibrary.simpleMessage("Confirmeu el PIN"), "confirmPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Confirma la contrassenya"), "confirmTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Confirmeu"), "confirmationCodeError" : MessageLookupByLibrary.simpleMessage("No s\'ha pogut verificar el codi, assegureu-vos que l\'heu introduït correctament"), "confirmationCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Codi de confirmació"), "connectingHeader" : MessageLookupByLibrary.simpleMessage("Connectant"), "contactAlreadyExistsError" : MessageLookupByLibrary.simpleMessage("El contacte ja existeix"), "contactDoesntExistError" : MessageLookupByLibrary.simpleMessage("El contacte no existeix"), "contactNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Nom de contacte"), "contactSheetHeader" : MessageLookupByLibrary.simpleMessage("Contacte"), "contactsHeader" : MessageLookupByLibrary.simpleMessage("Contactes"), "copiedAddressButton" : MessageLookupByLibrary.simpleMessage("Adreça copiada"), "copiedButton" : MessageLookupByLibrary.simpleMessage("Copiat"), "copyAddressButton" : MessageLookupByLibrary.simpleMessage("Copiar adreça"), "copyButton" : MessageLookupByLibrary.simpleMessage("Còpia"), "copyEncryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Copia la clau xifrada"), "copyKeyButton" : MessageLookupByLibrary.simpleMessage("Copia la clau privada"), "copyPublicKeyButton" : MessageLookupByLibrary.simpleMessage("Copia la clau pública"), "copyUnencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Copia la clau sense xifrar"), "countryCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Codi del país"), "createPINParagraph" : MessageLookupByLibrary.simpleMessage("Creeu un PIN de 6 dígits"), "createPrivateSaleButton" : MessageLookupByLibrary.simpleMessage("Crear una venda privada"), "createPrivateSaleHeader" : MessageLookupByLibrary.simpleMessage("Crea venda privada"), "createPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Introduïu un preu, un compte de recepció i una clau pública a continuació per crear una venda privada per a aquest compte."), "createPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Venda privada"), "createdPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("La venda privada s\'ha creat correctament. Us informarem si es compra."), "createdPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Creat"), "creatingPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Confirmeu la informació següent."), "creatingPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Creació"), "currencyHeader" : MessageLookupByLibrary.simpleMessage("Moneda"), "daemonHeader" : MessageLookupByLibrary.simpleMessage("Dimoni"), "decryptAndImportKeyHeader" : MessageLookupByLibrary.simpleMessage("Desxifra i importa"), "defaultHeader" : MessageLookupByLibrary.simpleMessage("Per defecte"), "deletePrivateKeyAndLogoutButton" : MessageLookupByLibrary.simpleMessage("Suprimeix la clau privada\nTanca la sessió"), "delistAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Suprimeix el compte (% 1)"), "delistFromSaleHeader" : MessageLookupByLibrary.simpleMessage("Elimina la venda"), "delistFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("Confirmeu que voleu suprimir aquest compte des de la venda."), "delistedFromSaleHeader" : MessageLookupByLibrary.simpleMessage("Eliminat de la venda"), "delistedFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("El vostre compte s\'ha suprimit correctament de la venda."), "delistedHeader" : MessageLookupByLibrary.simpleMessage("Suprimit"), "delistedSheetHeader" : MessageLookupByLibrary.simpleMessage("Suprimit"), "delistingSheetHeader" : MessageLookupByLibrary.simpleMessage("Suprimint"), "didNotGetResponseError" : MessageLookupByLibrary.simpleMessage("No s\'ha obtingut resposta del servidor"), "durationTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Durada"), "emptyPasswordError" : MessageLookupByLibrary.simpleMessage("La contrasenya no pot estar buida"), "encryptButton" : MessageLookupByLibrary.simpleMessage("Xifra"), "encryptKeyParagraph" : MessageLookupByLibrary.simpleMessage("Creeu una contrasenya nova per xifrar la vostra clau privada."), "encryptPayloadHeader" : MessageLookupByLibrary.simpleMessage("Xifra la càrrega útil"), "encryptSheetHeader" : MessageLookupByLibrary.simpleMessage("Xifra"), "encryptThePayloadHeader" : MessageLookupByLibrary.simpleMessage("Xifra la càrrega útil"), "encryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Clau xifrada"), "enterConfirmationCodeParagraph" : MessageLookupByLibrary.simpleMessage("Us hem enviat un codi de confirmació, introduïu-lo a continuació."), "enterPINParagraph" : MessageLookupByLibrary.simpleMessage("Introduïu el PIN"), "enterPINToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("Introduïu el PIN per desbloquejar Blaise"), "enterPhoneNumberParagraph" : MessageLookupByLibrary.simpleMessage("Introduïu el vostre número de telèfon a continuació."), "failedToEncryptPayloadError" : MessageLookupByLibrary.simpleMessage("No s\'ha pogut xifrar la càrrega útil"), "failedToImportContactsError" : MessageLookupByLibrary.simpleMessage("No s\'ha pogut importar contactes"), "failedToRemoveFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("No s\'ha pogut eliminar% 1 dels contactes"), "feeColonHeader" : MessageLookupByLibrary.simpleMessage("Taxa:"), "feeConfirmAmountParagraph" : MessageLookupByLibrary.simpleMessage("Per confirmar l’addició de taxa de Pascal% 1 a aquesta operació per continuar."), "feeOPDetails" : MessageLookupByLibrary.simpleMessage("quota"), "feeRequiredParagraph" : MessageLookupByLibrary.simpleMessage("Aquesta operació requereix un cànon."), "feeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Comissió"), "forSaleHeader" : MessageLookupByLibrary.simpleMessage("A la venda"), "freeAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Compte gratuït"), "freepasaComplete" : MessageLookupByLibrary.simpleMessage("Amb èxit, el nou compte estarà disponible després de 1 confirmació de xarxa"), "getAFreeAccountButton" : MessageLookupByLibrary.simpleMessage("Obtenir un compte gratuït"), "getAccountFirstParagraph" : MessageLookupByLibrary.simpleMessage("Hi ha dues opcions per obtenir el teu primer compte:"), "getAccountSecondParagraph" : MessageLookupByLibrary.simpleMessage("1- Podeu obtenir un compte gratuït mitjançant el vostre número de telèfon. Només es permet un compte per número de telèfon. "), "getAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Obtén un compte"), "getAccountThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- Podeu comprar tants comptes com vulgueu per % 1 Pascal (% 2). "), "getAccountThirdParagraphAlternative" : MessageLookupByLibrary.simpleMessage("2- Podeu comprar un compte per a % 1 Pascal (% 2). Comprar només un compte està permès per usuari. "), "getAccountThirdParagraphAlternative2" : MessageLookupByLibrary.simpleMessage("2- Podeu comprar un compte per a % 1 Pascal (% 2). Podeu comprar fins a% 3 comptes. "), "getAnAccountButton" : MessageLookupByLibrary.simpleMessage("Obtenir un compte"), "goBackButton" : MessageLookupByLibrary.simpleMessage("Torna"), "gotItButton" : MessageLookupByLibrary.simpleMessage("Ho tinc!"), "hideButton" : MessageLookupByLibrary.simpleMessage("Amaga"), "iHaveBackedItUpButton" : MessageLookupByLibrary.simpleMessage("Copia realitzada"), "importButton" : MessageLookupByLibrary.simpleMessage("Importa"), "importPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("Importa clau privada"), "importPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Importa clau privada"), "importPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("Introduïu la vostra clau privada a continuació."), "insufficientBalanceError" : MessageLookupByLibrary.simpleMessage("Saldo insuficient"), "invalidAccountError" : MessageLookupByLibrary.simpleMessage("Compte no vàlid"), "invalidAccountNameError" : MessageLookupByLibrary.simpleMessage("El nom del compte no és vàlid"), "invalidAddressError" : MessageLookupByLibrary.simpleMessage("Adreça no vàlida"), "invalidDestinationError" : MessageLookupByLibrary.simpleMessage("Destinació no vàlida"), "invalidPINParagraph" : MessageLookupByLibrary.simpleMessage("PIN no vàlid"), "invalidPasswordError" : MessageLookupByLibrary.simpleMessage("Contrasenya invàlida"), "invalidPhoneNumberParagraph" : MessageLookupByLibrary.simpleMessage("El número de telèfon no és vàlid"), "invalidPrivateKeyError" : MessageLookupByLibrary.simpleMessage("Clau privada no vàlida"), "invalidPublicKeyError" : MessageLookupByLibrary.simpleMessage("Clau pública no vàlida"), "invalidReceivingAccountError" : MessageLookupByLibrary.simpleMessage("Compte de recepció no vàlid"), "keyCopiedButton" : MessageLookupByLibrary.simpleMessage("Clau copiada"), "keyTypeNotSupportedHeader" : MessageLookupByLibrary.simpleMessage("Clau no compatible"), "keyTypeNotSupportedParagraph" : MessageLookupByLibrary.simpleMessage("Aquest tipus de clau privada encara no és compatible amb Blaise. Podeu crear una clau privada nova i transferir-ne els comptes mitjançant una cartera diferent."), "languageColonHeader" : MessageLookupByLibrary.simpleMessage("Llenguatge:"), "languageHeader" : MessageLookupByLibrary.simpleMessage("Llenguatge"), "listAccountForSaleHeader" : MessageLookupByLibrary.simpleMessage("Llista de compte en venda"), "listAccountForSaleOPDetails" : MessageLookupByLibrary.simpleMessage("Llista del compte en venda (% 1)"), "listForSaleButton" : MessageLookupByLibrary.simpleMessage("Posar a la venda"), "listForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Introduïu un preu i un compte que rebran el pagament per llistar aquest compte per vendre."), "listForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Llistat en venda"), "listedForSaleHeader" : MessageLookupByLibrary.simpleMessage("Llistat en venda"), "listedForSaleParagraph" : MessageLookupByLibrary.simpleMessage("El vostre compte s\'ha llistat amb èxit per a la venda. Us informarem si algú la compra."), "listedForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Enumerat"), "listingForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Confirmeu el preu i el compte que rebrà el pagament."), "listingForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Llistat"), "liveSupportButton" : MessageLookupByLibrary.simpleMessage("Assistència"), "lock15Header" : MessageLookupByLibrary.simpleMessage("Després de% 1 minuts"), "lock1Header" : MessageLookupByLibrary.simpleMessage("Després de% 1 minut"), "lock30Header" : MessageLookupByLibrary.simpleMessage("Després de% 1 minuts"), "lock5Header" : MessageLookupByLibrary.simpleMessage("Després de% 1 minuts"), "lock60Header" : MessageLookupByLibrary.simpleMessage("Després de% 1 minuts"), "lockInstantHeader" : MessageLookupByLibrary.simpleMessage("Instantàniament"), "lockedHeader" : MessageLookupByLibrary.simpleMessage("Bloquejat"), "lockedUntilBlockOPDetails" : MessageLookupByLibrary.simpleMessage("Bloquejat fins el bloc"), "logoutFirstDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage(" En tancar la sessió, suprimireu la clau privada i totes les dades relacionades amb Blaise d\'aquest dispositiu. Si la vostra clau privada no està feta una còpia de seguretat, no podreu tornar a accedir als vostres fons. Si hi ha una còpia de seguretat de la teva clau privada, no et preocuparà res."), "logoutHeader" : MessageLookupByLibrary.simpleMessage("Tancar sessió"), "logoutSecondDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Esteu segur que heu fet una còpia de seguretat de la vostra clau privada? Sempre que hàgiu fet una còpia de seguretat de la vostra clau privada, no us haureu de preocupar. "), "looksLikeEncryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("Sembla que una clau privada xifrada, introduïu la contrasenya per desxifrar-la i importar-la."), "manageHeader" : MessageLookupByLibrary.simpleMessage("Gestiona"), "manyFailedAttemptsParagraph" : MessageLookupByLibrary.simpleMessage("Massa intents fallits de desbloqueig"), "maturationOPDetails" : MessageLookupByLibrary.simpleMessage("maduració"), "multioperationOPDetails" : MessageLookupByLibrary.simpleMessage("Multioperació (% 1)"), "naOPDetails" : MessageLookupByLibrary.simpleMessage("N / A"), "nameChangedHeader" : MessageLookupByLibrary.simpleMessage("Canviat el nom"), "nameRequiredError" : MessageLookupByLibrary.simpleMessage("El seu nom és obligatori"), "nameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Nom"), "newAccountNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Nou nom del compte"), "newAccountParagraph" : MessageLookupByLibrary.simpleMessage("Aquest és el vostre nou compte. \n Un cop rebut Pascal , les operacions es mostraran com a continuació."), "newKeyBackUpConfirmParagraph" : MessageLookupByLibrary.simpleMessage("Esteu segur que heu creat una còpia de seguretat de la clau privada de la nova billetera?"), "newKeySecurityParagraph" : MessageLookupByLibrary.simpleMessage("A la pantalla següent, veureu la vostra clau privada nova. És una contrasenya per accedir als vostres fons. És fonamental que el feu una còpia de seguretat i no el compartiu mai amb ningú."), "newNameOPDetails" : MessageLookupByLibrary.simpleMessage("Nou nom"), "newPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("nova contrasenya"), "newPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("Nova clau privada"), "newPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Nova clau privada"), "newPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("A continuació, es mostra la clau privada de la nova cartera. És fonamental que feu una còpia de seguretat de la clau privada i no la guardeu mai com a text de pantalla o captura de pantalla. Recomanem escriure-la en un tros de paper i guardar-la fora de línia."), "newPublicKeyOPDetails" : MessageLookupByLibrary.simpleMessage("Nova clau pública"), "newWalletGreetingParagraph" : MessageLookupByLibrary.simpleMessage("Benvingut a Blaise Wallet . \n Podeu començar obtenint un compte."), "nextButton" : MessageLookupByLibrary.simpleMessage("Pròxim"), "noContactsToExportError" : MessageLookupByLibrary.simpleMessage("No hi ha contactes per exportar"), "noContactsToImportError" : MessageLookupByLibrary.simpleMessage("No hi ha contactes a importar"), "noGoBackButton" : MessageLookupByLibrary.simpleMessage("No, torna enrere"), "noHeader" : MessageLookupByLibrary.simpleMessage("No"), "noMatchPINParagraph" : MessageLookupByLibrary.simpleMessage("Els PIN no coincideixen"), "noMatchPasswordError" : MessageLookupByLibrary.simpleMessage("Les contrasenyes no coincideixen"), "noResultsFound" : MessageLookupByLibrary.simpleMessage("Sense resultats"), "noperationOPDetails" : MessageLookupByLibrary.simpleMessage("n_operació"), "notificationsHeader" : MessageLookupByLibrary.simpleMessage("Notificacions"), "nullOPDetails" : MessageLookupByLibrary.simpleMessage("nul"), "offHeader" : MessageLookupByLibrary.simpleMessage("Desactivat"), "okayButton" : MessageLookupByLibrary.simpleMessage("Bé"), "okayGoBackButton" : MessageLookupByLibrary.simpleMessage("D\'acord, torna"), "onHeader" : MessageLookupByLibrary.simpleMessage("Encès"), "opblockOPDetails" : MessageLookupByLibrary.simpleMessage("opbloquejar"), "openInExplorerButton" : MessageLookupByLibrary.simpleMessage("Obriu a l\'Explorador"), "operationDetailsButton" : MessageLookupByLibrary.simpleMessage("Detalls de l\'operació"), "operationsHeader" : MessageLookupByLibrary.simpleMessage("Operacions"), "ophashOPDetails" : MessageLookupByLibrary.simpleMessage("ophash"), "optxtOPDetails" : MessageLookupByLibrary.simpleMessage("optxt"), "optypeOPDetails" : MessageLookupByLibrary.simpleMessage("òptic"), "otherOperationsHeader" : MessageLookupByLibrary.simpleMessage("Altres operacions"), "passwordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Contrasenya"), "payloadOPDetails" : MessageLookupByLibrary.simpleMessage("Càrrega útil"), "payloadTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Càrrega útil"), "pendingHeader" : MessageLookupByLibrary.simpleMessage("Pendents"), "phoneNumberTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Número de telèfon"), "preferencesHeader" : MessageLookupByLibrary.simpleMessage("Preferències"), "priceRequiredError" : MessageLookupByLibrary.simpleMessage("El preu és obligatori"), "priceTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Preu"), "privacyPolicyHeader" : MessageLookupByLibrary.simpleMessage("Política de privacitat"), "privateKeySheetHeader" : MessageLookupByLibrary.simpleMessage("Clau privada"), "privateKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Clau privada"), "privateSaleHeader" : MessageLookupByLibrary.simpleMessage("Venda privada"), "publicKeyParagraph" : MessageLookupByLibrary.simpleMessage("A continuació, es mostra la vostra clau pública. Com el seu nom indica, es pretén compartir públicament i demostrar que una operació particular pertany a la vostra clau privada."), "publicKeySheetHeader" : MessageLookupByLibrary.simpleMessage("Clau pública"), "publicKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Clau pública"), "receiveAccountButton" : MessageLookupByLibrary.simpleMessage("Rep el compte"), "receiveAnAccountButton" : MessageLookupByLibrary.simpleMessage("Rebre un compte"), "receiveButton" : MessageLookupByLibrary.simpleMessage("Rebre"), "receivedHeader" : MessageLookupByLibrary.simpleMessage("Rebut"), "receivingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Compte de recepció"), "receivingAccountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Compte de recepció"), "recoverFundsOPDetails" : MessageLookupByLibrary.simpleMessage("Recuperació de fons (% 1)"), "removedFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("S\'ha eliminat% 1 dels contactes"), "requestButton" : MessageLookupByLibrary.simpleMessage("Sol·licitud"), "requestSheetHeader" : MessageLookupByLibrary.simpleMessage("Sol·licitud"), "scanQRCodeButton" : MessageLookupByLibrary.simpleMessage("Escaneig del codi QR"), "searchAccountNameButton" : MessageLookupByLibrary.simpleMessage("Cerca el nom del compte"), "searchForNameButton" : MessageLookupByLibrary.simpleMessage("Buscar nom"), "searchNameButton" : MessageLookupByLibrary.simpleMessage("Nom de la cerca"), "securityFirstHeader" : MessageLookupByLibrary.simpleMessage("Seguretat primer!"), "securityHeader" : MessageLookupByLibrary.simpleMessage("Seguretat"), "sellerAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Compte del venedor"), "sendAmountOPDetails" : MessageLookupByLibrary.simpleMessage("Enviar import"), "sendButton" : MessageLookupByLibrary.simpleMessage("Enviar"), "sendConfirmationButton" : MessageLookupByLibrary.simpleMessage("Confirmar l\'enviament"), "sendSheetHeader" : MessageLookupByLibrary.simpleMessage("Envia"), "sendingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Compte enviant"), "sendingConfirmParagraph" : MessageLookupByLibrary.simpleMessage("Confirmeu les dades de la transacció a enviar."), "sendingSheetHeader" : MessageLookupByLibrary.simpleMessage("S\'està enviant"), "sentHeader" : MessageLookupByLibrary.simpleMessage("Enviat"), "sentParagraph" : MessageLookupByLibrary.simpleMessage("La transacció s\'ha enviat correctament."), "sentSheetHeader" : MessageLookupByLibrary.simpleMessage("Enviat"), "setToDefaultButton" : MessageLookupByLibrary.simpleMessage("Establir a valors predeterminats"), "settingsHeader" : MessageLookupByLibrary.simpleMessage("Configuració"), "shareHeader" : MessageLookupByLibrary.simpleMessage("Comparteix Blaise"), "showButton" : MessageLookupByLibrary.simpleMessage("Mostra"), "signeraccountOPDetails" : MessageLookupByLibrary.simpleMessage("signer_account"), "somethingWentWrongError" : MessageLookupByLibrary.simpleMessage("Alguna cosa ha anat malament. Si us plau torna-ho a intentar després"), "successfullyImportedContactsParagraph" : MessageLookupByLibrary.simpleMessage("% 1 contactes importats amb èxit"), "systemDefaultHeader" : MessageLookupByLibrary.simpleMessage("Predeterminat del sistema"), "themeCopperHeader" : MessageLookupByLibrary.simpleMessage("Coure"), "themeDarkHeader" : MessageLookupByLibrary.simpleMessage("Fosc"), "themeHeader" : MessageLookupByLibrary.simpleMessage("Tema"), "themeLightHeader" : MessageLookupByLibrary.simpleMessage("Llum"), "threeCharacterNameError" : MessageLookupByLibrary.simpleMessage("Ha de tenir com a mínim 3 caràcters"), "timeOPDetails" : MessageLookupByLibrary.simpleMessage("temps"), "totalBalanceHeader" : MessageLookupByLibrary.simpleMessage("Saldo total"), "transactionOPDetails" : MessageLookupByLibrary.simpleMessage("Transacció (% 1)"), "transferAccountHeader" : MessageLookupByLibrary.simpleMessage("Compte de transferència"), "transferButton" : MessageLookupByLibrary.simpleMessage("Transferència"), "transferParagraph" : MessageLookupByLibrary.simpleMessage("Introduïu una clau pública a continuació per transferir-hi la propietat d’aquest compte."), "transferSheetHeader" : MessageLookupByLibrary.simpleMessage("Transferència"), "transferredHeader" : MessageLookupByLibrary.simpleMessage("Traspassat"), "transferredParagraph" : MessageLookupByLibrary.simpleMessage("El vostre compte s\'ha transferit correctament a la clau pública següent."), "transferredSheetHeader" : MessageLookupByLibrary.simpleMessage("Traspassat"), "transferringParagraph" : MessageLookupByLibrary.simpleMessage("Confirmeu la clau pública següent per transferir-hi la propietat d’aquest compte."), "transferringSheetHeader" : MessageLookupByLibrary.simpleMessage("Transferència"), "unconfirmedAccountHeader" : MessageLookupByLibrary.simpleMessage("Compte no confirmat"), "unconfirmedAccountParagraph" : MessageLookupByLibrary.simpleMessage("Aquest és un compte no confirmat . S\'ha transferit a tu, però abans de poder utilitzar-lo cal que existeixi una confirmació de xarxa. Generalment triguen uns 5 minuts, un cop finalitzat, podreu utilitzar aquest compte."), "undefinedHeader" : MessageLookupByLibrary.simpleMessage("Indefinit"), "unencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Clau no xifrada"), "uninstallDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Si perdeu el dispositiu o desinstalgeu Blaise Wallet, necessitareu la clau privada per recuperar els vostres fons."), "unknownOPDetails" : MessageLookupByLibrary.simpleMessage("Desconegut (% 1)"), "unlockButton" : MessageLookupByLibrary.simpleMessage("Desbloquejar"), "unlockWithBiometricsButton" : MessageLookupByLibrary.simpleMessage("Desbloquejar amb Biometrics"), "unlockWithPINButton" : MessageLookupByLibrary.simpleMessage("Desbloquegeu amb el PIN"), "urlChangedToParagraph" : MessageLookupByLibrary.simpleMessage("L\'URL s\'ha canviat a% 1"), "viewPublicKeyHeader" : MessageLookupByLibrary.simpleMessage("Veure clau pública"), "warningHeader" : MessageLookupByLibrary.simpleMessage("Avís"), "welcomeParagraph" : MessageLookupByLibrary.simpleMessage("Benvingut a la cartera Blaise. Per començar, podeu crear una clau privada o importar-ne una."), "yesAddFeeButton" : MessageLookupByLibrary.simpleMessage("Sí, afegeix comissió"), "yesHeader" : MessageLookupByLibrary.simpleMessage("Sí"), "yesImSureButton" : MessageLookupByLibrary.simpleMessage("Sí, n\'estic segur"), "zeroAmountError" : MessageLookupByLibrary.simpleMessage("La quantitat no pot ser 0"), "zeroPriceError" : MessageLookupByLibrary.simpleMessage("El preu no pot ser 0") }; } ================================================ FILE: lib/l10n/messages_de.dart ================================================ // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart // This is a library that provides messages for a de locale. All the // messages from the main program should be duplicated here with the same // function name. // Ignore issues from commonly used lints in this file. // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases // ignore_for_file:unused_import, file_names import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; final messages = new MessageLookup(); typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'de'; final messages = _notInlinedMessages(_notInlinedMessages); static _notInlinedMessages(_) => { "accountBalanceHeader" : MessageLookupByLibrary.simpleMessage("Kontostand"), "accountOPDetails" : MessageLookupByLibrary.simpleMessage("Konto"), "accountPriceOPDetails" : MessageLookupByLibrary.simpleMessage("Konto Preis"), "accountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Konto"), "accountToSendFromHeader" : MessageLookupByLibrary.simpleMessage("Absender"), "accountsHeader" : MessageLookupByLibrary.simpleMessage("Konten"), "addADurationButton" : MessageLookupByLibrary.simpleMessage("+ Dauer festlegen"), "addAPayloadButton" : MessageLookupByLibrary.simpleMessage("+ Betreff festlegen"), "addContactButton" : MessageLookupByLibrary.simpleMessage("Kontakt hinzufügen"), "addContactSheetHeader" : MessageLookupByLibrary.simpleMessage("Kontakt hinzufügen"), "addFeeHeader" : MessageLookupByLibrary.simpleMessage("Gebühr hinzufügen"), "addToContactsButton" : MessageLookupByLibrary.simpleMessage("Zu Kontakten hinzufügen"), "addedToContactsParagraph" : MessageLookupByLibrary.simpleMessage("%1 zu den Kontakten hinzugefügt"), "addressTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Kontonummer"), "amountRequiredError" : MessageLookupByLibrary.simpleMessage("Betrag wird benötigt"), "amountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Betrag"), "areYouSureHeader" : MessageLookupByLibrary.simpleMessage("Bist Du sicher?"), "authenticateOnLaunchHeader" : MessageLookupByLibrary.simpleMessage("Anmelden bei Start"), "authenticateToBackUpParagraph" : MessageLookupByLibrary.simpleMessage("Anmelden um den privaten Schlüssel zu sichern"), "authenticateToChangeNameParagraph" : MessageLookupByLibrary.simpleMessage("Anmelden um den Namen des Kontos auf \"%1\" zu ändern"), "authenticateToCreatePrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Anmelden um Konto privat zu verkaufen"), "authenticateToDelistParagraph" : MessageLookupByLibrary.simpleMessage("Anmelden um den Konto-Verkauf abzubrechen"), "authenticateToListForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Anmelden um Konto zu verkaufen"), "authenticateToSendParagraph" : MessageLookupByLibrary.simpleMessage("Anmelden um %1 Pascal zu senden"), "authenticateToTransferParagraph" : MessageLookupByLibrary.simpleMessage("Anmelden um Konto zu übertragen"), "authenticateToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("Authentifizieren um die Blaise Wallet zu entsperren"), "authenticationBiometricsHeader" : MessageLookupByLibrary.simpleMessage("Biometrie"), "authenticationMethodHeader" : MessageLookupByLibrary.simpleMessage("Authentifizierung"), "authenticationPINHeader" : MessageLookupByLibrary.simpleMessage("PIN"), "automaticallyLockHeader" : MessageLookupByLibrary.simpleMessage("Automatisch sperren"), "backUpKeyHeader" : MessageLookupByLibrary.simpleMessage("Schlüssel gesichert?"), "backUpPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Privaten Schlüssel sichern"), "backUpSheetHeader" : MessageLookupByLibrary.simpleMessage("Sicherung"), "backupEncryptedKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("Hier ist Dein mit einem Passwort chiffrierter privater Schlüssel. Du kannst Ihn problemlos in einem Passwort Manager speichern."), "backupEncryptedKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("Wenn Du das Passwort vergisst oder verlierst, kannst Du nicht mehr auf Deine Pascal zugreifen."), "backupKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("Du hast 2 Möglichkeiten, um Deinen privaten Schlüssel zu sichern:"), "backupKeyFourthParagraph" : MessageLookupByLibrary.simpleMessage("Wir empfehlen Dir, die nicht chiffrierte Version des Schlüssels auf einem Blatt Papier zu notieren. Die chiffrierte Version kann in einem Passwort Manager gespeichert werden."), "backupKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("1- Chiffrieren mit Hilfe eines Passworts"), "backupKeyThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- Nicht chiffriert, im Klartext und ungeschützt"), "backupUnencryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("Nachfolgend siehst Du Deinen privaten Schlüssel. Dieser ist nicht mit einem Passwort geschützt, deshalb ist es zwingend erforderlich, dass Du ihn sicher offline verwahrst! Wir empfehlen ihn auf Papier zu notieren."), "balanceHeader" : MessageLookupByLibrary.simpleMessage("Saldo"), "blockOPDetails" : MessageLookupByLibrary.simpleMessage("Block"), "blockchainRewardOPDetails" : MessageLookupByLibrary.simpleMessage("Blockchain Reward (%1)"), "borrowAccountParagraph" : MessageLookupByLibrary.simpleMessage("Um ein Konto zu kaufen, musst Du Dir zu Beginn eines ausleihen. Wenn Du innerhalb von %3 Tage(n) mindestens %1 Pascal (%2) an das geliehene Konto überweist, gehört es Dir und der Betrag von %1 Pascal wird automatisch abgezogen.\nAnsonsten geht das geliehene Konto nach %3 Tage(n) an uns zurück und ist nicht mehr Teil Deiner Wallet.\nWir empfehlen Dir, nicht zu große Beträge an das Konto zu senden, solange es nicht Dir gehört."), "borrowAnAccountButton" : MessageLookupByLibrary.simpleMessage("Konto leihen"), "borrowStarted" : MessageLookupByLibrary.simpleMessage("Konto-Kauf für %1 gestartet"), "borrowedAccountHeader" : MessageLookupByLibrary.simpleMessage("Geliehenes Konto"), "borrowedAccountPaidParagraph" : MessageLookupByLibrary.simpleMessage("Du hast das Konto gekauft!\nDie Übertragung läuft gerade. Dieser Vorgang dauert ungefähr 15 Minuten, manchmal kann es auch etwas länger dauern. Bitte habe ein wenig Geduld, Du kannst in Kürze loslegen."), "borrowedAccountParagraph" : MessageLookupByLibrary.simpleMessage("Dies ist ein geliehenes Konto.\nWenn Du in den nächsten %2 Tage(n), %3 Stunde(n) und %4 Minute(n) mindestens %1 Pascal an das Konto sendest gehört es Dir!"), "borrowedHeader" : MessageLookupByLibrary.simpleMessage("geliehen"), "borrowedTransferredHeader" : MessageLookupByLibrary.simpleMessage("Übertragung wartend"), "buyAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Konto gekauft (%1)"), "buyAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Konto kaufen"), "buyAnAccountButton" : MessageLookupByLibrary.simpleMessage("Konto kaufen"), "cancelButton" : MessageLookupByLibrary.simpleMessage("Abbrechen"), "cantSendToYourselfError" : MessageLookupByLibrary.simpleMessage("Du kannst nicht an dich selbst senden"), "changeAccountInfoOPDetails" : MessageLookupByLibrary.simpleMessage("Konto Daten geändert (%1)"), "changeAccountNameHeader" : MessageLookupByLibrary.simpleMessage("Konto Name ändern"), "changeDaemonButton" : MessageLookupByLibrary.simpleMessage("Server wechseln"), "changeDaemonParagraph" : MessageLookupByLibrary.simpleMessage("Trage eine Adresse zu einem anderen Pascal-Server ein"), "changeDaemonSheetHeader" : MessageLookupByLibrary.simpleMessage("Server wechseln"), "changeKeyOPDetails" : MessageLookupByLibrary.simpleMessage("Konto-Übertragung (%1)"), "changeKeySignedOPDetails" : MessageLookupByLibrary.simpleMessage("Fremde Konto-Übertragung (%1)"), "changeNameButton" : MessageLookupByLibrary.simpleMessage("Name ändern"), "changeNameParagraph" : MessageLookupByLibrary.simpleMessage("Trage einen neuen Namen für Dein Konto ein."), "changeNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Name ändern"), "changedNameParagraph" : MessageLookupByLibrary.simpleMessage("Der Name des Kontos wurde erfolgreich geändert."), "changedNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Geändert"), "changingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Ändere Konto"), "changingNameParagraph" : MessageLookupByLibrary.simpleMessage("Bitte bestätige den neuen Namen für Dein Konto."), "changingNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Speichere"), "checkOutBlaiseParagraph" : MessageLookupByLibrary.simpleMessage("Schau Dir Blaise an! Die sichere und einfach zu bedienende Pascal Wallet für iOS und Android: https://blaisewallet.com"), "closeButton" : MessageLookupByLibrary.simpleMessage("Schließen"), "confirmButton" : MessageLookupByLibrary.simpleMessage("Bestätigen"), "confirmPINParagraph" : MessageLookupByLibrary.simpleMessage("PIN bestätigen"), "confirmPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Passwort bestätigen"), "confirmTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Bestätigen"), "confirmationCodeError" : MessageLookupByLibrary.simpleMessage("Der Code war falsch, bitte probiere es erneut"), "confirmationCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Bestätigungs-Code"), "connectingHeader" : MessageLookupByLibrary.simpleMessage("Verbinde"), "contactAlreadyExistsError" : MessageLookupByLibrary.simpleMessage("Kontakt existiert bereits"), "contactDoesntExistError" : MessageLookupByLibrary.simpleMessage("Kontakt existiert nicht"), "contactNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Name"), "contactSheetHeader" : MessageLookupByLibrary.simpleMessage("Kontakt"), "contactsHeader" : MessageLookupByLibrary.simpleMessage("Kontakte"), "copiedAddressButton" : MessageLookupByLibrary.simpleMessage("Kontonummer kopiert"), "copiedButton" : MessageLookupByLibrary.simpleMessage("Kopiert"), "copyAddressButton" : MessageLookupByLibrary.simpleMessage("Kontonummer kopieren"), "copyButton" : MessageLookupByLibrary.simpleMessage("Kopieren"), "copyEncryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Kopieren"), "copyKeyButton" : MessageLookupByLibrary.simpleMessage("Kopieren"), "copyPublicKeyButton" : MessageLookupByLibrary.simpleMessage("Öffentlichen Schlüssel kopieren"), "copyUnencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Kopieren"), "countryCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Länder-Code"), "createPINParagraph" : MessageLookupByLibrary.simpleMessage("6-stellige PIN erstellen"), "createPrivateSaleButton" : MessageLookupByLibrary.simpleMessage("Private verkaufen"), "createPrivateSaleHeader" : MessageLookupByLibrary.simpleMessage("Privat verkaufen"), "createPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Trage den Preis, den Empfänger des Kaufpreises und den öffentlichen Schlüssel ein, um das Konto Privat zu verkaufen."), "createPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Privatverkauf"), "createdPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Das Konto steht jetzt zum privaten Verkauf. Sobald es gekauft wurde sagen wir Dir Bescheid."), "createdPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Erstellt"), "creatingPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Bitte bestätige die folgenden Angaben."), "creatingPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Erstelle"), "currencyHeader" : MessageLookupByLibrary.simpleMessage("Währung"), "daemonHeader" : MessageLookupByLibrary.simpleMessage("Server"), "decryptAndImportKeyHeader" : MessageLookupByLibrary.simpleMessage("Entschlüsseln und importieren"), "defaultHeader" : MessageLookupByLibrary.simpleMessage("Standard"), "deletePrivateKeyAndLogoutButton" : MessageLookupByLibrary.simpleMessage("Privaten Schlüssel löschen\nund abmelden"), "delistAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Verkauf abgebrochen (%1)"), "delistFromSaleHeader" : MessageLookupByLibrary.simpleMessage("Verkauf stoppen"), "delistFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("Bitte bestätige den Abbruch des Konto-Verkaufs."), "delistedFromSaleHeader" : MessageLookupByLibrary.simpleMessage("Verkauf abgebrochen"), "delistedFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("Der Verkauf wurde erfolgreich abgebrochen."), "delistedHeader" : MessageLookupByLibrary.simpleMessage("Verkauf abgebrochen"), "delistedSheetHeader" : MessageLookupByLibrary.simpleMessage("Verkauf abgebrochen"), "delistingSheetHeader" : MessageLookupByLibrary.simpleMessage("Breche Verkauf ab"), "didNotGetResponseError" : MessageLookupByLibrary.simpleMessage("Der Server antwortet nicht"), "durationTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Dauer"), "emptyPasswordError" : MessageLookupByLibrary.simpleMessage("Das Passwort darf nicht leer sein"), "encryptButton" : MessageLookupByLibrary.simpleMessage("Verschlüsseln"), "encryptKeyParagraph" : MessageLookupByLibrary.simpleMessage("Neues Passwort erstellen um den privaten Schlüssel verschlüsseln."), "encryptPayloadHeader" : MessageLookupByLibrary.simpleMessage("Betreff verschlüsseln"), "encryptSheetHeader" : MessageLookupByLibrary.simpleMessage("Verschlüsseln"), "encryptThePayloadHeader" : MessageLookupByLibrary.simpleMessage("Betreff verschlüsseln"), "encryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Chiffrierter Schlüssel"), "enterConfirmationCodeParagraph" : MessageLookupByLibrary.simpleMessage("Wir haben Dir einen Bestätigungs-Code gesendet. Bitte hier eintragen."), "enterPINParagraph" : MessageLookupByLibrary.simpleMessage("PIN eintragen"), "enterPINToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("PIN eintragen um die Blaise Wallet zu entsperren"), "enterPhoneNumberParagraph" : MessageLookupByLibrary.simpleMessage("Trage Deine Telefonnummer hier ein."), "failedToEncryptPayloadError" : MessageLookupByLibrary.simpleMessage("Fehler beim verschlüsseln des Betreffs"), "failedToImportContactsError" : MessageLookupByLibrary.simpleMessage("Fehler beim importieren der Kontakte"), "failedToRemoveFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("Fehler beim entfernen von %1 aus den Kontakten"), "feeColonHeader" : MessageLookupByLibrary.simpleMessage("Gebühr:"), "feeConfirmAmountParagraph" : MessageLookupByLibrary.simpleMessage("Bitte bestätige, dass %1 Pascal als Gebühr hinzugefügt werden."), "feeOPDetails" : MessageLookupByLibrary.simpleMessage("Gebühr"), "feeRequiredParagraph" : MessageLookupByLibrary.simpleMessage("Für diesen Vorgang wird eine Gebühr benötigt."), "feeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Gebühr"), "forSaleHeader" : MessageLookupByLibrary.simpleMessage("zu verkaufen"), "freeAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Kostenfreies Konto"), "freepasaComplete" : MessageLookupByLibrary.simpleMessage("Dein neues Konto wird nach dem nächsten Block zur Verfügung stehen"), "getAFreeAccountButton" : MessageLookupByLibrary.simpleMessage("Kostenloses Konto erhalten"), "getAccountFirstParagraph" : MessageLookupByLibrary.simpleMessage("Es gibt 2 Möglichkeiten, um Dein erstes Konto erhalten:"), "getAccountSecondParagraph" : MessageLookupByLibrary.simpleMessage("1- Du kannst ein kostenfreies Konto mit Hilfe Deiner Telefonnummer erhalten. Nur 1 Konto pro Telefonnummer erlaubt."), "getAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Konto anlegen"), "getAccountThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- Du kannst beliebig viele Konten für eine Gebühr von %1 Pascal (%2) erwerben."), "getAccountThirdParagraphAlternative" : MessageLookupByLibrary.simpleMessage("2- Du kannst ein Konto für %1 Pascal (%2) kaufen. Der Blaise-Service erlaubt den Erwerb von einem Konto pro Benutzer. Im Normalfall brauchst Du auch nur eins."), "getAccountThirdParagraphAlternative2" : MessageLookupByLibrary.simpleMessage("2- Du kannst ein Konto für %1 Pascal (%2) kaufen. Der Blaise-Service erlaubt den Erwerb von 3 Konten pro Benutzer. Im Normalfall brauchst Du lediglich eins."), "getAnAccountButton" : MessageLookupByLibrary.simpleMessage("Konto anlegen"), "goBackButton" : MessageLookupByLibrary.simpleMessage("Zurück"), "gotItButton" : MessageLookupByLibrary.simpleMessage("Verstanden!"), "hideButton" : MessageLookupByLibrary.simpleMessage("Verstecken"), "iHaveBackedItUpButton" : MessageLookupByLibrary.simpleMessage("Ich habe ihn gesichert"), "importButton" : MessageLookupByLibrary.simpleMessage("Importieren"), "importPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("Schlüssel importieren"), "importPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Schlüssel importieren"), "importPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("Trage Deinen privaten Schlüssel ein"), "insufficientBalanceError" : MessageLookupByLibrary.simpleMessage("Ungültiger Betrag"), "invalidAccountError" : MessageLookupByLibrary.simpleMessage("Ungültiges Konto"), "invalidAccountNameError" : MessageLookupByLibrary.simpleMessage("Ungültiger Konto Name"), "invalidAddressError" : MessageLookupByLibrary.simpleMessage("Ungültiges Konto"), "invalidDestinationError" : MessageLookupByLibrary.simpleMessage("Ungültiger Empfänger"), "invalidPINParagraph" : MessageLookupByLibrary.simpleMessage("Ungültige PIN"), "invalidPasswordError" : MessageLookupByLibrary.simpleMessage("Falsches Passwort"), "invalidPhoneNumberParagraph" : MessageLookupByLibrary.simpleMessage("Keine valide Telefonnummer"), "invalidPrivateKeyError" : MessageLookupByLibrary.simpleMessage("Ungültiger privater Schlüssel"), "invalidPublicKeyError" : MessageLookupByLibrary.simpleMessage("Ungültiger öffentl. Schlüssel"), "invalidReceivingAccountError" : MessageLookupByLibrary.simpleMessage("Ungültiger Empfänger"), "keyCopiedButton" : MessageLookupByLibrary.simpleMessage("Schlüssel kopiert"), "keyTypeNotSupportedHeader" : MessageLookupByLibrary.simpleMessage("Schlüssel nicht unterstützt"), "keyTypeNotSupportedParagraph" : MessageLookupByLibrary.simpleMessage("Dieser Schlüssel-Typ wird leider nicht unterstützt. Du kannst einen neuen privaten Schlüssel anlegen und das Konto mit Hilfe einer anderen Wallet auf diesen übertragen."), "languageColonHeader" : MessageLookupByLibrary.simpleMessage("Sprache:"), "languageHeader" : MessageLookupByLibrary.simpleMessage("Sprache"), "listAccountForSaleHeader" : MessageLookupByLibrary.simpleMessage("Konto verkaufen"), "listAccountForSaleOPDetails" : MessageLookupByLibrary.simpleMessage("Konto zum Verkauf (%1)"), "listForSaleButton" : MessageLookupByLibrary.simpleMessage("Verkaufen"), "listForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Trage 1. den Kaufpreis und 2. den Empfänger des Kaufpreises für dieses Konto ein."), "listForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Verkaufen"), "listedForSaleHeader" : MessageLookupByLibrary.simpleMessage("Zu verkaufen"), "listedForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Das Konto steht jetzt zum Verkauf. Sobald es gekauft wurde sagen wir Dir Bescheid."), "listedForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Zum Verkauf gelistet"), "listingForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Bitte bestätige den Betrag und den Empfänger der Überweisung."), "listingForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Verkaufen"), "liveSupportButton" : MessageLookupByLibrary.simpleMessage("Hilfe"), "lock15Header" : MessageLookupByLibrary.simpleMessage("Nach %1 Minuten"), "lock1Header" : MessageLookupByLibrary.simpleMessage("Nach %1 Minute"), "lock30Header" : MessageLookupByLibrary.simpleMessage("Nach %1 Minuten"), "lock5Header" : MessageLookupByLibrary.simpleMessage("Nach %1 Minuten"), "lock60Header" : MessageLookupByLibrary.simpleMessage("Nach %1 Minuten"), "lockInstantHeader" : MessageLookupByLibrary.simpleMessage("Sofort"), "lockedHeader" : MessageLookupByLibrary.simpleMessage("Gesperrt"), "lockedUntilBlockOPDetails" : MessageLookupByLibrary.simpleMessage("Gesperrt bis Block"), "logoutFirstDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Wenn Du dich abmeldest, werden Deine Schlüssel und alle Blaise bezogenen Daten von diesem Gerät gelöscht. Hast Du Deinen privaten Schlüssel gesichert? Wenn nicht, wirst Du alles verlieren. Wenn ja, brauchst Du Dir keine Sorgen zu machen."), "logoutHeader" : MessageLookupByLibrary.simpleMessage("Abmelden"), "logoutSecondDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Bist Du Dir sicher, dass Du Deinen privaten Schlüssel gesichert hast? Wenn ja, brauchst Du Dir keine Sorgen zu machen."), "looksLikeEncryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("Siehst aus als wäre der private Schlüssel chiffriert. Bitte trage das Passwort zum dechiffrieren ein"), "manageHeader" : MessageLookupByLibrary.simpleMessage("Verwalten"), "manyFailedAttemptsParagraph" : MessageLookupByLibrary.simpleMessage("Zu viele fehlgeschlagene Entsperrversuche"), "maturationOPDetails" : MessageLookupByLibrary.simpleMessage("Alter (in Blöcken)"), "multioperationOPDetails" : MessageLookupByLibrary.simpleMessage("Multi-Vorgang (%1)"), "naOPDetails" : MessageLookupByLibrary.simpleMessage("n.a."), "nameChangedHeader" : MessageLookupByLibrary.simpleMessage("Name geändert"), "nameRequiredError" : MessageLookupByLibrary.simpleMessage("Name wird benötigt"), "nameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Name"), "newAccountNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Neuer Konto Name"), "newAccountParagraph" : MessageLookupByLibrary.simpleMessage("Dies ist Dein neues Konto. \nSobald Du Pascal empfängst oder sendest, werden die Überweisungen hier aufgelistet."), "newKeyBackUpConfirmParagraph" : MessageLookupByLibrary.simpleMessage("Hast Du Deinen privaten Schlüssel wirklich gesichert?"), "newKeySecurityParagraph" : MessageLookupByLibrary.simpleMessage("Im nächsten Schritt siehst Du Deinen privaten Schlüssel. Mit diesem Schlüssel hast Du Zugriff auf Deine Pascal. Es ist wichtig, dass Du diesen Schlüssel sicherst und mit niemanden teilst!"), "newNameOPDetails" : MessageLookupByLibrary.simpleMessage("Neuer Name"), "newPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Neues Passwort"), "newPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("Neuer privater Schlüssel"), "newPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Neues Schlüsselpaar"), "newPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("Nachfolgend findest Du Deinen neuen privaten Schlüssel. Es ist unbedingt erforderlich, dass Du diesen sicherst - aber bitte nicht als Screenshot oder in Klartext auf Deinem Computer oder Smartphone. Wir empfehlen Dir ihn auf einem Blatt Papier zu notieren."), "newPublicKeyOPDetails" : MessageLookupByLibrary.simpleMessage("Neuer öffentl. Schlüssel"), "newWalletGreetingParagraph" : MessageLookupByLibrary.simpleMessage("Willkommen bei der Blaise Wallet.\n Zum Start musst Du Dir ein Konto anlegen."), "nextButton" : MessageLookupByLibrary.simpleMessage("Weiter"), "noContactsToExportError" : MessageLookupByLibrary.simpleMessage("Keine Kontakte zum exportieren"), "noContactsToImportError" : MessageLookupByLibrary.simpleMessage("Keine Kontakte zum importieren"), "noGoBackButton" : MessageLookupByLibrary.simpleMessage("Nein, zurück"), "noHeader" : MessageLookupByLibrary.simpleMessage("Nein"), "noMatchPINParagraph" : MessageLookupByLibrary.simpleMessage("Die PINs stimmen nicht überein"), "noMatchPasswordError" : MessageLookupByLibrary.simpleMessage("Die Passwörter stimmen nicht überein"), "noResultsFound" : MessageLookupByLibrary.simpleMessage("Keine Ergebnisse"), "noperationOPDetails" : MessageLookupByLibrary.simpleMessage("Konto Vorgangsnummer"), "notificationsHeader" : MessageLookupByLibrary.simpleMessage("Benachrichtigungen"), "nullOPDetails" : MessageLookupByLibrary.simpleMessage("null"), "offHeader" : MessageLookupByLibrary.simpleMessage("Aus"), "okayButton" : MessageLookupByLibrary.simpleMessage("Okay"), "okayGoBackButton" : MessageLookupByLibrary.simpleMessage("Okay, zurück"), "onHeader" : MessageLookupByLibrary.simpleMessage("An"), "opblockOPDetails" : MessageLookupByLibrary.simpleMessage("Position in Block"), "openInExplorerButton" : MessageLookupByLibrary.simpleMessage("Im explorer anzeigen"), "operationDetailsButton" : MessageLookupByLibrary.simpleMessage("Vorgangs-Details"), "operationsHeader" : MessageLookupByLibrary.simpleMessage("Vorgänge"), "ophashOPDetails" : MessageLookupByLibrary.simpleMessage("Vorgangs-Prüfsumme"), "optxtOPDetails" : MessageLookupByLibrary.simpleMessage("Vorgangs-Info"), "optypeOPDetails" : MessageLookupByLibrary.simpleMessage("Vorgangstyp"), "otherOperationsHeader" : MessageLookupByLibrary.simpleMessage("Andere Vorgänge"), "passwordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Passwort"), "payloadOPDetails" : MessageLookupByLibrary.simpleMessage("Betreff"), "payloadTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Betreff"), "pendingHeader" : MessageLookupByLibrary.simpleMessage("ausstehend"), "phoneNumberTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Telefon-Nummer"), "preferencesHeader" : MessageLookupByLibrary.simpleMessage("Konfiguration"), "priceRequiredError" : MessageLookupByLibrary.simpleMessage("Preis wird benötigt"), "priceTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Preis"), "privacyPolicyHeader" : MessageLookupByLibrary.simpleMessage("Datenschutzrichtlinie"), "privateKeySheetHeader" : MessageLookupByLibrary.simpleMessage("Privater Schlüssel"), "privateKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Privater Schlüssel"), "privateSaleHeader" : MessageLookupByLibrary.simpleMessage("Privater Verkauf"), "publicKeyParagraph" : MessageLookupByLibrary.simpleMessage("Nachfolgend siehst Du Deinen öffentlichen Schlüssel, der mit jedem geteilt und zur Verifizierung Deiner Vorgänge genutzt werden kann."), "publicKeySheetHeader" : MessageLookupByLibrary.simpleMessage("Öffentlicher Schlüssel"), "publicKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Öffentlicher Schlüssel"), "receiveAccountButton" : MessageLookupByLibrary.simpleMessage("Konto erhalten"), "receiveAnAccountButton" : MessageLookupByLibrary.simpleMessage("Ein Konto erhalten"), "receiveButton" : MessageLookupByLibrary.simpleMessage("Empfangen"), "receivedHeader" : MessageLookupByLibrary.simpleMessage("Empfangen"), "receivingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Empfänger"), "receivingAccountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Empfänger"), "recoverFundsOPDetails" : MessageLookupByLibrary.simpleMessage("Recover Funds (%1)"), "removedFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("%1 aus den Kontakten entfernt"), "requestButton" : MessageLookupByLibrary.simpleMessage("Anfrage"), "requestSheetHeader" : MessageLookupByLibrary.simpleMessage("Anfragen"), "scanQRCodeButton" : MessageLookupByLibrary.simpleMessage("QR Code scannen"), "searchAccountNameButton" : MessageLookupByLibrary.simpleMessage("Suche Name"), "searchForNameButton" : MessageLookupByLibrary.simpleMessage("Suche nach Namen"), "searchNameButton" : MessageLookupByLibrary.simpleMessage("Suche Name"), "securityFirstHeader" : MessageLookupByLibrary.simpleMessage("Sicherheit zuerst!"), "securityHeader" : MessageLookupByLibrary.simpleMessage("Sicherheit"), "sellerAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Verkäufer"), "sendAmountOPDetails" : MessageLookupByLibrary.simpleMessage("Betrag senden"), "sendButton" : MessageLookupByLibrary.simpleMessage("Senden"), "sendConfirmationButton" : MessageLookupByLibrary.simpleMessage("Bestätigung senden"), "sendSheetHeader" : MessageLookupByLibrary.simpleMessage("Senden"), "sendingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Absender"), "sendingConfirmParagraph" : MessageLookupByLibrary.simpleMessage("Bitte bestätige die Überweisungsdaten."), "sendingSheetHeader" : MessageLookupByLibrary.simpleMessage("Sende"), "sentHeader" : MessageLookupByLibrary.simpleMessage("Gesendet"), "sentParagraph" : MessageLookupByLibrary.simpleMessage("Die Überweisung wurde erfolgreich ausgeführt."), "sentSheetHeader" : MessageLookupByLibrary.simpleMessage("Gesendet"), "setToDefaultButton" : MessageLookupByLibrary.simpleMessage("Zurücksetzen"), "settingsHeader" : MessageLookupByLibrary.simpleMessage("Einstellungen"), "shareHeader" : MessageLookupByLibrary.simpleMessage("Blaise teilen"), "showButton" : MessageLookupByLibrary.simpleMessage("Anzeigen"), "signeraccountOPDetails" : MessageLookupByLibrary.simpleMessage("Signierer"), "somethingWentWrongError" : MessageLookupByLibrary.simpleMessage("Etwas ist schief gelaufen, probiere es später erneut"), "successfullyImportedContactsParagraph" : MessageLookupByLibrary.simpleMessage("%1 Kontakte erfolgreich importiert"), "systemDefaultHeader" : MessageLookupByLibrary.simpleMessage("Standard"), "themeCopperHeader" : MessageLookupByLibrary.simpleMessage("Kupfer"), "themeDarkHeader" : MessageLookupByLibrary.simpleMessage("Dunkel"), "themeHeader" : MessageLookupByLibrary.simpleMessage("Thema"), "themeLightHeader" : MessageLookupByLibrary.simpleMessage("Hell"), "threeCharacterNameError" : MessageLookupByLibrary.simpleMessage("Muss aus mindestens 3 Zeichen bestehen"), "timeOPDetails" : MessageLookupByLibrary.simpleMessage("Zeitpunkt"), "totalBalanceHeader" : MessageLookupByLibrary.simpleMessage("Kontostand aller Konten"), "transactionOPDetails" : MessageLookupByLibrary.simpleMessage("Überweisung (%1)"), "transferAccountHeader" : MessageLookupByLibrary.simpleMessage("Konto übertragen"), "transferButton" : MessageLookupByLibrary.simpleMessage("Übertragen"), "transferParagraph" : MessageLookupByLibrary.simpleMessage("Trage nachfolgend den öffentl. Schlüssel ein, an den das Konto übertragen wird."), "transferSheetHeader" : MessageLookupByLibrary.simpleMessage("Übertragen"), "transferredHeader" : MessageLookupByLibrary.simpleMessage("Übertragen"), "transferredParagraph" : MessageLookupByLibrary.simpleMessage("Dein Konto wurde erfolgreich an den folgenden öffentl. Schlüssel übertragen."), "transferredSheetHeader" : MessageLookupByLibrary.simpleMessage("Übertragen"), "transferringParagraph" : MessageLookupByLibrary.simpleMessage("Bitte bestätige den nachfolgenden öffentl. Schlüssel, um das Konto zu übertragen."), "transferringSheetHeader" : MessageLookupByLibrary.simpleMessage("Übertrage"), "unconfirmedAccountHeader" : MessageLookupByLibrary.simpleMessage("Noch nicht bestätigtes Konto"), "unconfirmedAccountParagraph" : MessageLookupByLibrary.simpleMessage("Dies ist ein nicht bestätigtes Konto. Es wurde Dir bereits zugeordnet, aber Du musst noch einen Block lang warten. Das dauert in der Regel ungefähr 5 Minuten. Sobald der Block abgeschlossen ist, hast Du vollen Zugriff auf dieses Konto."), "undefinedHeader" : MessageLookupByLibrary.simpleMessage("Unbekannt"), "unencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Klartext Schlüssel"), "uninstallDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Wenn Du Dein Gerät verlierst oder die Blaise Wallet deinstallierst, brauchst Du Deinen privaten Schlüssel für die Wiederherstellung."), "unknownOPDetails" : MessageLookupByLibrary.simpleMessage("Unbekannt (%1)"), "unlockButton" : MessageLookupByLibrary.simpleMessage("Entsperren"), "unlockWithBiometricsButton" : MessageLookupByLibrary.simpleMessage("Mit Biometrie entsperren"), "unlockWithPINButton" : MessageLookupByLibrary.simpleMessage("Mit PIN entsperren"), "urlChangedToParagraph" : MessageLookupByLibrary.simpleMessage("Pascal-Server geändert zu %1"), "viewPublicKeyHeader" : MessageLookupByLibrary.simpleMessage("Öffentlichen Schlüssel anzeigen"), "warningHeader" : MessageLookupByLibrary.simpleMessage("Warnung"), "welcomeParagraph" : MessageLookupByLibrary.simpleMessage("Willkommen bei der Blaise Wallet. Um zu starten musst Du entweder einen neuen privaten Schlüssel generieren oder einen bestehenden importieren."), "yesAddFeeButton" : MessageLookupByLibrary.simpleMessage("Ja, Gebühr hinzufügen"), "yesHeader" : MessageLookupByLibrary.simpleMessage("Ja"), "yesImSureButton" : MessageLookupByLibrary.simpleMessage("Ja, ich bin sicher"), "zeroAmountError" : MessageLookupByLibrary.simpleMessage("Betrag kann nicht 0 sein"), "zeroPriceError" : MessageLookupByLibrary.simpleMessage("Preis kann nicht 0 sein") }; } ================================================ FILE: lib/l10n/messages_en.dart ================================================ // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart // This is a library that provides messages for a en locale. All the // messages from the main program should be duplicated here with the same // function name. // Ignore issues from commonly used lints in this file. // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases // ignore_for_file:unused_import, file_names import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; final messages = new MessageLookup(); typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'en'; final messages = _notInlinedMessages(_notInlinedMessages); static _notInlinedMessages(_) => { "accountBalanceHeader" : MessageLookupByLibrary.simpleMessage("Account Balance"), "accountOPDetails" : MessageLookupByLibrary.simpleMessage("account"), "accountPriceOPDetails" : MessageLookupByLibrary.simpleMessage("Account Price"), "accountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Account"), "accountToSendFromHeader" : MessageLookupByLibrary.simpleMessage("Account to Send From"), "accountsHeader" : MessageLookupByLibrary.simpleMessage("Accounts"), "addADurationButton" : MessageLookupByLibrary.simpleMessage("+ Add a Duration"), "addAPayloadButton" : MessageLookupByLibrary.simpleMessage("+ Add a Payload"), "addContactButton" : MessageLookupByLibrary.simpleMessage("Add Contact"), "addContactSheetHeader" : MessageLookupByLibrary.simpleMessage("Add Contact"), "addFeeHeader" : MessageLookupByLibrary.simpleMessage("Add Fee"), "addToContactsButton" : MessageLookupByLibrary.simpleMessage("Add to Contacts"), "addedToContactsParagraph" : MessageLookupByLibrary.simpleMessage("%1 added to contacts"), "addressTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Address"), "amountRequiredError" : MessageLookupByLibrary.simpleMessage("Amount is required"), "amountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Amount"), "areYouSureHeader" : MessageLookupByLibrary.simpleMessage("Are You Sure?"), "authenticateOnLaunchHeader" : MessageLookupByLibrary.simpleMessage("Authenticate on Launch"), "authenticateToBackUpParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to back up private key"), "authenticateToChangeNameParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to change account name to \"%1\""), "authenticateToCreatePrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to create private sale"), "authenticateToDelistParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to delist the account from sale"), "authenticateToListForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to list account for sale"), "authenticateToSendParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to send %1 Pascal"), "authenticateToTransferParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to transfer account"), "authenticateToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to Unlock Blaise"), "authenticationBiometricsHeader" : MessageLookupByLibrary.simpleMessage("Biometrics"), "authenticationMethodHeader" : MessageLookupByLibrary.simpleMessage("Authentication Method"), "authenticationPINHeader" : MessageLookupByLibrary.simpleMessage("PIN"), "automaticallyLockHeader" : MessageLookupByLibrary.simpleMessage("Automatically Lock"), "backUpKeyHeader" : MessageLookupByLibrary.simpleMessage("Back Up Your Key!"), "backUpPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Back Up Private Key"), "backUpSheetHeader" : MessageLookupByLibrary.simpleMessage("Back Up"), "backupEncryptedKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("Below is your encrypted private key. It is protected by a password. You can store it safely on a password manager for your convenience."), "backupEncryptedKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("Since it is encrypted with your password, if you lose or forget your password, you won\'t be able to decrypt it and access your funds."), "backupKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("You have 2 options for backing up your private key:"), "backupKeyFourthParagraph" : MessageLookupByLibrary.simpleMessage("We recommend storing the unencrypted version offline, by writing it on a piece of paper. You can store the encrypted version on a password manager for your convenience."), "backupKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("1- Encrypted, which means it is protected by a password."), "backupKeyThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- Unencrypted, which means it is raw and not protected by a password."), "backupUnencryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("Below is your unencrypted private key. It is not protected by a password, which means it is crucial that you store it somewhere safe and offline. We recommend writing it on a piece of paper."), "balanceHeader" : MessageLookupByLibrary.simpleMessage("Balance"), "blockOPDetails" : MessageLookupByLibrary.simpleMessage("block"), "blockchainRewardOPDetails" : MessageLookupByLibrary.simpleMessage("Blockchain Reward (%1)"), "borrowAccountParagraph" : MessageLookupByLibrary.simpleMessage("To buy an account, first you’ll need to borrow one for free. If you send at least %1 Pascal (%2) to the account within %3 days, the account will be yours and %1 Pascal will be deducted from your balance automatically.\nOtherwise, it’ll return back to us at the end of %3 days and won’t belong to your wallet anymore.\nIt is recommended you only send a small amount of coins until you own the account."), "borrowAnAccountButton" : MessageLookupByLibrary.simpleMessage("Borrow An Account"), "borrowStarted" : MessageLookupByLibrary.simpleMessage("Purchase Started for %1"), "borrowedAccountHeader" : MessageLookupByLibrary.simpleMessage("Borrowed Account"), "borrowedAccountPaidParagraph" : MessageLookupByLibrary.simpleMessage("Your account has been purchased!\nThe transfer is currently processing. This process usually takes about 15 minutes, in some cases it may take slightly longer."), "borrowedAccountParagraph" : MessageLookupByLibrary.simpleMessage("This is a borrowed account.\nIf you send at least %1 Pascal to it in the next %2 days, %3 hours, and %4 minutes, it’ll be yours."), "borrowedHeader" : MessageLookupByLibrary.simpleMessage("Borrowed"), "borrowedTransferredHeader" : MessageLookupByLibrary.simpleMessage("Transfer Pending"), "buyAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Buy Account (%1)"), "buyAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Buy Account"), "buyAnAccountButton" : MessageLookupByLibrary.simpleMessage("Buy an Account"), "cancelButton" : MessageLookupByLibrary.simpleMessage("Cancel"), "cantSendToYourselfError" : MessageLookupByLibrary.simpleMessage("Can\'t send to yourself"), "changeAccountInfoOPDetails" : MessageLookupByLibrary.simpleMessage("Change Account Info (%1)"), "changeAccountNameHeader" : MessageLookupByLibrary.simpleMessage("Change Account Name"), "changeDaemonButton" : MessageLookupByLibrary.simpleMessage("Change Daemon"), "changeDaemonParagraph" : MessageLookupByLibrary.simpleMessage("Enter an address to use a different Pascal daemon for RPC requests."), "changeDaemonSheetHeader" : MessageLookupByLibrary.simpleMessage("Change Daemon"), "changeKeyOPDetails" : MessageLookupByLibrary.simpleMessage("Change key (%1)"), "changeKeySignedOPDetails" : MessageLookupByLibrary.simpleMessage("Change Key Signed (%1)"), "changeNameButton" : MessageLookupByLibrary.simpleMessage("Change Name"), "changeNameParagraph" : MessageLookupByLibrary.simpleMessage("Enter a name below to change your account\'s name."), "changeNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Change Name"), "changedNameParagraph" : MessageLookupByLibrary.simpleMessage("Your account name has been changed successfully."), "changedNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Changed"), "changingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Changing Account"), "changingNameParagraph" : MessageLookupByLibrary.simpleMessage("Confirm your new account name to proceed."), "changingNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Changing"), "checkOutBlaiseParagraph" : MessageLookupByLibrary.simpleMessage("Check out Blaise! Simple, sleek & secure Pascal wallet for iOS and Android: https://blaisewallet.com"), "closeButton" : MessageLookupByLibrary.simpleMessage("Close"), "confirmButton" : MessageLookupByLibrary.simpleMessage("Confirm"), "confirmPINParagraph" : MessageLookupByLibrary.simpleMessage("Confirm your PIN"), "confirmPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Confirm Password"), "confirmTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Confirm"), "confirmationCodeError" : MessageLookupByLibrary.simpleMessage("Failed to verify code, ensure you\'ve entered it correctly"), "confirmationCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Confirmation Code"), "connectingHeader" : MessageLookupByLibrary.simpleMessage("Connecting"), "contactAlreadyExistsError" : MessageLookupByLibrary.simpleMessage("Contact already exists"), "contactDoesntExistError" : MessageLookupByLibrary.simpleMessage("Contact doesn\'t exist"), "contactNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Contact Name"), "contactSheetHeader" : MessageLookupByLibrary.simpleMessage("Contact"), "contactsHeader" : MessageLookupByLibrary.simpleMessage("Contacts"), "copiedAddressButton" : MessageLookupByLibrary.simpleMessage("Address Copied"), "copiedButton" : MessageLookupByLibrary.simpleMessage("Copied"), "copyAddressButton" : MessageLookupByLibrary.simpleMessage("Copy Address"), "copyButton" : MessageLookupByLibrary.simpleMessage("Copy"), "copyEncryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Copy Encrypted Key"), "copyKeyButton" : MessageLookupByLibrary.simpleMessage("Copy Key"), "copyPublicKeyButton" : MessageLookupByLibrary.simpleMessage("Copy Public Key"), "copyUnencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Copy Unencrypted Key"), "countryCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Country Code"), "createPINParagraph" : MessageLookupByLibrary.simpleMessage("Create a 6-digit PIN"), "createPrivateSaleButton" : MessageLookupByLibrary.simpleMessage("Create Private Sale"), "createPrivateSaleHeader" : MessageLookupByLibrary.simpleMessage("Create Private Sale"), "createPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Enter a price, a receiving account, and a public key below to create a private sale for this account."), "createPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Private Sale"), "createdPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("The private sale has been created successfully. We’ll let you know if it is bought."), "createdPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Created"), "creatingPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Confirm the information below."), "creatingPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Creating"), "currencyHeader" : MessageLookupByLibrary.simpleMessage("Currency"), "daemonHeader" : MessageLookupByLibrary.simpleMessage("Daemon"), "decryptAndImportKeyHeader" : MessageLookupByLibrary.simpleMessage("Decrypt & Import"), "defaultHeader" : MessageLookupByLibrary.simpleMessage("Default"), "deletePrivateKeyAndLogoutButton" : MessageLookupByLibrary.simpleMessage("Delete Private Key\nAnd Logout"), "delistAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Delist Account (%1)"), "delistFromSaleHeader" : MessageLookupByLibrary.simpleMessage("Delist From Sale"), "delistFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("Confirm that you would like to delist this account from sale."), "delistedFromSaleHeader" : MessageLookupByLibrary.simpleMessage("Delisted From Sale"), "delistedFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("Your account has been successfully delisted from sale."), "delistedHeader" : MessageLookupByLibrary.simpleMessage("Delisted"), "delistedSheetHeader" : MessageLookupByLibrary.simpleMessage("Delisted"), "delistingSheetHeader" : MessageLookupByLibrary.simpleMessage("Delisting"), "didNotGetResponseError" : MessageLookupByLibrary.simpleMessage("Did not get a response from server"), "durationTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Duration"), "emptyPasswordError" : MessageLookupByLibrary.simpleMessage("Password can\'t be empty"), "encryptButton" : MessageLookupByLibrary.simpleMessage("Encrypt"), "encryptKeyParagraph" : MessageLookupByLibrary.simpleMessage("Create a new password to encrypt your private key."), "encryptPayloadHeader" : MessageLookupByLibrary.simpleMessage("Encrypt Payload"), "encryptSheetHeader" : MessageLookupByLibrary.simpleMessage("Encrypt"), "encryptThePayloadHeader" : MessageLookupByLibrary.simpleMessage("Encrypt the Payload"), "encryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Encrypted Key"), "enterConfirmationCodeParagraph" : MessageLookupByLibrary.simpleMessage("We have sent you a confirmation code, please enter it below."), "enterPINParagraph" : MessageLookupByLibrary.simpleMessage("Enter PIN"), "enterPINToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("Enter PIN to unlock Blaise"), "enterPhoneNumberParagraph" : MessageLookupByLibrary.simpleMessage("Enter your phone number below."), "failedToEncryptPayloadError" : MessageLookupByLibrary.simpleMessage("Failed to encrypt the payload"), "failedToImportContactsError" : MessageLookupByLibrary.simpleMessage("Failed to import contacts"), "failedToRemoveFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("Failed to remove %1 from contacts"), "feeColonHeader" : MessageLookupByLibrary.simpleMessage("Fee:"), "feeConfirmAmountParagraph" : MessageLookupByLibrary.simpleMessage("Please confirm the addition of %1 Pascal fee to this operation to continue."), "feeOPDetails" : MessageLookupByLibrary.simpleMessage("fee"), "feeRequiredParagraph" : MessageLookupByLibrary.simpleMessage("This operation requires a fee."), "feeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Fee"), "forSaleHeader" : MessageLookupByLibrary.simpleMessage("For Sale"), "freeAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Free Account"), "freepasaComplete" : MessageLookupByLibrary.simpleMessage("Success, your new account will be available after 1 network confirmation"), "getAFreeAccountButton" : MessageLookupByLibrary.simpleMessage("Get a Free Account"), "getAccountFirstParagraph" : MessageLookupByLibrary.simpleMessage("There are 2 options for getting your first account:"), "getAccountSecondParagraph" : MessageLookupByLibrary.simpleMessage("1- You can get a free account using your phone number. Only 1 account per phone number is allowed."), "getAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Get Account"), "getAccountThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- You can buy as many accounts as you want for %1 Pascal (%2)."), "getAccountThirdParagraphAlternative" : MessageLookupByLibrary.simpleMessage("2- You can buy an account for %1 Pascal (%2). Buying only 1 account is allowed per user."), "getAccountThirdParagraphAlternative2" : MessageLookupByLibrary.simpleMessage("2- You can buy an account for %1 Pascal (%2). You can buy up to %3 accounts."), "getAnAccountButton" : MessageLookupByLibrary.simpleMessage("Get an Account"), "goBackButton" : MessageLookupByLibrary.simpleMessage("Go Back"), "gotItButton" : MessageLookupByLibrary.simpleMessage("Got It!"), "hideButton" : MessageLookupByLibrary.simpleMessage("Hide"), "iHaveBackedItUpButton" : MessageLookupByLibrary.simpleMessage("I\'ve Backed It Up"), "importButton" : MessageLookupByLibrary.simpleMessage("Import"), "importPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("Import Private Key"), "importPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Import Private Key"), "importPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("Enter your private key below."), "insufficientBalanceError" : MessageLookupByLibrary.simpleMessage("Insufficient balance"), "invalidAccountError" : MessageLookupByLibrary.simpleMessage("Invalid account"), "invalidAccountNameError" : MessageLookupByLibrary.simpleMessage("Invalid account name"), "invalidAddressError" : MessageLookupByLibrary.simpleMessage("Invalid address"), "invalidDestinationError" : MessageLookupByLibrary.simpleMessage("Invalid destination"), "invalidPINParagraph" : MessageLookupByLibrary.simpleMessage("Invalid PIN"), "invalidPasswordError" : MessageLookupByLibrary.simpleMessage("Invalid password"), "invalidPhoneNumberParagraph" : MessageLookupByLibrary.simpleMessage("Phone number is not valid"), "invalidPrivateKeyError" : MessageLookupByLibrary.simpleMessage("Invalid private key"), "invalidPublicKeyError" : MessageLookupByLibrary.simpleMessage("Invalid public key"), "invalidReceivingAccountError" : MessageLookupByLibrary.simpleMessage("Invalid receiving account"), "keyCopiedButton" : MessageLookupByLibrary.simpleMessage("Key Copied"), "keyTypeNotSupportedHeader" : MessageLookupByLibrary.simpleMessage("Key Not Supported"), "keyTypeNotSupportedParagraph" : MessageLookupByLibrary.simpleMessage("This type of private key is not yet supported by Blaise. You may create a new private key and transfer your accounts to it using a different wallet."), "languageColonHeader" : MessageLookupByLibrary.simpleMessage("Language:"), "languageHeader" : MessageLookupByLibrary.simpleMessage("Language"), "listAccountForSaleHeader" : MessageLookupByLibrary.simpleMessage("List Account For Sale"), "listAccountForSaleOPDetails" : MessageLookupByLibrary.simpleMessage("List Account for Sale (%1)"), "listForSaleButton" : MessageLookupByLibrary.simpleMessage("List for Sale"), "listForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Enter a price and an account that will be receiving the payment to list this account for sale."), "listForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("List For Sale"), "listedForSaleHeader" : MessageLookupByLibrary.simpleMessage("Listed For Sale"), "listedForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Your account has been successfully listed for sale. We’ll let you know if someone buys it."), "listedForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Listed"), "listingForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Confirm the price and the account that will be receiving the payment."), "listingForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Listing"), "liveSupportButton" : MessageLookupByLibrary.simpleMessage("Support"), "lock15Header" : MessageLookupByLibrary.simpleMessage("After %1 minutes"), "lock1Header" : MessageLookupByLibrary.simpleMessage("After %1 minute"), "lock30Header" : MessageLookupByLibrary.simpleMessage("After %1 minutes"), "lock5Header" : MessageLookupByLibrary.simpleMessage("After %1 minutes"), "lock60Header" : MessageLookupByLibrary.simpleMessage("After %1 minutes"), "lockInstantHeader" : MessageLookupByLibrary.simpleMessage("Instantly"), "lockedHeader" : MessageLookupByLibrary.simpleMessage("Locked"), "lockedUntilBlockOPDetails" : MessageLookupByLibrary.simpleMessage("Locked Until Block"), "logoutFirstDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Logging out will remove your private key and all Blaise related data from this device. If your private key is not backed up, you will never be able to access your funds again. If your private key is backed up, you have nothing to worry about."), "logoutHeader" : MessageLookupByLibrary.simpleMessage("Logout"), "logoutSecondDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Are you sure that you\'ve backed up your private key? As long as you\'ve backed up your private key, you have nothing to worry about."), "looksLikeEncryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("This looks like an encrypted private key, please enter the password to decrypt and import it."), "manageHeader" : MessageLookupByLibrary.simpleMessage("Manage"), "manyFailedAttemptsParagraph" : MessageLookupByLibrary.simpleMessage("Too many failed unlock attempts"), "maturationOPDetails" : MessageLookupByLibrary.simpleMessage("maturation"), "multioperationOPDetails" : MessageLookupByLibrary.simpleMessage("Multioperation (%1)"), "naOPDetails" : MessageLookupByLibrary.simpleMessage("N/A"), "nameChangedHeader" : MessageLookupByLibrary.simpleMessage("Name Changed"), "nameRequiredError" : MessageLookupByLibrary.simpleMessage("Name is required"), "nameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Name"), "newAccountNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("New Account Name"), "newAccountParagraph" : MessageLookupByLibrary.simpleMessage("This is your new account.\nOnce you receive Pascal, operations will show up like below."), "newKeyBackUpConfirmParagraph" : MessageLookupByLibrary.simpleMessage("Are you sure that you have backed up your new wallet’s private key?"), "newKeySecurityParagraph" : MessageLookupByLibrary.simpleMessage("In the next screen, you\'ll see your new private key. It is a password to access your funds. It is crucial that you back it up and never share it with anyone."), "newNameOPDetails" : MessageLookupByLibrary.simpleMessage("New Name"), "newPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("New Password"), "newPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("New Private Key"), "newPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("New Private Key"), "newPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("Below is your new wallet’s private key. It is crucial that you backup your private key and never store it as plaintext or a screenshot. We recommend writing it on a piece of paper and storing it offline."), "newPublicKeyOPDetails" : MessageLookupByLibrary.simpleMessage("New Public Key"), "newWalletGreetingParagraph" : MessageLookupByLibrary.simpleMessage("Welcome to Blaise Wallet.\nYou can start by getting an account."), "nextButton" : MessageLookupByLibrary.simpleMessage("Next"), "noContactsToExportError" : MessageLookupByLibrary.simpleMessage("No contacts to export"), "noContactsToImportError" : MessageLookupByLibrary.simpleMessage("No contacts to import"), "noGoBackButton" : MessageLookupByLibrary.simpleMessage("No, Go Back"), "noHeader" : MessageLookupByLibrary.simpleMessage("No"), "noMatchPINParagraph" : MessageLookupByLibrary.simpleMessage("PINs do not match"), "noMatchPasswordError" : MessageLookupByLibrary.simpleMessage("Passwords don\'t match"), "noResultsFound" : MessageLookupByLibrary.simpleMessage("No results found"), "noperationOPDetails" : MessageLookupByLibrary.simpleMessage("n_operation"), "notificationsHeader" : MessageLookupByLibrary.simpleMessage("Notifications"), "nullOPDetails" : MessageLookupByLibrary.simpleMessage("null"), "offHeader" : MessageLookupByLibrary.simpleMessage("Off"), "okayButton" : MessageLookupByLibrary.simpleMessage("Okay"), "okayGoBackButton" : MessageLookupByLibrary.simpleMessage("Okay, Go Back"), "onHeader" : MessageLookupByLibrary.simpleMessage("On"), "opblockOPDetails" : MessageLookupByLibrary.simpleMessage("opblock"), "openInExplorerButton" : MessageLookupByLibrary.simpleMessage("Open in Explorer"), "operationDetailsButton" : MessageLookupByLibrary.simpleMessage("Operation Details"), "operationsHeader" : MessageLookupByLibrary.simpleMessage("Operations"), "ophashOPDetails" : MessageLookupByLibrary.simpleMessage("ophash"), "optxtOPDetails" : MessageLookupByLibrary.simpleMessage("optxt"), "optypeOPDetails" : MessageLookupByLibrary.simpleMessage("optype"), "otherOperationsHeader" : MessageLookupByLibrary.simpleMessage("Other Operations"), "passwordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Password"), "payloadOPDetails" : MessageLookupByLibrary.simpleMessage("Payload"), "payloadTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Payload"), "pendingHeader" : MessageLookupByLibrary.simpleMessage("Pending"), "phoneNumberTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Phone Number"), "preferencesHeader" : MessageLookupByLibrary.simpleMessage("Preferences"), "priceRequiredError" : MessageLookupByLibrary.simpleMessage("Price is required"), "priceTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Price"), "privacyPolicyHeader" : MessageLookupByLibrary.simpleMessage("Privacy Policy"), "privateKeySheetHeader" : MessageLookupByLibrary.simpleMessage("Private Key"), "privateKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Private Key"), "privateSaleHeader" : MessageLookupByLibrary.simpleMessage("Private Sale"), "publicKeyParagraph" : MessageLookupByLibrary.simpleMessage("Below is your public key. As the name suggests, it is intended to be shared publicly and prove that a particular operation belongs to your private key."), "publicKeySheetHeader" : MessageLookupByLibrary.simpleMessage("Public Key"), "publicKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Public Key"), "receiveAccountButton" : MessageLookupByLibrary.simpleMessage("Receive Account"), "receiveAnAccountButton" : MessageLookupByLibrary.simpleMessage("Receive an Account"), "receiveButton" : MessageLookupByLibrary.simpleMessage("Receive"), "receivedHeader" : MessageLookupByLibrary.simpleMessage("Received"), "receivingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Receiving Account"), "receivingAccountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Receiving Account"), "recoverFundsOPDetails" : MessageLookupByLibrary.simpleMessage("Recover Funds (%1)"), "removedFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("Removed %1 from contacts"), "requestButton" : MessageLookupByLibrary.simpleMessage("Request"), "requestSheetHeader" : MessageLookupByLibrary.simpleMessage("Request"), "scanQRCodeButton" : MessageLookupByLibrary.simpleMessage("Scan QR Code"), "searchAccountNameButton" : MessageLookupByLibrary.simpleMessage("Search Account Name"), "searchForNameButton" : MessageLookupByLibrary.simpleMessage("Search For Name"), "searchNameButton" : MessageLookupByLibrary.simpleMessage("Search Name"), "securityFirstHeader" : MessageLookupByLibrary.simpleMessage("Security First!"), "securityHeader" : MessageLookupByLibrary.simpleMessage("Security"), "sellerAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Seller Account"), "sendAmountOPDetails" : MessageLookupByLibrary.simpleMessage("Send Amount"), "sendButton" : MessageLookupByLibrary.simpleMessage("Send"), "sendConfirmationButton" : MessageLookupByLibrary.simpleMessage("Send Confirmation"), "sendSheetHeader" : MessageLookupByLibrary.simpleMessage("Send"), "sendingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Sending Account"), "sendingConfirmParagraph" : MessageLookupByLibrary.simpleMessage("Confirm the transaction details to send."), "sendingSheetHeader" : MessageLookupByLibrary.simpleMessage("Sending"), "sentHeader" : MessageLookupByLibrary.simpleMessage("Sent"), "sentParagraph" : MessageLookupByLibrary.simpleMessage("Transaction has been sent succesfully."), "sentSheetHeader" : MessageLookupByLibrary.simpleMessage("Sent"), "setToDefaultButton" : MessageLookupByLibrary.simpleMessage("Set to Default"), "settingsHeader" : MessageLookupByLibrary.simpleMessage("Settings"), "shareHeader" : MessageLookupByLibrary.simpleMessage("Share Blaise"), "showButton" : MessageLookupByLibrary.simpleMessage("Show"), "signeraccountOPDetails" : MessageLookupByLibrary.simpleMessage("signer_account"), "somethingWentWrongError" : MessageLookupByLibrary.simpleMessage("Something went wrong, please try again later"), "successfullyImportedContactsParagraph" : MessageLookupByLibrary.simpleMessage("Successfully imported %1 contacts"), "supportButton" : MessageLookupByLibrary.simpleMessage("Support"), "systemDefaultHeader" : MessageLookupByLibrary.simpleMessage("System Default"), "themeCopperHeader" : MessageLookupByLibrary.simpleMessage("Copper"), "themeDarkHeader" : MessageLookupByLibrary.simpleMessage("Dark"), "themeHeader" : MessageLookupByLibrary.simpleMessage("Theme"), "themeLightHeader" : MessageLookupByLibrary.simpleMessage("Light"), "threeCharacterNameError" : MessageLookupByLibrary.simpleMessage("Must be at least 3 characters"), "timeOPDetails" : MessageLookupByLibrary.simpleMessage("time"), "totalBalanceHeader" : MessageLookupByLibrary.simpleMessage("Total Balance"), "transactionOPDetails" : MessageLookupByLibrary.simpleMessage("Transaction (%1)"), "transferAccountHeader" : MessageLookupByLibrary.simpleMessage("Transfer Account"), "transferButton" : MessageLookupByLibrary.simpleMessage("Transfer"), "transferParagraph" : MessageLookupByLibrary.simpleMessage("Enter a public key below to transfer the ownership of this account to it."), "transferSheetHeader" : MessageLookupByLibrary.simpleMessage("Transfer"), "transferredHeader" : MessageLookupByLibrary.simpleMessage("Transferred"), "transferredParagraph" : MessageLookupByLibrary.simpleMessage("Your account has been transferred successfully to the public key below."), "transferredSheetHeader" : MessageLookupByLibrary.simpleMessage("Transferred"), "transferringParagraph" : MessageLookupByLibrary.simpleMessage("Confirm the public key below to transfer the ownership of this account to it."), "transferringSheetHeader" : MessageLookupByLibrary.simpleMessage("Transferring"), "unconfirmedAccountHeader" : MessageLookupByLibrary.simpleMessage("Unconfirmed Account"), "unconfirmedAccountParagraph" : MessageLookupByLibrary.simpleMessage("This is an unconfirmed account. It has been transferred to you, but there needs to be 1 network confirmation before you can use it. This usually takes about 5 minutes, once it\'s complete you\'ll be able to use this account."), "undefinedHeader" : MessageLookupByLibrary.simpleMessage("Undefined"), "unencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Unencrypted Key"), "uninstallDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("If you lose your device or uninstall Blaise Wallet, you\'ll need your private key to recover your funds."), "unknownOPDetails" : MessageLookupByLibrary.simpleMessage("Unknown (%1)"), "unlockButton" : MessageLookupByLibrary.simpleMessage("Unlock"), "unlockWithBiometricsButton" : MessageLookupByLibrary.simpleMessage("Unlock with Biometrics"), "unlockWithPINButton" : MessageLookupByLibrary.simpleMessage("Unlock with PIN"), "urlChangedToParagraph" : MessageLookupByLibrary.simpleMessage("URL changed to %1"), "viewPublicKeyHeader" : MessageLookupByLibrary.simpleMessage("View Public Key"), "warningHeader" : MessageLookupByLibrary.simpleMessage("Warning"), "welcomeParagraph" : MessageLookupByLibrary.simpleMessage("Welcome to Blaise Wallet. To begin, you can create a new private key or import one."), "yesAddFeeButton" : MessageLookupByLibrary.simpleMessage("Yes, Add Fee"), "yesHeader" : MessageLookupByLibrary.simpleMessage("Yes"), "yesImSureButton" : MessageLookupByLibrary.simpleMessage("Yes, I\'m Sure"), "zeroAmountError" : MessageLookupByLibrary.simpleMessage("Amount can\'t be 0"), "zeroPriceError" : MessageLookupByLibrary.simpleMessage("Price can\'t be 0") }; } ================================================ FILE: lib/l10n/messages_es.dart ================================================ // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart // This is a library that provides messages for a es locale. All the // messages from the main program should be duplicated here with the same // function name. // Ignore issues from commonly used lints in this file. // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases // ignore_for_file:unused_import, file_names import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; final messages = new MessageLookup(); typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'es'; final messages = _notInlinedMessages(_notInlinedMessages); static _notInlinedMessages(_) => { "accountBalanceHeader" : MessageLookupByLibrary.simpleMessage("Balance de cuenta"), "accountOPDetails" : MessageLookupByLibrary.simpleMessage("cuenta"), "accountPriceOPDetails" : MessageLookupByLibrary.simpleMessage("Precio de la cuenta"), "accountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Cuenta"), "accountToSendFromHeader" : MessageLookupByLibrary.simpleMessage("Cuenta desde la cual enviar"), "accountsHeader" : MessageLookupByLibrary.simpleMessage("Cuentas"), "addADurationButton" : MessageLookupByLibrary.simpleMessage("+ Agregar una duración"), "addAPayloadButton" : MessageLookupByLibrary.simpleMessage("+ Agregar un Payload"), "addContactButton" : MessageLookupByLibrary.simpleMessage("Agregar contacto"), "addContactSheetHeader" : MessageLookupByLibrary.simpleMessage("Agregar contacto"), "addFeeHeader" : MessageLookupByLibrary.simpleMessage("Agregar tarifa"), "addToContactsButton" : MessageLookupByLibrary.simpleMessage("Agregar a contactos"), "addedToContactsParagraph" : MessageLookupByLibrary.simpleMessage("%1 agregado a contactos"), "addressTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Dirección"), "amountRequiredError" : MessageLookupByLibrary.simpleMessage("El monto es obligatorio"), "amountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Monto"), "areYouSureHeader" : MessageLookupByLibrary.simpleMessage("¿Está seguro?"), "authenticateOnLaunchHeader" : MessageLookupByLibrary.simpleMessage("Autenticar al iniciar"), "authenticateToBackUpParagraph" : MessageLookupByLibrary.simpleMessage("Autenticar para hacer una copia de seguridad de la clave privada"), "authenticateToChangeNameParagraph" : MessageLookupByLibrary.simpleMessage("Autenticar para cambiar el nombre de la cuenta por \"%1\""), "authenticateToCreatePrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Autenticar para crear una venta privada"), "authenticateToDelistParagraph" : MessageLookupByLibrary.simpleMessage("Autenticar para quitar la cuenta de la venta"), "authenticateToListForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Autenticar para agregar la cuenta a la venta"), "authenticateToSendParagraph" : MessageLookupByLibrary.simpleMessage("Autenticar para enviar %1 Pascal"), "authenticateToTransferParagraph" : MessageLookupByLibrary.simpleMessage("Autenticar para transferir cuenta"), "authenticateToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("Autenticar para desbloquear a Blaise"), "authenticationBiometricsHeader" : MessageLookupByLibrary.simpleMessage("Datos biométricos"), "authenticationMethodHeader" : MessageLookupByLibrary.simpleMessage("Método de autenticación"), "authenticationPINHeader" : MessageLookupByLibrary.simpleMessage("PIN"), "automaticallyLockHeader" : MessageLookupByLibrary.simpleMessage("Bloqueo automático"), "backUpKeyHeader" : MessageLookupByLibrary.simpleMessage("¡Resguarde su clave!"), "backUpPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Respaldar clave privada"), "backUpSheetHeader" : MessageLookupByLibrary.simpleMessage("Respaldo"), "backupEncryptedKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("A continuación se muestra su clave privada encriptada. Está protegida por una contraseña. Puede almacenarla de forma segura en un administrador de contraseñas para su conveniencia."), "backupEncryptedKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("Como está encriptada con su contraseña, si la pierde o la olvida, no podrá descifrarla ni acceder a sus fondos."), "backupKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("Tiene dos opciones para respaldar su clave privada:"), "backupKeyFourthParagraph" : MessageLookupByLibrary.simpleMessage("Recomendamos almacenar la versión sin encriptar sin conexión, escribiéndola en una hoja de papel. Puede almacenar la versión encriptada en un administrador de contraseñas para su conveniencia."), "backupKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("1- Encriptada, significa que está protegida por una contraseña."), "backupKeyThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- Sin encriptar, lo que significa que se encuentra sin formato y no está protegida por una contraseña."), "backupUnencryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("A continuación se muestra su clave privada sin encriptar. No está protegida por una contraseña, lo que significa que es crucial que la guarde en un lugar seguro y sin conexión. Recomendamos escribirla en una hoja de papel."), "balanceHeader" : MessageLookupByLibrary.simpleMessage("Balance"), "blockOPDetails" : MessageLookupByLibrary.simpleMessage("bloque"), "blockchainRewardOPDetails" : MessageLookupByLibrary.simpleMessage("Recompensa Blockchain (%1)"), "borrowAccountParagraph" : MessageLookupByLibrary.simpleMessage("Para comprar una cuenta, primero deberá pedir prestada una de forma gratuita. Si envía al menos %1 Pascal (%2) a la cuenta dentro de %3 días, la cuenta será suya y %1 Pascal se deducirá de su saldo automáticamente. \nDe lo contrario, volverá a nosotros al final de %3 días y ya no pertenecerá a su billetera.\nSe recomienda solo enviar una pequeña cantidad de monedas hasta que posea la cuenta."), "borrowAnAccountButton" : MessageLookupByLibrary.simpleMessage("Pedir prestada una cuenta"), "borrowStarted" : MessageLookupByLibrary.simpleMessage("Compra iniciada por %1"), "borrowedAccountHeader" : MessageLookupByLibrary.simpleMessage("Cuenta prestada"), "borrowedAccountPaidParagraph" : MessageLookupByLibrary.simpleMessage("¡Su cuenta ha sido comprada!\nLa transferencia se está procesando actualmente. Este proceso generalmente toma unos 15 minutos, en algunos casos puede tardar un poco más."), "borrowedAccountParagraph" : MessageLookupByLibrary.simpleMessage("Esta es una cuenta prestada .\nSi le envía al menos %1 Pascal en los próximos %2 días,%3 horas y %4 minutos, será suya."), "borrowedHeader" : MessageLookupByLibrary.simpleMessage("Prestada"), "borrowedTransferredHeader" : MessageLookupByLibrary.simpleMessage("Transferencia pendiente"), "buyAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Comprar cuenta (%1)"), "buyAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Comprar cuenta"), "buyAnAccountButton" : MessageLookupByLibrary.simpleMessage("Comprar una cuenta"), "cancelButton" : MessageLookupByLibrary.simpleMessage("Cancelar"), "cantSendToYourselfError" : MessageLookupByLibrary.simpleMessage("No puede enviar a usted mismo"), "changeAccountInfoOPDetails" : MessageLookupByLibrary.simpleMessage("Cambiar información de la cuenta (%1)"), "changeAccountNameHeader" : MessageLookupByLibrary.simpleMessage("Cambiar nombre de cuenta"), "changeDaemonButton" : MessageLookupByLibrary.simpleMessage("Cambiar Daemon"), "changeDaemonParagraph" : MessageLookupByLibrary.simpleMessage("Ingrese una dirección para usar un daemon Pascal diferente para solicitudes RPC."), "changeDaemonSheetHeader" : MessageLookupByLibrary.simpleMessage("Cambiar daemon"), "changeKeyOPDetails" : MessageLookupByLibrary.simpleMessage("Cambiar clave (%1)"), "changeKeySignedOPDetails" : MessageLookupByLibrary.simpleMessage("Cambiar clave firmada (%1)"), "changeNameButton" : MessageLookupByLibrary.simpleMessage("Cambiar nombre"), "changeNameParagraph" : MessageLookupByLibrary.simpleMessage("Ingrese un nombre a continuación para modificar el nombre de su cuenta."), "changeNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Cambiar nombre"), "changedNameParagraph" : MessageLookupByLibrary.simpleMessage("Su nombre de cuenta ha sido modificado exitosamente."), "changedNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Cambiado"), "changingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Cambio de cuenta"), "changingNameParagraph" : MessageLookupByLibrary.simpleMessage("Confirme su nuevo nombre de cuenta para continuar."), "changingNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Cambiando"), "checkOutBlaiseParagraph" : MessageLookupByLibrary.simpleMessage("¡Vea Blaise! Cartera Pascal simple, elegante y segura para iOS y Android: https://blaisewallet.com"), "closeButton" : MessageLookupByLibrary.simpleMessage("Cerrar"), "confirmButton" : MessageLookupByLibrary.simpleMessage("Confirmar"), "confirmPINParagraph" : MessageLookupByLibrary.simpleMessage("Confirme su PIN"), "confirmPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Confirmar contraseña"), "confirmTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Confirmar"), "confirmationCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Código de confirmación"), "contactAlreadyExistsError" : MessageLookupByLibrary.simpleMessage("El contacto ya existe"), "contactDoesntExistError" : MessageLookupByLibrary.simpleMessage("El contacto no existe"), "contactNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Nombre de contacto"), "contactSheetHeader" : MessageLookupByLibrary.simpleMessage("Contacto"), "contactsHeader" : MessageLookupByLibrary.simpleMessage("Contactos"), "copiedAddressButton" : MessageLookupByLibrary.simpleMessage("Dirección copiada"), "copiedButton" : MessageLookupByLibrary.simpleMessage("Copiado"), "copyAddressButton" : MessageLookupByLibrary.simpleMessage("Copiar dirección"), "copyButton" : MessageLookupByLibrary.simpleMessage("Copiar"), "copyEncryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Copiar clave encriptada"), "copyKeyButton" : MessageLookupByLibrary.simpleMessage("Copiar clave"), "copyPublicKeyButton" : MessageLookupByLibrary.simpleMessage("Copiar clave pública"), "copyUnencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Copiar clave no encriptada"), "countryCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Código de país"), "createPINParagraph" : MessageLookupByLibrary.simpleMessage("Crear un PIN de 6 dígitos"), "createPrivateSaleButton" : MessageLookupByLibrary.simpleMessage("Crear venta privada"), "createPrivateSaleHeader" : MessageLookupByLibrary.simpleMessage("Crear venta privada"), "createPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Ingrese un precio, una cuenta receptora y una clave pública a continuación para crear una venta privada para esta cuenta."), "createPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Venta privada"), "createdPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("La venta privada se ha creado con éxito. Le haremos saber si se compra."), "createdPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Creada"), "creatingPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Confirme la información a continuación."), "creatingPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Creando"), "currencyHeader" : MessageLookupByLibrary.simpleMessage("Moneda"), "daemonHeader" : MessageLookupByLibrary.simpleMessage("Daemon"), "decryptAndImportKeyHeader" : MessageLookupByLibrary.simpleMessage("Descifrar e importar"), "defaultHeader" : MessageLookupByLibrary.simpleMessage("Por defecto"), "deletePrivateKeyAndLogoutButton" : MessageLookupByLibrary.simpleMessage("Borrar clave privada\ny cerrar sesión"), "delistAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Quitar cuenta de la venta (%1)"), "delistFromSaleHeader" : MessageLookupByLibrary.simpleMessage("Quitar de listado de venta"), "delistFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("Confirme que desea retirar de la venta esta cuenta."), "delistedFromSaleHeader" : MessageLookupByLibrary.simpleMessage("Retirada de la venta"), "delistedFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("Su cuenta ha sido retirada de la venta con éxito."), "delistedHeader" : MessageLookupByLibrary.simpleMessage("Retirada de llistado"), "delistedSheetHeader" : MessageLookupByLibrary.simpleMessage("Removida de la venta"), "delistingSheetHeader" : MessageLookupByLibrary.simpleMessage("Quietar de la venta"), "didNotGetResponseError" : MessageLookupByLibrary.simpleMessage("No se recibe respuesta del servidor"), "durationTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Duración"), "emptyPasswordError" : MessageLookupByLibrary.simpleMessage("La contraseña no puede estar vacía"), "encryptButton" : MessageLookupByLibrary.simpleMessage("Encriptar"), "encryptKeyParagraph" : MessageLookupByLibrary.simpleMessage("Crear una nueva contraseña para encriptar su clave privada."), "encryptPayloadHeader" : MessageLookupByLibrary.simpleMessage("Encriptar Payload"), "encryptSheetHeader" : MessageLookupByLibrary.simpleMessage("Encriptar"), "encryptThePayloadHeader" : MessageLookupByLibrary.simpleMessage("Encriptar el Payload"), "encryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Clave encriptada"), "enterConfirmationCodeParagraph" : MessageLookupByLibrary.simpleMessage("Hemos enviado un código de confirmación, por favor ingréselo a continuación."), "enterPINParagraph" : MessageLookupByLibrary.simpleMessage("Introduzca PIN"), "enterPINToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("Introduzca el PIN para desbloquear Blaise"), "enterPhoneNumberParagraph" : MessageLookupByLibrary.simpleMessage("Ingrese su número telefónico a continuación."), "failedToEncryptPayloadError" : MessageLookupByLibrary.simpleMessage("Error al encriptar el payload"), "failedToImportContactsError" : MessageLookupByLibrary.simpleMessage("Error al importar contactos"), "failedToRemoveFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("Error al remover %1 de contactos"), "feeColonHeader" : MessageLookupByLibrary.simpleMessage("Tarifa:"), "feeConfirmAmountParagraph" : MessageLookupByLibrary.simpleMessage("Confirme la adición de una comisión de %1 Pascal en operación para continuar."), "feeOPDetails" : MessageLookupByLibrary.simpleMessage("tarifa"), "feeRequiredParagraph" : MessageLookupByLibrary.simpleMessage("Está operación requiere una tarifa"), "feeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Tarifa"), "forSaleHeader" : MessageLookupByLibrary.simpleMessage("A la venta"), "freeAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Cuenta gratuita"), "getAFreeAccountButton" : MessageLookupByLibrary.simpleMessage("Obtener una cuenta gratuita"), "getAccountFirstParagraph" : MessageLookupByLibrary.simpleMessage("Existen dos opciones para obtener su primera cuenta:"), "getAccountSecondParagraph" : MessageLookupByLibrary.simpleMessage("1- Puede obtener una cuenta gratuita usando su número telefónico.\nSe permite una sola cuenta por número telefónico."), "getAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Obtener cuenta"), "getAccountThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- Puede comprar tantas cuentas como quiera por %1 Pascal (%2)."), "getAccountThirdParagraphAlternative" : MessageLookupByLibrary.simpleMessage("2- Puede comprar una cuenta por %1 Pascal (%2). Se permite comprar solo 1 cuenta por usuario."), "getAccountThirdParagraphAlternative2" : MessageLookupByLibrary.simpleMessage("2- Puede comprar una cuenta por %1 Pascal (%2). Puede comprar hasta %3 cuentas."), "getAnAccountButton" : MessageLookupByLibrary.simpleMessage("Obtener una cuenta"), "goBackButton" : MessageLookupByLibrary.simpleMessage("Volver"), "gotItButton" : MessageLookupByLibrary.simpleMessage("¡De acuerdo!"), "hideButton" : MessageLookupByLibrary.simpleMessage("Ocultar"), "iHaveBackedItUpButton" : MessageLookupByLibrary.simpleMessage("Lo he respaldado"), "importButton" : MessageLookupByLibrary.simpleMessage("Importar"), "importPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("Importar clave privada"), "importPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Importar clave privada"), "importPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("Ingrese su clave privada a continuación."), "insufficientBalanceError" : MessageLookupByLibrary.simpleMessage("Balance insuficiente"), "invalidAccountError" : MessageLookupByLibrary.simpleMessage("Cuenta inválida"), "invalidAccountNameError" : MessageLookupByLibrary.simpleMessage("Nombre de cuenta inválido"), "invalidAddressError" : MessageLookupByLibrary.simpleMessage("Dirección inválida"), "invalidDestinationError" : MessageLookupByLibrary.simpleMessage("Destino inválido"), "invalidPINParagraph" : MessageLookupByLibrary.simpleMessage("PIN inválido"), "invalidPasswordError" : MessageLookupByLibrary.simpleMessage("Contraseña inválida"), "invalidPrivateKeyError" : MessageLookupByLibrary.simpleMessage("Clave privada inválida"), "invalidPublicKeyError" : MessageLookupByLibrary.simpleMessage("Clave pública inválida"), "invalidReceivingAccountError" : MessageLookupByLibrary.simpleMessage("Cuenta de recepción inválida"), "keyCopiedButton" : MessageLookupByLibrary.simpleMessage("Clave Copiada"), "keyTypeNotSupportedHeader" : MessageLookupByLibrary.simpleMessage("Clave no soportada"), "keyTypeNotSupportedParagraph" : MessageLookupByLibrary.simpleMessage("Blaise aún no admite este tipo de clave privada. Puede crear una nueva clave privada y transferir sus cuentas a esta utilizando una billetera diferente."), "languageColonHeader" : MessageLookupByLibrary.simpleMessage("Idioma:"), "languageHeader" : MessageLookupByLibrary.simpleMessage("Idioma"), "listAccountForSaleHeader" : MessageLookupByLibrary.simpleMessage("Publicar cuenta a la venta"), "listAccountForSaleOPDetails" : MessageLookupByLibrary.simpleMessage("Publicar cuenta a la venta (%1)"), "listForSaleButton" : MessageLookupByLibrary.simpleMessage("Publicar a la venta"), "listForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Ingrese un precio y una cuenta que recibirá el pago para publicar a la venta esta cuenta."), "listForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Publicar a la venta"), "listedForSaleHeader" : MessageLookupByLibrary.simpleMessage("Publicada a la venta"), "listedForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Su cuenta ha sido exitosamente publicada a la venta. Le avisaremos si alguien la compra."), "listedForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Publicada"), "listingForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Confirme el precio y la cuenta que recibirá el pago."), "listingForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Publicando"), "lock15Header" : MessageLookupByLibrary.simpleMessage("Después de %1 minutos"), "lock1Header" : MessageLookupByLibrary.simpleMessage("Después de %1 minuto"), "lock30Header" : MessageLookupByLibrary.simpleMessage("Después de %1 minutos"), "lock5Header" : MessageLookupByLibrary.simpleMessage("Después de %1 minutos"), "lock60Header" : MessageLookupByLibrary.simpleMessage("Después de %1 minutos"), "lockInstantHeader" : MessageLookupByLibrary.simpleMessage("Instantáneamente"), "lockedHeader" : MessageLookupByLibrary.simpleMessage("Bloqueada"), "lockedUntilBlockOPDetails" : MessageLookupByLibrary.simpleMessage("Bloqueado hasta el bloque"), "logoutFirstDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Cerrar sesión eliminará su clave privada y todos los datos relacionados con Blaise de este dispositivo. Si no se realiza una copia de seguridad de su clave privada, nunca podrá volver a acceder a sus fondos. Si su clave privada está respaldada, no tiene nada de qué preocuparse."), "logoutHeader" : MessageLookupByLibrary.simpleMessage("Cerrar sesión"), "logoutSecondDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("¿Está seguro de que ha hecho una copia de seguridad de su clave privada? Mientras haya hecho una copia de seguridad de su clave privada, no tiene nada de qué preocuparse."), "looksLikeEncryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("Esta parece una clave privada encriptada, ingrese la contraseña para descifrarla e importarla."), "manageHeader" : MessageLookupByLibrary.simpleMessage("Gestionar"), "manyFailedAttemptsParagraph" : MessageLookupByLibrary.simpleMessage("Demasiados intentos de desbloqueo fallidos"), "maturationOPDetails" : MessageLookupByLibrary.simpleMessage("maduración"), "multioperationOPDetails" : MessageLookupByLibrary.simpleMessage("Multioperación (%1)"), "naOPDetails" : MessageLookupByLibrary.simpleMessage("N/A"), "nameChangedHeader" : MessageLookupByLibrary.simpleMessage("Nombre modificado"), "nameRequiredError" : MessageLookupByLibrary.simpleMessage("El nombre es obligatorio"), "nameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Nombre"), "newAccountNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Nuevo nombre de cuenta"), "newAccountParagraph" : MessageLookupByLibrary.simpleMessage("Esta es su nueva cuenta.\nUna vez que reciba Pascal, las operaciones se mostrarán como a continuación."), "newKeyBackUpConfirmParagraph" : MessageLookupByLibrary.simpleMessage("¿Estás seguro de que has respaldado la clave privada de tu nueva billetera?"), "newKeySecurityParagraph" : MessageLookupByLibrary.simpleMessage("En la siguiente pantalla, verá su nueva clave privada. Es una contraseña para acceder a sus fondos. Es crucial que la respalde y nunca la comparta con nadie."), "newNameOPDetails" : MessageLookupByLibrary.simpleMessage("Nuevo nombre"), "newPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Nueva contraseña"), "newPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("Nueva clave privada"), "newPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Nueva clave privada"), "newPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("A continuación se muestra la clave privada de su nueva billetera. Es crucial que haga una copia de seguridad de su clave privada y nunca la almacene como texto plano o como una captura de pantalla. Recomendamos escribirla en una hoja de papel y almacenarla sin conexión."), "newPublicKeyOPDetails" : MessageLookupByLibrary.simpleMessage("Nueva clave pública"), "newWalletGreetingParagraph" : MessageLookupByLibrary.simpleMessage("Bienvenido a Blaise Wallet.\nPuede comenzar obteniendo una cuenta."), "nextButton" : MessageLookupByLibrary.simpleMessage("Siguiente"), "noContactsToExportError" : MessageLookupByLibrary.simpleMessage("No hay contactos para exportar"), "noContactsToImportError" : MessageLookupByLibrary.simpleMessage("No hay contactos para importar"), "noGoBackButton" : MessageLookupByLibrary.simpleMessage("No, volver"), "noHeader" : MessageLookupByLibrary.simpleMessage("No"), "noMatchPINParagraph" : MessageLookupByLibrary.simpleMessage("Los PIN no coinciden"), "noMatchPasswordError" : MessageLookupByLibrary.simpleMessage("Contraseñan no coinciden"), "noResultsFound" : MessageLookupByLibrary.simpleMessage("No se han encontrado resultados"), "noperationOPDetails" : MessageLookupByLibrary.simpleMessage("n_operation"), "notificationsHeader" : MessageLookupByLibrary.simpleMessage("Notificaciones"), "nullOPDetails" : MessageLookupByLibrary.simpleMessage("null"), "offHeader" : MessageLookupByLibrary.simpleMessage("Apagado"), "okayGoBackButton" : MessageLookupByLibrary.simpleMessage("Ok, volver"), "onHeader" : MessageLookupByLibrary.simpleMessage("Encendido"), "opblockOPDetails" : MessageLookupByLibrary.simpleMessage("opblock"), "openInExplorerButton" : MessageLookupByLibrary.simpleMessage("Abrir en el explorador"), "operationDetailsButton" : MessageLookupByLibrary.simpleMessage("Detalles de la operación"), "operationsHeader" : MessageLookupByLibrary.simpleMessage("Operaciones"), "ophashOPDetails" : MessageLookupByLibrary.simpleMessage("ophash"), "optxtOPDetails" : MessageLookupByLibrary.simpleMessage("optxt"), "optypeOPDetails" : MessageLookupByLibrary.simpleMessage("optype"), "otherOperationsHeader" : MessageLookupByLibrary.simpleMessage("Otras operaciones"), "passwordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Contraseña"), "payloadOPDetails" : MessageLookupByLibrary.simpleMessage("Payload"), "payloadTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Payload"), "pendingHeader" : MessageLookupByLibrary.simpleMessage("Pendiente"), "phoneNumberTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Número telefónico"), "preferencesHeader" : MessageLookupByLibrary.simpleMessage("Preferencias"), "priceRequiredError" : MessageLookupByLibrary.simpleMessage("El precio es obligatorio"), "priceTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Precio"), "privacyPolicyHeader" : MessageLookupByLibrary.simpleMessage("Política de privacidad"), "privateKeySheetHeader" : MessageLookupByLibrary.simpleMessage("Clave privada"), "privateKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Clave privada"), "privateSaleHeader" : MessageLookupByLibrary.simpleMessage("Venta privada"), "publicKeyParagraph" : MessageLookupByLibrary.simpleMessage("A continuación se muestra su clave pública. Como su nombre lo indica, está destinada a ser compartida públicamente y probar que una operación particular pertenece a su clave privada."), "publicKeySheetHeader" : MessageLookupByLibrary.simpleMessage("Clave pública"), "publicKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Clave pública"), "receiveAccountButton" : MessageLookupByLibrary.simpleMessage("Recibir cuenta"), "receiveAnAccountButton" : MessageLookupByLibrary.simpleMessage("Recibir una cuenta"), "receiveButton" : MessageLookupByLibrary.simpleMessage("Recibir"), "receivedHeader" : MessageLookupByLibrary.simpleMessage("Recibido"), "receivingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Cuenta receptora"), "receivingAccountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Cuenta receptora"), "recoverFundsOPDetails" : MessageLookupByLibrary.simpleMessage("Recuperar fondos (%1)"), "removedFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("%1 removido de contactos"), "requestButton" : MessageLookupByLibrary.simpleMessage("Solicitar"), "requestSheetHeader" : MessageLookupByLibrary.simpleMessage("Solicitar"), "scanQRCodeButton" : MessageLookupByLibrary.simpleMessage("Escanear código QR"), "searchAccountNameButton" : MessageLookupByLibrary.simpleMessage("Buscar por nombre de cuenta"), "searchForNameButton" : MessageLookupByLibrary.simpleMessage("Buscar por nombre"), "searchNameButton" : MessageLookupByLibrary.simpleMessage("Buscar nombre"), "securityFirstHeader" : MessageLookupByLibrary.simpleMessage("¡La seguridad primero!"), "securityHeader" : MessageLookupByLibrary.simpleMessage("Seguridad"), "sellerAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Cuenta vendedora"), "sendAmountOPDetails" : MessageLookupByLibrary.simpleMessage("Cantidad a enviar"), "sendButton" : MessageLookupByLibrary.simpleMessage("Enviar"), "sendConfirmationButton" : MessageLookupByLibrary.simpleMessage("Enviar confirmación"), "sendSheetHeader" : MessageLookupByLibrary.simpleMessage("Enviar"), "sendingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Cuenta de envío"), "sendingConfirmParagraph" : MessageLookupByLibrary.simpleMessage("Confirme los detalles de la transacción para enviar."), "sendingSheetHeader" : MessageLookupByLibrary.simpleMessage("Enviando"), "sentHeader" : MessageLookupByLibrary.simpleMessage("Enviado"), "sentParagraph" : MessageLookupByLibrary.simpleMessage("La transacción ha sido enviada con éxito."), "sentSheetHeader" : MessageLookupByLibrary.simpleMessage("Enviado"), "setToDefaultButton" : MessageLookupByLibrary.simpleMessage("Establecer predeterminado"), "settingsHeader" : MessageLookupByLibrary.simpleMessage("Configuración"), "shareHeader" : MessageLookupByLibrary.simpleMessage("Compartir Blaise"), "showButton" : MessageLookupByLibrary.simpleMessage("Mostrar"), "signeraccountOPDetails" : MessageLookupByLibrary.simpleMessage("signer_account"), "somethingWentWrongError" : MessageLookupByLibrary.simpleMessage("Algo anduvo mal, por favor intente más tarde"), "successfullyImportedContactsParagraph" : MessageLookupByLibrary.simpleMessage("Exitosamente importados %1 contactos"), "systemDefaultHeader" : MessageLookupByLibrary.simpleMessage("Sistema por defecto"), "themeCopperHeader" : MessageLookupByLibrary.simpleMessage("Cobre"), "themeDarkHeader" : MessageLookupByLibrary.simpleMessage("Oscuro"), "themeHeader" : MessageLookupByLibrary.simpleMessage("Tema"), "themeLightHeader" : MessageLookupByLibrary.simpleMessage("Claro"), "threeCharacterNameError" : MessageLookupByLibrary.simpleMessage("Debe tener al menos 3 caracteres"), "timeOPDetails" : MessageLookupByLibrary.simpleMessage("Fecha"), "totalBalanceHeader" : MessageLookupByLibrary.simpleMessage("Balance total"), "transactionOPDetails" : MessageLookupByLibrary.simpleMessage("Transacción (%1)"), "transferAccountHeader" : MessageLookupByLibrary.simpleMessage("Transferir cuenta"), "transferButton" : MessageLookupByLibrary.simpleMessage("Transferir"), "transferParagraph" : MessageLookupByLibrary.simpleMessage("Ingrese una clave pública a continuación para transferirle la propiedad de esta cuenta."), "transferSheetHeader" : MessageLookupByLibrary.simpleMessage("Transferir"), "transferredHeader" : MessageLookupByLibrary.simpleMessage("Transferido"), "transferredParagraph" : MessageLookupByLibrary.simpleMessage("Su cuenta ha sido transferida exitosamente a la clave pública que se muestra a continuación."), "transferredSheetHeader" : MessageLookupByLibrary.simpleMessage("Transferido"), "transferringParagraph" : MessageLookupByLibrary.simpleMessage("Confirme la clave pública a continuación para transferirle la propiedad de esta cuenta."), "transferringSheetHeader" : MessageLookupByLibrary.simpleMessage("Transfiriendo"), "undefinedHeader" : MessageLookupByLibrary.simpleMessage("Indefinida"), "unencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Clave no encriptada"), "uninstallDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Si pierde su dispositivo o desinstala Blaise Wallet, necesitará su clave privada para recuperar sus fondos."), "unknownOPDetails" : MessageLookupByLibrary.simpleMessage("Desconocido (%1)"), "unlockButton" : MessageLookupByLibrary.simpleMessage("Desbloquear"), "unlockWithBiometricsButton" : MessageLookupByLibrary.simpleMessage("Desbloquear con datos biométricos"), "unlockWithPINButton" : MessageLookupByLibrary.simpleMessage("Desbloquear con PIN"), "urlChangedToParagraph" : MessageLookupByLibrary.simpleMessage("URL cambiada a %1"), "viewPublicKeyHeader" : MessageLookupByLibrary.simpleMessage("Ver clave pública"), "warningHeader" : MessageLookupByLibrary.simpleMessage("Advertencia"), "welcomeParagraph" : MessageLookupByLibrary.simpleMessage("Bienvenido a Blaise Wallet. Para empezar, puede crear una nueva clave privada o importar una."), "yesAddFeeButton" : MessageLookupByLibrary.simpleMessage("Si, agregar tarifa"), "yesHeader" : MessageLookupByLibrary.simpleMessage("Si"), "yesImSureButton" : MessageLookupByLibrary.simpleMessage("Si, estoy seguro"), "zeroAmountError" : MessageLookupByLibrary.simpleMessage("El monto no puede ser 0"), "zeroPriceError" : MessageLookupByLibrary.simpleMessage("El precio no puede ser 0") }; } ================================================ FILE: lib/l10n/messages_messages.dart ================================================ // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart // This is a library that provides messages for a messages locale. All the // messages from the main program should be duplicated here with the same // function name. // Ignore issues from commonly used lints in this file. // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases // ignore_for_file:unused_import, file_names import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; final messages = new MessageLookup(); typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'messages'; final messages = _notInlinedMessages(_notInlinedMessages); static _notInlinedMessages(_) => { "accountBalanceHeader" : MessageLookupByLibrary.simpleMessage("Account Balance"), "accountOPDetails" : MessageLookupByLibrary.simpleMessage("account"), "accountPriceOPDetails" : MessageLookupByLibrary.simpleMessage("Account Price"), "accountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Account"), "accountToSendFromHeader" : MessageLookupByLibrary.simpleMessage("Account to Send From"), "accountsHeader" : MessageLookupByLibrary.simpleMessage("Accounts"), "addADurationButton" : MessageLookupByLibrary.simpleMessage("+ Add a Duration"), "addAPayloadButton" : MessageLookupByLibrary.simpleMessage("+ Add a Payload"), "addContactButton" : MessageLookupByLibrary.simpleMessage("Add Contact"), "addContactSheetHeader" : MessageLookupByLibrary.simpleMessage("Add Contact"), "addFeeHeader" : MessageLookupByLibrary.simpleMessage("Add Fee"), "addToContactsButton" : MessageLookupByLibrary.simpleMessage("Add to Contacts"), "addedToContactsParagraph" : MessageLookupByLibrary.simpleMessage("%1 added to contacts"), "addressTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Address"), "amountRequiredError" : MessageLookupByLibrary.simpleMessage("Amount is required"), "amountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Amount"), "areYouSureHeader" : MessageLookupByLibrary.simpleMessage("Are You Sure?"), "authenticateOnLaunchHeader" : MessageLookupByLibrary.simpleMessage("Authenticate on Launch"), "authenticateToBackUpParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to back up private key"), "authenticateToChangeNameParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to change account name to \"%1\""), "authenticateToCreatePrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to create private sale"), "authenticateToDelistParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to delist the account from sale"), "authenticateToListForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to list account for sale"), "authenticateToSendParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to send %1 Pascal"), "authenticateToTransferParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to transfer account"), "authenticateToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("Authenticate to Unlock Blaise"), "authenticationBiometricsHeader" : MessageLookupByLibrary.simpleMessage("Biometrics"), "authenticationMethodHeader" : MessageLookupByLibrary.simpleMessage("Authentication Method"), "authenticationPINHeader" : MessageLookupByLibrary.simpleMessage("PIN"), "automaticallyLockHeader" : MessageLookupByLibrary.simpleMessage("Automatically Lock"), "backUpKeyHeader" : MessageLookupByLibrary.simpleMessage("Back Up Your Key!"), "backUpPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Back Up Private Key"), "backUpSheetHeader" : MessageLookupByLibrary.simpleMessage("Back Up"), "backupEncryptedKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("Below is your encrypted private key. It is protected by a password. You can store it safely on a password manager for your convenience."), "backupEncryptedKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("Since it is encrypted with your password, if you lose or forget your password, you won\'t be able to decrypt it and access your funds."), "backupKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("You have 2 options for backing up your private key:"), "backupKeyFourthParagraph" : MessageLookupByLibrary.simpleMessage("We recommend storing the unencrypted version offline, by writing it on a piece of paper. You can store the encrypted version on a password manager for your convenience."), "backupKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("1- Encrypted, which means it is protected by a password."), "backupKeyThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- Unencrypted, which means it is raw and not protected by a password."), "backupUnencryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("Below is your unencrypted private key. It is not protected by a password, which means it is crucial that you store it somewhere safe and offline. We recommend writing it on a piece of paper."), "balanceHeader" : MessageLookupByLibrary.simpleMessage("Balance"), "blockOPDetails" : MessageLookupByLibrary.simpleMessage("block"), "blockchainRewardOPDetails" : MessageLookupByLibrary.simpleMessage("Blockchain Reward (%1)"), "borrowAccountParagraph" : MessageLookupByLibrary.simpleMessage("To buy an account, first you’ll need to borrow one for free. If you send at least %1 Pascal (%2) to the account within %3 days, the account will be yours and %1 Pascal will be deducted from your balance automatically.\nOtherwise, it’ll return back to us at the end of %3 days and won’t belong to your wallet anymore.\nIt is recommended you only send a small amount of coins until you own the account."), "borrowAnAccountButton" : MessageLookupByLibrary.simpleMessage("Borrow An Account"), "borrowStarted" : MessageLookupByLibrary.simpleMessage("Purchase Started for %1"), "borrowedAccountHeader" : MessageLookupByLibrary.simpleMessage("Borrowed Account"), "borrowedAccountPaidParagraph" : MessageLookupByLibrary.simpleMessage("Your account has been purchased!\nThe transfer is currently processing. This process usually takes about 15 minutes, in some cases it may take slightly longer."), "borrowedAccountParagraph" : MessageLookupByLibrary.simpleMessage("This is a borrowed account.\nIf you send at least %1 Pascal to it in the next %2 days, %3 hours, and %4 minutes, it’ll be yours."), "borrowedHeader" : MessageLookupByLibrary.simpleMessage("Borrowed"), "borrowedTransferredHeader" : MessageLookupByLibrary.simpleMessage("Transfer Pending"), "buyAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Buy Account (%1)"), "buyAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Buy Account"), "buyAnAccountButton" : MessageLookupByLibrary.simpleMessage("Buy an Account"), "cancelButton" : MessageLookupByLibrary.simpleMessage("Cancel"), "cantSendToYourselfError" : MessageLookupByLibrary.simpleMessage("Can\'t send to yourself"), "changeAccountInfoOPDetails" : MessageLookupByLibrary.simpleMessage("Change Account Info (%1)"), "changeAccountNameHeader" : MessageLookupByLibrary.simpleMessage("Change Account Name"), "changeDaemonButton" : MessageLookupByLibrary.simpleMessage("Change Daemon"), "changeDaemonParagraph" : MessageLookupByLibrary.simpleMessage("Enter an address to use a different Pascal daemon for RPC requests."), "changeDaemonSheetHeader" : MessageLookupByLibrary.simpleMessage("Change Daemon"), "changeKeyOPDetails" : MessageLookupByLibrary.simpleMessage("Change key (%1)"), "changeKeySignedOPDetails" : MessageLookupByLibrary.simpleMessage("Change Key Signed (%1)"), "changeNameButton" : MessageLookupByLibrary.simpleMessage("Change Name"), "changeNameParagraph" : MessageLookupByLibrary.simpleMessage("Enter a name below to change your account\'s name."), "changeNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Change Name"), "changedNameParagraph" : MessageLookupByLibrary.simpleMessage("Your account name has been changed successfully."), "changedNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Changed"), "changingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Changing Account"), "changingNameParagraph" : MessageLookupByLibrary.simpleMessage("Confirm your new account name to proceed."), "changingNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Changing"), "checkOutBlaiseParagraph" : MessageLookupByLibrary.simpleMessage("Check out Blaise! Simple, sleek & secure Pascal wallet for iOS and Android: https://blaisewallet.com"), "closeButton" : MessageLookupByLibrary.simpleMessage("Close"), "confirmButton" : MessageLookupByLibrary.simpleMessage("Confirm"), "confirmPINParagraph" : MessageLookupByLibrary.simpleMessage("Confirm your PIN"), "confirmPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Confirm Password"), "confirmTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Confirm"), "confirmationCodeError" : MessageLookupByLibrary.simpleMessage("Failed to verify code, ensure you\'ve entered it correctly"), "confirmationCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Confirmation Code"), "connectingHeader" : MessageLookupByLibrary.simpleMessage("Connecting"), "contactAlreadyExistsError" : MessageLookupByLibrary.simpleMessage("Contact already exists"), "contactDoesntExistError" : MessageLookupByLibrary.simpleMessage("Contact doesn\'t exist"), "contactNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Contact Name"), "contactSheetHeader" : MessageLookupByLibrary.simpleMessage("Contact"), "contactsHeader" : MessageLookupByLibrary.simpleMessage("Contacts"), "copiedAddressButton" : MessageLookupByLibrary.simpleMessage("Address Copied"), "copiedButton" : MessageLookupByLibrary.simpleMessage("Copied"), "copyAddressButton" : MessageLookupByLibrary.simpleMessage("Copy Address"), "copyButton" : MessageLookupByLibrary.simpleMessage("Copy"), "copyEncryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Copy Encrypted Key"), "copyKeyButton" : MessageLookupByLibrary.simpleMessage("Copy Key"), "copyPublicKeyButton" : MessageLookupByLibrary.simpleMessage("Copy Public Key"), "copyUnencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Copy Unencrypted Key"), "countryCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Country Code"), "createPINParagraph" : MessageLookupByLibrary.simpleMessage("Create a 6-digit PIN"), "createPrivateSaleButton" : MessageLookupByLibrary.simpleMessage("Create Private Sale"), "createPrivateSaleHeader" : MessageLookupByLibrary.simpleMessage("Create Private Sale"), "createPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Enter a price, a receiving account, and a public key below to create a private sale for this account."), "createPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Private Sale"), "createdPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("The private sale has been created successfully. We’ll let you know if it is bought."), "createdPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Created"), "creatingPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Confirm the information below."), "creatingPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Creating"), "currencyHeader" : MessageLookupByLibrary.simpleMessage("Currency"), "daemonHeader" : MessageLookupByLibrary.simpleMessage("Daemon"), "decryptAndImportKeyHeader" : MessageLookupByLibrary.simpleMessage("Decrypt & Import"), "defaultHeader" : MessageLookupByLibrary.simpleMessage("Default"), "deletePrivateKeyAndLogoutButton" : MessageLookupByLibrary.simpleMessage("Delete Private Key\nAnd Logout"), "delistAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Delist Account (%1)"), "delistFromSaleHeader" : MessageLookupByLibrary.simpleMessage("Delist From Sale"), "delistFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("Confirm that you would like to delist this account from sale."), "delistedFromSaleHeader" : MessageLookupByLibrary.simpleMessage("Delisted From Sale"), "delistedFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("Your account has been successfully delisted from sale."), "delistedHeader" : MessageLookupByLibrary.simpleMessage("Delisted"), "delistedSheetHeader" : MessageLookupByLibrary.simpleMessage("Delisted"), "delistingSheetHeader" : MessageLookupByLibrary.simpleMessage("Delisting"), "didNotGetResponseError" : MessageLookupByLibrary.simpleMessage("Did not get a response from server"), "durationTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Duration"), "emptyPasswordError" : MessageLookupByLibrary.simpleMessage("Password can\'t be empty"), "encryptButton" : MessageLookupByLibrary.simpleMessage("Encrypt"), "encryptKeyParagraph" : MessageLookupByLibrary.simpleMessage("Create a new password to encrypt your private key."), "encryptPayloadHeader" : MessageLookupByLibrary.simpleMessage("Encrypt Payload"), "encryptSheetHeader" : MessageLookupByLibrary.simpleMessage("Encrypt"), "encryptThePayloadHeader" : MessageLookupByLibrary.simpleMessage("Encrypt the Payload"), "encryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Encrypted Key"), "enterConfirmationCodeParagraph" : MessageLookupByLibrary.simpleMessage("We have sent you a confirmation code, please enter it below."), "enterPINParagraph" : MessageLookupByLibrary.simpleMessage("Enter PIN"), "enterPINToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("Enter PIN to unlock Blaise"), "enterPhoneNumberParagraph" : MessageLookupByLibrary.simpleMessage("Enter your phone number below."), "failedToEncryptPayloadError" : MessageLookupByLibrary.simpleMessage("Failed to encrypt the payload"), "failedToImportContactsError" : MessageLookupByLibrary.simpleMessage("Failed to import contacts"), "failedToRemoveFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("Failed to remove %1 from contacts"), "feeColonHeader" : MessageLookupByLibrary.simpleMessage("Fee:"), "feeConfirmAmountParagraph" : MessageLookupByLibrary.simpleMessage("Please confirm the addition of %1 Pascal fee to this operation to continue."), "feeOPDetails" : MessageLookupByLibrary.simpleMessage("fee"), "feeRequiredParagraph" : MessageLookupByLibrary.simpleMessage("This operation requires a fee."), "feeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Fee"), "forSaleHeader" : MessageLookupByLibrary.simpleMessage("For Sale"), "freeAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Free Account"), "freepasaComplete" : MessageLookupByLibrary.simpleMessage("Success, your new account will be available after 1 network confirmation"), "getAFreeAccountButton" : MessageLookupByLibrary.simpleMessage("Get a Free Account"), "getAccountFirstParagraph" : MessageLookupByLibrary.simpleMessage("There are 2 options for getting your first account:"), "getAccountSecondParagraph" : MessageLookupByLibrary.simpleMessage("1- You can get a free account using your phone number. Only 1 account per phone number is allowed."), "getAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Get Account"), "getAccountThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- You can buy as many accounts as you want for %1 Pascal (%2)."), "getAccountThirdParagraphAlternative" : MessageLookupByLibrary.simpleMessage("2- You can buy an account for %1 Pascal (%2). Buying only 1 account is allowed per user."), "getAccountThirdParagraphAlternative2" : MessageLookupByLibrary.simpleMessage("2- You can buy an account for %1 Pascal (%2). You can buy up to %3 accounts."), "getAnAccountButton" : MessageLookupByLibrary.simpleMessage("Get an Account"), "goBackButton" : MessageLookupByLibrary.simpleMessage("Go Back"), "gotItButton" : MessageLookupByLibrary.simpleMessage("Got It!"), "hideButton" : MessageLookupByLibrary.simpleMessage("Hide"), "iHaveBackedItUpButton" : MessageLookupByLibrary.simpleMessage("I\'ve Backed It Up"), "importButton" : MessageLookupByLibrary.simpleMessage("Import"), "importPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("Import Private Key"), "importPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Import Private Key"), "importPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("Enter your private key below."), "insufficientBalanceError" : MessageLookupByLibrary.simpleMessage("Insufficient balance"), "invalidAccountError" : MessageLookupByLibrary.simpleMessage("Invalid account"), "invalidAccountNameError" : MessageLookupByLibrary.simpleMessage("Invalid account name"), "invalidAddressError" : MessageLookupByLibrary.simpleMessage("Invalid address"), "invalidDestinationError" : MessageLookupByLibrary.simpleMessage("Invalid destination"), "invalidPINParagraph" : MessageLookupByLibrary.simpleMessage("Invalid PIN"), "invalidPasswordError" : MessageLookupByLibrary.simpleMessage("Invalid password"), "invalidPhoneNumberParagraph" : MessageLookupByLibrary.simpleMessage("Phone number is not valid"), "invalidPrivateKeyError" : MessageLookupByLibrary.simpleMessage("Invalid private key"), "invalidPublicKeyError" : MessageLookupByLibrary.simpleMessage("Invalid public key"), "invalidReceivingAccountError" : MessageLookupByLibrary.simpleMessage("Invalid receiving account"), "keyCopiedButton" : MessageLookupByLibrary.simpleMessage("Key Copied"), "keyTypeNotSupportedHeader" : MessageLookupByLibrary.simpleMessage("Key Not Supported"), "keyTypeNotSupportedParagraph" : MessageLookupByLibrary.simpleMessage("This type of private key is not yet supported by Blaise. You may create a new private key and transfer your accounts to it using a different wallet."), "languageColonHeader" : MessageLookupByLibrary.simpleMessage("Language:"), "languageHeader" : MessageLookupByLibrary.simpleMessage("Language"), "listAccountForSaleHeader" : MessageLookupByLibrary.simpleMessage("List Account For Sale"), "listAccountForSaleOPDetails" : MessageLookupByLibrary.simpleMessage("List Account for Sale (%1)"), "listForSaleButton" : MessageLookupByLibrary.simpleMessage("List for Sale"), "listForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Enter a price and an account that will be receiving the payment to list this account for sale."), "listForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("List For Sale"), "listedForSaleHeader" : MessageLookupByLibrary.simpleMessage("Listed For Sale"), "listedForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Your account has been successfully listed for sale. We’ll let you know if someone buys it."), "listedForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Listed"), "listingForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Confirm the price and the account that will be receiving the payment."), "listingForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Listing"), "liveSupportButton" : MessageLookupByLibrary.simpleMessage("Support"), "lock15Header" : MessageLookupByLibrary.simpleMessage("After %1 minutes"), "lock1Header" : MessageLookupByLibrary.simpleMessage("After %1 minute"), "lock30Header" : MessageLookupByLibrary.simpleMessage("After %1 minutes"), "lock5Header" : MessageLookupByLibrary.simpleMessage("After %1 minutes"), "lock60Header" : MessageLookupByLibrary.simpleMessage("After %1 minutes"), "lockInstantHeader" : MessageLookupByLibrary.simpleMessage("Instantly"), "lockedHeader" : MessageLookupByLibrary.simpleMessage("Locked"), "lockedUntilBlockOPDetails" : MessageLookupByLibrary.simpleMessage("Locked Until Block"), "logoutFirstDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Logging out will remove your private key and all Blaise related data from this device. If your private key is not backed up, you will never be able to access your funds again. If your private key is backed up, you have nothing to worry about."), "logoutHeader" : MessageLookupByLibrary.simpleMessage("Logout"), "logoutSecondDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Are you sure that you\'ve backed up your private key? As long as you\'ve backed up your private key, you have nothing to worry about."), "looksLikeEncryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("This looks like an encrypted private key, please enter the password to decrypt and import it."), "manageHeader" : MessageLookupByLibrary.simpleMessage("Manage"), "manyFailedAttemptsParagraph" : MessageLookupByLibrary.simpleMessage("Too many failed unlock attempts"), "maturationOPDetails" : MessageLookupByLibrary.simpleMessage("maturation"), "multioperationOPDetails" : MessageLookupByLibrary.simpleMessage("Multioperation (%1)"), "naOPDetails" : MessageLookupByLibrary.simpleMessage("N/A"), "nameChangedHeader" : MessageLookupByLibrary.simpleMessage("Name Changed"), "nameRequiredError" : MessageLookupByLibrary.simpleMessage("Name is required"), "nameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Name"), "newAccountNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("New Account Name"), "newAccountParagraph" : MessageLookupByLibrary.simpleMessage("This is your new account.\nOnce you receive Pascal, operations will show up like below."), "newKeyBackUpConfirmParagraph" : MessageLookupByLibrary.simpleMessage("Are you sure that you have backed up your new wallet’s private key?"), "newKeySecurityParagraph" : MessageLookupByLibrary.simpleMessage("In the next screen, you\'ll see your new private key. It is a password to access your funds. It is crucial that you back it up and never share it with anyone."), "newNameOPDetails" : MessageLookupByLibrary.simpleMessage("New Name"), "newPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("New Password"), "newPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("New Private Key"), "newPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("New Private Key"), "newPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("Below is your new wallet’s private key. It is crucial that you backup your private key and never store it as plaintext or a screenshot. We recommend writing it on a piece of paper and storing it offline."), "newPublicKeyOPDetails" : MessageLookupByLibrary.simpleMessage("New Public Key"), "newWalletGreetingParagraph" : MessageLookupByLibrary.simpleMessage("Welcome to Blaise Wallet.\nYou can start by getting an account."), "nextButton" : MessageLookupByLibrary.simpleMessage("Next"), "noContactsToExportError" : MessageLookupByLibrary.simpleMessage("No contacts to export"), "noContactsToImportError" : MessageLookupByLibrary.simpleMessage("No contacts to import"), "noGoBackButton" : MessageLookupByLibrary.simpleMessage("No, Go Back"), "noHeader" : MessageLookupByLibrary.simpleMessage("No"), "noMatchPINParagraph" : MessageLookupByLibrary.simpleMessage("PINs do not match"), "noMatchPasswordError" : MessageLookupByLibrary.simpleMessage("Passwords don\'t match"), "noResultsFound" : MessageLookupByLibrary.simpleMessage("No results found"), "noperationOPDetails" : MessageLookupByLibrary.simpleMessage("n_operation"), "notificationsHeader" : MessageLookupByLibrary.simpleMessage("Notifications"), "nullOPDetails" : MessageLookupByLibrary.simpleMessage("null"), "offHeader" : MessageLookupByLibrary.simpleMessage("Off"), "okayButton" : MessageLookupByLibrary.simpleMessage("Okay"), "okayGoBackButton" : MessageLookupByLibrary.simpleMessage("Okay, Go Back"), "onHeader" : MessageLookupByLibrary.simpleMessage("On"), "opblockOPDetails" : MessageLookupByLibrary.simpleMessage("opblock"), "openInExplorerButton" : MessageLookupByLibrary.simpleMessage("Open in Explorer"), "operationDetailsButton" : MessageLookupByLibrary.simpleMessage("Operation Details"), "operationsHeader" : MessageLookupByLibrary.simpleMessage("Operations"), "ophashOPDetails" : MessageLookupByLibrary.simpleMessage("ophash"), "optxtOPDetails" : MessageLookupByLibrary.simpleMessage("optxt"), "optypeOPDetails" : MessageLookupByLibrary.simpleMessage("optype"), "otherOperationsHeader" : MessageLookupByLibrary.simpleMessage("Other Operations"), "passwordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Password"), "payloadOPDetails" : MessageLookupByLibrary.simpleMessage("Payload"), "payloadTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Payload"), "pendingHeader" : MessageLookupByLibrary.simpleMessage("Pending"), "phoneNumberTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Phone Number"), "preferencesHeader" : MessageLookupByLibrary.simpleMessage("Preferences"), "priceRequiredError" : MessageLookupByLibrary.simpleMessage("Price is required"), "priceTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Price"), "privacyPolicyHeader" : MessageLookupByLibrary.simpleMessage("Privacy Policy"), "privateKeySheetHeader" : MessageLookupByLibrary.simpleMessage("Private Key"), "privateKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Private Key"), "privateSaleHeader" : MessageLookupByLibrary.simpleMessage("Private Sale"), "publicKeyParagraph" : MessageLookupByLibrary.simpleMessage("Below is your public key. As the name suggests, it is intended to be shared publicly and prove that a particular operation belongs to your private key."), "publicKeySheetHeader" : MessageLookupByLibrary.simpleMessage("Public Key"), "publicKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Public Key"), "receiveAccountButton" : MessageLookupByLibrary.simpleMessage("Receive Account"), "receiveAnAccountButton" : MessageLookupByLibrary.simpleMessage("Receive an Account"), "receiveButton" : MessageLookupByLibrary.simpleMessage("Receive"), "receivedHeader" : MessageLookupByLibrary.simpleMessage("Received"), "receivingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Receiving Account"), "receivingAccountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Receiving Account"), "recoverFundsOPDetails" : MessageLookupByLibrary.simpleMessage("Recover Funds (%1)"), "removedFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("Removed %1 from contacts"), "requestButton" : MessageLookupByLibrary.simpleMessage("Request"), "requestSheetHeader" : MessageLookupByLibrary.simpleMessage("Request"), "scanQRCodeButton" : MessageLookupByLibrary.simpleMessage("Scan QR Code"), "searchAccountNameButton" : MessageLookupByLibrary.simpleMessage("Search Account Name"), "searchForNameButton" : MessageLookupByLibrary.simpleMessage("Search For Name"), "searchNameButton" : MessageLookupByLibrary.simpleMessage("Search Name"), "securityFirstHeader" : MessageLookupByLibrary.simpleMessage("Security First!"), "securityHeader" : MessageLookupByLibrary.simpleMessage("Security"), "sellerAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Seller Account"), "sendAmountOPDetails" : MessageLookupByLibrary.simpleMessage("Send Amount"), "sendButton" : MessageLookupByLibrary.simpleMessage("Send"), "sendConfirmationButton" : MessageLookupByLibrary.simpleMessage("Send Confirmation"), "sendSheetHeader" : MessageLookupByLibrary.simpleMessage("Send"), "sendingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Sending Account"), "sendingConfirmParagraph" : MessageLookupByLibrary.simpleMessage("Confirm the transaction details to send."), "sendingSheetHeader" : MessageLookupByLibrary.simpleMessage("Sending"), "sentHeader" : MessageLookupByLibrary.simpleMessage("Sent"), "sentParagraph" : MessageLookupByLibrary.simpleMessage("Transaction has been sent succesfully."), "sentSheetHeader" : MessageLookupByLibrary.simpleMessage("Sent"), "setToDefaultButton" : MessageLookupByLibrary.simpleMessage("Set to Default"), "settingsHeader" : MessageLookupByLibrary.simpleMessage("Settings"), "shareHeader" : MessageLookupByLibrary.simpleMessage("Share Blaise"), "showButton" : MessageLookupByLibrary.simpleMessage("Show"), "signeraccountOPDetails" : MessageLookupByLibrary.simpleMessage("signer_account"), "somethingWentWrongError" : MessageLookupByLibrary.simpleMessage("Something went wrong, please try again later"), "successfullyImportedContactsParagraph" : MessageLookupByLibrary.simpleMessage("Successfully imported %1 contacts"), "supportButton" : MessageLookupByLibrary.simpleMessage("Support"), "systemDefaultHeader" : MessageLookupByLibrary.simpleMessage("System Default"), "themeCopperHeader" : MessageLookupByLibrary.simpleMessage("Copper"), "themeDarkHeader" : MessageLookupByLibrary.simpleMessage("Dark"), "themeHeader" : MessageLookupByLibrary.simpleMessage("Theme"), "themeLightHeader" : MessageLookupByLibrary.simpleMessage("Light"), "threeCharacterNameError" : MessageLookupByLibrary.simpleMessage("Must be at least 3 characters"), "timeOPDetails" : MessageLookupByLibrary.simpleMessage("time"), "totalBalanceHeader" : MessageLookupByLibrary.simpleMessage("Total Balance"), "transactionOPDetails" : MessageLookupByLibrary.simpleMessage("Transaction (%1)"), "transferAccountHeader" : MessageLookupByLibrary.simpleMessage("Transfer Account"), "transferButton" : MessageLookupByLibrary.simpleMessage("Transfer"), "transferParagraph" : MessageLookupByLibrary.simpleMessage("Enter a public key below to transfer the ownership of this account to it."), "transferSheetHeader" : MessageLookupByLibrary.simpleMessage("Transfer"), "transferredHeader" : MessageLookupByLibrary.simpleMessage("Transferred"), "transferredParagraph" : MessageLookupByLibrary.simpleMessage("Your account has been transferred successfully to the public key below."), "transferredSheetHeader" : MessageLookupByLibrary.simpleMessage("Transferred"), "transferringParagraph" : MessageLookupByLibrary.simpleMessage("Confirm the public key below to transfer the ownership of this account to it."), "transferringSheetHeader" : MessageLookupByLibrary.simpleMessage("Transferring"), "unconfirmedAccountHeader" : MessageLookupByLibrary.simpleMessage("Unconfirmed Account"), "unconfirmedAccountParagraph" : MessageLookupByLibrary.simpleMessage("This is an unconfirmed account. It has been transferred to you, but there needs to be 1 network confirmation before you can use it. This usually takes about 5 minutes, once it\'s complete you\'ll be able to use this account."), "undefinedHeader" : MessageLookupByLibrary.simpleMessage("Undefined"), "unencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Unencrypted Key"), "uninstallDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("If you lose your device or uninstall Blaise Wallet, you\'ll need your private key to recover your funds."), "unknownOPDetails" : MessageLookupByLibrary.simpleMessage("Unknown (%1)"), "unlockButton" : MessageLookupByLibrary.simpleMessage("Unlock"), "unlockWithBiometricsButton" : MessageLookupByLibrary.simpleMessage("Unlock with Biometrics"), "unlockWithPINButton" : MessageLookupByLibrary.simpleMessage("Unlock with PIN"), "urlChangedToParagraph" : MessageLookupByLibrary.simpleMessage("URL changed to %1"), "viewPublicKeyHeader" : MessageLookupByLibrary.simpleMessage("View Public Key"), "warningHeader" : MessageLookupByLibrary.simpleMessage("Warning"), "welcomeParagraph" : MessageLookupByLibrary.simpleMessage("Welcome to Blaise Wallet. To begin, you can create a new private key or import one."), "yesAddFeeButton" : MessageLookupByLibrary.simpleMessage("Yes, Add Fee"), "yesHeader" : MessageLookupByLibrary.simpleMessage("Yes"), "yesImSureButton" : MessageLookupByLibrary.simpleMessage("Yes, I\'m Sure"), "zeroAmountError" : MessageLookupByLibrary.simpleMessage("Amount can\'t be 0"), "zeroPriceError" : MessageLookupByLibrary.simpleMessage("Price can\'t be 0") }; } ================================================ FILE: lib/l10n/messages_tr.dart ================================================ // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart // This is a library that provides messages for a tr locale. All the // messages from the main program should be duplicated here with the same // function name. // Ignore issues from commonly used lints in this file. // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases // ignore_for_file:unused_import, file_names import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; final messages = new MessageLookup(); typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'tr'; final messages = _notInlinedMessages(_notInlinedMessages); static _notInlinedMessages(_) => { "accountBalanceHeader" : MessageLookupByLibrary.simpleMessage("Hesap Bakiyesi"), "accountOPDetails" : MessageLookupByLibrary.simpleMessage("Hesap"), "accountPriceOPDetails" : MessageLookupByLibrary.simpleMessage("Hesap Fiyatı"), "accountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Hesap"), "accountToSendFromHeader" : MessageLookupByLibrary.simpleMessage("Gönderilecek Hesap"), "accountsHeader" : MessageLookupByLibrary.simpleMessage("Hesaplar"), "addADurationButton" : MessageLookupByLibrary.simpleMessage("+ Süre Ekle"), "addAPayloadButton" : MessageLookupByLibrary.simpleMessage("+ Not Ekle"), "addContactButton" : MessageLookupByLibrary.simpleMessage("Kişi Ekle"), "addContactSheetHeader" : MessageLookupByLibrary.simpleMessage("Kişi Ekle"), "addFeeHeader" : MessageLookupByLibrary.simpleMessage("Ücret Ekle"), "addToContactsButton" : MessageLookupByLibrary.simpleMessage("Rehbere Ekle"), "addedToContactsParagraph" : MessageLookupByLibrary.simpleMessage("%1 rehbere eklendi"), "addressTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Adres"), "amountRequiredError" : MessageLookupByLibrary.simpleMessage("Miktar gerekli"), "amountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Miktar"), "areYouSureHeader" : MessageLookupByLibrary.simpleMessage("Emin Misin?"), "authenticateOnLaunchHeader" : MessageLookupByLibrary.simpleMessage("Başlangıçta Doğrula"), "authenticateToBackUpParagraph" : MessageLookupByLibrary.simpleMessage("Gizli anahtarını yedeklemek için doğrula"), "authenticateToChangeNameParagraph" : MessageLookupByLibrary.simpleMessage("Hesap adını \"%1\" olarak değiştirmek için doğrula"), "authenticateToCreatePrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Özel satış oluşturmak için doğrula"), "authenticateToDelistParagraph" : MessageLookupByLibrary.simpleMessage("Hesabı satıştan kaldırmak için doğrula"), "authenticateToListForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Hesabı satışa çıkarmak için doğrula"), "authenticateToSendParagraph" : MessageLookupByLibrary.simpleMessage("%1 Pascal göndermek için doğrula"), "authenticateToTransferParagraph" : MessageLookupByLibrary.simpleMessage("Hesabı transfer etmek için doğrula"), "authenticateToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("Blaise\'i açmak için doğrula"), "authenticationBiometricsHeader" : MessageLookupByLibrary.simpleMessage("Biometrik"), "authenticationMethodHeader" : MessageLookupByLibrary.simpleMessage("Doğrulama Metodu"), "authenticationPINHeader" : MessageLookupByLibrary.simpleMessage("PIN"), "automaticallyLockHeader" : MessageLookupByLibrary.simpleMessage("Otomatik Kilitle"), "backUpKeyHeader" : MessageLookupByLibrary.simpleMessage("Anahtarını Yedekle!"), "backUpPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Gizli Anahtarı Yedekle"), "backUpSheetHeader" : MessageLookupByLibrary.simpleMessage("Yedekle"), "backupEncryptedKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("Şifreli gizli anahtarını aşağıda görebilirsin. Bir şifre ile korunduğu için, anahtarını güvenle bir şifre yöneticisinde saklayabilirsin."), "backupEncryptedKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("Anahtar şifreli olduğu için, şifreni unutman veya kaybetmen durumunda, şifrelemeyi açıp cüzdanına erişmen mümkün olmayacak."), "backupKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("Gizli anahtarını yedeklemek için iki seçeneğin var:"), "backupKeyFourthParagraph" : MessageLookupByLibrary.simpleMessage("Anahtarın ham halini bir kağıda yazıp çevrim dışı olarak saklamanı, şifreli halini de kolayca erişebilmen için bir şifre yöneticisinde saklamanı tavsiye ediyoruz."), "backupKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("2- Şifreli, yani bir şifre ile korunan."), "backupKeyThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- Ham, yani bir şifre ile korunmayan."), "backupUnencryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("Ham gizli anahtarını aşağıda görebilirsin. Bir şifre ile korunmadığı için, ham anahtarını güvenli ve çevrim dışı bir yerde saklaman çok önemli. Anahtarını bir kağıda yazarak saklamanı tavsiye ediyoruz."), "balanceHeader" : MessageLookupByLibrary.simpleMessage("Bakiye"), "blockOPDetails" : MessageLookupByLibrary.simpleMessage("Blok"), "blockchainRewardOPDetails" : MessageLookupByLibrary.simpleMessage("Blokzinciri Ödülü (%1)"), "borrowAccountParagraph" : MessageLookupByLibrary.simpleMessage("Bir hesap satın almak için, öncelikle bu hesabı ödünç alman gerekiyor. Ödünç aldığın hesaba %3 gün içinde %1 Pascal (%2) gönderirsen, hesap tamamen senin olacak ve hesaptan otomatik olarak %1 Pascal ücret kesilecek.\nAksi takdirde hesap %3 günün sonunda bize geri dönecek.\nHesap tamamen senin olmadan, hesaba fazla miktarda gönderi yapmamanı tavsiye ediyoruz."), "borrowAnAccountButton" : MessageLookupByLibrary.simpleMessage("Ödünç Hesap Al"), "borrowStarted" : MessageLookupByLibrary.simpleMessage("%1 için alım işlemi başladı"), "borrowedAccountHeader" : MessageLookupByLibrary.simpleMessage("Ödünç Hesap"), "borrowedAccountPaidParagraph" : MessageLookupByLibrary.simpleMessage("Hesap satın alındı!\nTransfer işlemi şuan devam ediyor. Transfer genellikle 15 dakika, bazen ise biraz daha uzun sürebilir."), "borrowedAccountParagraph" : MessageLookupByLibrary.simpleMessage("Bu bir ödünç hesap.\n%2 gün, %3 saat, %4 dakika içinde bu hesaba en az %1 Pascal gönderirsen, hesap senin olacak."), "borrowedHeader" : MessageLookupByLibrary.simpleMessage("Ödünç"), "borrowedTransferredHeader" : MessageLookupByLibrary.simpleMessage("Transfer Bekleniyor"), "buyAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Hesap Satın Alımı (%1)"), "buyAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Hesap Al"), "buyAnAccountButton" : MessageLookupByLibrary.simpleMessage("Hesap Satın Al"), "cancelButton" : MessageLookupByLibrary.simpleMessage("İptal"), "cantSendToYourselfError" : MessageLookupByLibrary.simpleMessage("Kendine gönderemezsin"), "changeAccountInfoOPDetails" : MessageLookupByLibrary.simpleMessage("Hesap Bilgisi Değişimi (%1)"), "changeAccountNameHeader" : MessageLookupByLibrary.simpleMessage("Hesap Adı Değiştir"), "changeDaemonButton" : MessageLookupByLibrary.simpleMessage("Değiştir"), "changeDaemonParagraph" : MessageLookupByLibrary.simpleMessage("Pascal erişim noktasını değiştirmek için yeni bir erişim noktası gir."), "changeDaemonSheetHeader" : MessageLookupByLibrary.simpleMessage("Erişim Noktası"), "changeKeyOPDetails" : MessageLookupByLibrary.simpleMessage("Anahtar Değişimi (%1)"), "changeKeySignedOPDetails" : MessageLookupByLibrary.simpleMessage("Anahtar Değişimi (%1)"), "changeNameButton" : MessageLookupByLibrary.simpleMessage("Adı Değiştir"), "changeNameParagraph" : MessageLookupByLibrary.simpleMessage("Hesap adını değiştirmek için aşağıya yeni bir hesap adı gir."), "changeNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Ad Değiştir"), "changedNameParagraph" : MessageLookupByLibrary.simpleMessage("Hesap adı başarıyla değiştirildi."), "changedNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Değiştirildi"), "changingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Değişen Hesap"), "changingNameParagraph" : MessageLookupByLibrary.simpleMessage("İlerlemek için hesabın yeni adını onayla."), "changingNameSheetHeader" : MessageLookupByLibrary.simpleMessage("Değiştiriliyor"), "checkOutBlaiseParagraph" : MessageLookupByLibrary.simpleMessage("Blaise\'i Dene! Sade, şık & güvenli bir Pascal cüzdanı. Hem iOS, hem Android\'de: https://blaisewallet.com"), "closeButton" : MessageLookupByLibrary.simpleMessage("Kapat"), "confirmButton" : MessageLookupByLibrary.simpleMessage("Onayla"), "confirmPINParagraph" : MessageLookupByLibrary.simpleMessage("PIN\'i onayla"), "confirmPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Şifreyi Onayla"), "confirmTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Onayla"), "confirmationCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Onay Kodu"), "contactAlreadyExistsError" : MessageLookupByLibrary.simpleMessage("Kişi zaten mevcut"), "contactDoesntExistError" : MessageLookupByLibrary.simpleMessage("Kişi bulunamadı"), "contactNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Kişi Adı"), "contactSheetHeader" : MessageLookupByLibrary.simpleMessage("Kişi"), "contactsHeader" : MessageLookupByLibrary.simpleMessage("Rehber"), "copiedAddressButton" : MessageLookupByLibrary.simpleMessage("Adres Kopyalandı"), "copiedButton" : MessageLookupByLibrary.simpleMessage("Kopyalandı"), "copyAddressButton" : MessageLookupByLibrary.simpleMessage("Adresi Kopyala"), "copyButton" : MessageLookupByLibrary.simpleMessage("Kopyala"), "copyEncryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Şifreli Anahtarı Kopyala"), "copyKeyButton" : MessageLookupByLibrary.simpleMessage("Anahtarı Kopyala"), "copyPublicKeyButton" : MessageLookupByLibrary.simpleMessage("Açık Anahtarı Kopyala"), "copyUnencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Ham Anahtarı Kopyala"), "countryCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Ülke Kodu"), "createPINParagraph" : MessageLookupByLibrary.simpleMessage("6 haneli bir PIN oluştur"), "createPrivateSaleButton" : MessageLookupByLibrary.simpleMessage("Özel Satış Oluştur"), "createPrivateSaleHeader" : MessageLookupByLibrary.simpleMessage("Özel Satış Oluştur"), "createPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Özel satış oluşturmak için fiyatı, ödemeyi alacak hesabı ve müşterinin açık anahtarını gir."), "createPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Özel Satış"), "createdPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Özel satış başarıyla oluşturuldu. Hesap satın alınırsa seni haberdar ederiz."), "createdPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Oluşturuldu"), "creatingPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("Aşağıdaki bilgileri doğrula."), "creatingPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Oluşturuluyor"), "currencyHeader" : MessageLookupByLibrary.simpleMessage("Para Birimi"), "daemonHeader" : MessageLookupByLibrary.simpleMessage("Erişim Noktası"), "decryptAndImportKeyHeader" : MessageLookupByLibrary.simpleMessage("Şifreyi Aç & İçe Aktar"), "defaultHeader" : MessageLookupByLibrary.simpleMessage("Varsayılan"), "deletePrivateKeyAndLogoutButton" : MessageLookupByLibrary.simpleMessage("Gizli Anahtarı Sil\nve Çık"), "delistAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Satıştan Kaldırma (%1)"), "delistFromSaleHeader" : MessageLookupByLibrary.simpleMessage("Satıştan Kaldır"), "delistFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("Hesabı satıştan kaldırmak istediğini onayla."), "delistedFromSaleHeader" : MessageLookupByLibrary.simpleMessage("Satıştan Kaldırıldı"), "delistedFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("Hesap satıştan başarıyla kaldırıldı."), "delistedHeader" : MessageLookupByLibrary.simpleMessage("Satıştan Kaldırıldı"), "delistedSheetHeader" : MessageLookupByLibrary.simpleMessage("Kaldırıldı"), "delistingSheetHeader" : MessageLookupByLibrary.simpleMessage("Kaldırılıyor"), "didNotGetResponseError" : MessageLookupByLibrary.simpleMessage("Sunucudan cevap alınamadı"), "durationTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Süre"), "emptyPasswordError" : MessageLookupByLibrary.simpleMessage("Şifre alanı boş olamaz"), "encryptButton" : MessageLookupByLibrary.simpleMessage("Şifrele"), "encryptKeyParagraph" : MessageLookupByLibrary.simpleMessage("Gizli anahtarını şifrelemek için yeni bir parola oluştur."), "encryptPayloadHeader" : MessageLookupByLibrary.simpleMessage("Notu Şifrele"), "encryptSheetHeader" : MessageLookupByLibrary.simpleMessage("Şifrele"), "encryptThePayloadHeader" : MessageLookupByLibrary.simpleMessage("Notu Şifrele"), "encryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Şifreli Anahtar"), "enterConfirmationCodeParagraph" : MessageLookupByLibrary.simpleMessage("Sana bir onay kodu yolladık, lütfen kodu aşağıya gir."), "enterPINParagraph" : MessageLookupByLibrary.simpleMessage("PIN\'i gir"), "enterPINToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("Blaise\'i açmak için PIN\'i gir"), "enterPhoneNumberParagraph" : MessageLookupByLibrary.simpleMessage("Telefon numaranı aşağıya gir."), "failedToEncryptPayloadError" : MessageLookupByLibrary.simpleMessage("Not şifreleme başarısız"), "failedToImportContactsError" : MessageLookupByLibrary.simpleMessage("Kişileri içe aktarma işlemi başarısız"), "failedToRemoveFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("%1 rehberden silinemedi"), "feeColonHeader" : MessageLookupByLibrary.simpleMessage("Ücret:"), "feeConfirmAmountParagraph" : MessageLookupByLibrary.simpleMessage("Devam etmek için %1 Pascal tutarında bir ücret eklenmesini onayla."), "feeOPDetails" : MessageLookupByLibrary.simpleMessage("Ücret"), "feeRequiredParagraph" : MessageLookupByLibrary.simpleMessage("Bu operasyon bir ücret gerektiriyor."), "feeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Ücret"), "forSaleHeader" : MessageLookupByLibrary.simpleMessage("Satılık"), "freeAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Ücretsiz Hesap"), "getAFreeAccountButton" : MessageLookupByLibrary.simpleMessage("Ücretsiz Hesap Al"), "getAccountFirstParagraph" : MessageLookupByLibrary.simpleMessage("İlk hesabını almak için 2 seçeneğin var:"), "getAccountSecondParagraph" : MessageLookupByLibrary.simpleMessage("1- Telefon numaranı kullanarak ücretsiz olarak bir hesap alabilirsin. Her telefon numarası için en fazla 1 hesap alabilirsin."), "getAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("Hesap Al"), "getAccountThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- %1 Pascal (%2) karşılığında istediğin kadar hesap satın alabilirsin."), "getAccountThirdParagraphAlternative" : MessageLookupByLibrary.simpleMessage("%1 Pascal (%2) karşılığında bir hesap satın alabilirsin. Her kullanıcı en fazla 1 adet hesap satın alabilir."), "getAccountThirdParagraphAlternative2" : MessageLookupByLibrary.simpleMessage("%1 Pascal (%2) karşılığında bir hesap satın alabilirsin. Her kullanıcı en fazla %3 adet hesap satın alabilir."), "getAnAccountButton" : MessageLookupByLibrary.simpleMessage("Hesap Al"), "goBackButton" : MessageLookupByLibrary.simpleMessage("Geri Dön"), "gotItButton" : MessageLookupByLibrary.simpleMessage("Tamamdır!"), "hideButton" : MessageLookupByLibrary.simpleMessage("Gizle"), "iHaveBackedItUpButton" : MessageLookupByLibrary.simpleMessage("Yedekledim"), "importButton" : MessageLookupByLibrary.simpleMessage("İçe Aktar"), "importPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("Anahtarı İçe Aktar"), "importPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Gizli Anahtarı İçe Aktar"), "importPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("Gizli anahtarını aşağıya gir."), "insufficientBalanceError" : MessageLookupByLibrary.simpleMessage("Bakiye yetersiz"), "invalidAccountError" : MessageLookupByLibrary.simpleMessage("Geçersiz hesap"), "invalidAccountNameError" : MessageLookupByLibrary.simpleMessage("Geçersiz hesap adı"), "invalidAddressError" : MessageLookupByLibrary.simpleMessage("Geçersiz adres"), "invalidDestinationError" : MessageLookupByLibrary.simpleMessage("Geçersiz varış noktası"), "invalidPINParagraph" : MessageLookupByLibrary.simpleMessage("Geçersiz PIN"), "invalidPasswordError" : MessageLookupByLibrary.simpleMessage("Geçersiz şifre"), "invalidPrivateKeyError" : MessageLookupByLibrary.simpleMessage("Geçersiz gizli anahtar"), "invalidPublicKeyError" : MessageLookupByLibrary.simpleMessage("Geçersiz açık anahtar"), "invalidReceivingAccountError" : MessageLookupByLibrary.simpleMessage("Geçersiz alıcı hesap"), "keyCopiedButton" : MessageLookupByLibrary.simpleMessage("Anahtar Kopyalandı"), "keyTypeNotSupportedHeader" : MessageLookupByLibrary.simpleMessage("Anahtar Desteklenmiyor"), "keyTypeNotSupportedParagraph" : MessageLookupByLibrary.simpleMessage("Blaise henüz bu türdeki gizli anahtarları desteklemiyor. Yeni bir gizli anahtar oluşturup, hesaplarını başka bir cüzdan kullanarak yeni gizli anahtarına transfer edebilirsin."), "languageColonHeader" : MessageLookupByLibrary.simpleMessage("Dil:"), "languageHeader" : MessageLookupByLibrary.simpleMessage("Dil"), "listAccountForSaleHeader" : MessageLookupByLibrary.simpleMessage("Hesabı Satışa Çıkar"), "listAccountForSaleOPDetails" : MessageLookupByLibrary.simpleMessage("Hesap Satışı (%1)"), "listForSaleButton" : MessageLookupByLibrary.simpleMessage("Şatışa Çıkar"), "listForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Bu hesabı satışa çıkarmak için aşağıya bir fiyat ve ödemeyi alacak hesabı gir."), "listForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Satış"), "listedForSaleHeader" : MessageLookupByLibrary.simpleMessage("Satışa Çıkarıldı"), "listedForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Hesabın satışa çıkarıldı. Satın alınırsa seni haberdar ederiz."), "listedForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Listelendi"), "listingForSaleParagraph" : MessageLookupByLibrary.simpleMessage("Fiyatı ve ödemeyi alacak hesabı onayla."), "listingForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("Listeleniyor"), "lock15Header" : MessageLookupByLibrary.simpleMessage("%1 dakika sonra"), "lock1Header" : MessageLookupByLibrary.simpleMessage("%1 dakika sonra"), "lock30Header" : MessageLookupByLibrary.simpleMessage("%1 dakika sonra"), "lock5Header" : MessageLookupByLibrary.simpleMessage("%1 dakika sonra"), "lock60Header" : MessageLookupByLibrary.simpleMessage("%1 dakika sonra"), "lockInstantHeader" : MessageLookupByLibrary.simpleMessage("Hemen"), "lockedHeader" : MessageLookupByLibrary.simpleMessage("Kilitli"), "lockedUntilBlockOPDetails" : MessageLookupByLibrary.simpleMessage("Bu Bloğa Kadar Kilitli"), "logoutFirstDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Çıkış yaparsan, gizli anahtarın ve Blaise ile alakalı tüm veriler bu cihazdan silinecek. Gizli anahtarını yedeklemediysen, cüzdanına ve bakiyene bir daha ulaşamazsın. Yedeklediysen, endişelenecek hiçbir şey yok."), "logoutHeader" : MessageLookupByLibrary.simpleMessage("Çıkış"), "logoutSecondDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Gizli anahtarını yedeklediğine emin misin? Yedeklediysen endişelenecek bir şey yok."), "looksLikeEncryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("Bu, şifreli bir gizli anahtara benziyor. Şifrelemeyi çözmek ve anahtarı içe aktarmak için şifreni gir."), "manageHeader" : MessageLookupByLibrary.simpleMessage("Yönet"), "manyFailedAttemptsParagraph" : MessageLookupByLibrary.simpleMessage("Çok fazla başarısız giriş denemesi yapıldı"), "maturationOPDetails" : MessageLookupByLibrary.simpleMessage("Olgunluk"), "multioperationOPDetails" : MessageLookupByLibrary.simpleMessage("Çoklu Operasyon (%1)"), "naOPDetails" : MessageLookupByLibrary.simpleMessage("Mevcut Değil"), "nameChangedHeader" : MessageLookupByLibrary.simpleMessage("Ad Değiştirildi"), "nameRequiredError" : MessageLookupByLibrary.simpleMessage("Ad gerekli"), "nameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Ad"), "newAccountNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Yeni Hesap Adı"), "newAccountParagraph" : MessageLookupByLibrary.simpleMessage("Yeni hesabına hoşgeldin.\nPascal aldığında veya gönderdiğinde, operasyonların aşağıdaki gibi görünecek."), "newKeyBackUpConfirmParagraph" : MessageLookupByLibrary.simpleMessage("Yeni cüzdanının gizli anahtarını yedeklediğine emin misin?"), "newKeySecurityParagraph" : MessageLookupByLibrary.simpleMessage("Bir sonraki ekranda yeni gizli anahtarını göreceksin. Bu, cüzdanına ve bakiyene ulaşmanı sağlayan bir çeşit şifre. Anahtarı yedeklemen ve kimseyle paylaşmaman çok önemli."), "newNameOPDetails" : MessageLookupByLibrary.simpleMessage("Yeni Ad"), "newPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Yeni Şifre"), "newPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("Yeni Gizli Anahtar"), "newPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("Yeni Gizli Anahtar"), "newPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("Aşağıda cüzdanın yeni gizli anahtarını görebilirsin. Anahtarını yedeklemen çok önemli. Anahtarı kesinlikle ekran görüntüsü olarak saklamamalısın. Anahtarı bir kağıda yazmanı ve çevrim dışı olarak saklamanı tavsiye ediyoruz."), "newPublicKeyOPDetails" : MessageLookupByLibrary.simpleMessage("Yeni Açık Anahtar"), "newWalletGreetingParagraph" : MessageLookupByLibrary.simpleMessage("Blaise\'e hoşgeldin.\nBir hesap alarak başlayabilirsin."), "nextButton" : MessageLookupByLibrary.simpleMessage("İleri"), "noContactsToExportError" : MessageLookupByLibrary.simpleMessage("Dışa aktarılacak kişi bulunamadı"), "noContactsToImportError" : MessageLookupByLibrary.simpleMessage("İçe aktarılacak kişi bulunamadı"), "noGoBackButton" : MessageLookupByLibrary.simpleMessage("Hayır, Geri Dön"), "noHeader" : MessageLookupByLibrary.simpleMessage("Hayır"), "noMatchPINParagraph" : MessageLookupByLibrary.simpleMessage("PIN\'ler uyuşmuyor"), "noMatchPasswordError" : MessageLookupByLibrary.simpleMessage("Şifreler uyuşmuyor"), "noResultsFound" : MessageLookupByLibrary.simpleMessage("Sonuç bulunamadı"), "noperationOPDetails" : MessageLookupByLibrary.simpleMessage("Toplam Operasyon"), "notificationsHeader" : MessageLookupByLibrary.simpleMessage("Bildirimler"), "nullOPDetails" : MessageLookupByLibrary.simpleMessage("Belirsiz"), "offHeader" : MessageLookupByLibrary.simpleMessage("Kapalı"), "okayGoBackButton" : MessageLookupByLibrary.simpleMessage("Tamamdır, Geri Dön"), "onHeader" : MessageLookupByLibrary.simpleMessage("Açık"), "opblockOPDetails" : MessageLookupByLibrary.simpleMessage("Bloktaki Sırası"), "openInExplorerButton" : MessageLookupByLibrary.simpleMessage("Tarayıcıda Aç"), "operationDetailsButton" : MessageLookupByLibrary.simpleMessage("Operasyon detayları"), "operationsHeader" : MessageLookupByLibrary.simpleMessage("Operasyonlar"), "ophashOPDetails" : MessageLookupByLibrary.simpleMessage("Operasyon Hash\'i"), "optxtOPDetails" : MessageLookupByLibrary.simpleMessage("Operasyon Notu"), "optypeOPDetails" : MessageLookupByLibrary.simpleMessage("Operasyon Tipi"), "otherOperationsHeader" : MessageLookupByLibrary.simpleMessage("Diğer Operasyonlar"), "passwordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Şifre"), "payloadOPDetails" : MessageLookupByLibrary.simpleMessage("Not"), "payloadTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Not"), "pendingHeader" : MessageLookupByLibrary.simpleMessage("Bekleniyor"), "phoneNumberTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Telefon Numarası"), "preferencesHeader" : MessageLookupByLibrary.simpleMessage("Tercihler"), "priceRequiredError" : MessageLookupByLibrary.simpleMessage("Fiyat gerekli"), "priceTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Fiyat"), "privacyPolicyHeader" : MessageLookupByLibrary.simpleMessage("Gizlilik Politikası"), "privateKeySheetHeader" : MessageLookupByLibrary.simpleMessage("Gizli Anahtar"), "privateKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Gizli Anahtar"), "privateSaleHeader" : MessageLookupByLibrary.simpleMessage("Özel Satış"), "publicKeyParagraph" : MessageLookupByLibrary.simpleMessage("Açık anahtarını aşağıda görebilirsin. Bu anahtar, herkese açık şekilde paylaşılmak ve bir operasyonun senin gizli anahtarına ait olduğu kanıtlamak için var."), "publicKeySheetHeader" : MessageLookupByLibrary.simpleMessage("Açık Anahtar"), "publicKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Açık Anahtar"), "receiveAccountButton" : MessageLookupByLibrary.simpleMessage("Hesap İste"), "receiveAnAccountButton" : MessageLookupByLibrary.simpleMessage("Hesap İste"), "receiveButton" : MessageLookupByLibrary.simpleMessage("İste"), "receivedHeader" : MessageLookupByLibrary.simpleMessage("Alındı"), "receivingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Alıcı Hesap"), "receivingAccountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("Ödemeyi Alacak Hesap"), "recoverFundsOPDetails" : MessageLookupByLibrary.simpleMessage("Bakiye Kurtarma (%1)"), "removedFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("%1 rehberden silindi"), "requestButton" : MessageLookupByLibrary.simpleMessage("İste"), "requestSheetHeader" : MessageLookupByLibrary.simpleMessage("İste"), "scanQRCodeButton" : MessageLookupByLibrary.simpleMessage("QR Kodu Tara"), "searchAccountNameButton" : MessageLookupByLibrary.simpleMessage("Hesap Adı Ara"), "searchForNameButton" : MessageLookupByLibrary.simpleMessage("Adı Ara"), "searchNameButton" : MessageLookupByLibrary.simpleMessage("Adı Ara"), "securityFirstHeader" : MessageLookupByLibrary.simpleMessage("Önce Güvenlik!"), "securityHeader" : MessageLookupByLibrary.simpleMessage("Güvenlik"), "sellerAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Satıcı Hesap"), "sendAmountOPDetails" : MessageLookupByLibrary.simpleMessage("Gönderi Miktarı"), "sendButton" : MessageLookupByLibrary.simpleMessage("Gönder"), "sendConfirmationButton" : MessageLookupByLibrary.simpleMessage("Onay Kodu Gönder"), "sendSheetHeader" : MessageLookupByLibrary.simpleMessage("Gönder"), "sendingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("Gönderici Hesap"), "sendingConfirmParagraph" : MessageLookupByLibrary.simpleMessage("Göndermek için işlem detaylarını onayla."), "sendingSheetHeader" : MessageLookupByLibrary.simpleMessage("Gönderiliyor"), "sentHeader" : MessageLookupByLibrary.simpleMessage("Gönderildi"), "sentParagraph" : MessageLookupByLibrary.simpleMessage("Gönderme işlemi başarıyla tamamlandı."), "sentSheetHeader" : MessageLookupByLibrary.simpleMessage("Gönderildi"), "setToDefaultButton" : MessageLookupByLibrary.simpleMessage("Varsayılan"), "settingsHeader" : MessageLookupByLibrary.simpleMessage("Ayarlar"), "shareHeader" : MessageLookupByLibrary.simpleMessage("Blaise\'i Paylaş"), "showButton" : MessageLookupByLibrary.simpleMessage("Göster"), "signeraccountOPDetails" : MessageLookupByLibrary.simpleMessage("İmzalayan Hesap"), "somethingWentWrongError" : MessageLookupByLibrary.simpleMessage("Bir şeyler ters gitti, daha sonra tekrar dene"), "successfullyImportedContactsParagraph" : MessageLookupByLibrary.simpleMessage("%1 kişi rehbere başarıyla eklendi"), "systemDefaultHeader" : MessageLookupByLibrary.simpleMessage("Sistem Varsayılanı"), "themeCopperHeader" : MessageLookupByLibrary.simpleMessage("Bakır"), "themeDarkHeader" : MessageLookupByLibrary.simpleMessage("Koyu"), "themeHeader" : MessageLookupByLibrary.simpleMessage("Tema"), "themeLightHeader" : MessageLookupByLibrary.simpleMessage("Açık"), "threeCharacterNameError" : MessageLookupByLibrary.simpleMessage("En az 3 karakter olmalı"), "timeOPDetails" : MessageLookupByLibrary.simpleMessage("Tarih"), "totalBalanceHeader" : MessageLookupByLibrary.simpleMessage("Genel Bakiye"), "transactionOPDetails" : MessageLookupByLibrary.simpleMessage("İşlem (%1)"), "transferAccountHeader" : MessageLookupByLibrary.simpleMessage("Hesabı Transfer Et"), "transferButton" : MessageLookupByLibrary.simpleMessage("Transfer Et"), "transferParagraph" : MessageLookupByLibrary.simpleMessage("Bu hesabı transfer etmek istediğin açık anahtarı aşağıya gir."), "transferSheetHeader" : MessageLookupByLibrary.simpleMessage("Transfer Et"), "transferredHeader" : MessageLookupByLibrary.simpleMessage("Transfer Edildi"), "transferredParagraph" : MessageLookupByLibrary.simpleMessage("Hesabın aşağıdaki açık anahtara başarıyla transfer edildi."), "transferredSheetHeader" : MessageLookupByLibrary.simpleMessage("Edildi"), "transferringParagraph" : MessageLookupByLibrary.simpleMessage("Bu hesabı transfer etmek istediğin açık anahtarı onayla."), "transferringSheetHeader" : MessageLookupByLibrary.simpleMessage("Ediliyor"), "undefinedHeader" : MessageLookupByLibrary.simpleMessage("Tanımsız"), "unencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("Ham Anahtar"), "uninstallDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("Cihazını kaybedersen veya Blaise\'i silersen, bakiyene ve cüzdanına tekrar ulaşmak için gizli anahtarına ihtiyaç duyacaksın."), "unknownOPDetails" : MessageLookupByLibrary.simpleMessage("Bilinmeyen (%1)"), "unlockButton" : MessageLookupByLibrary.simpleMessage("Kilidi Aç"), "unlockWithBiometricsButton" : MessageLookupByLibrary.simpleMessage("Biometrik ile Aç"), "unlockWithPINButton" : MessageLookupByLibrary.simpleMessage("PIN ile Aç"), "urlChangedToParagraph" : MessageLookupByLibrary.simpleMessage("URL %1 olarak değiştirildi"), "viewPublicKeyHeader" : MessageLookupByLibrary.simpleMessage("Açık Anahtarı Görüntüle"), "warningHeader" : MessageLookupByLibrary.simpleMessage("Uyarı"), "welcomeParagraph" : MessageLookupByLibrary.simpleMessage("Blaise\'e hoşgeldin. Başlamak için yeni bir gizli anahtar oluştur veya zaten bir gizli anahtarın varsa içe aktar."), "yesAddFeeButton" : MessageLookupByLibrary.simpleMessage("Evet, Ücreti Ekle"), "yesHeader" : MessageLookupByLibrary.simpleMessage("Evet"), "yesImSureButton" : MessageLookupByLibrary.simpleMessage("Evet, Eminim"), "zeroAmountError" : MessageLookupByLibrary.simpleMessage("Miktar sıfır olamaz"), "zeroPriceError" : MessageLookupByLibrary.simpleMessage("Fiyat sıfır olamaz") }; } ================================================ FILE: lib/l10n/messages_zh-Hans.dart ================================================ // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart // This is a library that provides messages for a zh_Hans locale. All the // messages from the main program should be duplicated here with the same // function name. // Ignore issues from commonly used lints in this file. // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases // ignore_for_file:unused_import, file_names import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; final messages = new MessageLookup(); typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'zh_Hans'; final messages = _notInlinedMessages(_notInlinedMessages); static _notInlinedMessages(_) => { "accountBalanceHeader" : MessageLookupByLibrary.simpleMessage("账号余额"), "accountOPDetails" : MessageLookupByLibrary.simpleMessage("账号"), "accountPriceOPDetails" : MessageLookupByLibrary.simpleMessage("账号价格"), "accountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("账号"), "accountToSendFromHeader" : MessageLookupByLibrary.simpleMessage("发送账号"), "accountsHeader" : MessageLookupByLibrary.simpleMessage("账号列表"), "addADurationButton" : MessageLookupByLibrary.simpleMessage("+ 添加时长"), "addAPayloadButton" : MessageLookupByLibrary.simpleMessage("+ 添加负载"), "addContactButton" : MessageLookupByLibrary.simpleMessage("添加联系人"), "addContactSheetHeader" : MessageLookupByLibrary.simpleMessage("添加联系人"), "addFeeHeader" : MessageLookupByLibrary.simpleMessage("添加手续费"), "addToContactsButton" : MessageLookupByLibrary.simpleMessage("添加到联系人"), "addedToContactsParagraph" : MessageLookupByLibrary.simpleMessage("已将%1添加到联系人"), "addressTextFieldHeader" : MessageLookupByLibrary.simpleMessage("地址"), "amountRequiredError" : MessageLookupByLibrary.simpleMessage("请输入金额"), "amountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("金额"), "areYouSureHeader" : MessageLookupByLibrary.simpleMessage("您确定吗?"), "authenticateOnLaunchHeader" : MessageLookupByLibrary.simpleMessage("启动时要求验证"), "authenticateToBackUpParagraph" : MessageLookupByLibrary.simpleMessage("验证以备份私钥"), "authenticateToChangeNameParagraph" : MessageLookupByLibrary.simpleMessage("验证将帐户名称更改为\"%1\""), "authenticateToCreatePrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("验证以创建新的私钥"), "authenticateToDelistParagraph" : MessageLookupByLibrary.simpleMessage("验证以下架正在出售的账号"), "authenticateToListForSaleParagraph" : MessageLookupByLibrary.simpleMessage("验证以上架出售账号"), "authenticateToSendParagraph" : MessageLookupByLibrary.simpleMessage("验证以发送%1Pascal"), "authenticateToTransferParagraph" : MessageLookupByLibrary.simpleMessage("验证以转移账号"), "authenticateToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("验证以解锁钱包"), "authenticationBiometricsHeader" : MessageLookupByLibrary.simpleMessage("生物验证"), "authenticationMethodHeader" : MessageLookupByLibrary.simpleMessage("验证方式"), "authenticationPINHeader" : MessageLookupByLibrary.simpleMessage("识别码"), "automaticallyLockHeader" : MessageLookupByLibrary.simpleMessage("自动锁定"), "backUpKeyHeader" : MessageLookupByLibrary.simpleMessage("备份您的私钥!"), "backUpPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("备份私钥"), "backUpSheetHeader" : MessageLookupByLibrary.simpleMessage("备份"), "backupEncryptedKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("以下是经过您的密码加密后的私钥。您可以选择将它保存到密码管理器中。"), "backupEncryptedKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("由于私钥经过了您的密码加密,如果您丢失或忘记了密码,那么您将无法再访问您的资金。"), "backupKeyFirstParagraph" : MessageLookupByLibrary.simpleMessage("您有两种可选方式来备份私钥:"), "backupKeyFourthParagraph" : MessageLookupByLibrary.simpleMessage("我们推荐以非加密方式将私钥抄写到纸上,离线保存。您也可以选择以加密方式将私钥保存到密码管理器中。"), "backupKeySecondParagraph" : MessageLookupByLibrary.simpleMessage("1- 加密方式。这意味着你的私钥将被密码保护。"), "backupKeyThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- 非加密方式。以原始明文方式备份,不通过密码保护。"), "backupUnencryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("以下是您未经加密的原始私钥。该私钥没有使用密码保护,请务必将其备份到安全且离线的地方。 我们推荐您将其抄写到纸上。"), "balanceHeader" : MessageLookupByLibrary.simpleMessage("余额"), "blockOPDetails" : MessageLookupByLibrary.simpleMessage("区块"), "blockchainRewardOPDetails" : MessageLookupByLibrary.simpleMessage("区块奖励 (%1)"), "borrowAccountParagraph" : MessageLookupByLibrary.simpleMessage("要购买一个帐户,您首先需要免费借用一个。如果您在%3天内至少存入%1 Pascal (%2)到该账号,该账号将是您的,并且%1 Pascal将自动从您的余额中扣除。\n否则,该账号将在%3天结束时返回给我们,不再属于您的钱包。\n建议您在完全拥有该帐号前只存入少量的Pascal。"), "borrowAnAccountButton" : MessageLookupByLibrary.simpleMessage("借用账号"), "borrowStarted" : MessageLookupByLibrary.simpleMessage("开始购买账号 %1"), "borrowedAccountHeader" : MessageLookupByLibrary.simpleMessage("借用的账号"), "borrowedAccountPaidParagraph" : MessageLookupByLibrary.simpleMessage("您已成功购买该账号!\n网络正在处理该操作,这个过程大约需要15分钟, 在某些情况下,可能需要稍长时间。"), "borrowedAccountParagraph" : MessageLookupByLibrary.simpleMessage("这是一个借用的账号。\n如果您在接下来的%2天 %3时 %4分内存入%1 Pascal到该账号中,您将永远拥有它。"), "borrowedHeader" : MessageLookupByLibrary.simpleMessage("借用中"), "borrowedTransferredHeader" : MessageLookupByLibrary.simpleMessage("处理中的转让"), "buyAccountOPDetails" : MessageLookupByLibrary.simpleMessage("购买账号 (%1)"), "buyAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("购买账号"), "buyAnAccountButton" : MessageLookupByLibrary.simpleMessage("购买账号"), "cancelButton" : MessageLookupByLibrary.simpleMessage("取消"), "cantSendToYourselfError" : MessageLookupByLibrary.simpleMessage("无法发送给自己"), "changeAccountInfoOPDetails" : MessageLookupByLibrary.simpleMessage("更改账号信息 (%1)"), "changeAccountNameHeader" : MessageLookupByLibrary.simpleMessage("更改账号名"), "changeDaemonButton" : MessageLookupByLibrary.simpleMessage("更改后台"), "changeDaemonParagraph" : MessageLookupByLibrary.simpleMessage("请输入一个新的后台地址以便进行RPC请求。"), "changeDaemonSheetHeader" : MessageLookupByLibrary.simpleMessage("更改后台"), "changeKeyOPDetails" : MessageLookupByLibrary.simpleMessage("更改密钥 (%1)"), "changeKeySignedOPDetails" : MessageLookupByLibrary.simpleMessage("签署更改密钥 (%1)"), "changeNameButton" : MessageLookupByLibrary.simpleMessage("修改账号名"), "changeNameParagraph" : MessageLookupByLibrary.simpleMessage("在下方输入一个名字来更改您的账号名。"), "changeNameSheetHeader" : MessageLookupByLibrary.simpleMessage("更名"), "changedNameParagraph" : MessageLookupByLibrary.simpleMessage("您已成功更改账号名。"), "changedNameSheetHeader" : MessageLookupByLibrary.simpleMessage("已更改"), "changingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("更改账号"), "changingNameParagraph" : MessageLookupByLibrary.simpleMessage("请确认您的新账号名。"), "changingNameSheetHeader" : MessageLookupByLibrary.simpleMessage("正在更改"), "checkOutBlaiseParagraph" : MessageLookupByLibrary.simpleMessage("了解Blaise,简单、安全又好用的Pascal钱包:https://blaisewallet.com"), "closeButton" : MessageLookupByLibrary.simpleMessage("关闭"), "confirmButton" : MessageLookupByLibrary.simpleMessage("确认"), "confirmPINParagraph" : MessageLookupByLibrary.simpleMessage("确认您的识别码"), "confirmPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("确认密码"), "confirmTextFieldHeader" : MessageLookupByLibrary.simpleMessage("确认"), "confirmationCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("验证码"), "contactAlreadyExistsError" : MessageLookupByLibrary.simpleMessage("联系人已存在"), "contactDoesntExistError" : MessageLookupByLibrary.simpleMessage("联系人不存在"), "contactNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("联系人名字"), "contactSheetHeader" : MessageLookupByLibrary.simpleMessage("联系人"), "contactsHeader" : MessageLookupByLibrary.simpleMessage("联系人"), "copiedAddressButton" : MessageLookupByLibrary.simpleMessage("地址复制成功"), "copiedButton" : MessageLookupByLibrary.simpleMessage("复制成功"), "copyAddressButton" : MessageLookupByLibrary.simpleMessage("复制地址"), "copyButton" : MessageLookupByLibrary.simpleMessage("复制"), "copyEncryptedKeyButton" : MessageLookupByLibrary.simpleMessage("复制已加密私钥"), "copyKeyButton" : MessageLookupByLibrary.simpleMessage("复制密钥"), "copyPublicKeyButton" : MessageLookupByLibrary.simpleMessage("复制公钥"), "copyUnencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("复制原始私钥"), "countryCodeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("国家代码"), "createPINParagraph" : MessageLookupByLibrary.simpleMessage("创建一个6位数的识别码"), "createPrivateSaleButton" : MessageLookupByLibrary.simpleMessage("创建非公开挂卖"), "createPrivateSaleHeader" : MessageLookupByLibrary.simpleMessage("对私出售该账号"), "createPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("输入售价、收款账号和公钥,以对该账号进行对私出售。"), "createPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("对私出售"), "createdPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("对私出售已经成功创建。如果账号被购买了,我们将会通知您。"), "createdPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("成功创建"), "creatingPrivateSaleParagraph" : MessageLookupByLibrary.simpleMessage("请确认以下信息。"), "creatingPrivateSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("创建"), "currencyHeader" : MessageLookupByLibrary.simpleMessage("货币单位"), "daemonHeader" : MessageLookupByLibrary.simpleMessage("后台设置"), "decryptAndImportKeyHeader" : MessageLookupByLibrary.simpleMessage("解密并导入"), "defaultHeader" : MessageLookupByLibrary.simpleMessage("默认"), "deletePrivateKeyAndLogoutButton" : MessageLookupByLibrary.simpleMessage("删去私钥\n并登出"), "delistAccountOPDetails" : MessageLookupByLibrary.simpleMessage("下架账号 (%1)"), "delistFromSaleHeader" : MessageLookupByLibrary.simpleMessage("取消出售"), "delistFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("请确认您想将此帐户从销售列表中下架。"), "delistedFromSaleHeader" : MessageLookupByLibrary.simpleMessage("已下架"), "delistedFromSaleParagraph" : MessageLookupByLibrary.simpleMessage("您的帐户已成功从销售列表中下架。"), "delistedHeader" : MessageLookupByLibrary.simpleMessage("已下架"), "delistedSheetHeader" : MessageLookupByLibrary.simpleMessage("成功下架"), "delistingSheetHeader" : MessageLookupByLibrary.simpleMessage("下架"), "didNotGetResponseError" : MessageLookupByLibrary.simpleMessage("服务器无响应"), "durationTextFieldHeader" : MessageLookupByLibrary.simpleMessage("时长"), "emptyPasswordError" : MessageLookupByLibrary.simpleMessage("密码不能为空"), "encryptButton" : MessageLookupByLibrary.simpleMessage("加密私钥"), "encryptKeyParagraph" : MessageLookupByLibrary.simpleMessage("创建一个密码,用来加密你的私钥。"), "encryptPayloadHeader" : MessageLookupByLibrary.simpleMessage("对负载加密"), "encryptSheetHeader" : MessageLookupByLibrary.simpleMessage("加密"), "encryptThePayloadHeader" : MessageLookupByLibrary.simpleMessage("对负载加密"), "encryptedKeyButton" : MessageLookupByLibrary.simpleMessage("显示已加密私钥"), "enterConfirmationCodeParagraph" : MessageLookupByLibrary.simpleMessage("请输入您收到的验证码。"), "enterPINParagraph" : MessageLookupByLibrary.simpleMessage("请输入输入识别码"), "enterPINToUnlockParagraph" : MessageLookupByLibrary.simpleMessage("输入识别码以解锁"), "enterPhoneNumberParagraph" : MessageLookupByLibrary.simpleMessage("在下面输入您的手机号码"), "failedToEncryptPayloadError" : MessageLookupByLibrary.simpleMessage("未能成功加密负载"), "failedToImportContactsError" : MessageLookupByLibrary.simpleMessage("导入联系人失败"), "failedToRemoveFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("未能从联系人中删除%1"), "feeColonHeader" : MessageLookupByLibrary.simpleMessage("手续费:"), "feeConfirmAmountParagraph" : MessageLookupByLibrary.simpleMessage("请确认将%1Pascal作为费用以继续此操作。"), "feeOPDetails" : MessageLookupByLibrary.simpleMessage("手续费"), "feeRequiredParagraph" : MessageLookupByLibrary.simpleMessage("该操作需要手续费。"), "feeTextFieldHeader" : MessageLookupByLibrary.simpleMessage("手续费"), "forSaleHeader" : MessageLookupByLibrary.simpleMessage("代售"), "freeAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("免费账号"), "getAFreeAccountButton" : MessageLookupByLibrary.simpleMessage("获取免费账号"), "getAccountFirstParagraph" : MessageLookupByLibrary.simpleMessage("您可以通过两种方式获得账号:"), "getAccountSecondParagraph" : MessageLookupByLibrary.simpleMessage("1- 通过手机号码获得账号,每个手机号码只能获得一个。"), "getAccountSheetHeader" : MessageLookupByLibrary.simpleMessage("获取一个账号"), "getAccountThirdParagraph" : MessageLookupByLibrary.simpleMessage("2- 您可以以单价%1 Pascal (%2)购买任意数量的账号。"), "getAccountThirdParagraphAlternative" : MessageLookupByLibrary.simpleMessage("2- 您可以花费%1 Pascal (%2)购买一个账号。 每个用户只允许购买一个账号。"), "getAccountThirdParagraphAlternative2" : MessageLookupByLibrary.simpleMessage("2- 您可以花费%1 Pascal (%2)购买一个账号。 您最多可以购买%3个账号。"), "getAnAccountButton" : MessageLookupByLibrary.simpleMessage("获取账号"), "goBackButton" : MessageLookupByLibrary.simpleMessage("返回"), "gotItButton" : MessageLookupByLibrary.simpleMessage("确认"), "hideButton" : MessageLookupByLibrary.simpleMessage("隐藏"), "iHaveBackedItUpButton" : MessageLookupByLibrary.simpleMessage("我已做好备份"), "importButton" : MessageLookupByLibrary.simpleMessage("导入"), "importPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("导入私钥"), "importPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("导入私钥"), "importPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("请在下方输入您的私钥:"), "insufficientBalanceError" : MessageLookupByLibrary.simpleMessage("余额不足"), "invalidAccountError" : MessageLookupByLibrary.simpleMessage("无效的账号"), "invalidAccountNameError" : MessageLookupByLibrary.simpleMessage("无效的账号名"), "invalidAddressError" : MessageLookupByLibrary.simpleMessage("无效的地址"), "invalidDestinationError" : MessageLookupByLibrary.simpleMessage("无效的目标"), "invalidPINParagraph" : MessageLookupByLibrary.simpleMessage("无效的识别码"), "invalidPasswordError" : MessageLookupByLibrary.simpleMessage("密码无效"), "invalidPrivateKeyError" : MessageLookupByLibrary.simpleMessage("无效的私钥"), "invalidPublicKeyError" : MessageLookupByLibrary.simpleMessage("无效的公钥"), "invalidReceivingAccountError" : MessageLookupByLibrary.simpleMessage("无效的收款账号"), "keyCopiedButton" : MessageLookupByLibrary.simpleMessage("成功复制密钥"), "keyTypeNotSupportedHeader" : MessageLookupByLibrary.simpleMessage("不被支持的私钥"), "keyTypeNotSupportedParagraph" : MessageLookupByLibrary.simpleMessage("Blaise钱包目前还不支持这种类型的私钥。您可以创建一个新的私钥,并使用其它钱包将您的帐户转移给该私钥。"), "languageColonHeader" : MessageLookupByLibrary.simpleMessage("语言:"), "languageHeader" : MessageLookupByLibrary.simpleMessage("语言"), "listAccountForSaleHeader" : MessageLookupByLibrary.simpleMessage("上架出售账号"), "listAccountForSaleOPDetails" : MessageLookupByLibrary.simpleMessage("上架出售账号 (%1)"), "listForSaleButton" : MessageLookupByLibrary.simpleMessage("挂卖账号"), "listForSaleParagraph" : MessageLookupByLibrary.simpleMessage("请输入该账号的售价和一个用来接收付款的账号。"), "listForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("上架出售"), "listedForSaleHeader" : MessageLookupByLibrary.simpleMessage("上架出售"), "listedForSaleParagraph" : MessageLookupByLibrary.simpleMessage("您的帐号正式挂牌出售。如果有人买了,我们将会通知您。"), "listedForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("成功上架"), "listingForSaleParagraph" : MessageLookupByLibrary.simpleMessage("请确认售价和收款账号。"), "listingForSaleSheetHeader" : MessageLookupByLibrary.simpleMessage("上架明细"), "lock15Header" : MessageLookupByLibrary.simpleMessage("%1 分钟后"), "lock1Header" : MessageLookupByLibrary.simpleMessage("%1 分钟后"), "lock30Header" : MessageLookupByLibrary.simpleMessage("%1 分钟后"), "lock5Header" : MessageLookupByLibrary.simpleMessage("%1 分钟后"), "lock60Header" : MessageLookupByLibrary.simpleMessage("%1 分钟后"), "lockInstantHeader" : MessageLookupByLibrary.simpleMessage("立刻"), "lockedHeader" : MessageLookupByLibrary.simpleMessage("已锁定"), "lockedUntilBlockOPDetails" : MessageLookupByLibrary.simpleMessage("解锁区块"), "logoutFirstDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("登出将会从这个设备删除您的私钥和所有与Blaise钱包相关的数据。如果您的私钥还未备份,您将永远无法再访问您的资金。如果您的私钥已备份,则无需担心。"), "logoutHeader" : MessageLookupByLibrary.simpleMessage("登出"), "logoutSecondDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("您确定已经备份了您的私钥吗?只要您备份了私钥,就没有什么好担心的."), "looksLikeEncryptedKeyParagraph" : MessageLookupByLibrary.simpleMessage("这看起来像是一个经过加密的私钥,请输入密码以解密并导入它。"), "manageHeader" : MessageLookupByLibrary.simpleMessage("管理"), "manyFailedAttemptsParagraph" : MessageLookupByLibrary.simpleMessage("解锁尝试失败次数太多"), "maturationOPDetails" : MessageLookupByLibrary.simpleMessage("成熟区块"), "multioperationOPDetails" : MessageLookupByLibrary.simpleMessage("多重操作 (%1)"), "naOPDetails" : MessageLookupByLibrary.simpleMessage("N/A"), "nameChangedHeader" : MessageLookupByLibrary.simpleMessage("更名成功"), "nameRequiredError" : MessageLookupByLibrary.simpleMessage("请输入名字"), "nameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("名字"), "newAccountNameTextFieldHeader" : MessageLookupByLibrary.simpleMessage("新账号名"), "newAccountParagraph" : MessageLookupByLibrary.simpleMessage("这是您的新账号。\n一旦您接收到Pascal,操作将如下所示。"), "newKeyBackUpConfirmParagraph" : MessageLookupByLibrary.simpleMessage("您确定已经将新钱包的私钥备份好了吗?"), "newKeySecurityParagraph" : MessageLookupByLibrary.simpleMessage("接下来您将看到您新建的私钥,请务必将其备份且不要泄露给任何人。"), "newNameOPDetails" : MessageLookupByLibrary.simpleMessage("新的账号名"), "newPasswordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("新密码"), "newPrivateKeyButton" : MessageLookupByLibrary.simpleMessage("新建私钥"), "newPrivateKeyHeader" : MessageLookupByLibrary.simpleMessage("新建私钥"), "newPrivateKeyParagraph" : MessageLookupByLibrary.simpleMessage("以下是您的新钱包私钥。请注意,您必须备份私钥但不要将其备份为纯文本或屏幕截图,我们建议您用笔将其抄写到纸上。"), "newPublicKeyOPDetails" : MessageLookupByLibrary.simpleMessage("新的公钥"), "newWalletGreetingParagraph" : MessageLookupByLibrary.simpleMessage("欢迎使用Blaise钱包。\n您可以从获取一个账号开始"), "nextButton" : MessageLookupByLibrary.simpleMessage("继续"), "noContactsToExportError" : MessageLookupByLibrary.simpleMessage("没有联系人可以导出"), "noContactsToImportError" : MessageLookupByLibrary.simpleMessage("没有联系人可以导入"), "noGoBackButton" : MessageLookupByLibrary.simpleMessage("返回"), "noHeader" : MessageLookupByLibrary.simpleMessage("否"), "noMatchPINParagraph" : MessageLookupByLibrary.simpleMessage("识别码不匹配"), "noMatchPasswordError" : MessageLookupByLibrary.simpleMessage("密码不匹配"), "noResultsFound" : MessageLookupByLibrary.simpleMessage("未找到结果"), "noperationOPDetails" : MessageLookupByLibrary.simpleMessage("操作数"), "notificationsHeader" : MessageLookupByLibrary.simpleMessage("通知"), "nullOPDetails" : MessageLookupByLibrary.simpleMessage("空"), "offHeader" : MessageLookupByLibrary.simpleMessage("禁用"), "okayGoBackButton" : MessageLookupByLibrary.simpleMessage("返回"), "onHeader" : MessageLookupByLibrary.simpleMessage("启用"), "opblockOPDetails" : MessageLookupByLibrary.simpleMessage("区块的第几笔操作"), "openInExplorerButton" : MessageLookupByLibrary.simpleMessage("在浏览器中打开"), "operationDetailsButton" : MessageLookupByLibrary.simpleMessage("操作细节"), "operationsHeader" : MessageLookupByLibrary.simpleMessage("操作记录"), "ophashOPDetails" : MessageLookupByLibrary.simpleMessage("操作哈希"), "optxtOPDetails" : MessageLookupByLibrary.simpleMessage("操作说明"), "optypeOPDetails" : MessageLookupByLibrary.simpleMessage("操作类型"), "otherOperationsHeader" : MessageLookupByLibrary.simpleMessage("其它操作"), "passwordTextFieldHeader" : MessageLookupByLibrary.simpleMessage("密码"), "payloadOPDetails" : MessageLookupByLibrary.simpleMessage("负载"), "payloadTextFieldHeader" : MessageLookupByLibrary.simpleMessage("负载"), "pendingHeader" : MessageLookupByLibrary.simpleMessage("处理中"), "phoneNumberTextFieldHeader" : MessageLookupByLibrary.simpleMessage("手机号码"), "preferencesHeader" : MessageLookupByLibrary.simpleMessage("偏好"), "priceRequiredError" : MessageLookupByLibrary.simpleMessage("请输入价格"), "priceTextFieldHeader" : MessageLookupByLibrary.simpleMessage("价格"), "privacyPolicyHeader" : MessageLookupByLibrary.simpleMessage("隐私政策"), "privateKeySheetHeader" : MessageLookupByLibrary.simpleMessage("私钥"), "privateKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("私钥"), "privateSaleHeader" : MessageLookupByLibrary.simpleMessage("对私出售"), "publicKeyParagraph" : MessageLookupByLibrary.simpleMessage("以下是您的公钥。顾名思义,它可用作公开共享,并证明某个特定操作属于您的私钥。"), "publicKeySheetHeader" : MessageLookupByLibrary.simpleMessage("公钥"), "publicKeyTextFieldHeader" : MessageLookupByLibrary.simpleMessage("公钥"), "receiveAccountButton" : MessageLookupByLibrary.simpleMessage("接收账号"), "receiveAnAccountButton" : MessageLookupByLibrary.simpleMessage("接收一个账号"), "receiveButton" : MessageLookupByLibrary.simpleMessage("接收"), "receivedHeader" : MessageLookupByLibrary.simpleMessage("已收到"), "receivingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("收款方账号"), "receivingAccountTextFieldHeader" : MessageLookupByLibrary.simpleMessage("收款账号"), "recoverFundsOPDetails" : MessageLookupByLibrary.simpleMessage("恢复资金 (%1)"), "removedFromContactsParagraph" : MessageLookupByLibrary.simpleMessage("已将%1从联系人中删除"), "requestButton" : MessageLookupByLibrary.simpleMessage("请求"), "requestSheetHeader" : MessageLookupByLibrary.simpleMessage("请求"), "scanQRCodeButton" : MessageLookupByLibrary.simpleMessage("扫描二维码"), "searchAccountNameButton" : MessageLookupByLibrary.simpleMessage("搜索账号名"), "searchForNameButton" : MessageLookupByLibrary.simpleMessage("搜索名字"), "searchNameButton" : MessageLookupByLibrary.simpleMessage("搜索名字"), "securityFirstHeader" : MessageLookupByLibrary.simpleMessage("安全第一"), "securityHeader" : MessageLookupByLibrary.simpleMessage("安全设置"), "sellerAccountOPDetails" : MessageLookupByLibrary.simpleMessage("卖方账号"), "sendAmountOPDetails" : MessageLookupByLibrary.simpleMessage("发送金额"), "sendButton" : MessageLookupByLibrary.simpleMessage("发送"), "sendConfirmationButton" : MessageLookupByLibrary.simpleMessage("确认发送"), "sendSheetHeader" : MessageLookupByLibrary.simpleMessage("发送"), "sendingAccountOPDetails" : MessageLookupByLibrary.simpleMessage("发送方账号"), "sendingConfirmParagraph" : MessageLookupByLibrary.simpleMessage("确认您的交易信息以发送。"), "sendingSheetHeader" : MessageLookupByLibrary.simpleMessage("正在发送"), "sentHeader" : MessageLookupByLibrary.simpleMessage("已发送"), "sentParagraph" : MessageLookupByLibrary.simpleMessage("成功发送交易。"), "sentSheetHeader" : MessageLookupByLibrary.simpleMessage("已发送"), "setToDefaultButton" : MessageLookupByLibrary.simpleMessage("设为默认"), "settingsHeader" : MessageLookupByLibrary.simpleMessage("设置"), "shareHeader" : MessageLookupByLibrary.simpleMessage("分享Blaise"), "showButton" : MessageLookupByLibrary.simpleMessage("显示"), "signeraccountOPDetails" : MessageLookupByLibrary.simpleMessage("签署账号"), "somethingWentWrongError" : MessageLookupByLibrary.simpleMessage("出错了,请稍后再试"), "successfullyImportedContactsParagraph" : MessageLookupByLibrary.simpleMessage("成功导入%1个联系人"), "systemDefaultHeader" : MessageLookupByLibrary.simpleMessage("系统默认"), "themeCopperHeader" : MessageLookupByLibrary.simpleMessage("铜色"), "themeDarkHeader" : MessageLookupByLibrary.simpleMessage("暗色"), "themeHeader" : MessageLookupByLibrary.simpleMessage("主题"), "themeLightHeader" : MessageLookupByLibrary.simpleMessage("高亮"), "threeCharacterNameError" : MessageLookupByLibrary.simpleMessage("账号名最少要三个字符"), "timeOPDetails" : MessageLookupByLibrary.simpleMessage("时间戳"), "totalBalanceHeader" : MessageLookupByLibrary.simpleMessage("总余额"), "transactionOPDetails" : MessageLookupByLibrary.simpleMessage("交易 (%1)"), "transferAccountHeader" : MessageLookupByLibrary.simpleMessage("转让账号"), "transferButton" : MessageLookupByLibrary.simpleMessage("转让账号"), "transferParagraph" : MessageLookupByLibrary.simpleMessage("在下方输入公钥,将此账号的所有权转移给它。"), "transferSheetHeader" : MessageLookupByLibrary.simpleMessage("转让"), "transferredHeader" : MessageLookupByLibrary.simpleMessage("已转让"), "transferredParagraph" : MessageLookupByLibrary.simpleMessage("您的帐号已成功转移给下方的公钥。"), "transferredSheetHeader" : MessageLookupByLibrary.simpleMessage("已转让"), "transferringParagraph" : MessageLookupByLibrary.simpleMessage("请确认以下公钥,此账号将转移给它。"), "transferringSheetHeader" : MessageLookupByLibrary.simpleMessage("正在转让"), "undefinedHeader" : MessageLookupByLibrary.simpleMessage("未定义操作"), "unencryptedKeyButton" : MessageLookupByLibrary.simpleMessage("显示未加密私钥"), "uninstallDisclaimerParagraph" : MessageLookupByLibrary.simpleMessage("如果您丢失了该设备或者卸载了Blaise钱包,您需要使用私钥来恢复资金。"), "unknownOPDetails" : MessageLookupByLibrary.simpleMessage("未知操作 (%1)"), "unlockButton" : MessageLookupByLibrary.simpleMessage("解锁"), "unlockWithBiometricsButton" : MessageLookupByLibrary.simpleMessage("生物特征解锁"), "unlockWithPINButton" : MessageLookupByLibrary.simpleMessage("用识别码解锁"), "urlChangedToParagraph" : MessageLookupByLibrary.simpleMessage("将URL变更为 %1"), "viewPublicKeyHeader" : MessageLookupByLibrary.simpleMessage("查看公钥"), "warningHeader" : MessageLookupByLibrary.simpleMessage("警告"), "welcomeParagraph" : MessageLookupByLibrary.simpleMessage("欢迎使用Blaise钱包。下一步,您可以创建新私钥或导入现有私钥。"), "yesAddFeeButton" : MessageLookupByLibrary.simpleMessage("确认添加手续费"), "yesHeader" : MessageLookupByLibrary.simpleMessage("是"), "yesImSureButton" : MessageLookupByLibrary.simpleMessage("确定"), "zeroAmountError" : MessageLookupByLibrary.simpleMessage("金额不能为0"), "zeroPriceError" : MessageLookupByLibrary.simpleMessage("价格不能为0") }; } ================================================ FILE: lib/localization.dart ================================================ import 'dart:async'; import 'package:blaise_wallet_flutter/model/available_languages.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; //import 'package:natrium_wallet_flutter/model/available_language.dart'; import 'l10n/messages_all.dart'; /// Localization class AppLocalization { static Locale currentLocale = Locale('en', 'US'); static Future load(Locale locale) { currentLocale = locale; final String name = locale.countryCode == null ? locale.languageCode : locale.toString(); final String localeName = Intl.canonicalizedLocale(name); return initializeMessages(localeName).then((bool _) { Intl.defaultLocale = localeName; return AppLocalization(); }); } static AppLocalization of(BuildContext context) { return Localizations.of(context, AppLocalization); } // **** BUTTONS **** // String get newPrivateKeyButton { return Intl.message("New Private Key", desc: 'A button that creates a new private key', name: 'newPrivateKeyButton'); } String get importPrivateKeyButton { return Intl.message("Import Private Key", desc: 'A button that imports a private key', name: 'importPrivateKeyButton'); } String get gotItButton { return Intl.message("Got It!", desc: 'A button that implies a message is understood', name: 'gotItButton'); } String get goBackButton { return Intl.message("Go Back", desc: 'A button to go back to previous screen', name: 'goBackButton'); } String get copyButton { return Intl.message("Copy", desc: 'A button to copy something', name: 'copyButton'); } String get copiedButton { return Intl.message("Copied", desc: 'A button to inform the user that something has been copied', name: 'copiedButton'); } String get keyCopiedButton { return Intl.message("Key Copied", desc: 'A button to inform the user that the key has been copied', name: 'keyCopiedButton'); } String get iHaveBackedItUpButton { return Intl.message("I've Backed It Up", desc: 'A button to confirm that something is backed up', name: 'iHaveBackedItUpButton'); } String get yesImSureButton { return Intl.message("Yes, I'm Sure", desc: 'A button to confirm if the user is sure', name: 'yesImSureButton'); } String get noGoBackButton { return Intl.message("No, Go Back", desc: 'A button to go back to previous screen if the user didnt do what the question asks', name: 'noGoBackButton'); } String get getAnAccountButton { return Intl.message("Get an Account", desc: 'A button to start the process of getting an account', name: 'getAnAccountButton'); } String get getAFreeAccountButton { return Intl.message("Get a Free Account", desc: 'A button to start the process of getting a free account', name: 'getAFreeAccountButton'); } String get buyAnAccountButton { return Intl.message("Buy an Account", desc: 'A button to start the process of buying an account', name: 'buyAnAccountButton'); } String get sendConfirmationButton { return Intl.message("Send Confirmation", desc: 'A button to request a confirmation to be sent', name: 'sendConfirmationButton'); } String get confirmButton { return Intl.message("Confirm", desc: 'A button to confirm that a process should be executed', name: 'confirmButton'); } String get borrowAnAccountButton { return Intl.message("Borrow An Account", desc: 'A button to borrow an account', name: 'borrowAnAccountButton'); } String get importButton { return Intl.message("Import", desc: 'A button to import something', name: 'importButton'); } String get receiveButton { return Intl.message("Receive", desc: 'A button to receive Pascal', name: 'receiveButton'); } String get sendButton { return Intl.message("Send", desc: 'A button to send Pascal', name: 'sendButton'); } String get copyAddressButton { return Intl.message("Copy Address", desc: 'A button to copy an address', name: 'copyAddressButton'); } String get copiedAddressButton { return Intl.message("Address Copied", desc: 'A button to inform the user that the address has been copied', name: 'copiedAddressButton'); } String get addToContactsButton { return Intl.message("Add to Contacts", desc: 'A button to add an account to contacts', name: 'addToContactsButton'); } String get operationDetailsButton { return Intl.message("Operation Details", desc: 'A button to view the details of an operation', name: 'operationDetailsButton'); } String get openInExplorerButton { return Intl.message("Open in Explorer", desc: 'A button to view the details of an operation on the Pascal explorer', name: 'openInExplorerButton'); } String get requestButton { return Intl.message("Request", desc: 'A button to request something', name: 'requestButton'); } String get addAPayloadButton { return Intl.message("+ Add a Payload", desc: 'A button to add a payload (note) to an operation', name: 'addAPayloadButton'); } String get addADurationButton { return Intl.message("+ Add a Duration", desc: 'A button to add a duration to the sale', name: 'addADurationButton'); } String get scanQRCodeButton { return Intl.message("Scan QR Code", desc: 'A button to scan a QR Code', name: 'scanQRCodeButton'); } String get cancelButton { return Intl.message("Cancel", desc: 'A button to cancel a process', name: 'cancelButton'); } String get closeButton { return Intl.message("Close", desc: 'A button to close a screen or a pop-up', name: 'closeButton'); } String get changeNameButton { return Intl.message("Change Name", desc: 'A button to change the name of an account', name: 'changeNameButton'); } String get transferButton { return Intl.message("Transfer", desc: 'A button to transfer the ownership of an account', name: 'transferButton'); } String get listForSaleButton { return Intl.message("List for Sale", desc: 'A button to list an account for sale', name: 'listForSaleButton'); } String get createPrivateSaleButton { return Intl.message("Create Private Sale", desc: 'A button to create a private sale for the account', name: 'createPrivateSaleButton'); } String get yesAddFeeButton { return Intl.message("Yes, Add Fee", desc: 'A button to confirm the addition of a fee to an operation', name: 'yesAddFeeButton'); } String get unlockButton { return Intl.message("Unlock", desc: 'A button to unlock the wallet', name: 'unlockButton'); } String get unlockWithBiometricsButton { return Intl.message("Unlock with Biometrics", desc: 'A button to unlock the wallet using biometrics', name: 'unlockWithBiometricsButton'); } String get unlockWithPINButton { return Intl.message("Unlock with PIN", desc: 'A button to unlock the wallet using PIN', name: 'unlockWithPINButton'); } String get setToDefaultButton { return Intl.message("Set to Default", desc: 'A button to set something to its default', name: 'setToDefaultButton'); } String get changeDaemonButton { return Intl.message("Change Daemon", desc: 'A button to change the Pascal daemon for RPC requests', name: 'changeDaemonButton'); } String get addContactButton { return Intl.message("Add Contact", desc: 'A button to add a contact', name: 'addContactButton'); } String get encryptedKeyButton { return Intl.message("Encrypted Key", desc: 'A button to view the encrypted key', name: 'encryptedKeyButton'); } String get unencryptedKeyButton { return Intl.message("Unencrypted Key", desc: 'A button to view the unencrypted key', name: 'unencryptedKeyButton'); } String get encryptButton { return Intl.message("Encrypt", desc: 'A button to encrypt the private key with a password', name: 'encryptButton'); } String get showButton { return Intl.message("Show", desc: 'A button to show something that is hidden', name: 'showButton'); } String get hideButton { return Intl.message("Hide", desc: 'A button to hide something that is shown', name: 'hideButton'); } String get copyEncryptedKeyButton { return Intl.message("Copy Encrypted Key", desc: 'A button to copy an encrypted key', name: 'copyEncryptedKeyButton'); } String get copyUnencryptedKeyButton { return Intl.message("Copy Unencrypted Key", desc: 'A button to copy an unencrypted key', name: 'copyUnencryptedKeyButton'); } String get copyKeyButton { return Intl.message("Copy Key", desc: 'A button to copy a key (private or public key)', name: 'copyKeyButton'); } String get copyPublicKeyButton { return Intl.message("Copy Public Key", desc: 'A button to copy a public key', name: 'copyPublicKeyButton'); } String get deletePrivateKeyAndLogoutButton { return Intl.message("Delete Private Key\nAnd Logout", desc: 'A button to delete the private key and logout', name: 'deletePrivateKeyAndLogoutButton'); } String get searchForNameButton { return Intl.message("Search For Name", desc: 'A button to search for an account name', name: 'searchForNameButton'); } String get searchAccountNameButton { return Intl.message("Search Account Name", desc: 'A button to search an account name', name: 'searchAccountNameButton'); } String get searchNameButton { return Intl.message("Search Name", desc: 'A button to search name', name: 'searchNameButton'); } String get okayGoBackButton { return Intl.message("Okay, Go Back", desc: 'A button to confirm and go back', name: 'okayGoBackButton'); } String get okayButton { return Intl.message("Okay", desc: 'A button that simply indicates a neutral action, like closing an informative dialog', name: 'okayButton'); } String get nextButton { return Intl.message("Next", desc: 'A button to the next screen', name: 'nextButton'); } String get receiveAccountButton { return Intl.message("Receive Account", desc: 'A button to open up the public key sheet(screen) that displays a QR code to receive an account', name: 'receiveAccountButton'); } String get receiveAnAccountButton { return Intl.message("Receive an Account", desc: 'A button to open up the public key sheet(screen) that displays a QR code to receive an account', name: 'receiveAnAccountButton'); } String get supportButton { return Intl.message("Support", desc: 'A button to open up the live support window', name: 'supportButton'); } String get liveSupportButton { return Intl.message("Support", desc: 'A button to open up the live support window', name: 'liveSupportButton'); } // **** BUTTONS END **** // // **** PARAGRAPHS **** // String get welcomeParagraph { return Intl.message( "Welcome to Blaise Wallet. To begin, you can create a new private key or import one.", desc: 'A paragraph that greets the user in the initial opening', name: 'welcomeParagraph'); } String get newKeySecurityParagraph { return Intl.message( "In the next screen, you'll see your new private key. It is a password to access your funds. It is crucial that you back it up and never share it with anyone.", desc: 'A paragraph that explains what users should do with their new private key', name: 'newKeySecurityParagraph'); } String get uninstallDisclaimerParagraph { return Intl.message( "If you lose your device or uninstall Blaise Wallet, you'll need your private key to recover your funds.", desc: 'A paragraph that gives a security disclaimer about what happens if the wallet is uninstalled', name: 'uninstallDisclaimerParagraph'); } String get newPrivateKeyParagraph { return Intl.message( "Below is your new wallet’s private key. It is crucial that you backup your private key and never store it as plaintext or a screenshot. We recommend writing it on a piece of paper and storing it offline.", desc: 'A paragraph that explains what users should do with their new private key', name: 'newPrivateKeyParagraph'); } String get newKeyBackUpConfirmParagraph { return Intl.message( "Are you sure that you have backed up your new wallet’s private key?", desc: 'A paragraph to confirm if the new private key is backed up', name: 'newKeyBackUpConfirmParagraph'); } String get newWalletGreetingParagraph { return Intl.message( "Welcome to Blaise Wallet.\nYou can start by getting an account.", desc: 'A paragraph to greet the user when a new wallet is created', name: 'newWalletGreetingParagraph'); } String get getAccountFirstParagraph { return Intl.message("There are 2 options for getting your first account:", desc: 'The first paragraph of the explanation for the process of getting an account', name: 'getAccountFirstParagraph'); } String get getAccountSecondParagraph { return Intl.message( "1- You can get a free account using your phone number. Only 1 account per phone number is allowed.", desc: 'The second paragraph of the explanation for the process of getting an account', name: 'getAccountSecondParagraph'); } String get getAccountThirdParagraph { return Intl.message( "2- You can buy as many accounts as you want for %1 Pascal (%2).", desc: 'The third paragraph of the explanation for the process of getting an account', name: 'getAccountThirdParagraph'); } String get getAccountThirdParagraphAlternative { return Intl.message( "2- You can buy an account for %1 Pascal (%2). Buying only 1 account is allowed per user.", desc: 'The third paragraph of the explanation for the process of getting an account', name: 'getAccountThirdParagraphAlternative'); } String get getAccountThirdParagraphAlternative2 { return Intl.message( "2- You can buy an account for %1 Pascal (%2). You can buy up to %3 accounts.", desc: 'The third paragraph of the explanation for the process of getting an account', name: 'getAccountThirdParagraphAlternative2'); } String get enterPhoneNumberParagraph { return Intl.message("Enter your phone number below.", desc: 'A paragraph that tells users to enter their phone number to the text field below', name: 'enterPhoneNumberParagraph'); } String get invalidPhoneNumberParagraph { return Intl.message("Phone number is not valid", desc: 'User has entered an invalid phone number', name: 'invalidPhoneNumberParagraph'); } String get enterConfirmationCodeParagraph { return Intl.message( "We have sent you a confirmation code, please enter it below.", desc: 'A paragraph that tells users to enter the confirmation code to the text field below', name: 'enterConfirmationCodeParagraph'); } String get confirmationCodeError { return Intl.message("Failed to verify code, ensure you've entered it correctly", desc: 'When a user enters their freepasa SMS code but it can\'t be verified', name: 'confirmationCodeError'); } String get freepasaComplete { return Intl.message("Success, your new account will be available after 1 network confirmation", desc: 'After the freepasa process is complete', name: 'freepasaComplete'); } String get unconfirmedAccountHeader { return Intl.message("Unconfirmed Account", desc: 'A user has an account in their wallet that has been transferred to them, but isnt confirmed yet. This is the info dialog header.', name: 'unconfirmedAccountHeader'); } String get unconfirmedAccountParagraph { return Intl.message("This is an unconfirmed account. It has been transferred to you, but there needs to be 1 network confirmation before you can use it. This usually takes about 5 minutes, once it's complete you'll be able to use this account.", desc: 'Explaining that an account can\'t be used until 1 network confirmation to the user.', name: 'unconfirmedAccountParagraph'); } String get borrowStarted { return Intl.message( "Purchase Started for %1", desc: "Users may see this after starting the account purchase process", name: 'borrowStarted' ); } String get borrowAccountParagraph { return Intl.message( "To buy an account, first you’ll need to borrow one for free. If you send at least %1 Pascal (%2) to the account within %3 days, the account will be yours and %1 Pascal will be deducted from your balance automatically.\nOtherwise, it’ll return back to us at the end of %3 days and won’t belong to your wallet anymore.\nIt is recommended you only send a small amount of coins until you own the account.", desc: 'A paragraph that explains the process of borrowing & buying an account', name: 'borrowAccountParagraph'); } String get importPrivateKeyParagraph { return Intl.message("Enter your private key below.", desc: 'A paragraph that tells the user to enter their private key to the text field below', name: 'importPrivateKeyParagraph'); } String get looksLikeEncryptedKeyParagraph { return Intl.message( "This looks like an encrypted private key, please enter the password to decrypt and import it.", desc: 'A paragraph that tells the user that the key looks like an encrypted one and it needs to be decrypted to import', name: 'looksLikeEncryptedKeyParagraph'); } // Settings Related Paragraphs String get changeDaemonParagraph { return Intl.message( "Enter an address to use a different Pascal daemon for RPC requests.", desc: 'A paragraph that tells the user to enter a new daemon address below', name: 'changeDaemonParagraph'); } String get urlChangedToParagraph { return Intl.message("URL changed to %1", desc: 'A paragraph that tells the user that the URL is changed to the entered URL', name: 'urlChangedToParagraph'); } String get backupKeyFirstParagraph { return Intl.message("You have 2 options for backing up your private key:", desc: 'The first paragraph of the explanation for the process of backing up the private key', name: 'backupKeyFirstParagraph'); } String get backupKeySecondParagraph { return Intl.message( "1- Encrypted, which means it is protected by a password.", desc: 'The second paragraph of the explanation for the process of backing up the private key', name: 'backupKeySecondParagraph'); } String get backupKeyThirdParagraph { return Intl.message( "2- Unencrypted, which means it is raw and not protected by a password.", desc: 'The third paragraph of the explanation for the process of backing up the private key', name: 'backupKeyThirdParagraph'); } String get backupKeyFourthParagraph { return Intl.message( "We recommend storing the unencrypted version offline, by writing it on a piece of paper. You can store the encrypted version on a password manager for your convenience.", desc: 'The fourth paragraph of the explanation for the process of backing up the private key', name: 'backupKeyFourthParagraph'); } String get encryptKeyParagraph { return Intl.message("Create a new password to encrypt your private key.", desc: 'A paragraph that tells the user to create a new password to encrypt their key', name: 'encryptKeyParagraph'); } String get backupEncryptedKeyFirstParagraph { return Intl.message( "Below is your encrypted private key. It is protected by a password. You can store it safely on a password manager for your convenience.", desc: 'A paragraph that explains how an encrypted private key can be backed up', name: 'backupEncryptedKeyFirstParagraph'); } String get backupEncryptedKeySecondParagraph { return Intl.message( "Since it is encrypted with your password, if you lose or forget your password, you won't be able to decrypt it and access your funds.", desc: 'A paragraph that gives a disclaimer about what would happen in case the password that was used to encrypt the private key is lost or forgotten', name: 'backupEncryptedKeySecondParagraph'); } String get backupUnencryptedKeyParagraph { return Intl.message( "Below is your unencrypted private key. It is not protected by a password, which means it is crucial that you store it somewhere safe and offline. We recommend writing it on a piece of paper.", desc: 'A paragraph that explains the process of backing up the unencrypted private key', name: 'backupUnencryptedKeyParagraph'); } String get publicKeyParagraph { return Intl.message( "Below is your public key. As the name suggests, it is intended to be shared publicly and prove that a particular operation belongs to your private key.", desc: 'A paragraph that explains what a public key is', name: 'publicKeyParagraph'); } String get borrowedAccountParagraph { return Intl.message( "This is a borrowed account.\nIf you send at least %1 Pascal to it in the next %2 days, %3 hours, and %4 minutes, it’ll be yours.", desc: 'A paragraph that explains what a borrowed account is', name: 'borrowedAccountParagraph'); } String get borrowedAccountPaidParagraph { return Intl.message( "Your account has been purchased!\nThe transfer is currently processing. This process usually takes about 15 minutes, in some cases it may take slightly longer.", desc: 'A paragraph that explains that the account has been purchased and transfer is currently processing', name: 'borrowedAccountPaidParagraph'); } String get logoutFirstDisclaimerParagraph { return Intl.message( "Logging out will remove your private key and all Blaise related data from this device. If your private key is not backed up, you will never be able to access your funds again. If your private key is backed up, you have nothing to worry about.", desc: 'The first part of the disclaimer that is shown when the user tries to log out.', name: 'logoutFirstDisclaimerParagraph'); } String get logoutSecondDisclaimerParagraph { return Intl.message( "Are you sure that you've backed up your private key? As long as you've backed up your private key, you have nothing to worry about.", desc: 'The second part of the disclaimer that is shown when the user tries to log out.', name: 'logoutSecondDisclaimerParagraph'); } // Settings related paragraphs END // Operation Related Paragraphs String get noResultsFound { return Intl.message("No results found", desc: 'When searching for account name has returned 0 results', name: 'noResultsFound'); } String get sendingConfirmParagraph { return Intl.message("Confirm the transaction details to send.", desc: 'A paragraph that tells the user to confirm the info below to send', name: 'sendingConfirmParagraph'); } String get sentParagraph { return Intl.message("Transaction has been sent succesfully.", desc: 'A paragraph that informs the user that the transaction has been sent succesfully', name: 'sentParagraph'); } String get changeNameParagraph { return Intl.message("Enter a name below to change your account's name.", desc: 'A paragraph that tells the user to enter a new account name below', name: 'changeNameParagraph'); } String get changingNameParagraph { return Intl.message("Confirm your new account name to proceed.", desc: 'A paragraph that tells the user to confirm the new account name', name: 'changingNameParagraph'); } String get changedNameParagraph { return Intl.message("Your account name has been changed successfully.", desc: 'A paragraph that informs the user that the account name has been changed successfully', name: 'changedNameParagraph'); } String get transferParagraph { return Intl.message( "Enter a public key below to transfer the ownership of this account to it.", desc: 'A paragraph that tells the user to enter a public key to the text field below to transfer the ownership of the account', name: 'transferParagraph'); } String get transferringParagraph { return Intl.message( "Confirm the public key below to transfer the ownership of this account to it.", desc: 'A paragraph that tells the user to confirm the public key below to proceed with the transfer', name: 'transferringParagraph'); } String get transferredParagraph { return Intl.message( "Your account has been transferred successfully to the public key below.", desc: 'A paragraph that informs the user that the account transfer has been completed successfully', name: 'transferredParagraph'); } String get listForSaleParagraph { return Intl.message( "Enter a price and an account that will be receiving the payment to list this account for sale.", desc: 'A paragraph that tells the user to enter a price and a receiver account to list the account for sale', name: 'listForSaleParagraph'); } String get listingForSaleParagraph { return Intl.message( "Confirm the price and the account that will be receiving the payment.", desc: 'A paragraph that tells the user to confirm the price and the receiver account', name: 'listingForSaleParagraph'); } String get listedForSaleParagraph { return Intl.message( "Your account has been successfully listed for sale. We’ll let you know if someone buys it.", desc: 'A paragraph that informs the user that the account has been listed for sale successfully', name: 'listedForSaleParagraph'); } String get createPrivateSaleParagraph { return Intl.message( "Enter a price, a receiving account, and a public key below to create a private sale for this account.", desc: 'A paragraph that tells the user to enter a price, a receiver account, and a public key to create a private sale for the account', name: 'createPrivateSaleParagraph'); } String get creatingPrivateSaleParagraph { return Intl.message("Confirm the information below.", desc: 'A paragraph that tells the user to confirm the information below.', name: 'creatingPrivateSaleParagraph'); } String get createdPrivateSaleParagraph { return Intl.message( "The private sale has been created successfully. We’ll let you know if it is bought.", desc: 'A paragraph that informs the user that the private sale has been created successfully', name: 'createdPrivateSaleParagraph'); } String get delistFromSaleParagraph { return Intl.message( "Confirm that you would like to delist this account from sale.", desc: 'A paragraph that tells the users to confirm that they would like to delist the account from sale.', name: 'delistFromSaleParagraph'); } String get delistedFromSaleParagraph { return Intl.message( "Your account has been successfully delisted from sale.", desc: 'A paragraph that informs the user that the account has been delisted from sale successfully', name: 'delistedFromSaleParagraph'); } // Operation Related Paragraphs END String get feeRequiredParagraph { return Intl.message("This operation requires a fee.", desc: 'A paragraph to indicate that the operation requires a fee', name: 'feeRequiredParagraph'); } String get feeConfirmAmountParagraph { return Intl.message( "Please confirm the addition of %1 Pascal fee to this operation to continue.", desc: 'A paragraph to tell the user to confirm the addition of a fee', name: 'feeConfirmAmountParagraph'); } String get keyTypeNotSupportedParagraph { return Intl.message( "This type of private key is not yet supported by Blaise. You may create a new private key and transfer your accounts to it using a different wallet.", desc: 'A paragraph to tell the user that the private key type is not supported', name: 'keyTypeNotSupportedParagraph'); } // PIN Screen String get enterPINToUnlockParagraph { return Intl.message("Enter PIN to unlock Blaise", desc: 'A paragraph that tells the user to enter the PIN to unlock the wallet', name: 'enterPINToUnlockParagraph'); } String get authenticateToUnlockParagraph { return Intl.message("Authenticate to Unlock Blaise", desc: 'A paragraph that tells the user to authenticate to unlock the wallet', name: 'authenticateToUnlockParagraph'); } String get manyFailedAttemptsParagraph { return Intl.message("Too many failed unlock attempts", desc: 'A paragraph to inform the user that there was too many failed unlock attempts', name: 'manyFailedAttemptsParagraph'); } String get authenticateToChangeNameParagraph { return Intl.message("Authenticate to change account name to \"%1\"", desc: 'A paragraph that tells the user to authenticate to change the name of the account', name: 'authenticateToChangeNameParagraph'); } String get authenticateToDelistParagraph { return Intl.message("Authenticate to delist the account from sale", desc: 'A paragraph that tells the user to authenticate to delist the account from sale', name: 'authenticateToDelistParagraph'); } String get authenticateToListForSaleParagraph { return Intl.message("Authenticate to list account for sale", desc: 'A paragraph that tells the user to authenticate to list the account for sale', name: 'authenticateToListForSaleParagraph'); } String get authenticateToCreatePrivateSaleParagraph { return Intl.message("Authenticate to create private sale", desc: 'A paragraph that tells the user to authenticate to create a private sale for the account', name: 'authenticateToCreatePrivateSaleParagraph'); } String get authenticateToTransferParagraph { return Intl.message("Authenticate to transfer account", desc: 'A paragraph that tells the user to authenticate to transfer the ownership of the account', name: 'authenticateToTransferParagraph'); } String get authenticateToSendParagraph { return Intl.message("Authenticate to send %1 Pascal", desc: 'A paragraph that tells the user to authenticate to send a specified amount of Pascal', name: 'authenticateToSendParagraph'); } String get authenticateToBackUpParagraph { return Intl.message("Authenticate to back up private key", desc: 'A paragraph that tells the user to authenticate to back up the private key', name: 'authenticateToBackUpParagraph'); } String get invalidPINParagraph { return Intl.message("Invalid PIN", desc: 'A paragraph that tells the user that the entered PIN is invalid', name: 'invalidPINParagraph'); } String get noMatchPINParagraph { return Intl.message("PINs do not match", desc: 'A paragraph that tells the user that the entered PINs do not match', name: 'noMatchPINParagraph'); } String get confirmPINParagraph { return Intl.message("Confirm your PIN", desc: 'A paragraph that tells the user to confirm the PIN', name: 'confirmPINParagraph'); } String get enterPINParagraph { return Intl.message("Enter PIN", desc: 'A paragraph that tells the user to enter the PIN', name: 'enterPINParagraph'); } String get createPINParagraph { return Intl.message("Create a 6-digit PIN", desc: 'A paragraph that tells the user to create a PIN', name: 'createPINParagraph'); } // PIN Screen END String get addedToContactsParagraph { return Intl.message("%1 added to contacts", desc: 'A paragraph that tells the user that the contact has been added to contacts', name: 'addedToContactsParagraph'); } String get removedFromContactsParagraph { return Intl.message("Removed %1 from contacts", desc: 'A paragraph that tells the user that the contact has been removed from contacts', name: 'removedFromContactsParagraph'); } String get failedToRemoveFromContactsParagraph { return Intl.message("Failed to remove %1 from contacts", desc: 'A paragraph that tells the user that the contact removel process is failed', name: 'failedToRemoveFromContactsParagraph'); } String get successfullyImportedContactsParagraph { return Intl.message("Successfully imported %1 contacts", desc: 'A paragraph to tell the user that a specific number of contacts was successfully imported', name: 'successfullyImportedContactsParagraph'); } String get checkOutBlaiseParagraph { return Intl.message( "Check out Blaise! Simple, sleek & secure Pascal wallet for iOS and Android: https://blaisewallet.com", desc: 'A paragraph that is shared when the user shares Blaise with others via the option in the settings', name: 'checkOutBlaiseParagraph'); } String get newAccountParagraph { return Intl.message( "This is your new account.\nOnce you receive Pascal, operations will show up like below.", desc: 'A paragraph that is shown in the operations list of a new account as an explainer', name: 'newAccountParagraph'); } // **** PARAGRAPHS END **** // // **** HEADERS **** // // Settings Headers String get settingsHeader { return Intl.message("Settings", desc: 'Header for the settings', name: 'settingsHeader'); } String get preferencesHeader { return Intl.message("Preferences", desc: 'Header for the preferences section', name: 'preferencesHeader'); } String get currencyHeader { return Intl.message("Currency", desc: 'Header for the currencies', name: 'currencyHeader'); } String get languageHeader { return Intl.message("Language", desc: 'Header for the languages', name: 'languageHeader'); } String get languageColonHeader { return Intl.message("Language:", desc: 'Header for the language option on welcome page', name: 'languageColonHeader'); } String get systemDefaultHeader { return Intl.message("System Default", desc: 'Header for system default', name: 'systemDefaultHeader'); } String get themeHeader { return Intl.message("Theme", desc: 'Header for the themes', name: 'themeHeader'); } String get themeLightHeader { return Intl.message("Light", desc: 'Header for the light theme', name: 'themeLightHeader'); } String get themeDarkHeader { return Intl.message("Dark", desc: 'Header for the dark theme', name: 'themeDarkHeader'); } String get themeCopperHeader { return Intl.message("Copper", desc: 'Header for the copper theme', name: 'themeCopperHeader'); } String get notificationsHeader { return Intl.message("Notifications", desc: 'Header for the notifications', name: 'notificationsHeader'); } String get securityHeader { return Intl.message("Security", desc: 'Header for the security section', name: 'securityHeader'); } String get authenticationMethodHeader { return Intl.message("Authentication Method", desc: 'Header for the authentication method', name: 'authenticationMethodHeader'); } String get authenticationPINHeader { return Intl.message("PIN", desc: 'Header for the PIN authentication method', name: 'authenticationPINHeader'); } String get authenticationBiometricsHeader { return Intl.message("Biometrics", desc: 'Header for the biometric authentication method', name: 'authenticationBiometricsHeader'); } String get authenticateOnLaunchHeader { return Intl.message("Authenticate on Launch", desc: 'Header for the authenticate on launch option', name: 'authenticateOnLaunchHeader'); } String get yesHeader { return Intl.message("Yes", desc: 'Header for the yes option', name: 'yesHeader'); } String get noHeader { return Intl.message("No", desc: 'Header for the no option', name: 'noHeader'); } String get automaticallyLockHeader { return Intl.message("Automatically Lock", desc: 'Header for the automatically lock option', name: 'automaticallyLockHeader'); } String get lockInstantHeader { return Intl.message("Instantly", desc: 'Header for instantly locking option', name: 'lockInstantHeader'); } String get lock1Header { return Intl.message("After %1 minute", desc: 'Header for locking after 1 minute option', name: 'lock1Header'); } String get lock5Header { return Intl.message("After %1 minutes", desc: 'Header for locking after 5 minutes option', name: 'lock5Header'); } String get lock15Header { return Intl.message("After %1 minutes", desc: 'Header for locking after 15 minutes option', name: 'lock15Header'); } String get lock30Header { return Intl.message("After %1 minutes", desc: 'Header for locking after 30 minutes option', name: 'lock30Header'); } String get lock60Header { return Intl.message("After %1 minutes", desc: 'Header for locking after 60 minutes option', name: 'lock60Header'); } String get daemonHeader { return Intl.message("Daemon", desc: 'Header for Pascal daemon setting', name: 'daemonHeader'); } String get defaultHeader { return Intl.message("Default", desc: 'Header for default option', name: 'defaultHeader'); } String get manageHeader { return Intl.message("Manage", desc: 'Header for the manage section', name: 'manageHeader'); } String get contactsHeader { return Intl.message("Contacts", desc: 'Header for the contacts section in settings', name: 'contactsHeader'); } String get backUpPrivateKeyHeader { return Intl.message("Back Up Private Key", desc: 'Header for the back up private key option in settings', name: 'backUpPrivateKeyHeader'); } String get viewPublicKeyHeader { return Intl.message("View Public Key", desc: 'Header for the view public key option in settings', name: 'viewPublicKeyHeader'); } String get shareHeader { return Intl.message("Share Blaise", desc: 'Header for the share Blaise option in settings', name: 'shareHeader'); } String get logoutHeader { return Intl.message("Logout", desc: 'Header for the logout option in settings', name: 'logoutHeader'); } String get privacyPolicyHeader { return Intl.message("Privacy Policy", desc: 'Header for the privacy policy option in settings', name: 'privacyPolicyHeader'); } // Settings Headers END // Operations List Headers String get changeAccountNameHeader { return Intl.message("Change Account Name", desc: 'Header for the change account name option in other operations list', name: 'changeAccountNameHeader'); } String get transferAccountHeader { return Intl.message("Transfer Account", desc: 'Header for the transfer account option in other operations list', name: 'transferAccountHeader'); } String get listAccountForSaleHeader { return Intl.message("List Account For Sale", desc: 'Header for the list account for sale option in other operations list', name: 'listAccountForSaleHeader'); } String get createPrivateSaleHeader { return Intl.message("Create Private Sale", desc: 'Header for the create private sale option in other operations list', name: 'createPrivateSaleHeader'); } String get delistFromSaleHeader { return Intl.message("Delist From Sale", desc: 'Header for the delist from sale option in other operations list', name: 'delistFromSaleHeader'); } // Operations List Headers END // Sheet Headers String get getAccountSheetHeader { return Intl.message("Get Account", desc: 'Header for the get account sheet (screen)', name: 'getAccountSheetHeader'); } String get freeAccountSheetHeader { return Intl.message("Free Account", desc: 'Header for the free account sheet (screen)', name: 'freeAccountSheetHeader'); } String get buyAccountSheetHeader { return Intl.message("Buy Account", desc: 'Header for the buy account sheet (screen)', name: 'buyAccountSheetHeader'); } String get sendSheetHeader { return Intl.message("Send", desc: 'Header for send sheet (screen)', name: 'sendSheetHeader'); } String get sendingSheetHeader { return Intl.message("Sending", desc: 'Header for sending sheet (screen)', name: 'sendingSheetHeader'); } String get sentSheetHeader { return Intl.message("Sent", desc: 'Header for sent sheet (screen)', name: 'sentSheetHeader'); } String get requestSheetHeader { return Intl.message("Request", desc: 'Header for request sheet (screen)', name: 'requestSheetHeader'); } String get changeNameSheetHeader { return Intl.message("Change Name", desc: 'Header for change name sheet (screen)', name: 'changeNameSheetHeader'); } String get changingNameSheetHeader { return Intl.message("Changing", desc: 'Header for name changing sheet (screen)', name: 'changingNameSheetHeader'); } String get changedNameSheetHeader { return Intl.message("Changed", desc: 'Header for name changed sheet (screen)', name: 'changedNameSheetHeader'); } String get transferSheetHeader { return Intl.message("Transfer", desc: 'Header for transfer sheet (screen)', name: 'transferSheetHeader'); } String get transferringSheetHeader { return Intl.message("Transferring", desc: 'Header for transferring sheet (screen)', name: 'transferringSheetHeader'); } String get transferredSheetHeader { return Intl.message("Transferred", desc: 'Header for transferred sheet (screen)', name: 'transferredSheetHeader'); } String get listForSaleSheetHeader { return Intl.message("List For Sale", desc: 'Header for list for sale sheet (screen)', name: 'listForSaleSheetHeader'); } String get listingForSaleSheetHeader { return Intl.message("Listing", desc: 'Header for listing for sale sheet (screen)', name: 'listingForSaleSheetHeader'); } String get listedForSaleSheetHeader { return Intl.message("Listed", desc: 'Header for listed for sale sheet (screen)', name: 'listedForSaleSheetHeader'); } String get createPrivateSaleSheetHeader { return Intl.message("Private Sale", desc: 'Header for create private sale sheet (screen)', name: 'createPrivateSaleSheetHeader'); } String get creatingPrivateSaleSheetHeader { return Intl.message("Creating", desc: 'Header for creating private sale sheet (screen)', name: 'creatingPrivateSaleSheetHeader'); } String get createdPrivateSaleSheetHeader { return Intl.message("Created", desc: 'Header for created private sale sheet (screen)', name: 'createdPrivateSaleSheetHeader'); } String get delistingSheetHeader { return Intl.message("Delisting", desc: 'Header for delisting sheet (screen)', name: 'delistingSheetHeader'); } String get delistedSheetHeader { return Intl.message("Delisted", desc: 'Header for delisted sheet (screen)', name: 'delistedSheetHeader'); } String get addContactSheetHeader { return Intl.message("Add Contact", desc: 'Header for add contact sheet (screen)', name: 'addContactSheetHeader'); } String get contactSheetHeader { return Intl.message("Contact", desc: 'Header for contact details sheet (screen)', name: 'contactSheetHeader'); } String get publicKeySheetHeader { return Intl.message("Public Key", desc: 'Header for public key sheet (screen)', name: 'publicKeySheetHeader'); } String get privateKeySheetHeader { return Intl.message("Private Key", desc: 'Header for private key sheet (screen)', name: 'privateKeySheetHeader'); } String get backUpSheetHeader { return Intl.message("Back Up", desc: 'Header for back up sheet (screen)', name: 'backUpSheetHeader'); } String get encryptSheetHeader { return Intl.message("Encrypt", desc: 'Header for encrypt sheet (screen)', name: 'encryptSheetHeader'); } String get changeDaemonSheetHeader { return Intl.message("Change Daemon", desc: 'Header for change daemon sheet (screen)', name: 'changeDaemonSheetHeader'); } // Sheet Headers END // Full screen Headers String get securityFirstHeader { return Intl.message("Security First!", desc: 'Header for security first screen', name: 'securityFirstHeader'); } String get newPrivateKeyHeader { return Intl.message("New Private Key", desc: 'Header for new private key screen', name: 'newPrivateKeyHeader'); } String get importPrivateKeyHeader { return Intl.message("Import Private Key", desc: 'Header for import private key screen', name: 'importPrivateKeyHeader'); } String get decryptAndImportKeyHeader { return Intl.message("Decrypt & Import", desc: 'Header for decrypt & import private key screen', name: 'decryptAndImportKeyHeader'); } String get backUpKeyHeader { return Intl.message("Back Up Your Key!", desc: 'Header for back up your key screen', name: 'backUpKeyHeader'); } String get lockedHeader { return Intl.message("Locked", desc: 'Header for locked screen', name: 'lockedHeader'); } // Full screen Headers END // Text Field Headers String get privateKeyTextFieldHeader { return Intl.message("Private Key", desc: 'Header for private key text field', name: 'privateKeyTextFieldHeader'); } String get passwordTextFieldHeader { return Intl.message("Password", desc: 'Header for password text field', name: 'passwordTextFieldHeader'); } String get newPasswordTextFieldHeader { return Intl.message("New Password", desc: 'Header for new password text field', name: 'newPasswordTextFieldHeader'); } String get confirmPasswordTextFieldHeader { return Intl.message("Confirm Password", desc: 'Header for confirm password text field', name: 'confirmPasswordTextFieldHeader'); } String get confirmTextFieldHeader { return Intl.message("Confirm", desc: 'Header for confirm text field', name: 'confirmTextFieldHeader'); } String get countryCodeTextFieldHeader { return Intl.message("Country Code", desc: 'Header for country code text field', name: 'countryCodeTextFieldHeader'); } String get phoneNumberTextFieldHeader { return Intl.message("Phone Number", desc: 'Header for phone number text field', name: 'phoneNumberTextFieldHeader'); } String get confirmationCodeTextFieldHeader { return Intl.message("Confirmation Code", desc: 'Header for confirmation code text field', name: 'confirmationCodeTextFieldHeader'); } String get accountTextFieldHeader { return Intl.message("Account", desc: 'Header for account text field', name: 'accountTextFieldHeader'); } String get addressTextFieldHeader { return Intl.message("Address", desc: 'Header for address text field', name: 'addressTextFieldHeader'); } String get contactNameTextFieldHeader { return Intl.message("Contact Name", desc: 'Header for contact name text field', name: 'contactNameTextFieldHeader'); } String get amountTextFieldHeader { return Intl.message("Amount", desc: 'Header for amount text field', name: 'amountTextFieldHeader'); } String get payloadTextFieldHeader { return Intl.message("Payload", desc: 'Header for payload text field', name: 'payloadTextFieldHeader'); } String get nameTextFieldHeader { return Intl.message("Name", desc: 'Header for name text field', name: 'nameTextFieldHeader'); } String get newAccountNameTextFieldHeader { return Intl.message("New Account Name", desc: 'Header for new account name text field', name: 'newAccountNameTextFieldHeader'); } String get publicKeyTextFieldHeader { return Intl.message("Public Key", desc: 'Header for public key text field', name: 'publicKeyTextFieldHeader'); } String get priceTextFieldHeader { return Intl.message("Price", desc: 'Header for price text field', name: 'priceTextFieldHeader'); } String get receivingAccountTextFieldHeader { return Intl.message("Receiving Account", desc: 'Header for receiving account text field', name: 'receivingAccountTextFieldHeader'); } String get durationTextFieldHeader { return Intl.message("Duration", desc: 'Header for duration text field', name: 'durationTextFieldHeader'); } String get feeTextFieldHeader { return Intl.message("Fee", desc: 'Header for fee text field', name: 'feeTextFieldHeader'); } // Text Field Headers END // Dialog Headers String get otherOperationsHeader { return Intl.message("Other Operations", desc: 'Header for other operations dialog', name: 'otherOperationsHeader'); } String get warningHeader { return Intl.message("Warning", desc: 'Header for warning dialog', name: 'warningHeader'); } String get areYouSureHeader { return Intl.message("Are You Sure?", desc: 'Header for are you sure dialog', name: 'areYouSureHeader'); } String get addFeeHeader { return Intl.message("Add Fee", desc: 'Header for add fee dialog', name: 'addFeeHeader'); } String get keyTypeNotSupportedHeader { return Intl.message("Key Not Supported", desc: 'Header for key not supported dialog', name: 'keyTypeNotSupportedHeader'); } String get accountToSendFromHeader { return Intl.message("Account to Send From", desc: 'Header for account to send from dialog', name: 'accountToSendFromHeader'); } // Dialog Headers END // Operation List Item Headers String get sentHeader { return Intl.message("Sent", desc: 'Header for sent type operation list item', name: 'sentHeader'); } String get receivedHeader { return Intl.message("Received", desc: 'Header for received type operation list item', name: 'receivedHeader'); } String get nameChangedHeader { return Intl.message("Name Changed", desc: 'Header for listed for sale type operation list item', name: 'nameChangedHeader'); } String get listedForSaleHeader { return Intl.message("Listed For Sale", desc: 'Header for listed for sale type operation list item', name: 'listedForSaleHeader'); } String get privateSaleHeader { return Intl.message("Private Sale", desc: 'Header for private sale type operation list item', name: 'privateSaleHeader'); } String get delistedFromSaleHeader { return Intl.message("Delisted From Sale", desc: 'Header for delisted from sale type operation list item', name: 'delistedFromSaleHeader'); } String get delistedHeader { return Intl.message("Delisted", desc: 'Header for delisted type operation list item', name: 'delistedHeader'); } String get undefinedHeader { return Intl.message("Undefined", desc: 'Header for undefined type operation list item', name: 'undefinedHeader'); } String get transferredHeader { return Intl.message("Transferred", desc: 'Header for transferred type operation list item', name: 'transferredHeader'); } // Operation List Item Headers END // Live chat String get connectingHeader { return Intl.message( "Connecting", desc: 'A header to let the user now that Blaise is currently connecting to (or loading) live chat.', name: 'connectingHeader'); } // Miscellaneous Headers String get balanceHeader { return Intl.message("Balance", desc: 'Header for balance', name: 'balanceHeader'); } String get totalBalanceHeader { return Intl.message("Total Balance", desc: 'Header for total balance', name: 'totalBalanceHeader'); } String get accountBalanceHeader { return Intl.message("Account Balance", desc: 'Header for account balance', name: 'accountBalanceHeader'); } String get accountsHeader { return Intl.message("Accounts", desc: 'Header for accounts', name: 'accountsHeader'); } String get operationsHeader { return Intl.message("Operations", desc: 'Header for operations', name: 'operationsHeader'); } String get encryptThePayloadHeader { return Intl.message("Encrypt the Payload", desc: 'Header for encrypt the payload switch', name: 'encryptThePayloadHeader'); } String get encryptPayloadHeader { return Intl.message("Encrypt Payload", desc: 'Header for encrypt payload switch', name: 'encryptPayloadHeader'); } String get forSaleHeader { return Intl.message("For Sale", desc: 'Header of for sale tag', name: 'forSaleHeader'); } String get borrowedHeader { return Intl.message("Borrowed", desc: 'Header for borrowed tag', name: 'borrowedHeader'); } String get borrowedTransferredHeader { return Intl.message("Transfer Pending", desc: 'Header for borrowed tag, after account is transferred but not confirmed', name: 'borrowedTransferredHeader'); } String get borrowedAccountHeader { return Intl.message("Borrowed Account", desc: 'Header for borrowed account tag', name: 'borrowedAccountHeader'); } String get feeColonHeader { return Intl.message("Fee:", desc: 'Header for fee amount', name: 'feeColonHeader'); } String get pendingHeader { return Intl.message("Pending", desc: 'Header to indicate that an operation is pending', name: 'pendingHeader'); } String get onHeader { return Intl.message("On", desc: "A header to indicate that something is on", name: "onHeader"); } String get offHeader { return Intl.message("Off", desc: "A header to indicate that something is off", name: "offHeader"); } // Miscellaneous Headers // **** HEADERS END **** // // **** ERROR TEXT **** // String get priceRequiredError { return Intl.message("Price is required", desc: 'Error that tells the user that the price is required', name: 'priceRequiredError'); } String get amountRequiredError { return Intl.message("Amount is required", desc: 'Error that tells the user that the amount is required', name: 'amountRequiredError'); } String get nameRequiredError { return Intl.message("Name is required", desc: 'Error that tells the user that the name is required', name: 'nameRequiredError'); } String get zeroPriceError { return Intl.message("Price can't be 0", desc: 'Error that tells the user that the price cant be zero', name: 'zeroPriceError'); } String get zeroAmountError { return Intl.message("Amount can't be 0", desc: 'Error that tells the user that the amount cant be zero', name: 'zeroAmountError'); } String get invalidAccountNameError { return Intl.message("Invalid account name", desc: 'Error that tells the user that the account name is invalid', name: 'invalidAccountNameError'); } String get invalidReceivingAccountError { return Intl.message("Invalid receiving account", desc: 'Error that tells the user that the receiving account is invalid', name: 'invalidReceivingAccountError'); } String get invalidPublicKeyError { return Intl.message("Invalid public key", desc: 'Error that tells the user that the public key is invalid', name: 'invalidPublicKeyError'); } String get invalidPrivateKeyError { return Intl.message("Invalid private key", desc: 'Error that tells the user that the private key is invalid', name: 'invalidPrivateKeyError'); } String get invalidAddressError { return Intl.message("Invalid address", desc: 'Error that tells the user that the address is invalid', name: 'invalidAddressError'); } String get invalidAccountError { return Intl.message("Invalid account", desc: 'Error that tells the user that the account is invalid', name: 'invalidAccountError'); } String get invalidDestinationError { return Intl.message("Invalid destination", desc: 'Error that tells the user that the destination is invalid', name: 'invalidDestinationError'); } String get insufficientBalanceError { return Intl.message("Insufficient balance", desc: 'Error that tells the user that the balance is insufficient', name: 'insufficientBalanceError'); } String get threeCharacterNameError { return Intl.message("Must be at least 3 characters", desc: 'Error that tells the user that the account name cant be shorter than 3 characters', name: 'threeCharacterNameError'); } String get contactDoesntExistError { return Intl.message("Contact doesn't exist", desc: 'Error that tells the user that the contact doesnt exist', name: 'contactDoesntExistError'); } String get contactAlreadyExistsError { return Intl.message("Contact already exists", desc: 'Error that tells the user that the contact already exists', name: 'contactAlreadyExistsError'); } String get cantSendToYourselfError { return Intl.message("Can't send to yourself", desc: 'Error that tells the user that you cant send to yourself', name: 'cantSendToYourselfError'); } String get somethingWentWrongError { return Intl.message("Something went wrong, please try again later", desc: 'Error that tells the user that something went wrong', name: 'somethingWentWrongError'); } String get failedToEncryptPayloadError { return Intl.message("Failed to encrypt the payload", desc: 'Error that tells the user that payload encrypt is failed', name: 'failedToEncryptPayloadError'); } String get emptyPasswordError { return Intl.message("Password can't be empty", desc: 'Error that tells the user that the password cant be empty', name: 'emptyPasswordError'); } String get noMatchPasswordError { return Intl.message("Passwords don't match", desc: 'Error that tells the user that the passwords dont match', name: 'noMatchPasswordError'); } String get invalidPasswordError { return Intl.message("Invalid password", desc: 'Error that tells the user that the password is invalid', name: 'invalidPasswordError'); } String get didNotGetResponseError { return Intl.message("Did not get a response from server", desc: 'Error that tells the user that there is no response from the server', name: 'didNotGetResponseError'); } String get noContactsToExportError { return Intl.message("No contacts to export", desc: 'Error that tells the user that there is no contacts to export', name: 'noContactsToExportError'); } String get noContactsToImportError { return Intl.message("No contacts to import", desc: 'Error that tells the user that there is no contacts to import', name: 'noContactsToImportError'); } String get failedToImportContactsError { return Intl.message("Failed to import contacts", desc: 'Error that tells the user that there is no contacts to export', name: 'failedToImportContactsError'); } // **** ERROR TEXT END **** // // **** OPDETAILS **** // String get blockchainRewardOPDetails { return Intl.message("Blockchain Reward (%1)", desc: 'Operation details header for blockchain reward', name: 'blockchainRewardOPDetails'); } String get transactionOPDetails { return Intl.message("Transaction (%1)", desc: 'Operation details header for transaction', name: 'transactionOPDetails'); } String get changeKeyOPDetails { return Intl.message("Change key (%1)", desc: 'Operation details header for change key', name: 'changeKeyOPDetails'); } String get recoverFundsOPDetails { return Intl.message("Recover Funds (%1)", desc: 'Operation details header for recover funds', name: 'recoverFundsOPDetails'); } String get listAccountForSaleOPDetails { return Intl.message("List Account for Sale (%1)", desc: 'Operation details header for list account for sale', name: 'listAccountForSaleOPDetails'); } String get delistAccountOPDetails { return Intl.message("Delist Account (%1)", desc: 'Operation details header for delist account', name: 'delistAccountOPDetails'); } String get buyAccountOPDetails { return Intl.message("Buy Account (%1)", desc: 'Operation details header for buy account', name: 'buyAccountOPDetails'); } String get changeKeySignedOPDetails { return Intl.message("Change Key Signed (%1)", desc: 'Operation details header for change key signed', name: 'changeKeySignedOPDetails'); } String get changeAccountInfoOPDetails { return Intl.message("Change Account Info (%1)", desc: 'Operation details header for change account info', name: 'changeAccountInfoOPDetails'); } String get multioperationOPDetails { return Intl.message("Multioperation (%1)", desc: 'Operation details header for multioperation', name: 'multioperationOPDetails'); } String get unknownOPDetails { return Intl.message("Unknown (%1)", desc: 'Operation details header for unknown', name: 'unknownOPDetails'); } String get sendingAccountOPDetails { return Intl.message("Sending Account", desc: 'Operation details header for sending account', name: 'sendingAccountOPDetails'); } String get receivingAccountOPDetails { return Intl.message("Receiving Account", desc: 'Operation details header for receiving account', name: 'receivingAccountOPDetails'); } String get changingAccountOPDetails { return Intl.message("Changing Account", desc: 'Operation details header for changing account', name: 'changingAccountOPDetails'); } String get sendAmountOPDetails { return Intl.message("Send Amount", desc: 'Operation details header for send amount', name: 'sendAmountOPDetails'); } String get payloadOPDetails { return Intl.message("Payload", desc: 'Operation details header for payload', name: 'payloadOPDetails'); } String get newPublicKeyOPDetails { return Intl.message("New Public Key", desc: 'Operation details header for new public key', name: 'newPublicKeyOPDetails'); } String get newNameOPDetails { return Intl.message("New Name", desc: 'Operation details header for new name', name: 'newNameOPDetails'); } String get sellerAccountOPDetails { return Intl.message("Seller Account", desc: 'Operation details header for seller account', name: 'sellerAccountOPDetails'); } String get accountPriceOPDetails { return Intl.message("Account Price", desc: 'Operation details header for account price', name: 'accountPriceOPDetails'); } String get lockedUntilBlockOPDetails { return Intl.message("Locked Until Block", desc: 'Operation details header for locked until block', name: 'lockedUntilBlockOPDetails'); } String get blockOPDetails { return Intl.message("block", desc: 'Operation details header for block', name: 'blockOPDetails'); } String get optxtOPDetails { return Intl.message("optxt", desc: 'Operation details header for optxt', name: 'optxtOPDetails'); } String get timeOPDetails { return Intl.message("time", desc: 'Operation details header for time', name: 'timeOPDetails'); } String get naOPDetails { return Intl.message("N/A", desc: 'Operation details header for N/A', name: 'naOPDetails'); } String get ophashOPDetails { return Intl.message("ophash", desc: 'Operation details header for ophash', name: 'ophashOPDetails'); } String get optypeOPDetails { return Intl.message("optype", desc: 'Operation details header for optype', name: 'optypeOPDetails'); } String get maturationOPDetails { return Intl.message("maturation", desc: 'Operation details header for maturation', name: 'maturationOPDetails'); } String get nullOPDetails { return Intl.message("null", desc: 'Operation details header for null', name: 'nullOPDetails'); } String get feeOPDetails { return Intl.message("fee", desc: 'Operation details header for fee', name: 'feeOPDetails'); } String get opblockOPDetails { return Intl.message("opblock", desc: 'Operation details header for opblock', name: 'opblockOPDetails'); } String get noperationOPDetails { return Intl.message("n_operation", desc: 'Operation details header for n_operation', name: 'noperationOPDetails'); } String get accountOPDetails { return Intl.message("account", desc: 'Operation details header for account', name: 'accountOPDetails'); } String get signeraccountOPDetails { return Intl.message("signer_account", desc: 'Operation details header for signer_account', name: 'signeraccountOPDetails'); } // **** OPDETAILS END**** // } class AppLocalizationsDelegate extends LocalizationsDelegate { final LanguageSetting languageSetting; const AppLocalizationsDelegate(this.languageSetting); @override bool isSupported(Locale locale) { return languageSetting != null; } @override Future load(Locale locale) { if (languageSetting.language == AvailableLanguage.DEFAULT) { return AppLocalization.load(locale); } return AppLocalization.load(Locale(languageSetting.getLocaleString())); } @override bool shouldReload(LocalizationsDelegate old) { return true; } } ================================================ FILE: lib/main.dart ================================================ import 'dart:io'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/available_languages.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/account/account.dart'; import 'package:blaise_wallet_flutter/ui/intro/intro_security_first.dart'; import 'package:blaise_wallet_flutter/ui/lockscreen/lock_screen.dart'; import 'package:blaise_wallet_flutter/ui/overview/overview.dart'; import 'package:blaise_wallet_flutter/ui/intro/intro_backup_confirm.dart'; import 'package:blaise_wallet_flutter/ui/intro/intro_decrypt_and_import_private_key.dart'; import 'package:blaise_wallet_flutter/ui/intro/intro_import_private_key.dart'; import 'package:blaise_wallet_flutter/ui/intro/intro_new_private_key.dart'; import 'package:blaise_wallet_flutter/ui/settings/contacts/contacts.dart'; import 'package:blaise_wallet_flutter/ui/settings/security.dart'; import 'package:blaise_wallet_flutter/ui/util/routes.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/intro/intro_welcome.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:logger/logger.dart'; import 'package:oktoast/oktoast.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); // Register services setupServiceLocator(); // Setup logger, only show warning and higher in release mode. if (kReleaseMode) { Logger.level = Level.warning; } else { Logger.level = Level.debug; } // Setup firebase await Firebase.initializeApp(); // Run app SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]) .then((_) { runApp(StateContainer(child: App())); }); } class App extends StatefulWidget { @override _AppState createState() => _AppState(); } class _AppState extends State { @override void initState() { super.initState(); } // This widget is the root of the application. @override Widget build(BuildContext context) { SystemChrome.setSystemUIOverlayStyle( StateContainer.of(context).curTheme.statusBar); return OKToast( textStyle: AppStyles.snackbar(context), backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, child: MaterialApp( debugShowCheckedModeBanner: false, title: 'Blaise', theme: ThemeData( dialogBackgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, primaryColor: StateContainer.of(context).curTheme.primary, accentColor: StateContainer.of(context).curTheme.primary, backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, fontFamily: 'Metropolis', brightness: StateContainer.of(context).curTheme.brightness, splashColor: StateContainer.of(context).curTheme.primary30, highlightColor: StateContainer.of(context).curTheme.primary15, ), localizationsDelegates: [ AppLocalizationsDelegate(StateContainer.of(context).curLanguage), GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, GlobalWidgetsLocalizations.delegate ], initialRoute: '/', onGenerateRoute: (RouteSettings settings) { switch (settings.name) { case '/': return NoTransitionRoute( builder: (context) => Splash(), settings: settings, ); case '/lock_screen': if (settings.arguments != null && settings.arguments is TransitionOption && settings.arguments == TransitionOption.NONE) { return NoTransitionRoute( builder: (context) => LockScreenPage(), settings: settings, ); } return MaterialPageRoute( builder: (context) => LockScreenPage(), settings: settings, ); case '/intro_welcome': return NoTransitionRoute( builder: (context) => IntroWelcomePage(), settings: settings, ); case '/intro_security_first': return MaterialPageRoute( builder: (context) => IntroSecurityFirstPage(), settings: settings, ); case '/intro_new_private_key': return MaterialPageRoute( builder: (context) => IntroNewPrivateKeyPage(), settings: settings, ); case '/intro_backup_confirm': return MaterialPageRoute( builder: (context) => IntroBackupConfirmPage(), settings: settings, ); case '/intro_import_private_key': return MaterialPageRoute( builder: (context) => IntroImportPrivateKeyPage(), settings: settings, ); case '/intro_decrypt_and_import_private_key': return MaterialPageRoute( builder: (context) => IntroDecryptAndImportPrivateKeyPage( encryptedKey: settings.arguments), settings: settings, ); case '/overview': if (settings.arguments != null && settings.arguments is TransitionOption && settings.arguments == TransitionOption.NONE) { return NoTransitionRoute( builder: (context) => OverviewPage(), settings: settings, ); } return MaterialPageRoute( builder: (context) => OverviewPage(), settings: settings, ); case '/account': return MaterialPageRoute( builder: (context) => AccountPage(account: settings.arguments), settings: settings, ); case '/security': return MaterialPageRoute( builder: (context) => SecurityPage(), settings: settings, ); case '/contacts': return MaterialPageRoute( builder: (context) => ContactsPage(account: settings.arguments), settings: settings, ); default: return null; } }, locale: StateContainer.of(context).curLanguage == null || StateContainer.of(context).curLanguage.language == AvailableLanguage.DEFAULT ? null : StateContainer.of(context).curLanguage.getLocale(), supportedLocales: [ // Languages const Locale('en', 'US'), // English const Locale('de', 'DE'), // German const Locale('es'), // Spanish const Locale('tr'), // Turkish const Locale('ar'), // Arabic const Locale.fromSubtags( languageCode: 'zh', scriptCode: 'Hans'), // Chinese Simplified // Currency-default requires country included const Locale("es", "AR"), const Locale("en", "AU"), const Locale("en", "US"), const Locale("pt", "BR"), const Locale("en", "CA"), const Locale("de", "CH"), const Locale("es", "CL"), const Locale("zh", "CN"), const Locale("cs", "CZ"), const Locale("da", "DK"), const Locale("fr", "FR"), const Locale("en", "GB"), const Locale("zh", "HK"), const Locale("hu", "HU"), const Locale("id", "ID"), const Locale("he", "IL"), const Locale("hi", "IN"), const Locale("ja", "JP"), const Locale("ko", "KR"), const Locale("es", "MX"), const Locale("ta", "MY"), const Locale("en", "NZ"), const Locale("tl", "PH"), const Locale("ur", "PK"), const Locale("pl", "PL"), const Locale("ru", "RU"), const Locale("sv", "SE"), const Locale("zh", "SG"), const Locale("th", "TH"), const Locale("tr", "TR"), const Locale("en", "TW"), const Locale("es", "VE"), const Locale("en", "ZA"), const Locale("en", "US"), const Locale("es", "AR"), const Locale("de", "AT"), const Locale("fr", "BE"), const Locale("de", "BE"), const Locale("nl", "BE"), const Locale("tr", "CY"), const Locale("et", "EE"), const Locale("fi", "FI"), const Locale("fr", "FR"), const Locale("el", "GR"), const Locale("es", "AR"), const Locale("en", "IE"), const Locale("it", "IT"), const Locale("es", "AR"), const Locale("lv", "LV"), const Locale("lt", "LT"), const Locale("fr", "LU"), const Locale("en", "MT"), const Locale("nl", "NL"), const Locale("pt", "PT"), const Locale("sk", "SK"), const Locale("sl", "SI"), const Locale("es", "ES"), const Locale("ar", "AE"), // UAE const Locale("ar", "SA"), // Saudi Arabia const Locale("ar", "KW"), // Kuwait ], ), ); } } /// Splash /// Default page route that determines if user is logged in and routes them appropriately. class Splash extends StatefulWidget { @override SplashState createState() => SplashState(); } class SplashState extends State with WidgetsBindingObserver { bool _hasCheckedLoggedIn; Future checkLoggedIn({bool retry = false, bool legacyStorage = false}) async { try { if (!_hasCheckedLoggedIn) { _hasCheckedLoggedIn = true; if (await sl.get().getFirstLaunch()) { await sl.get().deleteAll(firstLaunch: true); await sl.get().deleteAll(); await sl.get().setFirstLaunch(); Navigator.of(context).pushReplacementNamed('/intro_welcome'); } else if ((await sl.get().getPrivateKey() != null) && (await sl.get().getPrivateKeyBackedUp())) { if (await sl.get().getLock() || await sl.get().shouldLock()) { Navigator.of(context).pushReplacementNamed('/lock_screen', arguments: TransitionOption.NONE); } else { walletState.requestUpdate(); Navigator.of(context).pushReplacementNamed('/overview', arguments: TransitionOption.NONE); } } else { Navigator.of(context).pushReplacementNamed('/intro_welcome'); } } } catch (e) { // Attempt to retry if this failed if (!retry) { await sl.get().deleteAll(); await sl.get().deleteAll(); checkLoggedIn(retry: true, legacyStorage: false); } else if (Platform.isAndroid && e.toString().contains("flutter_secure") && !legacyStorage) { /// Fallback secure storage /// A very small percentage of users are encountering issues writing to the /// Android keyStore using the flutter_secure_storage plugin. /// /// Instead of telling them they are out of luck, this is an automatic "fallback" /// It will generate a 64-byte secret using the native android "bottlerocketstudios" Vault /// This secret is used to encrypt sensitive data and save it in SharedPreferences if (!(await sl.get().useLegacyStorage())) { await sl.get().setUseLegacyStorage(); checkLoggedIn(retry: true, legacyStorage: true); } } } } @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _hasCheckedLoggedIn = false; if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) { SchedulerBinding.instance.addPostFrameCallback((_) => checkLoggedIn()); } } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { // Account for user changing locale when leaving the app switch (state) { case AppLifecycleState.paused: super.didChangeAppLifecycleState(state); break; case AppLifecycleState.resumed: setLanguage(); super.didChangeAppLifecycleState(state); break; default: super.didChangeAppLifecycleState(state); break; } } void setLanguage() { setState(() { StateContainer.of(context).deviceLocale = Localizations.localeOf(context); }); sl.get().getLanguage().then((setting) { setState(() { StateContainer.of(context).updateLanguage(setting); }); }); } void setDeviceLocaleAndCurrency() { setState(() { StateContainer.of(context).deviceLocale = Localizations.localeOf(context); sl .get() .getCurrency(StateContainer.of(context).deviceLocale) .then((currency) { StateContainer.of(context).curCurrency = currency; }); }); } @override Widget build(BuildContext context) { setDeviceLocaleAndCurrency(); setLanguage(); return Scaffold( backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, ); } } ================================================ FILE: lib/model/authentication_method.dart ================================================ import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/setting_item.dart'; import 'package:flutter/material.dart'; enum AuthMethod { PIN, BIOMETRICS } /// Represent the available authentication methods our app supports class AuthenticationMethod extends SettingSelectionItem { AuthMethod method; AuthenticationMethod(this.method); String getDisplayName(BuildContext context) { switch (method) { case AuthMethod.BIOMETRICS: return AppLocalization.of(context).authenticationBiometricsHeader; case AuthMethod.PIN: return AppLocalization.of(context).authenticationPINHeader; default: return AppLocalization.of(context).authenticationPINHeader; } } // For saving to shared prefs int getIndex() { return method.index; } } ================================================ FILE: lib/model/available_currency.dart ================================================ import 'package:blaise_wallet_flutter/model/setting_item.dart'; import 'package:flutter/material.dart'; import 'dart:ui'; enum AvailableCurrencyEnum { USD, ARS, AUD, BRL, CAD, CHF, CLP, CNY, CZK, DKK, EUR, GBP, HKD, HUF, IDR, ILS, INR, JPY, KRW, KWD, MXN, MYR, NOK, NZD, PHP, PKR, PLN, RUB, SAR, SEK, SGD, THB, TRY, TWD, AED, ZAR } /// Represent the available authentication methods our app supports class AvailableCurrency extends SettingSelectionItem { AvailableCurrencyEnum currency; AvailableCurrency(this.currency); String getIso4217Code() { return currency.toString().split('.')[1]; } String getDisplayName(BuildContext context) { return getCurrencySymbol() + " " + getDisplayNameNoSymbol(); } String getDisplayNameNoSymbol() { switch (getIso4217Code()) { case "ARS": return "Argentine Peso"; case "AUD": return "Australian Dollar"; case "BRL": return "Brazilian Real"; case "CAD": return "Canadian Dollar"; case "CHF": return "Swiss Franc"; case "CLP": return "Chilean Peso"; case "CNY": return "Chinese Yuan"; case "CZK": return "Czech Koruna"; case "DKK": return "Danish Krone"; case "EUR": return "Euro"; case "GBP": return "Great Britain Pound"; case "HKD": return "Hong Kong Dollar"; case "HUF": return "Hungarian Forint"; case "IDR": return "Indonesian Rupiah"; case "ILS": return "Israeli Shekel"; case "INR": return "Indian Rupee"; case "JPY": return "Japanese Yen"; case "KRW": return "South Korean Won"; case "KWD": return "Kuwaiti Dinar"; case "MXN": return "Mexican Peso"; case "MYR": return "Malaysian Ringgit"; case "NOK": return "Norwegian Krone"; case "NZD": return "New Zealand Dollar"; case "PHP": return "Philippine Peso"; case "PKR": return "Pakistani Rupee"; case "PLN": return "Polish Zloty"; case "RUB": return "Russian Ruble"; case "SAR": return "Saudi Riyal"; case "SEK": return "Swedish Krona"; case "SGD": return "Singapore Dollar"; case "THB": return "Thai Baht"; case "TRY": return "Turkish Lira"; case "TWD": return "Taiwan Dollar"; case "AED": return "UAE Dirham"; case "ZAR": return "South African Rand"; case "USD": default: return "US Dollar"; } } String getCurrencySymbol() { switch (getIso4217Code()) { case "ARS": return "\$"; case "AUD": return "\$"; case "BRL": return "R\$"; case "CAD": return "\$"; case "CHF": return "CHF"; case "CLP": return "\$"; case "CNY": return "¥"; case "CZK": return "Kč"; case "DKK": return "kr."; case "EUR": return "€"; case "GBP": return "£"; case "HKD": return "HK\$"; case "HUF": return "Ft"; case "IDR": return "Rp"; case "ILS": return "₪"; case "INR": return "₹"; case "JPY": return "¥"; case "KRW": return "₩"; case "KWD": return "KD"; case "MXN": return "\$"; case "MYR": return "RM"; case "NOK": return "kr"; case "NZD": return "\$"; case "PHP": return "₱"; case "PKR": return "Rs"; case "PLN": return "zł"; case "RUB": return "\u20BD"; case "SAR": return "SR"; case "SEK": return "kr"; case "SGD": return "\$"; case "THB": return "THB"; case "TRY": return "₺"; case "TWD": return "NT\$"; case "AED": return "د.إ"; case "ZAR": return "R\$"; case "USD": default: return "\$"; } } Locale getLocale() { switch (getIso4217Code()) { case "ARS": return Locale("es", "AR"); case "AUD": return Locale("en", "AU"); case "BRL": return Locale("pt", "BR"); case "CAD": return Locale("en", "CA"); case "CHF": return Locale("de", "CH"); case "CLP": return Locale("es", "CL"); case "CNY": return Locale("zh", "CN"); case "CZK": return Locale("cs", "CZ"); case "DKK": return Locale("da", "DK"); case "EUR": return Locale("fr", "FR"); case "GBP": return Locale("en", "GB"); case "HKD": return Locale("zh", "HK"); case "HUF": return Locale("hu", "HU"); case "IDR": return Locale("id", "ID"); case "ILS": return Locale("he", "IL"); case "INR": return Locale("hi", "IN"); case "JPY": return Locale("ja", "JP"); case "KRW": return Locale("ko", "KR"); case "KWD": return Locale("ar", "KW"); case "MXN": return Locale("es", "MX"); case "MYR": return Locale("ta", "MY"); case "NOK": return Locale("no", "NO"); case "NZD": return Locale("en", "NZ"); case "PHP": return Locale("tl", "PH"); case "PKR": return Locale("ur", "PK"); case "PLN": return Locale("pl", "PL"); case "RUB": return Locale("ru", "RU"); case "SAR": return Locale("ar", "SA"); case "SEK": return Locale("sv", "SE"); case "SGD": return Locale("zh", "SG"); case "THB": return Locale("th", "TH"); case "TRY": return Locale("tr", "TR"); case "TWD": return Locale("en", "TW"); case "AED": return Locale("ar", "AE"); case "ZAR": return Locale("en", "ZA"); case "USD": default: return Locale("en", "US"); } } // For saving to shared prefs int getIndex() { return currency.index; } // Get best currency for a given locale // Default to USD static AvailableCurrency getBestForLocale(Locale locale) { for (AvailableCurrencyEnum value in AvailableCurrencyEnum.values) { AvailableCurrency currency = AvailableCurrency(value); if (locale != null && locale.countryCode != null) { // Special cases if (['AT', 'BE', 'CY', 'EE', 'FI', 'FR', 'DE', 'GR', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PT', 'SK', 'SI', 'ES'].contains(locale.countryCode)) { return AvailableCurrency(AvailableCurrencyEnum.EUR); } else if (currency.getLocale().countryCode.toUpperCase() == locale.countryCode.toUpperCase()) { return currency; } } } return AvailableCurrency(AvailableCurrencyEnum.USD); } } ================================================ FILE: lib/model/available_languages.dart ================================================ import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/setting_item.dart'; import 'package:flutter/material.dart'; enum AvailableLanguage { DEFAULT, ENGLISH, ARABIC, CATALAN, CHINESE_SIMPLIFIED, GERMAN, SPANISH, TURKISH } /// Represent the available languages our app supports class LanguageSetting extends SettingSelectionItem { AvailableLanguage language; LanguageSetting(this.language); String getDisplayName(BuildContext context) { switch (language) { case AvailableLanguage.ENGLISH: return "English (en)"; case AvailableLanguage.CATALAN: return "Català (ca)"; case AvailableLanguage.ARABIC: return "العَرَبِيَّة‎ (ar)"; case AvailableLanguage.CHINESE_SIMPLIFIED: return "简体字 (zh-Hans)"; case AvailableLanguage.GERMAN: return "Deutsch (de)"; case AvailableLanguage.SPANISH: return "Español (es)"; case AvailableLanguage.TURKISH: return "Türkçe (tr)"; default: return AppLocalization.of(context).systemDefaultHeader; } } String getLocaleString() { switch (language) { case AvailableLanguage.ENGLISH: return "en"; case AvailableLanguage.ARABIC: return "ar"; case AvailableLanguage.CATALAN: return "ca"; case AvailableLanguage.CHINESE_SIMPLIFIED: return "zh-Hans"; case AvailableLanguage.GERMAN: return "de"; case AvailableLanguage.SPANISH: return "es"; case AvailableLanguage.TURKISH: return "tr"; default: return "DEFAULT"; } } Locale getLocale() { String localeStr = getLocaleString(); if (localeStr == 'DEFAULT') { return Locale('en'); } else if (localeStr == 'zh-Hans' || localeStr == 'zh-Hant') { return Locale.fromSubtags(languageCode: 'zh', scriptCode: localeStr.split('-')[1]); } return Locale(localeStr); } // For saving to shared prefs String getId() { return language.toString(); } } ================================================ FILE: lib/model/available_themes.dart ================================================ import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/setting_item.dart'; import 'package:blaise_wallet_flutter/themes.dart'; import 'package:flutter/material.dart'; enum ThemeOptions { LIGHT, DARK, COPPER } /// Represent notification on/off setting class ThemeSetting extends SettingSelectionItem { ThemeOptions theme; ThemeSetting(this.theme); String getDisplayName(BuildContext context) { switch (theme) { case ThemeOptions.DARK: return AppLocalization.of(context).themeDarkHeader; case ThemeOptions.COPPER: return AppLocalization.of(context).themeCopperHeader; case ThemeOptions.LIGHT: default: return AppLocalization.of(context).themeLightHeader; } } BaseTheme getTheme() { switch (theme) { case ThemeOptions.COPPER: return BlaiseCopperTheme(); case ThemeOptions.DARK: return BlaiseDarkTheme(); case ThemeOptions.LIGHT: default: return BlaiseLightTheme(); } } // For saving to shared prefs int getIndex() { return theme.index; } } ================================================ FILE: lib/model/db/appdb.dart ================================================ import 'dart:async'; import 'dart:io' as io; import 'package:flutter/foundation.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path_provider/path_provider.dart'; import 'package:blaise_wallet_flutter/model/db/contact.dart'; class DBHelper{ static const int DB_VERSION = 1; static const String CONTACTS_SQL = """CREATE TABLE Contacts( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, account INTEGER, payload TEXT)"""; static Database _db; Future get db async { if(_db != null) return _db; _db = await initDb(); return _db; } initDb() async { io.Directory documentsDirectory = await getApplicationDocumentsDirectory(); String path = join(documentsDirectory.path, "blaise.db"); var theDb = await openDatabase(path, version: DB_VERSION, onCreate: _onCreate, onUpgrade: _onUpgrade); return theDb; } void _onCreate(Database db, int version) async { // When creating the db, create the tables await db.execute(CONTACTS_SQL); } void _onUpgrade(Database db, int oldVersion, int newVersion) async { return; } // Contacts Future> getContacts() async { var dbClient = await db; List list = await dbClient.rawQuery('SELECT * FROM Contacts ORDER BY name'); List contacts = new List(); for (int i = 0; i < list.length; i++) { contacts.add(new Contact(id: list[i]["id"], name: list[i]["name"], account: AccountNumber.fromInt(list[i]["account"]), payload: list[i]["payload"])); } return contacts; } Future> getContactsWithNameLike(String pattern) async { var dbClient = await db; List list = await dbClient.rawQuery('SELECT * FROM Contacts WHERE name LIKE \'%$pattern%\' ORDER BY LOWER(name)'); List contacts = new List(); for (int i = 0; i < list.length; i++) { contacts.add(new Contact(id: list[i]["id"], name: list[i]["name"], account: AccountNumber.fromInt(list[i]["account"]), payload: list[i]["payload"])); } return contacts; } Future getContactWithAccount(AccountNumber account) async { var dbClient = await db; List list = await dbClient.rawQuery('SELECT * FROM Contacts WHERE account = ?', [account.account]); if (list.length > 0) { return Contact(id: list[0]["id"], name: list[0]["name"], account: AccountNumber.fromInt(list[0]["account"]), payload: list[0]["payload"]); } return null; } Future getContactWithName(String name) async { var dbClient = await db; List list = await dbClient.rawQuery('SELECT * FROM Contacts WHERE name = ?', [name]); if (list.length > 0) { return Contact(id: list[0]["id"], name: list[0]["name"], account: AccountNumber.fromInt(list[0]["account"]), payload: list[0]["payload"]); } return null; } Future contactExistsWithName(String name) async { var dbClient = await db; int count = Sqflite.firstIntValue(await dbClient.rawQuery('SELECT count(*) FROM Contacts WHERE lower(name) = ?', [name.toLowerCase()])); return count > 0; } Future contactExistsWithAccount(AccountNumber account) async { var dbClient = await db; int count = Sqflite.firstIntValue(await dbClient.rawQuery('SELECT count(*) FROM Contacts WHERE account = ?', [account.account])); return count > 0; } Future saveContact(Contact contact) async { var dbClient = await db; return await dbClient.rawInsert('INSERT INTO Contacts (name, account, payload) values(?, ?, ?)', [contact.name, contact.account.account, contact.payload]); } Future saveContacts(List contacts) async { int count = 0; for (Contact c in contacts) { if (await saveContact(c) > 0) { count++; } } return count; } Future deleteContact(Contact contact) async { var dbClient = await db; return await dbClient.rawDelete("DELETE FROM Contacts WHERE account = ?", [contact.account.account]) > 0; } } ================================================ FILE: lib/model/db/contact.dart ================================================ import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:meta/meta.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:quiver/core.dart'; part 'contact.g.dart'; @JsonSerializable() class Contact { @JsonKey(ignore:true) int id; @JsonKey(name:'name') String name; @JsonKey(name:'account', fromJson: intToAccountNum, toJson: accountNumToInt) AccountNumber account; @JsonKey(name:'payload') String payload; Contact({@required this.name, @required this.account, this.id, this.payload = ""}); factory Contact.fromJson(Map json) => _$ContactFromJson(json); Map toJson() => _$ContactToJson(this); bool operator ==(o) => o is Contact && o.name == name && o.account == account && o.payload == payload; int get hashCode => hash3(name.hashCode, account.hashCode, payload.hashCode); } ================================================ FILE: lib/model/db/contact.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'contact.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** Contact _$ContactFromJson(Map json) { return Contact( name: json['name'] as String, account: intToAccountNum(json['account'] as int), payload: json['payload'] as String, ); } Map _$ContactToJson(Contact instance) => { 'name': instance.name, 'account': accountNumToInt(instance.account), 'payload': instance.payload, }; ================================================ FILE: lib/model/lock_timeout.dart ================================================ import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/setting_item.dart'; import 'package:flutter/material.dart'; enum LockTimeoutOption { ZERO, ONE, FIVE, FIFTEEN, THIRTY, SIXTY } /// Represent auto-lock delay when requiring auth to open class LockTimeoutSetting extends SettingSelectionItem { LockTimeoutOption setting; LockTimeoutSetting(this.setting); String getDisplayName(BuildContext context) { switch (setting) { case LockTimeoutOption.ZERO: return AppLocalization.of(context).lockInstantHeader; case LockTimeoutOption.ONE: return AppLocalization.of(context).lock1Header.replaceAll("%1", "1"); case LockTimeoutOption.FIVE: return AppLocalization.of(context).lock5Header.replaceAll("%1", "5"); case LockTimeoutOption.FIFTEEN: return AppLocalization.of(context).lock15Header.replaceAll("%1", "15"); case LockTimeoutOption.THIRTY: return AppLocalization.of(context).lock30Header.replaceAll("%1", "30"); case LockTimeoutOption.SIXTY: return AppLocalization.of(context).lock60Header.replaceAll("%1", "60"); default: return AppLocalization.of(context).lock1Header.replaceAll("%1", "1"); } } Duration getDuration() { switch (setting) { case LockTimeoutOption.ZERO: return Duration(seconds: 3); case LockTimeoutOption.ONE: return Duration(minutes: 1); case LockTimeoutOption.FIVE: return Duration(minutes: 5); case LockTimeoutOption.FIFTEEN: return Duration(minutes: 15); case LockTimeoutOption.THIRTY: return Duration(minutes: 30); case LockTimeoutOption.SIXTY: return Duration(minutes: 60); default: return Duration(minutes: 1); } } // For saving to shared prefs int getIndex() { return setting.index; } } ================================================ FILE: lib/model/notification_enabled.dart ================================================ import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/setting_item.dart'; import 'package:flutter/material.dart'; enum NotificationOptions { ON, OFF } /// Represent notification on/off setting class NotificationSetting extends SettingSelectionItem { NotificationOptions setting; NotificationSetting(this.setting); String getDisplayName(BuildContext context) { switch (setting) { case NotificationOptions.ON: return AppLocalization.of(context).onHeader; case NotificationOptions.OFF: default: return AppLocalization.of(context).offHeader; } } // For saving to shared prefs int getIndex() { return setting.index; } } ================================================ FILE: lib/model/setting_item.dart ================================================ import 'package:flutter/material.dart'; /// Models that are used in settings dialogs/dropdowns abstract class SettingSelectionItem { String getDisplayName(BuildContext context); } ================================================ FILE: lib/model/unlock_setting.dart ================================================ import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/setting_item.dart'; import 'package:flutter/material.dart'; enum UnlockOption { YES, NO } /// Represent authenticate to open setting class UnlockSetting extends SettingSelectionItem { UnlockOption setting; UnlockSetting(this.setting); String getDisplayName(BuildContext context) { switch (setting) { case UnlockOption.YES: return AppLocalization.of(context).yesHeader; case UnlockOption.NO: default: return AppLocalization.of(context).noHeader; } } // For saving to shared prefs int getIndex() { return setting.index; } } ================================================ FILE: lib/network/http_client.dart ================================================ import 'package:blaise_wallet_flutter/network/model/request/borrow_request.dart'; import 'package:blaise_wallet_flutter/network/model/request/freepasa_get_request.dart'; import 'package:blaise_wallet_flutter/network/model/request/freepasa_verify_request.dart'; import 'package:blaise_wallet_flutter/network/model/request/getborrowed_request.dart'; import 'package:blaise_wallet_flutter/network/model/response/borrow_response.dart'; import 'package:blaise_wallet_flutter/network/model/response/getborrowed_response.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:logger/logger.dart'; /// Blaise HTTP API Client class HttpAPI { static const String API_URL = 'https://blaiseapi.appditto.com/v1'; static Logger log = sl.get(); static Future getBorrowed(String b58pubkey) async { try { GetBorrowedRequest request = GetBorrowedRequest(b58pubkey: b58pubkey); http.Response response = await http.post(Uri.parse(API_URL), body: json.encode(request.toJson())); if (response.statusCode != 200) { return null; } Map jsonResp = json.decode(response.body); if (jsonResp.containsKey('borrowed_account') && jsonResp['borrowed_account'] != '') { GetBorrowedResponse bResp = GetBorrowedResponse.fromJson(jsonResp); return bResp.account; } else if (jsonResp['borrowed_account'] == '') { return BorrowResponse(); } return null; } catch (e) { return null; } } static Future borrowAccount(String b58pubkey) async { try { BorrowRequest request = BorrowRequest(b58pubkey: b58pubkey); http.Response response = await http.post(Uri.parse(API_URL), body: json.encode(request.toJson())); if (response.statusCode != 200) { return null; } Map jsonResp = json.decode(response.body); if (jsonResp.containsKey('pasa')) { BorrowResponse bResp = BorrowResponse.fromJson(jsonResp); return bResp; } return null; } catch (e) { return null; } } /// Request a FreePASA, using phone number /// Response is a request ID, which can be used in the verify phone number request static Future getFreePASA( String isoCode, String phoneNumber, String b58pubkey) async { try { FreePASAGetRequest request = FreePASAGetRequest( phoneIso: isoCode, phoneNumber: phoneNumber, b58pubkey: b58pubkey); http.Response response = await http.post(Uri.parse(API_URL), body: json.encode(request.toJson())); if (response.statusCode != 200) { return null; } Map jsonResp = json.decode(response.body); if (jsonResp.containsKey('success')) { return jsonResp['success']; } return null; } catch (e) { return null; } } /// Verify a FreePASA, using phone number /// Response is the account that has been received static Future verifyFreePASA(String requestId, String code) async { try { FreePASAVerifyRequest request = FreePASAVerifyRequest(requestId: requestId, code: code); http.Response response = await http.post(Uri.parse(API_URL), body: json.encode(request.toJson())); if (response.statusCode != 200) { log.d("RECEIVED STATUS CODE NOT OK ${response.statusCode}"); return null; } Map jsonResp = json.decode(response.body); log.d("Received response ${response.body}"); if (jsonResp.containsKey('success')) { log.d("New account is ${jsonResp['success']}"); return int.parse(jsonResp['success'].toString()); } return null; } catch (e) { log.e("Caughe exception in freepasa request ${e.toString()}", e); return null; } } } ================================================ FILE: lib/network/model/base_request.dart ================================================ abstract class BaseRequest { Map toJson(); } ================================================ FILE: lib/network/model/request/borrow_request.dart ================================================ import 'package:blaise_wallet_flutter/network/model/base_request.dart'; import 'package:json_annotation/json_annotation.dart'; part 'borrow_request.g.dart'; @JsonSerializable() class BorrowRequest extends BaseRequest { @JsonKey(name:'action') String action; @JsonKey(name:'b58_pubkey') String b58pubkey; BorrowRequest({this.action = "borrow_account", this.b58pubkey}) : super(); factory BorrowRequest.fromJson(Map json) => _$BorrowRequestFromJson(json); Map toJson() => _$BorrowRequestToJson(this); } ================================================ FILE: lib/network/model/request/borrow_request.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'borrow_request.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** BorrowRequest _$BorrowRequestFromJson(Map json) { return BorrowRequest( action: json['action'] as String, b58pubkey: json['b58_pubkey'] as String, ); } Map _$BorrowRequestToJson(BorrowRequest instance) => { 'action': instance.action, 'b58_pubkey': instance.b58pubkey, }; ================================================ FILE: lib/network/model/request/fcm_delete_account_request.dart ================================================ import 'package:blaise_wallet_flutter/network/model/base_request.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:meta/meta.dart'; part 'fcm_delete_account_request.g.dart'; @JsonSerializable() class FcmDeleteAccountRequest extends BaseRequest { @JsonKey(name:'action') String action; @JsonKey(name:'account', includeIfNull: false) int account; @JsonKey(name:'fcm_token', includeIfNull: false) String fcmToken; FcmDeleteAccountRequest({@required int account, @required String fcmToken}) : super() { this.action = 'fcm_update'; this.account = account; this.fcmToken = fcmToken; } factory FcmDeleteAccountRequest.fromJson(Map json) => _$FcmDeleteAccountRequestFromJson(json); Map toJson() => _$FcmDeleteAccountRequestToJson(this); } ================================================ FILE: lib/network/model/request/fcm_delete_account_request.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'fcm_delete_account_request.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** FcmDeleteAccountRequest _$FcmDeleteAccountRequestFromJson( Map json) { return FcmDeleteAccountRequest( account: json['account'] as int, fcmToken: json['fcm_token'] as String, )..action = json['action'] as String; } Map _$FcmDeleteAccountRequestToJson( FcmDeleteAccountRequest instance) { final val = { 'action': instance.action, }; void writeNotNull(String key, dynamic value) { if (value != null) { val[key] = value; } } writeNotNull('account', instance.account); writeNotNull('fcm_token', instance.fcmToken); return val; } ================================================ FILE: lib/network/model/request/fcm_update_bulk_request.dart ================================================ import 'package:blaise_wallet_flutter/network/model/base_request.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:meta/meta.dart'; part 'fcm_update_bulk_request.g.dart'; @JsonSerializable() class FcmUpdateBulkRequest extends BaseRequest { @JsonKey(name:'action') String action; @JsonKey(name:'account', includeIfNull: false) List accounts; @JsonKey(name:'b58_pubkey', includeIfNull: false) String b58pubkey; @JsonKey(name:'fcm_token', includeIfNull: false) String fcmToken; @JsonKey(name:'enabled') bool enabled; FcmUpdateBulkRequest({@required this.accounts, @required this.fcmToken, @required this.enabled, @required this.b58pubkey}) : super() { this.action = 'fcm_update_bulk'; } factory FcmUpdateBulkRequest.fromJson(Map json) => _$FcmUpdateBulkRequestFromJson(json); Map toJson() => _$FcmUpdateBulkRequestToJson(this); } ================================================ FILE: lib/network/model/request/fcm_update_bulk_request.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'fcm_update_bulk_request.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** FcmUpdateBulkRequest _$FcmUpdateBulkRequestFromJson(Map json) { return FcmUpdateBulkRequest( accounts: (json['account'] as List)?.map((e) => e as int)?.toList(), fcmToken: json['fcm_token'] as String, enabled: json['enabled'] as bool, b58pubkey: json['b58_pubkey'] as String, )..action = json['action'] as String; } Map _$FcmUpdateBulkRequestToJson( FcmUpdateBulkRequest instance) { final val = { 'action': instance.action, }; void writeNotNull(String key, dynamic value) { if (value != null) { val[key] = value; } } writeNotNull('account', instance.accounts); writeNotNull('b58_pubkey', instance.b58pubkey); writeNotNull('fcm_token', instance.fcmToken); val['enabled'] = instance.enabled; return val; } ================================================ FILE: lib/network/model/request/fcm_update_request.dart ================================================ import 'package:blaise_wallet_flutter/network/model/base_request.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:meta/meta.dart'; part 'fcm_update_request.g.dart'; @JsonSerializable() class FcmUpdateRequest extends BaseRequest { @JsonKey(name:'action') String action; @JsonKey(name:'account', includeIfNull: false) int account; @JsonKey(name:'b58_pubkey', includeIfNull: false) String b58pubkey; @JsonKey(name:'fcm_token', includeIfNull: false) String fcmToken; @JsonKey(name:'enabled') bool enabled; FcmUpdateRequest({@required this.account, @required this.fcmToken, @required this.enabled, @required this.b58pubkey}) : super() { this.action = 'fcm_update'; } factory FcmUpdateRequest.fromJson(Map json) => _$FcmUpdateRequestFromJson(json); Map toJson() => _$FcmUpdateRequestToJson(this); } ================================================ FILE: lib/network/model/request/fcm_update_request.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'fcm_update_request.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** FcmUpdateRequest _$FcmUpdateRequestFromJson(Map json) { return FcmUpdateRequest( account: json['account'] as int, fcmToken: json['fcm_token'] as String, enabled: json['enabled'] as bool, b58pubkey: json['b58_pubkey'] as String, )..action = json['action'] as String; } Map _$FcmUpdateRequestToJson(FcmUpdateRequest instance) { final val = { 'action': instance.action, }; void writeNotNull(String key, dynamic value) { if (value != null) { val[key] = value; } } writeNotNull('account', instance.account); writeNotNull('b58_pubkey', instance.b58pubkey); writeNotNull('fcm_token', instance.fcmToken); val['enabled'] = instance.enabled; return val; } ================================================ FILE: lib/network/model/request/freepasa_get_request.dart ================================================ import 'package:blaise_wallet_flutter/network/model/base_request.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:meta/meta.dart'; part 'freepasa_get_request.g.dart'; @JsonSerializable() class FreePASAGetRequest extends BaseRequest { @JsonKey(name:'action') String action; @JsonKey(name:'phone_iso') String phoneIso; @JsonKey(name:'phone_number') String phoneNumber; @JsonKey(name:'b58_pubkey') String b58pubkey; FreePASAGetRequest({@required this.phoneIso, @required this.phoneNumber, @required this.b58pubkey}) : super() { this.action = 'freepasa_get'; } factory FreePASAGetRequest.fromJson(Map json) => _$FreePASAGetRequestFromJson(json); Map toJson() => _$FreePASAGetRequestToJson(this); } ================================================ FILE: lib/network/model/request/freepasa_get_request.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'freepasa_get_request.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** FreePASAGetRequest _$FreePASAGetRequestFromJson(Map json) { return FreePASAGetRequest( phoneIso: json['phone_iso'] as String, phoneNumber: json['phone_number'] as String, b58pubkey: json['b58_pubkey'] as String, )..action = json['action'] as String; } Map _$FreePASAGetRequestToJson(FreePASAGetRequest instance) => { 'action': instance.action, 'phone_iso': instance.phoneIso, 'phone_number': instance.phoneNumber, 'b58_pubkey': instance.b58pubkey, }; ================================================ FILE: lib/network/model/request/freepasa_verify_request.dart ================================================ import 'package:blaise_wallet_flutter/network/model/base_request.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:meta/meta.dart'; part 'freepasa_verify_request.g.dart'; @JsonSerializable() class FreePASAVerifyRequest extends BaseRequest { @JsonKey(name:'action') String action; @JsonKey(name:'request_id') String requestId; @JsonKey(name:'code') String code; FreePASAVerifyRequest({@required this.requestId, @required this.code}) : super() { this.action = 'freepasa_verify'; } factory FreePASAVerifyRequest.fromJson(Map json) => _$FreePASAVerifyRequestFromJson(json); Map toJson() => _$FreePASAVerifyRequestToJson(this); } ================================================ FILE: lib/network/model/request/freepasa_verify_request.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'freepasa_verify_request.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** FreePASAVerifyRequest _$FreePASAVerifyRequestFromJson( Map json) { return FreePASAVerifyRequest( requestId: json['request_id'] as String, code: json['code'] as String, )..action = json['action'] as String; } Map _$FreePASAVerifyRequestToJson( FreePASAVerifyRequest instance) => { 'action': instance.action, 'request_id': instance.requestId, 'code': instance.code, }; ================================================ FILE: lib/network/model/request/getborrowed_request.dart ================================================ import 'package:blaise_wallet_flutter/network/model/base_request.dart'; import 'package:json_annotation/json_annotation.dart'; part 'getborrowed_request.g.dart'; @JsonSerializable() class GetBorrowedRequest extends BaseRequest { @JsonKey(name:'action') String action; @JsonKey(name:'b58_pubkey') String b58pubkey; GetBorrowedRequest({this.action = "getborrowed", this.b58pubkey}) : super(); factory GetBorrowedRequest.fromJson(Map json) => _$GetBorrowedRequestFromJson(json); Map toJson() => _$GetBorrowedRequestToJson(this); } ================================================ FILE: lib/network/model/request/getborrowed_request.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'getborrowed_request.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** GetBorrowedRequest _$GetBorrowedRequestFromJson(Map json) { return GetBorrowedRequest( action: json['action'] as String, b58pubkey: json['b58_pubkey'] as String, ); } Map _$GetBorrowedRequestToJson(GetBorrowedRequest instance) => { 'action': instance.action, 'b58_pubkey': instance.b58pubkey, }; ================================================ FILE: lib/network/model/request/subscribe_request.dart ================================================ import 'package:blaise_wallet_flutter/network/model/base_request.dart'; import 'package:json_annotation/json_annotation.dart'; part 'subscribe_request.g.dart'; @JsonSerializable() class SubscribeRequest extends BaseRequest { @JsonKey(name:'action') String action; @JsonKey(name:'account', includeIfNull: false) int account; @JsonKey(name:'currency', includeIfNull: false) String currency; @JsonKey(name:'uuid', includeIfNull: false) String uuid; @JsonKey(name:'b58_pubkey', includeIfNull: false) String b58pubkey; @JsonKey(name:'fcm_token', includeIfNull: false) String fcmToken; @JsonKey(name:'notification_enabled', includeIfNull: false) bool notificationEnabled; SubscribeRequest({this.action = "account_subscribe", this.account, this.currency, this.uuid, this.b58pubkey, this.fcmToken, this.notificationEnabled}) : super(); factory SubscribeRequest.fromJson(Map json) => _$SubscribeRequestFromJson(json); Map toJson() => _$SubscribeRequestToJson(this); } ================================================ FILE: lib/network/model/request/subscribe_request.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'subscribe_request.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** SubscribeRequest _$SubscribeRequestFromJson(Map json) { return SubscribeRequest( action: json['action'] as String, account: json['account'] as int, currency: json['currency'] as String, uuid: json['uuid'] as String, b58pubkey: json['b58_pubkey'] as String, fcmToken: json['fcm_token'] as String, notificationEnabled: json['notification_enabled'] as bool, ); } Map _$SubscribeRequestToJson(SubscribeRequest instance) { final val = { 'action': instance.action, }; void writeNotNull(String key, dynamic value) { if (value != null) { val[key] = value; } } writeNotNull('account', instance.account); writeNotNull('currency', instance.currency); writeNotNull('uuid', instance.uuid); writeNotNull('b58_pubkey', instance.b58pubkey); writeNotNull('fcm_token', instance.fcmToken); writeNotNull('notification_enabled', instance.notificationEnabled); return val; } ================================================ FILE: lib/network/model/request_item.dart ================================================ import 'dart:convert'; /// Top-level function for running in isolate via flutter compute function String encodeRequestItem(dynamic request) { return json.encode(request.toJson()); } class RequestItem { // After this time a request will expire static const int EXPIRE_TIME_S = 15; DateTime expireDt; bool isProcessing; T request; RequestItem(T request) { this.expireDt = DateTime.now().add(new Duration(seconds: EXPIRE_TIME_S)); this.isProcessing = false; this.request = request; } } ================================================ FILE: lib/network/model/response/accounts_response_borrowed.dart ================================================ import 'package:blaise_wallet_flutter/network/model/response/borrow_response.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:pascaldart/pascaldart.dart'; part 'accounts_response_borrowed.g.dart'; @JsonSerializable() class AccountsResponseBorrowed extends AccountsResponse { @JsonKey(name: 'borrowed_account') BorrowResponse borrowedAccount; @JsonKey(name: 'borrow_eligible') bool borrowEligible; AccountsResponseBorrowed({this.borrowedAccount, this.borrowEligible}) : super(); factory AccountsResponseBorrowed.fromJson(Map json) => _$AccountsResponseBorrowedFromJson(json); Map toJson() => _$AccountsResponseBorrowedToJson(this); } ================================================ FILE: lib/network/model/response/accounts_response_borrowed.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'accounts_response_borrowed.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** AccountsResponseBorrowed _$AccountsResponseBorrowedFromJson( Map json) { return AccountsResponseBorrowed( borrowedAccount: json['borrowed_account'] == null ? null : BorrowResponse.fromJson( json['borrowed_account'] as Map), borrowEligible: json['borrow_eligible'] as bool, )..accounts = (json['result'] as List) ?.map((e) => e == null ? null : PascalAccount.fromJson(e as Map)) ?.toList(); } Map _$AccountsResponseBorrowedToJson( AccountsResponseBorrowed instance) => { 'result': instance.accounts, 'borrowed_account': instance.borrowedAccount, 'borrow_eligible': instance.borrowEligible, }; ================================================ FILE: lib/network/model/response/borrow_response.dart ================================================ import 'package:json_annotation/json_annotation.dart'; import 'package:pascaldart/pascaldart.dart'; part 'borrow_response.g.dart'; DateTime _toDateTime(int v) { return v == null ? null : DateTime.fromMillisecondsSinceEpoch(v); } int _fromDateTime(DateTime v) { return v?.millisecondsSinceEpoch; } @JsonSerializable() class BorrowResponse { @JsonKey(name:'pasa', fromJson: intToAccountNum, toJson: accountNumToInt) AccountNumber account; @JsonKey( name: 'expires', fromJson: _toDateTime, toJson: _fromDateTime, includeIfNull: false) DateTime expiry; @JsonKey( name: 'price', includeIfNull: false, toJson: currencyToDouble, fromJson: pascalToCurrency) Currency price; @JsonKey(name: 'paid') bool paid; @JsonKey(name: 'transferred') bool transferred; BorrowResponse(); factory BorrowResponse.fromJson(Map json) => _$BorrowResponseFromJson(json); Map toJson() => _$BorrowResponseToJson(this); } ================================================ FILE: lib/network/model/response/borrow_response.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'borrow_response.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** BorrowResponse _$BorrowResponseFromJson(Map json) { return BorrowResponse() ..account = intToAccountNum(json['pasa'] as int) ..expiry = _toDateTime(json['expires'] as int) ..price = pascalToCurrency(json['price'] as num) ..paid = json['paid'] as bool ..transferred = json['transferred'] as bool; } Map _$BorrowResponseToJson(BorrowResponse instance) { final val = { 'pasa': accountNumToInt(instance.account), }; void writeNotNull(String key, dynamic value) { if (value != null) { val[key] = value; } } writeNotNull('expires', _fromDateTime(instance.expiry)); writeNotNull('price', currencyToDouble(instance.price)); val['paid'] = instance.paid; val['transferred'] = instance.transferred; return val; } ================================================ FILE: lib/network/model/response/error_response.dart ================================================ import 'package:json_annotation/json_annotation.dart'; part 'error_response.g.dart'; @JsonSerializable() class ErrorResponse { @JsonKey(name:'error') String error; ErrorResponse(); factory ErrorResponse.fromJson(Map json) => _$ErrorResponseFromJson(json); Map toJson() => _$ErrorResponseToJson(this); } ================================================ FILE: lib/network/model/response/error_response.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'error_response.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** ErrorResponse _$ErrorResponseFromJson(Map json) { return ErrorResponse()..error = json['error'] as String; } Map _$ErrorResponseToJson(ErrorResponse instance) => { 'error': instance.error, }; ================================================ FILE: lib/network/model/response/getborrowed_response.dart ================================================ import 'package:blaise_wallet_flutter/network/model/response/borrow_response.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:pascaldart/pascaldart.dart'; part 'getborrowed_response.g.dart'; @JsonSerializable() class GetBorrowedResponse { @JsonKey(name:'borrowed_account') BorrowResponse account; GetBorrowedResponse(); factory GetBorrowedResponse.fromJson(Map json) => _$GetBorrowedResponseFromJson(json); Map toJson() => _$GetBorrowedResponseToJson(this); } ================================================ FILE: lib/network/model/response/getborrowed_response.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'getborrowed_response.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** GetBorrowedResponse _$GetBorrowedResponseFromJson(Map json) { return GetBorrowedResponse() ..account = json['borrowed_account'] == null ? null : BorrowResponse.fromJson( json['borrowed_account'] as Map); } Map _$GetBorrowedResponseToJson( GetBorrowedResponse instance) => { 'borrowed_account': instance.account, }; ================================================ FILE: lib/network/model/response/price_response.dart ================================================ import 'package:json_annotation/json_annotation.dart'; part 'price_response.g.dart'; double _toDouble(v) { return double.tryParse(v.toString()); } @JsonSerializable() class PriceResponse { @JsonKey(name:'currency') String currency; @JsonKey(name:'price', fromJson:_toDouble) double price; @JsonKey(name:'btc', fromJson:_toDouble) double btcPrice; PriceResponse(); factory PriceResponse.fromJson(Map json) => _$PriceResponseFromJson(json); Map toJson() => _$PriceResponseToJson(this); } ================================================ FILE: lib/network/model/response/price_response.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'price_response.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** PriceResponse _$PriceResponseFromJson(Map json) { return PriceResponse() ..currency = json['currency'] as String ..price = _toDouble(json['price']) ..btcPrice = _toDouble(json['btc']); } Map _$PriceResponseToJson(PriceResponse instance) => { 'currency': instance.currency, 'price': instance.price, 'btc': instance.btcPrice, }; ================================================ FILE: lib/network/model/response/subscribe_response.dart ================================================ import 'package:json_annotation/json_annotation.dart'; part 'subscribe_response.g.dart'; double _toDouble(v) { return double.tryParse(v.toString()); } /// For running in an isolate, needs to be top-level function SubscribeResponse subscribeResponseFromJson(Map json) { return SubscribeResponse.fromJson(json); } @JsonSerializable() class SubscribeResponse { // Server provides a uuid for each connection @JsonKey(name:'uuid') String uuid; @JsonKey(name:'price', fromJson:_toDouble) double price; @JsonKey(name:'btc', fromJson:_toDouble) double btcPrice; @JsonKey(name:'borrow_eligible', defaultValue: false) bool borrowEligible; SubscribeResponse(); factory SubscribeResponse.fromJson(Map json) => _$SubscribeResponseFromJson(json); Map toJson() => _$SubscribeResponseToJson(this); } ================================================ FILE: lib/network/model/response/subscribe_response.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'subscribe_response.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** SubscribeResponse _$SubscribeResponseFromJson(Map json) { return SubscribeResponse() ..uuid = json['uuid'] as String ..price = _toDouble(json['price']) ..btcPrice = _toDouble(json['btc']) ..borrowEligible = json['borrow_eligible'] as bool ?? false; } Map _$SubscribeResponseToJson(SubscribeResponse instance) => { 'uuid': instance.uuid, 'price': instance.price, 'btc': instance.btcPrice, 'borrow_eligible': instance.borrowEligible, }; ================================================ FILE: lib/network/ws_client.dart ================================================ import 'dart:async'; import 'dart:collection'; import 'dart:convert'; import 'package:blaise_wallet_flutter/bus/events.dart'; import 'package:blaise_wallet_flutter/constants.dart'; import 'package:blaise_wallet_flutter/network/model/base_request.dart'; import 'package:blaise_wallet_flutter/network/model/request/subscribe_request.dart'; import 'package:blaise_wallet_flutter/network/model/request_item.dart'; import 'package:blaise_wallet_flutter/network/model/response/price_response.dart'; import 'package:blaise_wallet_flutter/network/model/response/subscribe_response.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:flutter/foundation.dart'; import 'package:pascaldart/pascaldart.dart' as pd; import 'package:web_socket_channel/io.dart'; import 'package:package_info/package_info.dart'; import 'package:logger/logger.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:synchronized/synchronized.dart'; // For running in isolate Map decodeJson(dynamic src) { return json.decode(src); } // WSClient singleton class WSClient { final Logger log = sl.get(); // For all requests we place them on a queue with expiry to be processed sequentially Queue _requestQueue; // WS Client IOWebSocketChannel _channel; // WS connection status bool _isConnected; bool _isConnecting; bool suspended; // When the app explicity closes the connection // Lock instnace for synchronization Lock _lock; // Constructor WSClient() { _requestQueue = Queue(); _isConnected = false; _isConnecting = false; suspended = false; _lock = Lock(); initCommunication(unsuspend: true); } // Re-connect handling bool _isInRetryState = false; StreamSubscription reconnectStream; /// Retry up to once per 3 seconds Future reconnectToService() async { if (_isInRetryState) { return; } else if (reconnectStream != null) { reconnectStream.cancel(); } _isInRetryState = true; log.d("Retrying connection in 3 seconds..."); Future delayed = new Future.delayed(new Duration(seconds: 3)); delayed.then((_) { return true; }); reconnectStream = delayed.asStream().listen((_) { log.d("Attempting connection to service"); initCommunication(unsuspend: true); _isInRetryState = false; }); } // Connect to server Future initCommunication({bool unsuspend = false}) async { if (_isConnected || _isConnecting) { return; } else if (suspended && !unsuspend) { return; } else if (!unsuspend) { reconnectToService(); return; } _isConnecting = true; try { var packageInfo = await PackageInfo.fromPlatform(); _isConnecting = true; suspended = false; _channel = new IOWebSocketChannel .connect(AppConstants.WS_API_URL, headers: { 'X-Client-Version': packageInfo.buildNumber }); log.d("Connected to service"); _isConnecting = false; _isConnected = true; EventTaxiImpl.singleton().fire(ConnStatusEvent(status: ConnectionStatus.CONNECTED)); _channel.stream.listen(_onMessageReceived, onDone: connectionClosed, onError: connectionClosedError); } catch(e){ log.e("Error from service ${e.toString()}", e); _isConnected = false; _isConnecting = false; EventTaxiImpl.singleton().fire(ConnStatusEvent(status: ConnectionStatus.DISCONNECTED)); } } // Connection closed (normally) void connectionClosed() { _isConnected = false; _isConnecting = false; clearQueue(); log.d("disconnected from service"); // Send disconnected message EventTaxiImpl.singleton().fire(ConnStatusEvent(status: ConnectionStatus.DISCONNECTED)); } // Connection closed (with error) void connectionClosedError(e) { _isConnected = false; _isConnecting = false; clearQueue(); log.d("disconnected from service with error ${e.toString()}"); // Send disconnected message EventTaxiImpl.singleton().fire(ConnStatusEvent(status: ConnectionStatus.DISCONNECTED)); } // Close connection void reset({bool suspend = false}){ suspended = suspend; if (_channel != null && _channel.sink != null) { _channel.sink.close(); _isConnected = false; _isConnecting = false; } } // Send message Future _send(String message) async { bool reset = false; try { if (_channel != null && _channel.sink != null && _isConnected) { _channel.sink.add(message); } else { reset = true; // Re-establish connection } } catch (e) { reset = true; } finally { if (reset) { // Reset queue item statuses _requestQueue.forEach((requestItem) { requestItem.isProcessing = false; }); if (!_isConnecting && !suspended) { initCommunication(); } } } } Future _onMessageReceived(dynamic message) async { if (suspended) { return; } await _lock.synchronized(() async { _isConnected = true; _isConnecting = false; // TODO showing full length for debugging log.d(message); //log.d("Received ${message.length > 30 ? message.substring(0, 30) : message}"); Map msg = await compute(decodeJson, message); // Determine response type if (msg.containsKey("subscribe") || msg.containsKey("error") && msg.containsKey("currency")) { // Subscribe response SubscribeResponse resp = await compute(subscribeResponseFromJson, msg); // Post to callbacks EventTaxiImpl.singleton().fire(SubscribeEvent(response: resp)); } else if (msg.containsKey("currency") && msg.containsKey("price") && msg.containsKey("btc")) { // Price info sent from server PriceResponse resp = PriceResponse.fromJson(msg); EventTaxiImpl.singleton().fire(PriceEvent(response: resp)); } else if (msg.containsKey("block") && msg.containsKey("ophash")) { // Operation pd.PascalOperation op = pd.PascalOperation.fromJson(msg); EventTaxiImpl.singleton().fire(NewOperationEvent(operation: op)); } return; }); } /* Send Request */ Future sendRequest(BaseRequest request) async { // We don't care about order or server response in these requests log.d("sending ${json.encode(request.toJson())}"); _send(await compute(encodeRequestItem, request)); } /* Enqueue Request */ void queueRequest(BaseRequest request) { log.d("requetest ${json.encode(request.toJson())}, q length: ${_requestQueue.length}"); _requestQueue.add(new RequestItem(request)); } /* Process Queue */ Future processQueue() async { await _lock.synchronized(() async { log.d("Request Queue length ${_requestQueue.length}"); if (_requestQueue != null && _requestQueue.length > 0) { RequestItem requestItem = _requestQueue.first; if (requestItem != null && !requestItem.isProcessing) { if (!_isConnected && !_isConnecting && !suspended) { initCommunication(); return; } else if (suspended) { return; } requestItem.isProcessing = true; String requestJson = await compute(encodeRequestItem, requestItem.request); log.d("Sending: $requestJson"); await _send(requestJson); } else if (requestItem != null && (DateTime .now() .difference(requestItem.expireDt) .inSeconds > RequestItem.EXPIRE_TIME_S)) { pop(); processQueue(); } } }); } // Queue Utilities void removeSubscribeFromQueue() { if (_requestQueue != null && _requestQueue.length > 0) { List toRemove = new List(); _requestQueue.forEach((requestItem) { if ((requestItem.request is SubscribeRequest) && !requestItem.isProcessing) { toRemove.add(requestItem); } }); toRemove.forEach((requestItem) { _requestQueue.remove(requestItem); }); } } RequestItem pop() { return _requestQueue.length > 0 ? _requestQueue.removeFirst() : null; } RequestItem peek() { return _requestQueue.length > 0 ? _requestQueue.first : null; } /// Clear entire queue, except for AccountsBalancesRequest void clearQueue() { List reQueue = List(); _requestQueue.forEach((requestItem) { }); _requestQueue.clear(); // Re-queue requests reQueue.forEach((requestItem) { requestItem.isProcessing = false; _requestQueue.add(requestItem); }); } Queue get requestQueue => _requestQueue; } ================================================ FILE: lib/service_locator.dart ================================================ import 'package:blaise_wallet_flutter/model/db/appdb.dart'; import 'package:blaise_wallet_flutter/network/ws_client.dart'; import 'package:blaise_wallet_flutter/util/pascal_util.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:get_it/get_it.dart'; import 'package:logger/logger.dart'; /// Service locator that provides various singletons GetIt sl = GetIt.instance; void setupServiceLocator() { sl.registerLazySingleton(() => PascalUtil()); sl.registerLazySingleton(() => Vault()); sl.registerLazySingleton(() => SharedPrefsUtil()); sl.registerLazySingleton(() => DBHelper()); sl.registerLazySingleton(() => WSClient()); sl.registerLazySingleton(() => Logger()); } ================================================ FILE: lib/store/account/account.dart ================================================ import 'dart:convert'; import 'dart:typed_data'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/update_history_event.dart'; import 'package:blaise_wallet_flutter/constants.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; import 'package:mobx/mobx.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:logger/logger.dart'; part 'account.g.dart'; class Account = AccountBase with _$Account; /// State for a specific account abstract class AccountBase with Store { Logger log = sl.get(); @observable bool operationsLoading = true; @observable RPCClient rpcClient; @observable PascalAccount account; @observable Currency accountBalance; @observable List operations; @observable List operationsToDisplay; @observable bool paid; AccountBase({@required this.rpcClient, @required this.account}) { this.accountBalance = account.balance; this.paid = false; } @action void decrementBalance(Currency delta) { this.account.balance -= delta; this.accountBalance -= delta; } @action void incrementBalance(Currency delta) { this.account.balance += delta; this.accountBalance += delta; } @action Future jRpcRequest(Map request) async { /// This custom request includes borrowed accounts request['id'] = this.rpcClient.id; String responseJson = await this.rpcClient.rpcPost(request); if (responseJson == null) { throw Exception('Did not receive a response'); } // Parse base response BaseResponse resp = BaseResponse.fromJson(json.decode(responseJson)); // Determine if error response if (resp is Map && resp.result.containsKey('code') && resp.result.containsKey('message')) { return ErrorResponse.fromJson(resp.result); } // Determine correct response type if (resp.result.containsKey('paid')) { this.paid = resp.result['paid']; resp.result.remove('paid'); } return PascalAccount.fromJson(resp.result); } @action Future updateAccount() async { // Update account information via getaccount, return false is unsuccessful true otherwise bool hasCustomDaemon = (await sl.get().getRpcUrl()) != AppConstants.DEFAULT_RPC_HTTP_URL; GetAccountRequest request = GetAccountRequest(account: this.account.account.account); Map requestJson = request.toJson(); if (!hasCustomDaemon && account.isBorrowed) { requestJson['params'].putIfAbsent('borrowed', () => true); } RPCResponse resp = await this.jRpcRequest(requestJson); if (resp.isError) { return false; } PascalAccount updatedAccount = resp; // See if this accounts borrowed status has changed if (this.account.isBorrowed) { String curAcctPubkey = PublicKeyCoder().encodeToBase58(updatedAccount.encPubkey); String curWalletPubkey = PublicKeyCoder().encodeToBase58(walletState.publicKey); if (curAcctPubkey != curWalletPubkey) { updatedAccount.isBorrowed = true; } } this.account = updatedAccount; this.accountBalance = updatedAccount.balance; return true; } @action void addNewOperation(PascalOperation op) { /// Add operation if: /// 1) We don't have it /// 2) We do have it, but it has since been confirmed /// 3) This account is not borrowed if (this.operations == null) { return; } if (!this.operations.contains(op) || this.operations.contains(op) && this.operations.firstWhere((nOp) => nOp == op).maturation == null && op.maturation != null) { if (this.operations.contains(op)) { this.operations.remove(op); } this.operations.insert(0, op); // Re-sort this list // Remove pendings List pendings = this.operations.where((op) => op.maturation == null).toList(); this.operations.removeWhere((op) => op.maturation == null); // Sort by time this.operations.sort((a, b) => b.time.compareTo(a.time)); this.operations.insertAll(0, pendings); // Update to display this.operationsToDisplay = getOperationsToDisplay(); // Refresh total wallet balance EventTaxiImpl.singleton().fire(UpdateHistoryEvent()); } } @action void diffAndSortOperations(List newOperationList) { // Get pendings List pendingOperations = newOperationList.where((op) => op.maturation == null).toList(); // Remove pendings that have since been confirmed this.operations.removeWhere((op) => op.maturation == null && !pendingOperations.contains(op)); // Add all operations not already in the list and not pending this.operations.addAll(newOperationList.where((op) => (!this.operations.contains(op)) && op.maturation != null)); // Sort by time this.operations.sort((a, b) => b.time.compareTo(a.time)); // Add all pendings this.operations.removeWhere((op) => pendingOperations.contains(op)); this.operations.insertAll(0, pendingOperations); } @action Future getAccountOperations() async { GetAccountOperationsRequest request = GetAccountOperationsRequest(account: account.account.account, start: -1); RPCResponse resp = await this.rpcClient.makeRpcRequest(request); if (resp.isError) { ErrorResponse err = resp; log.e("getaccountoperations resulted in error ${err.errorMessage}"); return null; } OperationsResponse opResp = resp; if (this.operations == null) { this.operations = opResp.operations; this.operationsToDisplay = getOperationsToDisplay(); } else { // Diff and update operations this.diffAndSortOperations(opResp.operations); this.operationsToDisplay = getOperationsToDisplay(); EventTaxiImpl.singleton().fire(UpdateHistoryEvent()); } this.operationsLoading = false; } @action List getOperationsToDisplay() { return this.operations.where((op) => shouldDisplayOperation(op)).toList(); } @action bool shouldDisplayOperation(PascalOperation op) { if (op.optype == OpType.TRANSACTION) { if (op.senders.length > 0 && op.receivers.length > 0 && op.senders[0].nOperation == 1 && op.receivers[0].receivingAccount == AccountNumber("1000")) { return false; } return true; } else if (op.optype == OpType.CHANGE_ACCOUNT_INFO) { if (op.changers.length > 0 && op.changers[0].newName != null) { return true; } } else if (op.optype == OpType.LIST_FORSALE) { if (op.changers.length >0 && op.changers[0].sellerAccount != null && op.changers[0].accountPrice != null) { return true; } } else if (op.optype == OpType.DELIST_FORSALE) { return true; } return false; } @action bool hasOperationsToDisplay() { if (this.operationsLoading || this.operations == null || this.operations.length == 0) { return false; } for (PascalOperation op in this.operations) { if (shouldDisplayOperation(op)) { return true; } } return false; } @action void changeAccountState(AccountState accountState) { this.account.state = accountState; } @action Future doSend({@required String amount, @required String destination, Currency fee, Uint8List encryptedPayload, String payload = ""}) async { fee = fee == null ? Currency('0') : fee; // Construct send TransactionOperation op = TransactionOperation( sender: this.account.account, target: AccountNumber(destination), amount: Currency(amount) ) ..withNOperation(this.account.nOperation + 1) ..withPayload(encryptedPayload == null ? PDUtil.stringToBytesUtf8(payload) : encryptedPayload) ..withFee(fee) ..sign(PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes(await sl.get().getPrivateKey()))); // Construct execute request ExecuteOperationsRequest request = ExecuteOperationsRequest( rawOperations: PDUtil.byteToHex(RawOperationCoder.encodeToBytes(op)) ); // Make request RPCResponse resp = await this.rpcClient.makeRpcRequest(request); if (resp.isError) { return resp; } OperationsResponse opResp = resp; if (opResp.operations[0].valid == null || opResp.operations[0].valid) { this.decrementBalance(Currency(amount)); this.account.nOperation++; this.getAccountOperations(); } return resp; } @action Future transferAccount(String strPubkey, {Currency fee}) async { PublicKey newPubkey; try { newPubkey = PublicKeyCoder().decodeFromBase58(strPubkey); } catch (e) { try { newPubkey = PublicKeyCoder().decodeFromBytes(PDUtil.hexToBytes(strPubkey)); } catch (e) { throw Exception('Invalid Public key'); } } fee = fee == null ? Currency('0') : fee; // Construct transfer ChangeKeyOperation op = ChangeKeyOperation( signer: account.account, newPublicKey: newPubkey ) ..withNOperation(account.nOperation + 1) ..withPayload(PDUtil.stringToBytesUtf8("")) ..withFee(fee) ..sign(PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes(await sl.get().getPrivateKey()))); // Construct execute request ExecuteOperationsRequest request = ExecuteOperationsRequest( rawOperations: PDUtil.byteToHex(RawOperationCoder.encodeToBytes(op)) ); // Make request RPCResponse resp = await this.rpcClient.makeRpcRequest(request); return resp; } @action Future changeAccountName(AccountName newName, {Currency fee}) async { fee = fee == null ? Currency('0') : fee; // Construct name change ChangeAccountInfoOperation op = ChangeAccountInfoOperation( accountSigner: account.account, targetSigner: account.account, newName: newName, withNewName: true ) ..withNOperation(account.nOperation + 1) ..withPayload(PDUtil.stringToBytesUtf8("")) ..withFee(fee) ..sign(PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes(await sl.get().getPrivateKey()))); // Construct execute request ExecuteOperationsRequest request = ExecuteOperationsRequest( rawOperations: PDUtil.byteToHex(RawOperationCoder.encodeToBytes(op)) ); // Make request RPCResponse resp = await this.rpcClient.makeRpcRequest(request); if (resp.isError) { return resp; } OperationsResponse opResp = resp; if (opResp.operations[0].valid == null || opResp.operations[0].valid) { this.account.nOperation++; this.getAccountOperations(); } return resp; } @action Future listAccountForSale(Currency price, AccountNumber accountToPay, {PublicKey newPubKey, Currency fee}) async { fee = fee == null ? Currency('0') : fee; // Construct list for sale ListForSaleOperation op = ListForSaleOperation( accountSigner: account.account, targetSigner: account.account, price: price, accountToPay: accountToPay, newPublicKey: newPubKey ) ..withNOperation(account.nOperation + 1) ..withPayload(PDUtil.stringToBytesUtf8("")) ..withFee(fee) ..sign(PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes(await sl.get().getPrivateKey()))); // Execute request ExecuteOperationsRequest request = ExecuteOperationsRequest( rawOperations: PDUtil.byteToHex(RawOperationCoder.encodeToBytes(op)) ); // Make request RPCResponse resp = await this.rpcClient.makeRpcRequest(request); if (resp.isError) { return resp; } OperationsResponse opResp = resp; if (opResp.operations[0].valid == null || opResp.operations[0].valid) { this.account.nOperation++; this.getAccountOperations(); } return resp; } @action Future delistAccountForSale({Currency fee}) async { fee = fee == null ? Currency('0') : fee; // Construct list for sale DeListForSaleOperation op = DeListForSaleOperation( accountSigner: account.account, targetSigner: account.account ) ..withNOperation(account.nOperation + 1) ..withPayload(PDUtil.stringToBytesUtf8("")) ..withFee(fee) ..sign(PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes(await sl.get().getPrivateKey()))); // Execute request ExecuteOperationsRequest request = ExecuteOperationsRequest( rawOperations: PDUtil.byteToHex(RawOperationCoder.encodeToBytes(op)) ); // Make request RPCResponse resp = await this.rpcClient.makeRpcRequest(request); if (resp.isError) { return resp; } OperationsResponse opResp = resp; if (opResp.operations[0].valid == null || opResp.operations[0].valid) { this.account.nOperation++; this.getAccountOperations(); } return resp; } @action Future encryptPayloadEcies(String payload, AccountNumber account) async { try { // Do getaccountrequest to get the public key of this account GetAccountRequest req = GetAccountRequest( account: account.account ); // Execute request RPCResponse resp = await rpcClient.makeRpcRequest(req); if (resp.isError) { return null; } PascalAccount receiverAcct = resp; return EciesCrypt.encrypt(PDUtil.stringToBytesUtf8(payload), receiverAcct.encPubkey); } catch (e) { return null; } } } ================================================ FILE: lib/store/account/account.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'account.dart'; // ************************************************************************** // StoreGenerator // ************************************************************************** // ignore_for_file: non_constant_identifier_names, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic mixin _$Account on AccountBase, Store { final _$operationsLoadingAtom = Atom(name: 'AccountBase.operationsLoading'); @override bool get operationsLoading { _$operationsLoadingAtom.context.enforceReadPolicy(_$operationsLoadingAtom); _$operationsLoadingAtom.reportObserved(); return super.operationsLoading; } @override set operationsLoading(bool value) { _$operationsLoadingAtom.context.conditionallyRunInAction(() { super.operationsLoading = value; _$operationsLoadingAtom.reportChanged(); }, _$operationsLoadingAtom, name: '${_$operationsLoadingAtom.name}_set'); } final _$rpcClientAtom = Atom(name: 'AccountBase.rpcClient'); @override RPCClient get rpcClient { _$rpcClientAtom.context.enforceReadPolicy(_$rpcClientAtom); _$rpcClientAtom.reportObserved(); return super.rpcClient; } @override set rpcClient(RPCClient value) { _$rpcClientAtom.context.conditionallyRunInAction(() { super.rpcClient = value; _$rpcClientAtom.reportChanged(); }, _$rpcClientAtom, name: '${_$rpcClientAtom.name}_set'); } final _$accountAtom = Atom(name: 'AccountBase.account'); @override PascalAccount get account { _$accountAtom.context.enforceReadPolicy(_$accountAtom); _$accountAtom.reportObserved(); return super.account; } @override set account(PascalAccount value) { _$accountAtom.context.conditionallyRunInAction(() { super.account = value; _$accountAtom.reportChanged(); }, _$accountAtom, name: '${_$accountAtom.name}_set'); } final _$accountBalanceAtom = Atom(name: 'AccountBase.accountBalance'); @override Currency get accountBalance { _$accountBalanceAtom.context.enforceReadPolicy(_$accountBalanceAtom); _$accountBalanceAtom.reportObserved(); return super.accountBalance; } @override set accountBalance(Currency value) { _$accountBalanceAtom.context.conditionallyRunInAction(() { super.accountBalance = value; _$accountBalanceAtom.reportChanged(); }, _$accountBalanceAtom, name: '${_$accountBalanceAtom.name}_set'); } final _$operationsAtom = Atom(name: 'AccountBase.operations'); @override List get operations { _$operationsAtom.context.enforceReadPolicy(_$operationsAtom); _$operationsAtom.reportObserved(); return super.operations; } @override set operations(List value) { _$operationsAtom.context.conditionallyRunInAction(() { super.operations = value; _$operationsAtom.reportChanged(); }, _$operationsAtom, name: '${_$operationsAtom.name}_set'); } final _$operationsToDisplayAtom = Atom(name: 'AccountBase.operationsToDisplay'); @override List get operationsToDisplay { _$operationsToDisplayAtom.context .enforceReadPolicy(_$operationsToDisplayAtom); _$operationsToDisplayAtom.reportObserved(); return super.operationsToDisplay; } @override set operationsToDisplay(List value) { _$operationsToDisplayAtom.context.conditionallyRunInAction(() { super.operationsToDisplay = value; _$operationsToDisplayAtom.reportChanged(); }, _$operationsToDisplayAtom, name: '${_$operationsToDisplayAtom.name}_set'); } final _$paidAtom = Atom(name: 'AccountBase.paid'); @override bool get paid { _$paidAtom.context.enforceReadPolicy(_$paidAtom); _$paidAtom.reportObserved(); return super.paid; } @override set paid(bool value) { _$paidAtom.context.conditionallyRunInAction(() { super.paid = value; _$paidAtom.reportChanged(); }, _$paidAtom, name: '${_$paidAtom.name}_set'); } final _$jRpcRequestAsyncAction = AsyncAction('jRpcRequest'); @override Future jRpcRequest(Map request) { return _$jRpcRequestAsyncAction.run(() => super.jRpcRequest(request)); } final _$updateAccountAsyncAction = AsyncAction('updateAccount'); @override Future updateAccount() { return _$updateAccountAsyncAction.run(() => super.updateAccount()); } final _$getAccountOperationsAsyncAction = AsyncAction('getAccountOperations'); @override Future getAccountOperations() { return _$getAccountOperationsAsyncAction .run(() => super.getAccountOperations()); } final _$doSendAsyncAction = AsyncAction('doSend'); @override Future doSend( {@required String amount, @required String destination, Currency fee, Uint8List encryptedPayload, String payload = ""}) { return _$doSendAsyncAction.run(() => super.doSend( amount: amount, destination: destination, fee: fee, encryptedPayload: encryptedPayload, payload: payload)); } final _$transferAccountAsyncAction = AsyncAction('transferAccount'); @override Future transferAccount(String strPubkey, {Currency fee}) { return _$transferAccountAsyncAction .run(() => super.transferAccount(strPubkey, fee: fee)); } final _$changeAccountNameAsyncAction = AsyncAction('changeAccountName'); @override Future changeAccountName(AccountName newName, {Currency fee}) { return _$changeAccountNameAsyncAction .run(() => super.changeAccountName(newName, fee: fee)); } final _$listAccountForSaleAsyncAction = AsyncAction('listAccountForSale'); @override Future listAccountForSale( Currency price, AccountNumber accountToPay, {PublicKey newPubKey, Currency fee}) { return _$listAccountForSaleAsyncAction.run(() => super.listAccountForSale( price, accountToPay, newPubKey: newPubKey, fee: fee)); } final _$delistAccountForSaleAsyncAction = AsyncAction('delistAccountForSale'); @override Future delistAccountForSale({Currency fee}) { return _$delistAccountForSaleAsyncAction .run(() => super.delistAccountForSale(fee: fee)); } final _$encryptPayloadEciesAsyncAction = AsyncAction('encryptPayloadEcies'); @override Future encryptPayloadEcies(String payload, AccountNumber account) { return _$encryptPayloadEciesAsyncAction .run(() => super.encryptPayloadEcies(payload, account)); } final _$AccountBaseActionController = ActionController(name: 'AccountBase'); @override void decrementBalance(Currency delta) { final _$actionInfo = _$AccountBaseActionController.startAction(); try { return super.decrementBalance(delta); } finally { _$AccountBaseActionController.endAction(_$actionInfo); } } @override void incrementBalance(Currency delta) { final _$actionInfo = _$AccountBaseActionController.startAction(); try { return super.incrementBalance(delta); } finally { _$AccountBaseActionController.endAction(_$actionInfo); } } @override void addNewOperation(PascalOperation op) { final _$actionInfo = _$AccountBaseActionController.startAction(); try { return super.addNewOperation(op); } finally { _$AccountBaseActionController.endAction(_$actionInfo); } } @override void diffAndSortOperations(List newOperationList) { final _$actionInfo = _$AccountBaseActionController.startAction(); try { return super.diffAndSortOperations(newOperationList); } finally { _$AccountBaseActionController.endAction(_$actionInfo); } } @override List getOperationsToDisplay() { final _$actionInfo = _$AccountBaseActionController.startAction(); try { return super.getOperationsToDisplay(); } finally { _$AccountBaseActionController.endAction(_$actionInfo); } } @override bool shouldDisplayOperation(PascalOperation op) { final _$actionInfo = _$AccountBaseActionController.startAction(); try { return super.shouldDisplayOperation(op); } finally { _$AccountBaseActionController.endAction(_$actionInfo); } } @override bool hasOperationsToDisplay() { final _$actionInfo = _$AccountBaseActionController.startAction(); try { return super.hasOperationsToDisplay(); } finally { _$AccountBaseActionController.endAction(_$actionInfo); } } @override void changeAccountState(AccountState accountState) { final _$actionInfo = _$AccountBaseActionController.startAction(); try { return super.changeAccountState(accountState); } finally { _$AccountBaseActionController.endAction(_$actionInfo); } } @override String toString() { final string = 'operationsLoading: ${operationsLoading.toString()},rpcClient: ${rpcClient.toString()},account: ${account.toString()},accountBalance: ${accountBalance.toString()},operations: ${operations.toString()},operationsToDisplay: ${operationsToDisplay.toString()},paid: ${paid.toString()}'; return '{$string}'; } } ================================================ FILE: lib/store/wallet/wallet.dart ================================================ import 'dart:convert'; import 'dart:ui'; import 'package:blaise_wallet_flutter/constants.dart'; import 'package:blaise_wallet_flutter/model/available_currency.dart'; import 'package:blaise_wallet_flutter/network/http_client.dart'; import 'package:blaise_wallet_flutter/network/model/request/fcm_delete_account_request.dart'; import 'package:blaise_wallet_flutter/network/model/request/fcm_update_bulk_request.dart'; import 'package:blaise_wallet_flutter/network/model/request/fcm_update_request.dart'; import 'package:blaise_wallet_flutter/network/model/request/subscribe_request.dart'; import 'package:blaise_wallet_flutter/network/model/response/accounts_response_borrowed.dart'; import 'package:blaise_wallet_flutter/network/model/response/borrow_response.dart'; import 'package:blaise_wallet_flutter/network/model/response/subscribe_response.dart'; import 'package:blaise_wallet_flutter/network/ws_client.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/store/account/account.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:mobx/mobx.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:logger/logger.dart'; import 'package:intl/intl.dart'; import 'package:decimal/decimal.dart'; part 'wallet.g.dart'; class Wallet = WalletBase with _$Wallet; /// The global wallet state and mutation actions abstract class WalletBase with Store { final Currency NO_FEE = Currency('0'); final Currency MIN_FEE = Currency('0.0001'); final Logger log = sl.get(); @observable bool walletLoading = true; @observable Currency totalWalletBalance = Currency('0'); @observable List walletAccounts = []; @observable RPCClient rpcClient; @observable PublicKey publicKey; @observable Map accountStateMap = Map(); @observable double localCurrencyPrice; @observable double btcPrice; @observable String uuid; @observable AccountNumber activeAccount; @observable BorrowResponse borrowedAccount; @observable bool isBorrowEligible = false; @observable bool hasExceededBorrowLimit = false; @observable bool hasReceivedSubscribeResponse = false; @action Future initializeRpc() async { this.rpcClient = RPCClient(rpcAddress: await sl.get().getRpcUrl()); } @action Future getBalanceAndInsertBorrowed() async { if (borrowedAccount != null && !this.walletAccounts.contains(borrowedAccount.account)) { RPCResponse resp = await rpcClient.makeRpcRequest( GetAccountRequest(account: borrowedAccount.account.account)); if (resp is PascalAccount) { resp.isBorrowed = true; this .walletAccounts .removeWhere((acct) => acct.account == borrowedAccount.account); this.walletAccounts.add(resp); return resp; } } return null; } @action Future updateBorrowed() async { BorrowResponse resp = await HttpAPI.getBorrowed( PublicKeyCoder().encodeToBase58(this.publicKey)); if (resp == null) { isBorrowEligible = false; } else if (resp.account == null) { isBorrowEligible = true; borrowedAccount = null; } else { isBorrowEligible = false; borrowedAccount = resp; return await this.getBalanceAndInsertBorrowed(); } return null; } @action Future initiateBorrow() async { if (isBorrowEligible) { BorrowResponse resp = await HttpAPI.borrowAccount( PublicKeyCoder().encodeToBase58(this.publicKey)); if (resp == null) { isBorrowEligible = false; } else { isBorrowEligible = false; borrowedAccount = resp; return await this.getBalanceAndInsertBorrowed(); } } return null; } @action Future findAccountsRequest(FindAccountsRequest request) async { /// This custom request includes borrowed accounts request.id = this.rpcClient.id; String responseJson = await this.rpcClient.rpcPost(request.toJson()); if (responseJson == null) { throw Exception('Did not receive a response'); } // Parse base response BaseResponse resp = BaseResponse.fromJson(json.decode(responseJson)); // Determine if error response if (resp is Map && resp.result.containsKey('code') && resp.result.containsKey('message')) { return ErrorResponse.fromJson(resp.result); } // Determine correct response type return AccountsResponseBorrowed.fromJson(json.decode(responseJson)); } @action Future> findAccountsWithNameLike(String name) async { // TODO - this RPC request is broken when exact is set to false FindAccountsRequest findAccountsRequest = FindAccountsRequest(name: name, exact: true); RPCResponse resp = await this.rpcClient.makeRpcRequest(findAccountsRequest); if (resp.isError) { ErrorResponse err = resp; log.d("findaccounts returned error ${err.errorMessage}"); return null; } AccountsResponse accountsResponse = resp; return accountsResponse.accounts; } @action Future loadWallet() async { if (this.publicKey == null) { PrivateKey privKey = PrivateKeyCoder().decodeFromBytes( PDUtil.hexToBytes(await sl.get().getPrivateKey())); this.publicKey = Keys.fromPrivateKey(privKey).publicKey; } /// Load the initial wallet, returns false if it loaded with an error if (this.rpcClient == null) { await initializeRpc(); } // Get total balance and list of accounts // TODO - pagination? FindAccountsRequest findAccountsRequest = FindAccountsRequest( b58Pubkey: PublicKeyCoder().encodeToBase58(this.publicKey), max: 25); RPCResponse resp = await this.findAccountsRequest(findAccountsRequest); if (resp.isError) { ErrorResponse err = resp; log.d("findaccounts returned error ${err.errorMessage}"); return false; } AccountsResponseBorrowed accountsResponse = resp; bool hasCustomDaemon = (await sl.get().getRpcUrl()) != AppConstants.DEFAULT_RPC_HTTP_URL; if (hasCustomDaemon) { PascalAccount borrowedAccount = this .walletAccounts .firstWhere((acct) => acct.isBorrowed, orElse: () => null); if (borrowedAccount != null && !accountsResponse.accounts.contains(borrowedAccount)) { accountsResponse.accounts.add(borrowedAccount); } } else if (accountsResponse.borrowedAccount != null) { accountsResponse.accounts .where((acct) => acct.account == accountsResponse.borrowedAccount.account) .forEach((acct) { acct.isBorrowed = true; }); this.borrowedAccount = accountsResponse.borrowedAccount; this.isBorrowEligible = false; } else if (accountsResponse.borrowedAccount == null) { this.isBorrowEligible = true; this.borrowedAccount = null; } if (accountsResponse.borrowEligible != null) { this.hasExceededBorrowLimit = !accountsResponse.borrowEligible; } // Check for freepasa account AccountNumber freepasaAccount = await sl.get().getFreepasaAccount(); if (freepasaAccount != null) { PascalAccount fpasaAccount = PascalAccount( account: freepasaAccount, balance: Currency('0'), isFreepasa: true); fpasaAccount.name = AccountName(""); if (!accountsResponse.accounts.contains(fpasaAccount)) { accountsResponse.accounts.add(fpasaAccount); } } this.walletAccounts = accountsResponse.accounts; Currency totalBalance = Currency('0'); this.walletAccounts.forEach((acct) { totalBalance += acct.balance; }); if (hasCustomDaemon) { this.updateBorrowed(); } this.totalWalletBalance = totalBalance; this.walletLoading = false; return true; } @action Account getAccountState(PascalAccount account) { accountStateMap.putIfAbsent(account.account.account, () => Account(rpcClient: this.rpcClient, account: account)); return accountStateMap[account.account.account]; } @action void changeRpcUrl(String rpcUrl) { this.rpcClient = RPCClient(rpcAddress: rpcUrl); accountStateMap.forEach((k, v) { v.rpcClient = this.rpcClient; }); } @action void removeAccount(PascalAccount account) { // Remove account from wallet this.totalWalletBalance -= account.balance; this.walletAccounts.removeWhere((acct) => acct == account); this.accountStateMap.remove(account.account.account); this.fcmDeleteAccount(account.account); } @action void updateAccountName(PascalAccount account, AccountName newName) { this .walletAccounts .where((acct) => acct.account == account.account) .forEach((pa) { pa.name = newName; }); if (this.accountStateMap.containsKey(account.account.account)) { this.accountStateMap[account.account.account].account.name = newName; } } @action List getNonzeroBalanceAccounts() { if (walletLoading || walletAccounts.length == 0) { return []; } List ret = []; walletAccounts.forEach((acct) { if (acct.balance > Currency('0')) { ret.add(acct); } }); return ret; } @action bool shouldHaveFee() { return true; /* for (Account accountState in accountStateMap.values) { if (accountState.operations == null) { continue; } if (accountState.operations.indexWhere((operation) => operation.maturation == null && operation.fee == NO_FEE && operation.signerAccount == accountState.account.account) > -1) { return true; } } return false;*/ } @action String getLocalCurrencyDisplay( {AvailableCurrency currency, Currency amount, int decimalDigits}) { if (localCurrencyPrice == null) { return null; } currency = currency ?? AvailableCurrency(AvailableCurrencyEnum.USD); Decimal converted = Decimal.parse(localCurrencyPrice.toString()) * Decimal.parse(amount.toStringOpt()); return NumberFormat.currency( locale: currency.getLocale().toString(), name: currency.getIso4217Code(), symbol: currency.getCurrencySymbol(), decimalDigits: decimalDigits) .format(converted.toDouble()); } /// Websocket Actions // Websocket Methods @action void disconnect() { sl.get().reset(suspend: true); } @action void reconnect() { sl.get().initCommunication(unsuspend: true); } @action Future requestUpdate() async { if (this.publicKey == null) { PrivateKey privKey = PrivateKeyCoder().decodeFromBytes( PDUtil.hexToBytes(await sl.get().getPrivateKey())); this.publicKey = Keys.fromPrivateKey(privKey).publicKey; } String uuid = await sl.get().getUuid(); AvailableCurrency curCurrency = await sl.get().getCurrency(Locale("en", "US")); String fcmToken = await FirebaseMessaging.instance.getToken(); bool notificationsEnabled = await sl.get().getNotificationsOn(); sl.get().clearQueue(); sl.get().queueRequest(SubscribeRequest( currency: curCurrency.getIso4217Code(), uuid: uuid, account: this.activeAccount == null ? null : this.activeAccount.account, fcmToken: fcmToken, notificationEnabled: notificationsEnabled, b58pubkey: PublicKeyCoder().encodeToBase58(this.publicKey))); sl.get().processQueue(); } @action Future fcmUpdate(AccountNumber account) async { if (this.publicKey == null) { PrivateKey privKey = PrivateKeyCoder().decodeFromBytes( PDUtil.hexToBytes(await sl.get().getPrivateKey())); this.publicKey = Keys.fromPrivateKey(privKey).publicKey; } bool enabled = await sl.get().getNotificationsOn(); String fcmToken = await FirebaseMessaging.instance.getToken(); sl.get().sendRequest(FcmUpdateRequest( account: account.account, enabled: enabled, fcmToken: fcmToken, b58pubkey: PublicKeyCoder().encodeToBase58(this.publicKey))); } @action Future fcmDeleteAccount(AccountNumber account) async { String fcmToken = await FirebaseMessaging.instance.getToken(); sl.get().sendRequest( FcmDeleteAccountRequest(account: account.account, fcmToken: fcmToken)); } @action Future fcmUpdateBulk({bool forceDisable = false}) async { if (this.publicKey == null) { PrivateKey privKey = PrivateKeyCoder().decodeFromBytes( PDUtil.hexToBytes(await sl.get().getPrivateKey())); this.publicKey = Keys.fromPrivateKey(privKey).publicKey; } if (walletLoading) { return; } bool enabled = forceDisable ? false : await sl.get().getNotificationsOn(); String fcmToken = await FirebaseMessaging.instance.getToken(); List accounts = []; for (PascalAccount acct in walletAccounts) { if (!acct.isBorrowed) { accounts.add(acct.account.account); } } sl.get().sendRequest(FcmUpdateBulkRequest( accounts: accounts, enabled: enabled, fcmToken: fcmToken, b58pubkey: PublicKeyCoder().encodeToBase58(this.publicKey))); } @action void addNewOp(PascalOperation op) { this.accountStateMap.forEach((k, acct) { if (op.senders.isNotEmpty && op.senders[0].sendingAccount == acct.account.account) { acct.addNewOperation(op); } else if (op.receivers.isNotEmpty && op.receivers[0].receivingAccount == acct.account.account) { acct.addNewOperation(op); } }); } @action void reset() { // Reset all properties (for when logging out, etc) this.fcmUpdateBulk(forceDisable: true); this.walletLoading = true; this.totalWalletBalance = Currency('0'); this.rpcClient = null; this.walletAccounts = []; this.publicKey = null; this.accountStateMap = Map(); this.borrowedAccount = null; this.isBorrowEligible = false; this.hasExceededBorrowLimit = false; } } ================================================ FILE: lib/store/wallet/wallet.g.dart ================================================ // GENERATED CODE - DO NOT MODIFY BY HAND part of 'wallet.dart'; // ************************************************************************** // StoreGenerator // ************************************************************************** // ignore_for_file: non_constant_identifier_names, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic mixin _$Wallet on WalletBase, Store { final _$walletLoadingAtom = Atom(name: 'WalletBase.walletLoading'); @override bool get walletLoading { _$walletLoadingAtom.context.enforceReadPolicy(_$walletLoadingAtom); _$walletLoadingAtom.reportObserved(); return super.walletLoading; } @override set walletLoading(bool value) { _$walletLoadingAtom.context.conditionallyRunInAction(() { super.walletLoading = value; _$walletLoadingAtom.reportChanged(); }, _$walletLoadingAtom, name: '${_$walletLoadingAtom.name}_set'); } final _$totalWalletBalanceAtom = Atom(name: 'WalletBase.totalWalletBalance'); @override Currency get totalWalletBalance { _$totalWalletBalanceAtom.context .enforceReadPolicy(_$totalWalletBalanceAtom); _$totalWalletBalanceAtom.reportObserved(); return super.totalWalletBalance; } @override set totalWalletBalance(Currency value) { _$totalWalletBalanceAtom.context.conditionallyRunInAction(() { super.totalWalletBalance = value; _$totalWalletBalanceAtom.reportChanged(); }, _$totalWalletBalanceAtom, name: '${_$totalWalletBalanceAtom.name}_set'); } final _$walletAccountsAtom = Atom(name: 'WalletBase.walletAccounts'); @override List get walletAccounts { _$walletAccountsAtom.context.enforceReadPolicy(_$walletAccountsAtom); _$walletAccountsAtom.reportObserved(); return super.walletAccounts; } @override set walletAccounts(List value) { _$walletAccountsAtom.context.conditionallyRunInAction(() { super.walletAccounts = value; _$walletAccountsAtom.reportChanged(); }, _$walletAccountsAtom, name: '${_$walletAccountsAtom.name}_set'); } final _$rpcClientAtom = Atom(name: 'WalletBase.rpcClient'); @override RPCClient get rpcClient { _$rpcClientAtom.context.enforceReadPolicy(_$rpcClientAtom); _$rpcClientAtom.reportObserved(); return super.rpcClient; } @override set rpcClient(RPCClient value) { _$rpcClientAtom.context.conditionallyRunInAction(() { super.rpcClient = value; _$rpcClientAtom.reportChanged(); }, _$rpcClientAtom, name: '${_$rpcClientAtom.name}_set'); } final _$publicKeyAtom = Atom(name: 'WalletBase.publicKey'); @override PublicKey get publicKey { _$publicKeyAtom.context.enforceReadPolicy(_$publicKeyAtom); _$publicKeyAtom.reportObserved(); return super.publicKey; } @override set publicKey(PublicKey value) { _$publicKeyAtom.context.conditionallyRunInAction(() { super.publicKey = value; _$publicKeyAtom.reportChanged(); }, _$publicKeyAtom, name: '${_$publicKeyAtom.name}_set'); } final _$accountStateMapAtom = Atom(name: 'WalletBase.accountStateMap'); @override Map get accountStateMap { _$accountStateMapAtom.context.enforceReadPolicy(_$accountStateMapAtom); _$accountStateMapAtom.reportObserved(); return super.accountStateMap; } @override set accountStateMap(Map value) { _$accountStateMapAtom.context.conditionallyRunInAction(() { super.accountStateMap = value; _$accountStateMapAtom.reportChanged(); }, _$accountStateMapAtom, name: '${_$accountStateMapAtom.name}_set'); } final _$localCurrencyPriceAtom = Atom(name: 'WalletBase.localCurrencyPrice'); @override double get localCurrencyPrice { _$localCurrencyPriceAtom.context .enforceReadPolicy(_$localCurrencyPriceAtom); _$localCurrencyPriceAtom.reportObserved(); return super.localCurrencyPrice; } @override set localCurrencyPrice(double value) { _$localCurrencyPriceAtom.context.conditionallyRunInAction(() { super.localCurrencyPrice = value; _$localCurrencyPriceAtom.reportChanged(); }, _$localCurrencyPriceAtom, name: '${_$localCurrencyPriceAtom.name}_set'); } final _$btcPriceAtom = Atom(name: 'WalletBase.btcPrice'); @override double get btcPrice { _$btcPriceAtom.context.enforceReadPolicy(_$btcPriceAtom); _$btcPriceAtom.reportObserved(); return super.btcPrice; } @override set btcPrice(double value) { _$btcPriceAtom.context.conditionallyRunInAction(() { super.btcPrice = value; _$btcPriceAtom.reportChanged(); }, _$btcPriceAtom, name: '${_$btcPriceAtom.name}_set'); } final _$uuidAtom = Atom(name: 'WalletBase.uuid'); @override String get uuid { _$uuidAtom.context.enforceReadPolicy(_$uuidAtom); _$uuidAtom.reportObserved(); return super.uuid; } @override set uuid(String value) { _$uuidAtom.context.conditionallyRunInAction(() { super.uuid = value; _$uuidAtom.reportChanged(); }, _$uuidAtom, name: '${_$uuidAtom.name}_set'); } final _$activeAccountAtom = Atom(name: 'WalletBase.activeAccount'); @override AccountNumber get activeAccount { _$activeAccountAtom.context.enforceReadPolicy(_$activeAccountAtom); _$activeAccountAtom.reportObserved(); return super.activeAccount; } @override set activeAccount(AccountNumber value) { _$activeAccountAtom.context.conditionallyRunInAction(() { super.activeAccount = value; _$activeAccountAtom.reportChanged(); }, _$activeAccountAtom, name: '${_$activeAccountAtom.name}_set'); } final _$borrowedAccountAtom = Atom(name: 'WalletBase.borrowedAccount'); @override BorrowResponse get borrowedAccount { _$borrowedAccountAtom.context.enforceReadPolicy(_$borrowedAccountAtom); _$borrowedAccountAtom.reportObserved(); return super.borrowedAccount; } @override set borrowedAccount(BorrowResponse value) { _$borrowedAccountAtom.context.conditionallyRunInAction(() { super.borrowedAccount = value; _$borrowedAccountAtom.reportChanged(); }, _$borrowedAccountAtom, name: '${_$borrowedAccountAtom.name}_set'); } final _$isBorrowEligibleAtom = Atom(name: 'WalletBase.isBorrowEligible'); @override bool get isBorrowEligible { _$isBorrowEligibleAtom.context.enforceReadPolicy(_$isBorrowEligibleAtom); _$isBorrowEligibleAtom.reportObserved(); return super.isBorrowEligible; } @override set isBorrowEligible(bool value) { _$isBorrowEligibleAtom.context.conditionallyRunInAction(() { super.isBorrowEligible = value; _$isBorrowEligibleAtom.reportChanged(); }, _$isBorrowEligibleAtom, name: '${_$isBorrowEligibleAtom.name}_set'); } final _$hasExceededBorrowLimitAtom = Atom(name: 'WalletBase.hasExceededBorrowLimit'); @override bool get hasExceededBorrowLimit { _$hasExceededBorrowLimitAtom.context .enforceReadPolicy(_$hasExceededBorrowLimitAtom); _$hasExceededBorrowLimitAtom.reportObserved(); return super.hasExceededBorrowLimit; } @override set hasExceededBorrowLimit(bool value) { _$hasExceededBorrowLimitAtom.context.conditionallyRunInAction(() { super.hasExceededBorrowLimit = value; _$hasExceededBorrowLimitAtom.reportChanged(); }, _$hasExceededBorrowLimitAtom, name: '${_$hasExceededBorrowLimitAtom.name}_set'); } final _$hasReceivedSubscribeResponseAtom = Atom(name: 'WalletBase.hasReceivedSubscribeResponse'); @override bool get hasReceivedSubscribeResponse { _$hasReceivedSubscribeResponseAtom.context .enforceReadPolicy(_$hasReceivedSubscribeResponseAtom); _$hasReceivedSubscribeResponseAtom.reportObserved(); return super.hasReceivedSubscribeResponse; } @override set hasReceivedSubscribeResponse(bool value) { _$hasReceivedSubscribeResponseAtom.context.conditionallyRunInAction(() { super.hasReceivedSubscribeResponse = value; _$hasReceivedSubscribeResponseAtom.reportChanged(); }, _$hasReceivedSubscribeResponseAtom, name: '${_$hasReceivedSubscribeResponseAtom.name}_set'); } final _$initializeRpcAsyncAction = AsyncAction('initializeRpc'); @override Future initializeRpc() { return _$initializeRpcAsyncAction.run(() => super.initializeRpc()); } final _$getBalanceAndInsertBorrowedAsyncAction = AsyncAction('getBalanceAndInsertBorrowed'); @override Future getBalanceAndInsertBorrowed() { return _$getBalanceAndInsertBorrowedAsyncAction .run(() => super.getBalanceAndInsertBorrowed()); } final _$updateBorrowedAsyncAction = AsyncAction('updateBorrowed'); @override Future updateBorrowed() { return _$updateBorrowedAsyncAction.run(() => super.updateBorrowed()); } final _$initiateBorrowAsyncAction = AsyncAction('initiateBorrow'); @override Future initiateBorrow() { return _$initiateBorrowAsyncAction.run(() => super.initiateBorrow()); } final _$findAccountsRequestAsyncAction = AsyncAction('findAccountsRequest'); @override Future findAccountsRequest(FindAccountsRequest request) { return _$findAccountsRequestAsyncAction .run(() => super.findAccountsRequest(request)); } final _$findAccountsWithNameLikeAsyncAction = AsyncAction('findAccountsWithNameLike'); @override Future> findAccountsWithNameLike(String name) { return _$findAccountsWithNameLikeAsyncAction .run(() => super.findAccountsWithNameLike(name)); } final _$loadWalletAsyncAction = AsyncAction('loadWallet'); @override Future loadWallet() { return _$loadWalletAsyncAction.run(() => super.loadWallet()); } final _$requestUpdateAsyncAction = AsyncAction('requestUpdate'); @override Future requestUpdate() { return _$requestUpdateAsyncAction.run(() => super.requestUpdate()); } final _$fcmUpdateAsyncAction = AsyncAction('fcmUpdate'); @override Future fcmUpdate(AccountNumber account) { return _$fcmUpdateAsyncAction.run(() => super.fcmUpdate(account)); } final _$fcmDeleteAccountAsyncAction = AsyncAction('fcmDeleteAccount'); @override Future fcmDeleteAccount(AccountNumber account) { return _$fcmDeleteAccountAsyncAction .run(() => super.fcmDeleteAccount(account)); } final _$fcmUpdateBulkAsyncAction = AsyncAction('fcmUpdateBulk'); @override Future fcmUpdateBulk({bool forceDisable = false}) { return _$fcmUpdateBulkAsyncAction .run(() => super.fcmUpdateBulk(forceDisable: forceDisable)); } final _$WalletBaseActionController = ActionController(name: 'WalletBase'); @override Account getAccountState(PascalAccount account) { final _$actionInfo = _$WalletBaseActionController.startAction(); try { return super.getAccountState(account); } finally { _$WalletBaseActionController.endAction(_$actionInfo); } } @override void changeRpcUrl(String rpcUrl) { final _$actionInfo = _$WalletBaseActionController.startAction(); try { return super.changeRpcUrl(rpcUrl); } finally { _$WalletBaseActionController.endAction(_$actionInfo); } } @override void removeAccount(PascalAccount account) { final _$actionInfo = _$WalletBaseActionController.startAction(); try { return super.removeAccount(account); } finally { _$WalletBaseActionController.endAction(_$actionInfo); } } @override void updateAccountName(PascalAccount account, AccountName newName) { final _$actionInfo = _$WalletBaseActionController.startAction(); try { return super.updateAccountName(account, newName); } finally { _$WalletBaseActionController.endAction(_$actionInfo); } } @override List getNonzeroBalanceAccounts() { final _$actionInfo = _$WalletBaseActionController.startAction(); try { return super.getNonzeroBalanceAccounts(); } finally { _$WalletBaseActionController.endAction(_$actionInfo); } } @override bool shouldHaveFee() { final _$actionInfo = _$WalletBaseActionController.startAction(); try { return super.shouldHaveFee(); } finally { _$WalletBaseActionController.endAction(_$actionInfo); } } @override String getLocalCurrencyDisplay( {AvailableCurrency currency, Currency amount, int decimalDigits}) { final _$actionInfo = _$WalletBaseActionController.startAction(); try { return super.getLocalCurrencyDisplay( currency: currency, amount: amount, decimalDigits: decimalDigits); } finally { _$WalletBaseActionController.endAction(_$actionInfo); } } @override void disconnect() { final _$actionInfo = _$WalletBaseActionController.startAction(); try { return super.disconnect(); } finally { _$WalletBaseActionController.endAction(_$actionInfo); } } @override void reconnect() { final _$actionInfo = _$WalletBaseActionController.startAction(); try { return super.reconnect(); } finally { _$WalletBaseActionController.endAction(_$actionInfo); } } @override void addNewOp(PascalOperation op) { final _$actionInfo = _$WalletBaseActionController.startAction(); try { return super.addNewOp(op); } finally { _$WalletBaseActionController.endAction(_$actionInfo); } } @override void reset() { final _$actionInfo = _$WalletBaseActionController.startAction(); try { return super.reset(); } finally { _$WalletBaseActionController.endAction(_$actionInfo); } } @override String toString() { final string = 'walletLoading: ${walletLoading.toString()},totalWalletBalance: ${totalWalletBalance.toString()},walletAccounts: ${walletAccounts.toString()},rpcClient: ${rpcClient.toString()},publicKey: ${publicKey.toString()},accountStateMap: ${accountStateMap.toString()},localCurrencyPrice: ${localCurrencyPrice.toString()},btcPrice: ${btcPrice.toString()},uuid: ${uuid.toString()},activeAccount: ${activeAccount.toString()},borrowedAccount: ${borrowedAccount.toString()},isBorrowEligible: ${isBorrowEligible.toString()},hasExceededBorrowLimit: ${hasExceededBorrowLimit.toString()},hasReceivedSubscribeResponse: ${hasReceivedSubscribeResponse.toString()}'; return '{$string}'; } } ================================================ FILE: lib/themes.dart ================================================ import 'dart:io'; import 'package:barcode_scan/barcode_scan.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; abstract class BaseTheme { int themeID; Color primary; Color primary60; Color primary50; Color primary30; Color primary15; Color primary10; Color secondary; Color success; Color success30; Color success15; Color success10; Color danger; Color danger30; Color danger15; Color backgroundPrimary; Color backgroundPrimary60; Color backgroundPrimary30; Color backgroundPrimary15; Color backgroundSecondary; Color shadow; Color shadow50; Color shadow10; Color textDark; Color textDark60; Color textDark50; Color textDark40; Color textDark30; Color textDark15; Color textDark10; Color textLight; Color textLight30; Color textLight15; Color overlay30; Color overlay20; Color overlay15; Color overlay10; Color switchKnob; Color switchTrack; LinearGradient gradientPrimary; LinearGradient gradientListTop; BoxShadow shadowPrimaryOne; BoxShadow shadowPrimaryTwo; BoxShadow shadowSuccessOne; BoxShadow shadowSuccessTwo; BoxShadow shadowDangerOne; BoxShadow shadowDangerTwo; BoxShadow shadowSucces; BoxShadow shadowTextDark; BoxShadow shadowTextDarkTwo; BoxShadow shadowMainCard; BoxShadow shadowAccountCard; BoxShadow shadowBottomBar; BoxShadow shadowSettingsList; String illustrationNewWallet; String illustrationBackup; String illustrationTwoOptions; String illustrationBorrowed; String illustrationSecurity; String animationWelcome; String animationSend; String animationNameChange; String animationSale; String animationTransfer; String animationGetAccount; String animationSearch; Brightness brightness; SystemUiOverlayStyle statusBar; AppIconEnum appIcon; OverlayTheme scannerTheme; /// Operator overrides bool operator ==(o) => (o != null && o.hashCode == hashCode); int get hashCode => themeID.hashCode; } class BlaiseLightTheme extends BaseTheme { int themeID = 1; static const Color orange = Color(0xFFF7941F); static const Color yellow = Color(0xFFFCC642); static const Color teal = Color(0xFF00C5C3); static const Color red = Color(0xFFFF6C59); static const Color white = Color(0xFFFFFFFF); static const Color white00 = Color(0x00FFFFFF); static const Color grayDark = Color(0xFF6B6C71); static const Color black = Color(0xFF000000); Color primary = orange; Color primary60 = orange.withOpacity(0.6); Color primary50 = orange.withOpacity(0.5); Color primary30 = orange.withOpacity(0.3); Color primary15 = orange.withOpacity(0.15); Color primary10 = orange.withOpacity(0.10); Color secondary = yellow; Color success = teal; Color success30 = teal.withOpacity(0.3); Color success15 = teal.withOpacity(0.15); Color success10 = teal.withOpacity(0.1); Color danger = red; Color danger30 = red.withOpacity(0.3); Color danger15 = red.withOpacity(0.15); Color backgroundPrimary = white; Color backgroundPrimary60 = white.withOpacity(0.6); Color backgroundPrimary30 = white.withOpacity(0.3); Color backgroundPrimary15 = white.withOpacity(0.15); Color backgroundSecondary = white; Color textDark = grayDark; Color textDark60 = grayDark.withOpacity(0.6); Color textDark50 = grayDark.withOpacity(0.5); Color textDark40 = grayDark.withOpacity(0.4); Color textDark30 = grayDark.withOpacity(0.3); Color textDark15 = grayDark.withOpacity(0.15); Color textDark10 = grayDark.withOpacity(0.1); Color shadow = grayDark; Color shadow50 = grayDark.withOpacity(0.5); Color shadow10 = grayDark.withOpacity(0.1); Color textLight = white; Color textLight30 = white.withOpacity(0.3); Color textLight15 = white.withOpacity(0.15); Color overlay30 = black.withOpacity(0.3); Color overlay20 = black.withOpacity(0.2); Color overlay15 = black.withOpacity(0.15); Color overlay10 = black.withOpacity(0.10); Color switchKnob = white; Color switchTrack = black.withOpacity(0.1); LinearGradient gradientPrimary = LinearGradient( begin: Alignment.bottomLeft, end: Alignment.topRight, stops: [0.0, 1], colors: [orange, yellow], ); LinearGradient gradientListTop = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: [0.0, 1], colors: [white, white00], ); BoxShadow shadowPrimaryOne = BoxShadow( color: orange.withOpacity(0.5), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowPrimaryTwo = BoxShadow( color: orange.withOpacity(0.25), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowSuccessOne = BoxShadow( color: teal.withOpacity(0.5), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowSuccessTwo = BoxShadow( color: teal.withOpacity(0.25), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowSuccess = BoxShadow( color: teal.withOpacity(0.25), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowTextDark = BoxShadow( color: grayDark.withOpacity(0.15), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowTextDarkTwo = BoxShadow( color: grayDark.withOpacity(0.25), offset: Offset(0, 12), blurRadius: 24, spreadRadius: -4.0); BoxShadow shadowMainCard = BoxShadow( color: grayDark.withOpacity(0.2), offset: Offset(0, 10), blurRadius: 20, spreadRadius: -3.3); BoxShadow shadowAccountCard = BoxShadow( color: grayDark.withOpacity(0.15), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -2.6); BoxShadow shadowBottomBar = BoxShadow( color: grayDark.withOpacity(0.2), offset: Offset(0, -15), blurRadius: 30, spreadRadius: -5.0); BoxShadow shadowSettingsList = BoxShadow( color: grayDark.withOpacity(0.4), offset: Offset(0, 10), blurRadius: 30, spreadRadius: -5.0); BoxShadow shadowDangerOne = BoxShadow( color: red.withOpacity(0.5), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowDangerTwo = BoxShadow( color: red.withOpacity(0.25), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); String illustrationNewWallet = 'assets/illustration_new_wallet.svg'; String illustrationBackup = 'assets/illustration_backup.svg'; String illustrationTwoOptions = 'assets/illustration_two_options.svg'; String illustrationBorrowed = 'assets/illustration_borrowed.svg'; String illustrationSecurity = 'assets/illustration_security.svg'; String animationWelcome = 'assets/animation_welcome.flr'; String animationSend = 'assets/animation_send.flr'; String animationNameChange = 'assets/animation_name_change.flr'; String animationSale = 'assets/animation_sale.flr'; String animationTransfer = 'assets/animation_transfer.flr'; String animationGetAccount = 'assets/animation_get_account.flr'; String animationSearch = 'assets/animation_search.flr'; Brightness brightness = Brightness.dark; SystemUiOverlayStyle statusBar = SystemUiOverlayStyle.light.copyWith(statusBarColor: Colors.transparent); AppIconEnum appIcon = AppIconEnum.LIGHT; OverlayTheme scannerTheme = OverlayTheme.BLAISE; } class BlaiseDarkTheme extends BaseTheme { int themeID = 2; static const Color blueish = Color(0xFF8287B5); static const Color blueish2 = Color(0xFF6F70A8); static const Color blueishLight = Color(0xFFACB7D1); static const Color green = Color(0xFF7CBFA1); static const Color red = Color(0xFFCC7A7A); static const Color white = Color(0xFFFFFFFF); static const Color white00 = Color(0x00FFFFFF); static const Color grayLight = Color(0xFFB5B5BF); static const Color grayLightish = Color(0xFF2D3136); static const Color grayDark = Color(0xFF1C1E21); static const Color grayDark00 = Color(0x001C1E21); static const Color black = Color(0xFF000000); Color primary = blueish; Color primary60 = blueish.withOpacity(0.6); Color primary50 = blueish.withOpacity(0.5); Color primary30 = blueish.withOpacity(0.3); Color primary15 = blueish.withOpacity(0.15); Color primary10 = blueish.withOpacity(0.10); Color secondary = blueishLight; Color success = green; Color success30 = green.withOpacity(0.3); Color success15 = green.withOpacity(0.15); Color success10 = green.withOpacity(0.1); Color danger = red; Color danger30 = red.withOpacity(0.3); Color danger15 = red.withOpacity(0.15); Color backgroundPrimary = grayDark; Color backgroundPrimary60 = grayDark.withOpacity(0.6); Color backgroundPrimary30 = grayDark.withOpacity(0.3); Color backgroundPrimary15 = grayDark.withOpacity(0.15); Color backgroundSecondary = grayDark; Color textDark = grayLight; Color textDark60 = grayLight.withOpacity(0.6); Color textDark50 = grayLight.withOpacity(0.5); Color textDark40 = grayLight.withOpacity(0.4); Color textDark30 = grayLight.withOpacity(0.3); Color textDark15 = grayLight.withOpacity(0.15); Color textDark10 = grayLight.withOpacity(0.1); Color shadow = black; Color shadow50 = black.withOpacity(0.5); Color shadow10 = black.withOpacity(0.1); Color textLight = grayDark; Color textLight30 = grayDark.withOpacity(0.3); Color textLight15 = grayDark.withOpacity(0.15); Color overlay30 = black.withOpacity(0.3); Color overlay20 = black.withOpacity(0.2); Color overlay15 = black.withOpacity(0.15); Color overlay10 = black.withOpacity(0.10); Color switchKnob = grayLightish; Color switchTrack = black.withOpacity(0.3); LinearGradient gradientPrimary = LinearGradient( begin: Alignment.bottomLeft, end: Alignment.topRight, stops: [0.0, 1], colors: [blueish2, blueishLight], ); LinearGradient gradientListTop = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: [0.0, 1], colors: [grayDark, grayDark00], ); BoxShadow shadowPrimaryOne = BoxShadow( color: blueish.withOpacity(0.1), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowPrimaryTwo = BoxShadow( color: blueish.withOpacity(0.05), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowSuccessOne = BoxShadow( color: green.withOpacity(0.1), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowSuccessTwo = BoxShadow( color: green.withOpacity(0.05), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowTextDark = BoxShadow( color: black.withOpacity(0.25), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowTextDarkTwo = BoxShadow( color: black.withOpacity(0.35), offset: Offset(0, 12), blurRadius: 24, spreadRadius: -4.0); BoxShadow shadowMainCard = BoxShadow( color: black.withOpacity(0.25), offset: Offset(0, 10), blurRadius: 20, spreadRadius: -3.3); BoxShadow shadowAccountCard = BoxShadow( color: black.withOpacity(0.25), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -2.6); BoxShadow shadowBottomBar = BoxShadow( color: black.withOpacity(0.25), offset: Offset(0, -15), blurRadius: 30, spreadRadius: -5.0); BoxShadow shadowSettingsList = BoxShadow( color: black.withOpacity(0.5), offset: Offset(0, 10), blurRadius: 30, spreadRadius: -5.0); BoxShadow shadowDangerOne = BoxShadow( color: red.withOpacity(0.1), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowDangerTwo = BoxShadow( color: red.withOpacity(0.05), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); String illustrationNewWallet = 'assets/illustration_new_wallet_dark.svg'; String illustrationBackup = 'assets/illustration_backup_dark.svg'; String illustrationTwoOptions = 'assets/illustration_two_options_dark.svg'; String illustrationBorrowed = 'assets/illustration_borrowed_dark.svg'; String illustrationSecurity = 'assets/illustration_security_dark.svg'; String animationWelcome = 'assets/animation_welcome_dark.flr'; String animationSend = 'assets/animation_send_dark.flr'; String animationNameChange = 'assets/animation_name_change_dark.flr'; String animationSale = 'assets/animation_sale_dark.flr'; String animationTransfer = 'assets/animation_transfer_dark.flr'; String animationGetAccount = 'assets/animation_get_account_dark.flr'; String animationSearch = 'assets/animation_search.flr'; Brightness brightness = Brightness.light; SystemUiOverlayStyle statusBar = SystemUiOverlayStyle.dark.copyWith(statusBarColor: Colors.transparent); AppIconEnum appIcon = AppIconEnum.DARK; OverlayTheme scannerTheme = OverlayTheme.BLAISEDARK; } class BlaiseCopperTheme extends BaseTheme { int themeID = 3; static const Color orange = Color(0xFFDD8D52); static const Color orange2 = Color(0xFFCB7244); static const Color orangeLight = Color(0xFFFFBF6A); static const Color blue = Color(0xFF5A73F2); static const Color red = Color(0xFFF25A5A); static const Color white = Color(0xFFFFFFFF); static const Color white00 = Color(0x00FFFFFF); static const Color grayLight = Color(0xFFB9BAC4); static const Color grayLightish = Color(0xFF585A6F); static const Color grayDark = Color(0xFF2B2C37); static const Color grayDark00 = Color(0x002B2C37); static const Color black = Color(0xFF000000); Color primary = orange; Color primary60 = orange.withOpacity(0.6); Color primary50 = orange.withOpacity(0.5); Color primary30 = orange.withOpacity(0.3); Color primary15 = orange.withOpacity(0.15); Color primary10 = orange.withOpacity(0.10); Color secondary = orangeLight; Color success = blue; Color success30 = blue.withOpacity(0.3); Color success15 = blue.withOpacity(0.15); Color success10 = blue.withOpacity(0.1); Color danger = red; Color danger30 = red.withOpacity(0.3); Color danger15 = red.withOpacity(0.15); Color backgroundPrimary = grayDark; Color backgroundPrimary60 = grayDark.withOpacity(0.6); Color backgroundPrimary30 = grayDark.withOpacity(0.3); Color backgroundPrimary15 = grayDark.withOpacity(0.15); Color backgroundSecondary = grayDark; Color textDark = grayLight; Color textDark60 = grayLight.withOpacity(0.6); Color textDark50 = grayLight.withOpacity(0.5); Color textDark40 = grayLight.withOpacity(0.4); Color textDark30 = grayLight.withOpacity(0.3); Color textDark15 = grayLight.withOpacity(0.15); Color textDark10 = grayLight.withOpacity(0.1); Color shadow = black; Color shadow50 = black.withOpacity(0.5); Color shadow10 = black.withOpacity(0.1); Color textLight = grayDark; Color textLight30 = grayDark.withOpacity(0.3); Color textLight15 = grayDark.withOpacity(0.15); Color overlay30 = black.withOpacity(0.3); Color overlay20 = black.withOpacity(0.2); Color overlay15 = black.withOpacity(0.15); Color overlay10 = black.withOpacity(0.10); Color switchKnob = grayLightish; Color switchTrack = black.withOpacity(0.3); LinearGradient gradientPrimary = LinearGradient( begin: Alignment.bottomLeft, end: Alignment.topRight, stops: [0.0, 1], colors: [orange2, orangeLight], ); LinearGradient gradientListTop = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: [0.0, 1], colors: [grayDark, grayDark00], ); BoxShadow shadowPrimaryOne = BoxShadow( color: orange.withOpacity(0.1), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowPrimaryTwo = BoxShadow( color: orange.withOpacity(0.05), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowSuccessOne = BoxShadow( color: blue.withOpacity(0.1), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowSuccessTwo = BoxShadow( color: blue.withOpacity(0.05), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowTextDark = BoxShadow( color: black.withOpacity(0.25), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowTextDarkTwo = BoxShadow( color: black.withOpacity(0.35), offset: Offset(0, 12), blurRadius: 24, spreadRadius: -4.0); BoxShadow shadowMainCard = BoxShadow( color: black.withOpacity(0.25), offset: Offset(0, 10), blurRadius: 20, spreadRadius: -3.3); BoxShadow shadowAccountCard = BoxShadow( color: black.withOpacity(0.25), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -2.6); BoxShadow shadowBottomBar = BoxShadow( color: black.withOpacity(0.25), offset: Offset(0, -15), blurRadius: 30, spreadRadius: -5.0); BoxShadow shadowSettingsList = BoxShadow( color: black.withOpacity(0.5), offset: Offset(0, 10), blurRadius: 30, spreadRadius: -5.0); BoxShadow shadowDangerOne = BoxShadow( color: red.withOpacity(0.1), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); BoxShadow shadowDangerTwo = BoxShadow( color: red.withOpacity(0.05), offset: Offset(0, 8), blurRadius: 16, spreadRadius: -4.0); String illustrationNewWallet = 'assets/illustration_new_wallet_copper.svg'; String illustrationBackup = 'assets/illustration_backup_copper.svg'; String illustrationTwoOptions = 'assets/illustration_two_options_copper.svg'; String illustrationBorrowed = 'assets/illustration_borrowed_copper.svg'; String illustrationSecurity = 'assets/illustration_security_copper.svg'; String animationWelcome = 'assets/animation_welcome_copper.flr'; String animationSend = 'assets/animation_send_copper.flr'; String animationNameChange = 'assets/animation_name_change_copper.flr'; String animationSale = 'assets/animation_sale_copper.flr'; String animationTransfer = 'assets/animation_transfer_copper.flr'; String animationGetAccount = 'assets/animation_get_account_copper.flr'; String animationSearch = 'assets/animation_search.flr'; Brightness brightness = Brightness.light; SystemUiOverlayStyle statusBar = SystemUiOverlayStyle.dark.copyWith(statusBarColor: Colors.transparent); AppIconEnum appIcon = AppIconEnum.COPPER; OverlayTheme scannerTheme = OverlayTheme.COPPER; } enum AppIconEnum { LIGHT, DARK, COPPER } class AppIcon { static const _channel = const MethodChannel('fappchannel'); static Future setAppIcon(AppIconEnum iconToChange) async { if (!Platform.isIOS) { return null; } String iconStr = "Light"; switch (iconToChange) { case AppIconEnum.DARK: iconStr = "Dark"; break; case AppIconEnum.COPPER: iconStr = "Copper"; break; case AppIconEnum.LIGHT: default: iconStr = "Light"; break; } final Map params = { 'icon': iconStr, }; return await _channel.invokeMethod('changeIcon', params); } } ================================================ FILE: lib/ui/account/account.dart ================================================ import 'dart:async'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/events.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/db/appdb.dart'; import 'package:blaise_wallet_flutter/model/db/contact.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/store/account/account.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/change_name/change_name_sheet.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/delist_for_sale/delisting_for_sale.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/list_for_sale/list_for_sale_sheet.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/private_sale/create_private_sale_sheet.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/transfer_account/transfer_account_sheet.dart'; import 'package:blaise_wallet_flutter/ui/account/receive/receive_sheet.dart'; import 'package:blaise_wallet_flutter/ui/account/send/send_sheet.dart'; import 'package:blaise_wallet_flutter/ui/account/operation_sheet.dart'; import 'package:blaise_wallet_flutter/ui/settings/settings.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/operation_list_item.dart'; import 'package:blaise_wallet_flutter/ui/widgets/overlay_dialog.dart'; import 'package:blaise_wallet_flutter/ui/widgets/placeholder_operation_list_item.dart'; import 'package:blaise_wallet_flutter/ui/widgets/reactive_refresh.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/ui/widgets/svg_repaint.dart'; import 'package:blaise_wallet_flutter/util/haptic_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:logger/logger.dart'; import 'package:pascaldart/pascaldart.dart'; class AccountPage extends StatefulWidget { final PascalAccount account; AccountPage({@required this.account}); @override _AccountPageState createState() => _AccountPageState(); } class _AccountPageState extends State with SingleTickerProviderStateMixin, WidgetsBindingObserver { final Logger log = sl.get(); GlobalKey _scaffoldKey = GlobalKey(); Account accountState; // Opacity Animation Animation _opacityAnimation; AnimationController _opacityAnimationController; // Refresh indicator bool _isRefreshing; // Reference to contacts list List _contacts; // Last refresh DateTime _lastRefresh; // Account price String accountPrice = "0.25"; // Borrowed account expiration String untilExpirationDays = ""; String untilExpirationHours = ""; String untilExpirationMinutes = ""; void formatExpiryDate(DateTime expiry) { if (expiry != null) { DateTime now = DateTime.now().toUtc(); int diffS = expiry.difference(now).inSeconds; if (diffS <= 60) { // Seconds only untilExpirationDays = "0"; untilExpirationHours = "0"; untilExpirationMinutes = "0"; } else if (diffS <= 3600) { // Minutes String minutesStr = ""; int minutes = diffS ~/ 60; minutesStr = minutes.toString(); untilExpirationDays = "0"; untilExpirationHours = "0"; untilExpirationMinutes = minutesStr; } else if (diffS <= 86400) { // Hours:Minutes String hoursStr = ""; int hours = diffS ~/ 3600; hoursStr = hours.toString(); diffS = diffS % 3600; String minutesStr = ""; int minutes = diffS ~/ 60; minutesStr = minutes.toString(); untilExpirationDays = "0"; untilExpirationHours = hoursStr; untilExpirationMinutes = minutesStr; } else { // Days:Hours:Minutes String daysStr = ""; int days = diffS ~/ 86400; daysStr = days.toString(); diffS = diffS % 86400; String hoursStr = ""; int hours = diffS ~/ 3600; hoursStr = hours.toString(); diffS = diffS % 3600; String minutesStr = ""; int minutes = diffS ~/ 60; minutesStr = minutes.toString(); untilExpirationDays = daysStr; untilExpirationHours = hoursStr; untilExpirationMinutes = minutesStr; } } } @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _isRefreshing = false; _registerBus(); this.accountState = walletState.getAccountState(widget.account); this.accountState.updateAccount(); if (!this.accountState.account.isBorrowed) { this.accountState.getAccountOperations(); } walletState.activeAccount = widget.account.account; // Opacity Animation _opacityAnimationController = new AnimationController( duration: const Duration(milliseconds: 500), vsync: this, ); _opacityAnimation = new Tween(begin: 1.0, end: 0.4).animate( CurvedAnimation( parent: _opacityAnimationController, curve: Curves.easeIn, reverseCurve: Curves.easeOut, ), ); _startAnimation(); // Contacts _updateContacts(); // Subscribe for updates walletState.requestUpdate(); // Update FCM token if (!this.accountState.account.isBorrowed) { walletState.fcmUpdate(widget.account.account); } } void _animationStatusListener(AnimationStatus status) { switch (status) { case AnimationStatus.dismissed: _opacityAnimationController.forward(); break; case AnimationStatus.completed: _opacityAnimationController.reverse(); break; default: return null; } } void _animationControllerListener() { if (accountState.operationsLoading || accountState.operationsToDisplay == null) { setState(() {}); } else { _disposeAnimations(); } } void _disposeAnimations() { try { _opacityAnimation?.removeStatusListener(_animationStatusListener); _opacityAnimationController?.removeListener(_animationControllerListener); _opacityAnimationController?.dispose(); } catch (e) {} } @override void dispose() { WidgetsBinding.instance.removeObserver(this); _disposeAnimations(); _destroyBus(); walletState.activeAccount = null; super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { case AppLifecycleState.paused: super.didChangeAppLifecycleState(state); break; case AppLifecycleState.resumed: // Do an auto-refresh if (_lastRefresh == null || DateTime.now().toUtc().difference(_lastRefresh).inSeconds > 300) { _refresh(socketUpdate: false); } walletState.requestUpdate(); super.didChangeAppLifecycleState(state); break; default: super.didChangeAppLifecycleState(state); break; } } void _startAnimation() { _opacityAnimationController.addListener(_animationControllerListener); _opacityAnimation.addStatusListener(_animationStatusListener); _opacityAnimationController.forward(); } Future _updateContacts() async { List contacts = await sl.get().getContacts(); if (mounted) { setState(() { _contacts = contacts; }); } } List getOperationsList() { return [ DialogListItem( option: AppLocalization.of(context).changeAccountNameHeader, action: () { Navigator.of(context).pop(); AppSheets.showBottomSheet( context: context, widget: ChangeNameSheet(account: accountState.account)); }, ), DialogListItem( option: AppLocalization.of(context).transferAccountHeader, action: () { Navigator.of(context).pop(); AppSheets.showBottomSheet( context: context, widget: TransferAccountSheet( account: accountState.account, )); }, ), DialogListItem( option: AppLocalization.of(context).listAccountForSaleHeader, action: () { Navigator.of(context).pop(); AppSheets.showBottomSheet( context: context, widget: ListForSaleSheet(account: accountState.account)); }, disabled: accountState == null || accountState.account.state == AccountState.LISTED), DialogListItem( option: AppLocalization.of(context).createPrivateSaleHeader, action: () { Navigator.of(context).pop(); AppSheets.showBottomSheet( context: context, widget: CreatePrivateSaleSheet(account: accountState.account)); }, disabled: accountState == null || accountState.account.state == AccountState.LISTED), DialogListItem( option: AppLocalization.of(context).delistFromSaleHeader, action: () { Navigator.of(context).pop(); AppSheets.showBottomSheet( context: context, widget: DelistingForSaleSheet( account: accountState.account, fee: walletState.shouldHaveFee() ? walletState.MIN_FEE : walletState.NO_FEE, )); }, disabled: accountState == null || accountState.account.state != AccountState.LISTED), ]; } StreamSubscription _historySub; StreamSubscription _contactModifiedSub; void _registerBus() { _historySub = EventTaxiImpl.singleton() .registerTo() .listen((event) { walletState.loadWallet(); }); _contactModifiedSub = EventTaxiImpl.singleton() .registerTo() .listen((event) { _updateContacts(); }); } void _destroyBus() { if (_historySub != null) { _historySub.cancel(); } if (_contactModifiedSub != null) { _contactModifiedSub.cancel(); } } // Refresh list Future _refresh({bool socketUpdate = true}) async { _lastRefresh = DateTime.now().toUtc(); setState(() { _isRefreshing = true; }); HapticUtil.success(); // Hide refresh indicator after 3 seconds if no server response Future.delayed(Duration(seconds: 3), () { if (mounted) { setState(() { _isRefreshing = false; }); } }); if (socketUpdate) { walletState.requestUpdate(); } if (!widget.account.isBorrowed) { this.accountState?.updateAccount(); this.accountState?.getAccountOperations()?.whenComplete(() { if (mounted) { setState(() { _isRefreshing = false; }); } }); } else { this.accountState?.updateAccount()?.whenComplete(() { if (mounted) { if (this.accountState.account.isBorrowed) { setState(() { _isRefreshing = false; }); } else { walletState.updateBorrowed().then((_) { if (mounted) { setState(() { _isRefreshing = false; }); if (walletState.borrowedAccount == null) { walletState.loadWallet(); Navigator.of(context).pushReplacementNamed('/account', arguments: this.accountState.account); } } }); } } }); } } @override Widget build(BuildContext context) { // The main scaffold that holds everything return Scaffold( drawerEdgeDragWidth: 200, key: _scaffoldKey, resizeToAvoidBottomInset: false, endDrawer: SizedBox( width: double.infinity, child: Drawer(child: SettingsPage(account: accountState))), backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, body: LayoutBuilder( builder: (context, constraints) => Column( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // A stack for the main card and the background gradient Stack( children: [ // Container for the gradient background Container( height: 65 + (MediaQuery.of(context).padding.top) + (20 - (MediaQuery.of(context).padding.bottom) / 2), decoration: BoxDecoration( gradient: StateContainer.of(context) .curTheme .gradientPrimary, ), ), //Container for the main card Container( height: 130, margin: EdgeInsetsDirectional.fromSTEB( 12, (MediaQuery.of(context).padding.top) + (20 - (MediaQuery.of(context).padding.bottom) / 2), 12, 0), width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context) .curTheme .gradientPrimary, borderRadius: BorderRadius.circular(12), boxShadow: [ StateContainer.of(context) .curTheme .shadowMainCard, ]), child: ClipRRect( borderRadius: BorderRadius.circular(12), child: Material( color: Colors.transparent, child: InkWell( onTap: widget.account.isBorrowed ? () { return null; } : () { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization.of(context) .otherOperationsHeader, optionsList: getOperationsList())); }, highlightColor: StateContainer.of(context) .curTheme .textLight15, splashColor: StateContainer.of(context) .curTheme .textLight30, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ // Back icon and price text Container( height: 130, width: 60, alignment: Alignment(-1, 0), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Back icon Container( margin: EdgeInsetsDirectional.only( top: 2, start: 2), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context) .curTheme .textLight15, splashColor: StateContainer.of(context) .curTheme .textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( 50.0)), padding: EdgeInsetsDirectional.only( end: 10), child: Icon(AppIcons.back, color: StateContainer.of(context) .curTheme .textLight, size: 22)), ), // Price text Observer( builder: (BuildContext context) { if (walletState.localCurrencyPrice == null) { return Opacity( opacity: _opacityAnimation.value, child: Container( decoration: BoxDecoration( color: StateContainer.of(context) .curTheme .textLight .withOpacity(0.75), borderRadius: BorderRadius.circular( 100), ), child: AutoSizeText( " ", style: AppStyles .paragraphTextLightSmallSemiBold( context), ), ), ); } else { return Container( margin: EdgeInsetsDirectional.only( start: 16, bottom: 12), child: AutoSizeText( walletState .getLocalCurrencyDisplay( currency: StateContainer.of( context) .curCurrency, amount: Currency('1'), decimalDigits: 3), maxLines: 1, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.start, style: AppStyles .paragraphTextLightSmallSemiBold( context), ), ); } }) ], ), ), // Column for balance texts Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ // Container for "TOTAL BALANCE" text Container( margin: EdgeInsetsDirectional.fromSTEB( 12, 0, 12, 0), child: AutoSizeText( toUppercase( AppLocalization.of(context) .accountBalanceHeader, context), style: AppStyles.paragraphTextLightSmall( context), ), ), // Container for the balance Container( width: MediaQuery.of(context).size.width - 168, margin: EdgeInsetsDirectional.fromSTEB( 12, 4, 12, 4), child: Observer( builder: (BuildContext context) { Currency bal = accountState.accountBalance; return AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontTextLightPascal( context), ), TextSpan( text: " ", style: TextStyle( fontSize: 12)), TextSpan( text: bal.toStringOpt(), style: AppStyles.header( context)) ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 28, ), ); }, ), ), // Container for the fiat conversion Observer( builder: (BuildContext context) { if (walletState.localCurrencyPrice == null) { return Opacity( opacity: _opacityAnimation.value, child: Container( decoration: BoxDecoration( color: StateContainer.of(context) .curTheme .textLight .withOpacity(0.75), borderRadius: BorderRadius.circular( 100), ), child: AutoSizeText( " ", style: AppStyles .paragraphTextLightSmallSemiBold( context), ), ), ); } else { return Container( margin: EdgeInsetsDirectional .fromSTEB(12, 0, 12, 0), child: AutoSizeText( "(${walletState.getLocalCurrencyDisplay(currency: StateContainer.of(context).curCurrency, amount: accountState.accountBalance)})", style: AppStyles .paragraphTextLightSmall( context), ), ); } }, ) ], ), // Column for settings icon and other operations icon Container( width: 60, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.end, children: [ // Settings Icon Container( margin: EdgeInsetsDirectional.only( top: 2, end: 2), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context) .curTheme .textLight15, splashColor: StateContainer.of(context) .curTheme .textLight30, onPressed: () { _scaffoldKey.currentState .openEndDrawer(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( 50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.settings, color: StateContainer.of(context) .curTheme .textLight, size: 24)), ), // Other Operations Icon Container( margin: EdgeInsetsDirectional.only( bottom: 2, end: 2), height: 50, width: 50, child: !widget.account.isBorrowed ? FlatButton( highlightColor: StateContainer.of(context) .curTheme .textLight15, splashColor: StateContainer.of(context) .curTheme .textLight30, onPressed: () { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization .of( context) .otherOperationsHeader, optionsList: getOperationsList())); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( 50.0)), padding: EdgeInsetsDirectional.only( start: 8, top: 6), child: Icon(AppIcons.edit, color: StateContainer.of( context) .curTheme .textLight, size: 18)) : SizedBox()), ], ), ), ], ), ), ), ), ), ], ), widget.account.isBorrowed ? // Paragraph and illustration Expanded( child: ReactiveRefreshIndicator( backgroundColor: StateContainer.of(context) .curTheme .backgroundPrimary, onRefresh: _refresh, isRefreshing: _isRefreshing, child: SingleChildScrollView( physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsets.all(0), //Container for the paragraph child: Container( height: MediaQuery.of(context).size.height - (130 + ((MediaQuery.of(context) .padding .top) + (20 - (MediaQuery.of(context) .padding .bottom) / 2))) - ((MediaQuery.of(context).padding.bottom) + (24 - (MediaQuery.of(context) .padding .bottom) / 2)) - 50 - 20, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 0, 30, 0), child: Observer( builder: (context) { String msgStr = ""; List msg; if ((walletState.borrowedAccount != null && walletState .borrowedAccount .paid) || accountState.paid) { msgStr = AppLocalization.of( context) .borrowedAccountPaidParagraph; msg = formatLocalizedColors( context, msgStr); } else { formatExpiryDate(walletState .borrowedAccount?.expiry); msgStr = AppLocalization.of( context) .borrowedAccountParagraph .replaceAll( "%1", accountPrice) .replaceAll("%2", untilExpirationDays) .replaceAll("%3", untilExpirationHours) .replaceAll('%4', untilExpirationMinutes); msg = formatLocalizedColors( context, msgStr); } return AutoSizeText.rich( TextSpan(children: msg), stepGranularity: 0.5, maxLines: 10, minFontSize: 8, textAlign: TextAlign.center, style: TextStyle(fontSize: 14), ); }, )), // Container for the illustration Container( margin: EdgeInsetsDirectional.only( top: 24, bottom: 24, ), child: SvgRepaintAsset( asset: StateContainer.of(context) .curTheme .illustrationBorrowed, width: MediaQuery.of(context) .size .width * 0.8, height: MediaQuery.of(context) .size .width * 0.8 * 132 / 295), ), ], ), ), ))) : // Wallet Cards Expanded( child: widget.account.isBorrowed ? SizedBox() : Column( children: [ // Accounts text Container( margin: EdgeInsetsDirectional.fromSTEB( 24, 18, 24, 4), alignment: Alignment(-1, 0), child: AutoSizeText( toUppercase( AppLocalization.of(context) .operationsHeader, context), style: AppStyles.headerSmall(context), textAlign: TextAlign.left, stepGranularity: 0.5, maxLines: 1, ), ), // Expanded list Expanded( // Container for the list child: Container( margin: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 0), width: double.maxFinite, decoration: BoxDecoration( color: StateContainer.of(context) .curTheme .backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), boxShadow: [ StateContainer.of(context) .curTheme .shadowSettingsList, ], ), // Operations List child: Observer( builder: (BuildContext context) { if (accountState .operationsLoading || accountState.operations == null) { return ReactiveRefreshIndicator( backgroundColor: StateContainer.of(context) .curTheme .backgroundPrimary, onRefresh: _refresh, isRefreshing: _isRefreshing, child: ClipRRect( borderRadius: BorderRadius.only( topLeft: Radius .circular(12), topRight: Radius .circular( 12)), child: Opacity( opacity: _opacityAnimation .value, child: ListView( padding: EdgeInsetsDirectional .only( bottom: 24), children: [ PlaceholderOperationListItem( type: PlaceholderOperationType .Received), PlaceholderOperationListItem( type: PlaceholderOperationType .Sent), PlaceholderOperationListItem( type: PlaceholderOperationType .Received), PlaceholderOperationListItem( type: PlaceholderOperationType .Received), PlaceholderOperationListItem( type: PlaceholderOperationType .Sent), PlaceholderOperationListItem( type: PlaceholderOperationType .Sent), PlaceholderOperationListItem( type: PlaceholderOperationType .Received), PlaceholderOperationListItem( type: PlaceholderOperationType .Sent), ]), ))); } else { return ClipRRect( borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12)), child: ReactiveRefreshIndicator( backgroundColor: StateContainer.of( context) .curTheme .backgroundPrimary, onRefresh: _refresh, isRefreshing: _isRefreshing, child: accountState .hasOperationsToDisplay() ? ListView.builder( physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsetsDirectional .only( bottom: 24), itemCount: accountState .operationsToDisplay .length, itemBuilder: (context, index) { return _buildAccountHistoryItem( accountState .operationsToDisplay[ index]); }) : ListView( padding: EdgeInsetsDirectional .only( bottom: 24), children: getPlaceholderCards(), )), ); } }, ), ), ), ], ), ), // Bottom bar Align( alignment: Alignment.bottomCenter, child: Container( width: double.maxFinite, decoration: BoxDecoration( color: StateContainer.of(context) .curTheme .backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), boxShadow: [ StateContainer.of(context).curTheme.shadowBottomBar, ], ), child: Container( margin: EdgeInsetsDirectional.only(top: 4), child: Row( children: [ Observer( builder: (BuildContext context) { PascalAccount account = accountState.account; return AppButton( text: AppLocalization.of(context).receiveButton, type: AppButtonType.PrimaryLeft, onPressed: () { AppSheets.showBottomSheet( context: context, widget: ReceiveSheet( accountName: account.name.accountName, accountNumber: account.account, )); }, ); }, ), Observer( builder: (BuildContext context) { return AppButton( text: AppLocalization.of(context).sendButton, type: AppButtonType.PrimaryRight, disabled: accountState.accountBalance > Currency('0') ? false || widget.account.isBorrowed : true, onPressed: () { AppSheets.showBottomSheet( context: context, widget: SendSheet( account: accountState.account, localCurrency: StateContainer.of(context) .curCurrency), ); }, ); }, ) ], ), ), ), ), ], ), ), ], ), ), ); } Widget _buildAccountHistoryItem(PascalOperation op) { if (op.optype == OpType.TRANSACTION) { OperationType type; if (op.senders[0].sendingAccount == accountState.account.account) { type = OperationType.Sent; } else { type = OperationType.Received; } AccountNumber accountToCheck = type == OperationType.Received ? op.senders[0].sendingAccount : op.receivers[0].receivingAccount; List contacts = _contacts.where((c) => c.account == accountToCheck).toList(); Contact c; if (contacts.length > 0) { c = contacts[0]; } return OperationListItem( type: type, amount: op.receivers[0].amount.toStringOpt(), address: c == null ? accountToCheck.toString() : c.name, isContact: c != null, date: op.maturation != null ? UIUtil.formatDateStr(op.time) : AppLocalization.of(context).pendingHeader, payload: op.receivers[0].payload, onPressed: () { AppSheets.showBottomSheet( context: context, animationDurationMs: 200, widget: OperationSheet( payload: op.receivers[0].payload, ophash: op.ophash, operation: op, isContact: c != null, account: type == OperationType.Received ? op.senders[0].sendingAccount : op.receivers[0].receivingAccount, )); }, ); } else if (op.optype == OpType.CHANGE_ACCOUNT_INFO) { // Only show change name if (op.changers[0].newName != null) { return OperationListItem( type: OperationType.NameChanged, address: op.changers[0].changingAccount.toString(), date: op.maturation != null ? UIUtil.formatDateStr(op.time) : AppLocalization.of(context).pendingHeader, payload: "", name: op.changers[0].newName.toString(), onPressed: () { AppSheets.showBottomSheet( context: context, animationDurationMs: 200, widget: OperationSheet( payload: "", ophash: op.ophash, operation: op, account: op.changers[0].changingAccount, )); }, ); } } else if (op.optype == OpType.LIST_FORSALE) { return OperationListItem( type: OperationType.ListedForSale, address: op.changers[0].sellerAccount.toString(), date: op.maturation != null ? UIUtil.formatDateStr(op.time) : AppLocalization.of(context).pendingHeader, payload: "", price: op.changers[0].accountPrice.toStringOpt(), onPressed: () { AppSheets.showBottomSheet( context: context, animationDurationMs: 200, widget: OperationSheet( payload: "", ophash: op.ophash, operation: op, account: op.changers[0].sellerAccount, )); }, ); } else if (op.optype == OpType.DELIST_FORSALE) { return OperationListItem( type: OperationType.DelistedForSale, address: op.signerAccount.toString(), date: op.maturation != null ? UIUtil.formatDateStr(op.time) : AppLocalization.of(context).pendingHeader, payload: "", onPressed: () { AppSheets.showBottomSheet( context: context, animationDurationMs: 200, widget: OperationSheet( payload: "", ophash: op.ophash, operation: op, account: op.signerAccount, )); }, ); } return SizedBox(); } List getPlaceholderCards() { List history = []; // Show welcome history.add(OperationListItem(type: OperationType.Welcome)); history.add(OperationListItem( type: OperationType.Sent, amount: "1,111", address: "111111-11", date: "May 11 • 11:11", )); history.add(OperationListItem( type: OperationType.Received, amount: "1,111", address: "111111-11", date: "May 11 • 11:11", )); return history; } } ================================================ FILE: lib/ui/account/operation_details_sheet.dart ================================================ import 'dart:async'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/webview.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pascaldart/pascaldart.dart'; class OperationDetailsSheet extends StatefulWidget { final PascalOperation operation; OperationDetailsSheet({@required this.operation}); _OperationDetailsSheetState createState() => _OperationDetailsSheetState(); } class _OperationDetailsSheetState extends State { @override void initState() { super.initState(); } String getOptypeDisplay(int optype) { switch (optype) { case OpType.BLOCKCHAIN_REWARD: return AppLocalization.of(context) .blockchainRewardOPDetails .replaceAll("%1", optype.toString()); case OpType.TRANSACTION: return AppLocalization.of(context) .transactionOPDetails .replaceAll("%1", optype.toString()); case OpType.CHANGE_KEY: return AppLocalization.of(context) .changeKeyOPDetails .replaceAll("%1", optype.toString()); case OpType.RECOVER_FUNDS: return AppLocalization.of(context) .recoverFundsOPDetails .replaceAll("%1", optype.toString()); case OpType.LIST_FORSALE: return AppLocalization.of(context) .listAccountForSaleHeader .replaceAll("%1", optype.toString()); case OpType.DELIST_FORSALE: return AppLocalization.of(context) .delistAccountOPDetails .replaceAll("%1", optype.toString()); case OpType.BUY_ACCOUNT: return AppLocalization.of(context) .buyAccountOPDetails .replaceAll("%1", optype.toString()); case OpType.CHANGE_KEY_SIGNED: return AppLocalization.of(context) .changeKeySignedOPDetails .replaceAll("%1", optype.toString()); case OpType.CHANGE_ACCOUNT_INFO: return AppLocalization.of(context) .changeAccountInfoOPDetails .replaceAll("%1", optype.toString()); case OpType.MULTIOPERATION: return AppLocalization.of(context) .multioperationOPDetails .replaceAll("%1", optype.toString()); default: return AppLocalization.of(context) .unknownOPDetails .replaceAll("%1", optype.toString()); } } int getNOperation() { int nOp; try { if (widget.operation.optype == OpType.TRANSACTION) { nOp = widget.operation.senders[0].nOperation; } else { nOp = widget.operation.changers[0].nOperation; } } catch (e) { nOp = -1; } return nOp; } Widget getSendingAccount() { if (widget.operation.optype == OpType.TRANSACTION) { try { return TransactionDetailsListItem( header: AppLocalization.of(context).sendingAccountOPDetails, value: widget.operation.senders[0].sendingAccount.toString()); } catch (e) { return SizedBox(); } } return SizedBox(); } Widget getReceivingAccount() { if (widget.operation.optype == OpType.TRANSACTION) { try { return TransactionDetailsListItem( header: AppLocalization.of(context).receivingAccountOPDetails, value: widget.operation.receivers[0].receivingAccount.toString()); } catch (e) { return SizedBox(); } } return SizedBox(); } Widget getChangingAccount() { if (widget.operation.optype != OpType.TRANSACTION) { try { return TransactionDetailsListItem( header: AppLocalization.of(context).changingAccountOPDetails, value: widget.operation.changers[0].changingAccount.toString()); } catch (e) { return SizedBox(); } } return SizedBox(); } Widget getSendAmount() { if (widget.operation.optype != OpType.TRANSACTION) { try { return TransactionDetailsListItem( header: AppLocalization.of(context).sendAmountOPDetails, value: widget.operation.senders[0].amount.toStringOpt()); } catch (e) { return SizedBox(); } } return SizedBox(); } Widget getPayload() { if (widget.operation.optype == OpType.TRANSACTION) { try { return TransactionDetailsListItem( header: AppLocalization.of(context).payloadOPDetails, value: widget.operation.senders[0].payload); } catch (e) { return SizedBox(); } } return SizedBox(); } Widget getNewPublickey() { if (widget.operation.optype != OpType.TRANSACTION) { try { return TransactionDetailsListItem( header: AppLocalization.of(context).newPublicKeyOPDetails, value: PublicKeyCoder() .encodeToBase58(widget.operation.changers[0].newEncPubkey)); } catch (e) { return SizedBox(); } } return SizedBox(); } Widget getNewName() { if (widget.operation.optype != OpType.TRANSACTION) { try { return TransactionDetailsListItem( header: AppLocalization.of(context).newNameOPDetails, value: widget.operation.changers[0].newName.toString()); } catch (e) { return SizedBox(); } } return SizedBox(); } Widget getSeller() { if (widget.operation.optype != OpType.TRANSACTION) { try { return TransactionDetailsListItem( header: AppLocalization.of(context).sellerAccountOPDetails, value: widget.operation.changers[0].sellerAccount.toString()); } catch (e) { return SizedBox(); } } return SizedBox(); } Widget getAccountPrice() { if (widget.operation.optype != OpType.TRANSACTION) { try { return TransactionDetailsListItem( header: AppLocalization.of(context).accountPriceOPDetails, value: widget.operation.changers[0].accountPrice.toStringOpt()); } catch (e) { return SizedBox(); } } return SizedBox(); } Widget getLockedUntilBlock() { if (widget.operation.optype != OpType.TRANSACTION) { try { return TransactionDetailsListItem( header: AppLocalization.of(context).lockedUntilBlockOPDetails, value: widget.operation.changers[0].lockedUntilBlock.toString()); } catch (e) { return SizedBox(); } } return SizedBox(); } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: ClipRRect( borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12)), child: Material( child: Column( children: [ // Transaction Details Expanded( child: Stack( children: [ // The List SingleChildScrollView( padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 50), child: Column( children: [ TransactionDetailsListItem( header: AppLocalization.of(context) .blockOPDetails, value: widget.operation.block.toString()), TransactionDetailsListItem( header: AppLocalization.of(context) .optxtOPDetails, value: widget.operation.optxt), TransactionDetailsListItem( header: AppLocalization.of(context) .timeOPDetails, value: widget.operation.maturation == null ? AppLocalization.of(context) .naOPDetails : UIUtil.formatDateStrLong( widget.operation.time)), getSendingAccount(), getSendAmount(), getPayload(), getReceivingAccount(), getNewName(), TransactionDetailsListItem( header: AppLocalization.of(context) .ophashOPDetails, value: widget.operation.ophash, withDivider: false), TransactionDetailsListItem( header: AppLocalization.of(context) .optypeOPDetails, value: getOptypeDisplay( widget.operation.optype)), TransactionDetailsListItem( header: AppLocalization.of(context) .maturationOPDetails, value: widget.operation.maturation == null ? AppLocalization.of(context) .nullOPDetails : widget.operation.maturation .toString()), TransactionDetailsListItem( header: AppLocalization.of(context) .feeOPDetails, value: widget.operation.fee .toPositive() .toStringOpt()), TransactionDetailsListItem( header: AppLocalization.of(context) .opblockOPDetails, value: widget.operation.opblock.toString()), TransactionDetailsListItem( header: AppLocalization.of(context) .noperationOPDetails, value: getNOperation().toString()), getSeller(), getAccountPrice(), getLockedUntilBlock(), getNewPublickey(), getChangingAccount(), TransactionDetailsListItem( header: AppLocalization.of(context) .accountOPDetails, value: widget.operation.account.toString()), TransactionDetailsListItem( header: AppLocalization.of(context) .signeraccountOPDetails, value: widget.operation.signerAccount .toString()), ], ), ), // Bottom Gradient Align( alignment: Alignment.bottomCenter, child: Container( height: 40, width: double.maxFinite, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: [ 0.0, 1.0 ], colors: [ StateContainer.of(context) .curTheme .backgroundPrimary .withOpacity(0.0), StateContainer.of(context) .curTheme .backgroundPrimary, ]), borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), ), ) ], ), ), //"Open in Explorer" button Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).openInExplorerButton, buttonTop: true, onPressed: () { AppWebView.showWebView(context, 'http://explorer.pascalcoin.org/findoperation.php?ophash=${widget.operation.ophash}'); }, ), ], ), // "Close" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ), ), ], ); } } class TransactionDetailsListItem extends StatefulWidget { final String header; final String value; final bool withDivider; TransactionDetailsListItem( {this.header, this.value, this.withDivider = true}); _TransactionDetailsListItemState createState() => _TransactionDetailsListItemState(); } class _TransactionDetailsListItemState extends State { @override void initState() { super.initState(); } Timer _copiedTimer; bool _copied = false; @override Widget build(BuildContext context) { return Column(children: [ Container( width: double.maxFinite, color: _copied ? StateContainer.of(context).curTheme.success : StateContainer.of(context).curTheme.backgroundPrimary, child: FlatButton( onPressed: () { Clipboard.setData(ClipboardData(text: widget.value.trim())); setState(() { _copied = true; }); if (_copiedTimer != null) { _copiedTimer.cancel(); } _copiedTimer = Timer(const Duration(milliseconds: 1000), () { if (mounted) { setState(() { _copied = false; }); } }); }, splashColor: StateContainer.of(context).curTheme.textDark30, highlightColor: StateContainer.of(context).curTheme.textDark15, padding: EdgeInsetsDirectional.fromSTEB(0, 16, 0, 16), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: MediaQuery.of(context).size.width - 100, child: AutoSizeText( widget.header, style: _copied ? AppStyles.textLightSmall400(context) : AppStyles.textDarkSmall400(context), textAlign: TextAlign.center, ), ), Container( width: MediaQuery.of(context).size.width - 100, margin: EdgeInsetsDirectional.only(top: 6), child: AutoSizeText( widget.value, style: _copied ? AppStyles.textLightLarge700(context) : AppStyles.textDarkLarge700(context), textAlign: TextAlign.center, ), ), _copied ? Container( alignment: Alignment(1, 0), margin: EdgeInsetsDirectional.only(end: 16), child: AutoSizeText( AppLocalization.of(context).copiedButton, style: AppStyles.textLightSmall700(context), textAlign: TextAlign.end, ), ) : SizedBox() ], ), ), ), widget.withDivider ? Container( width: double.maxFinite, height: 1, color: StateContainer.of(context).curTheme.textDark10, ) : SizedBox() ]); } } ================================================ FILE: lib/ui/account/operation_sheet.dart ================================================ import 'dart:async'; import 'dart:typed_data'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/account/operation_details_sheet.dart'; import 'package:blaise_wallet_flutter/ui/settings/contacts/add_contact_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pascaldart/crypto.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:quiver/strings.dart'; class OperationSheet extends StatefulWidget { final String payload; final String ophash; final AccountNumber account; final bool isContact; final PascalOperation operation; OperationSheet( {@required this.ophash, @required this.account, @required this.operation, this.payload, this.isContact = false}); _OperationSheetState createState() => _OperationSheetState(); } class _OperationSheetState extends State { bool _addressCopied; Timer _addressCopiedTimer; String payload; bool payloadCopied; Timer payloadCopiedTimer; @override void initState() { super.initState(); _addressCopied = false; payloadCopied = false; if (isNotEmpty(widget.payload)) { try { payload = PDUtil.bytesToUtf8String(PDUtil.hexToBytes(widget.payload)); } catch (e) { // Try to decrypt this payload with private key sl.get().getPrivateKey().then((pkey) { PrivateKey pk = PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes(pkey)); try { Uint8List result = EciesCrypt.decrypt(PDUtil.hexToBytes(widget.payload), pk); String asUtf8 = PDUtil.bytesToUtf8String(result); setState(() { payload = asUtf8; }); } catch (e) {} }); } } } @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ isNotEmpty(payload) ? GestureDetector( onTapDown: (details) { _copyPayloadToClipboard(); }, child: Column( children: [ Container( margin: EdgeInsetsDirectional.only(top: 20), child: AutoSizeText( payloadCopied ? AppLocalization.of(context).copiedButton : AppLocalization.of(context) .payloadTextFieldHeader, maxLines: 1, stepGranularity: 1, minFontSize: 8, textAlign: TextAlign.start, style: payloadCopied ? AppStyles.headerSmallBoldSuccess(context) : AppStyles.headerSmallBold(context), ), ), Container( margin: EdgeInsetsDirectional.only( start: 24, end: 24, top: 10, bottom: 4), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: payloadCopied ? StateContainer.of(context) .curTheme .success15 : StateContainer.of(context) .curTheme .textDark15), color: payloadCopied ? StateContainer.of(context) .curTheme .success10 : StateContainer.of(context) .curTheme .textDark10, ), child: AutoSizeText( payload, maxLines: 6, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: payloadCopied ? AppStyles.paragraphMediumSuccess(context) : AppStyles.paragraphMedium(context), ), ) ], ), ) : SizedBox(), //"Copy Address", "Add to Contacts" and "Operation Details" buttons Row( children: [ widget.operation.optype == OpType.TRANSACTION ? AppButton( type: _addressCopied ? AppButtonType.Success : AppButtonType.Primary, text: _addressCopied ? AppLocalization.of(context).copiedAddressButton : AppLocalization.of(context).copyAddressButton, buttonTop: true, onPressed: () { Clipboard.setData( ClipboardData(text: widget.account.toString())); setState(() { _addressCopied = true; }); if (_addressCopiedTimer != null) { _addressCopiedTimer.cancel(); } _addressCopiedTimer = Timer(const Duration(milliseconds: 1500), () { if (mounted) { setState(() { _addressCopied = false; }); } }); }, ) : SizedBox(), ], ), widget.isContact || widget.operation.optype != OpType.TRANSACTION ? SizedBox() : Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).addToContactsButton, buttonMiddle: true, onPressed: () { Navigator.pop(context); AppSheets.showBottomSheet( context: context, widget: AddContactSheet( account: widget.account, )); }, ), ], ), widget.operation.optype != OpType.TRANSACTION ? SizedBox(height: 4) : SizedBox(), // "Operation Details" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).operationDetailsButton, onPressed: () { Navigator.of(context).pop(); AppSheets.showBottomSheet( context: context, widget: OperationDetailsSheet( operation: widget.operation)); }, ), ], ), ], ), ), ], ); } void _copyPayloadToClipboard() { Clipboard.setData(ClipboardData(text: this.payload)); setState(() { payloadCopied = true; }); if (payloadCopiedTimer != null) { payloadCopiedTimer.cancel(); } payloadCopiedTimer = Timer(const Duration(milliseconds: 1500), () { if (mounted) { setState(() { payloadCopied = false; }); } }); } } ================================================ FILE: lib/ui/account/other_operations/change_name/change_name_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/change_name/changing_name_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/formatters.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/error_container.dart'; import 'package:blaise_wallet_flutter/ui/widgets/fee_container.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:pascaldart/pascaldart.dart'; class ChangeNameSheet extends StatefulWidget { final PascalAccount account; ChangeNameSheet({@required this.account}); _ChangeNameSheetState createState() => _ChangeNameSheetState(); } class _ChangeNameSheetState extends State { FocusNode _nameFocus; TextEditingController _nameController; String _nameError; // Fee bool _hasFee; @override void initState() { super.initState(); this._nameFocus = FocusNode(); this._nameController = TextEditingController(); this._hasFee = walletState.shouldHaveFee(); } @override Widget build(BuildContext context) { return TapOutsideUnfocus( child: Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase( AppLocalization.of(context).changeNameSheetHeader, context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 40, 30, 20), child: AutoSizeText( AppLocalization.of(context).changeNameParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), Expanded( child: KeyboardAvoider( duration: Duration(milliseconds: 0), autoScroll: true, focusPadding: 40, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Container for the name text field Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 10, 30, 0), child: AppTextField( label: AppLocalization.of(context) .newAccountNameTextFieldHeader, style: AppStyles.paragraphMedium(context), maxLines: 1, controller: _nameController, focusNode: _nameFocus, inputFormatters: [ FilteringTextInputFormatter.allow(RegExp( r'[0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*()\-+{}[\]_:"|<>,\.\?\/~]')), PascalNameFormatter() ], onChanged: (nt) { if (_nameError != null) { setState(() { _nameError = null; }); } }, ), ), // Fee container _hasFee ? FeeContainer( feeText: walletState.MIN_FEE.toStringOpt()) : SizedBox(), // Error Text ErrorContainer( errorText: _nameError == null ? "" : _nameError, ), // Bottom Margin SizedBox(height: 24), ], ), ), ), ], ), ), // "Change Name" button Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).changeNameButton, onPressed: () { validateAndChangeName(); }, ), ], ), ], ), ), ), ], )); } void validateAndChangeName() { try { if (_nameController.text.isNotEmpty && _nameController.text.length < 3) { setState(() { _nameError = AppLocalization.of(context).threeCharacterNameError; }); return; } AccountName accountName = AccountName(_nameController.text); AppSheets.showBottomSheet( context: context, widget: ChangingNameSheet( account: widget.account, newName: accountName, fee: _hasFee ? walletState.MIN_FEE : walletState.NO_FEE), noBlur: true); } catch (e) { setState(() { _nameError = AppLocalization.of(context).invalidAccountNameError; }); } } } ================================================ FILE: lib/ui/account/other_operations/change_name/changed_name_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:pascaldart/pascaldart.dart'; class ChangedNameSheet extends StatefulWidget { final AccountName newName; final Currency fee; ChangedNameSheet({@required this.newName, @required this.fee}); _ChangedNameSheetState createState() => _ChangedNameSheetState(); } class _ChangedNameSheetState extends State { @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( width: double.maxFinite, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: [ 0.0, 0.7, 0.7, 1.0 ], colors: [ StateContainer.of(context).curTheme.success, StateContainer.of(context).curTheme.success, StateContainer.of(context).curTheme.backgroundPrimary, StateContainer.of(context).curTheme.backgroundPrimary, ]), borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Container( margin: EdgeInsetsDirectional.only(top: 8), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Sized Box SizedBox( height: 50, width: 65, ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .changedNameSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), // Tick Container( margin: EdgeInsetsDirectional.only(top: 8), height: 110, width: 110, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: StateContainer.of(context).curTheme.success, boxShadow: [ StateContainer.of(context) .curTheme .shadowTextDarkTwo ], ), child: Icon( AppIcons.tick, size: 40, color: StateContainer.of(context) .curTheme .backgroundPrimary, ), ), ], ), ), ), Expanded( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 40, 30, 0), child: AutoSizeText( AppLocalization.of(context).changedNameParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), // New Account Name Header Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .newAccountNameTextFieldHeader, style: AppStyles.textFieldLabelSuccess(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the name Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: AutoSizeText( widget.newName.toString(), maxLines: 6, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.paragraphMedium(context), ), ), // "Fee" header widget.fee != Currency("0") ? Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .feeTextFieldHeader, style: AppStyles.textFieldLabelSuccess(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ) : SizedBox(), // Container for the fee widget.fee != Currency("0") ? Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .success15), color: StateContainer.of(context) .curTheme .success10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontSuccessBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.fee.toStringOpt(), style: AppStyles.balanceSmallSuccess( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ) : SizedBox(), // Bottom Margin SizedBox(height: 24), ], ), ), ), // "CANCEL" button Row( children: [ AppButton( type: AppButtonType.SuccessOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } } ================================================ FILE: lib/ui/account/other_operations/change_name/changing_name_sheet.dart ================================================ import 'dart:async'; import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/authenticated_event.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/store/account/account.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/change_name/changed_name_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/routes.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/pin_screen.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/util/authentication.dart'; import 'package:blaise_wallet_flutter/util/haptic_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:flare_flutter/flare_actor.dart'; import 'package:flutter/material.dart'; import 'package:pascaldart/pascaldart.dart'; class ChangingNameSheet extends StatefulWidget { final PascalAccount account; final AccountName newName; final Currency fee; ChangingNameSheet( {@required this.account, @required this.newName, @required this.fee}); _ChangingNameSheetState createState() => _ChangingNameSheetState(); } class _ChangingNameSheetState extends State { OverlayEntry _overlay; Account accountState; StreamSubscription _authSub; void _registerBus() { _authSub = EventTaxiImpl.singleton() .registerTo() .listen((event) { if (event.authType == AUTH_EVENT_TYPE.CHANGE) { doChange(); } }); } void _destroyBus() { if (_authSub != null) { _authSub.cancel(); } } @override void dispose() { _destroyBus(); super.dispose(); } @override void initState() { super.initState(); _registerBus(); this.accountState = walletState.getAccountState(widget.account); } void showOverlay(BuildContext context) { OverlayState overlayState = Overlay.of(context); _overlay = OverlayEntry( builder: (context) => BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container( width: double.maxFinite, height: double.maxFinite, color: StateContainer.of(context).curTheme.overlay20, child: Center( child: //Container for the animation Container( margin: EdgeInsetsDirectional.only( top: MediaQuery.of(context).padding.top), //Width/Height ratio for the animation is needed because BoxFit is not working as expected width: double.maxFinite, height: MediaQuery.of(context).size.width, child: Center( child: FlareActor( StateContainer.of(context).curTheme.animationNameChange, animation: "main", fit: BoxFit.contain, ), ), ), ), ), ), ); overlayState.insert(_overlay); } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .changingNameSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 40, 30, 0), child: AutoSizeText( AppLocalization.of(context).changingNameParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), // "Address" header Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .newAccountNameTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the name Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: AutoSizeText( widget.newName.toString(), maxLines: 2, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.paragraphMedium(context), ), ), // "Fee" header widget.fee != Currency("0") ? Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).feeTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ) : SizedBox(), // Container for the fee widget.fee != Currency("0") ? Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .primary15), color: StateContainer.of(context) .curTheme .primary10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontPrimaryBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.fee.toStringOpt(), style: AppStyles.balanceSmall(context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ) : SizedBox(), // Bottom Margin SizedBox(height: 24), ], ), ), // "CONFIRM" button Row( children: [ AppButton( type: AppButtonType.Primary, text: toUppercase(AppLocalization.of(context) .confirmButton ,context), buttonTop: true, onPressed: () async { if (await authenticate()) { EventTaxiImpl.singleton() .fire(AuthenticatedEvent(AUTH_EVENT_TYPE.CHANGE)); } }, ), ], ), // "CANCEL" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: toUppercase(AppLocalization.of(context) .cancelButton ,context), onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } Future doChange({Currency fee}) async { fee = fee == null ? widget.fee : fee; try { showOverlay(context); RPCResponse result = await accountState.changeAccountName(widget.newName, fee: fee); if (result.isError) { ErrorResponse errResp = result; UIUtil.showSnackbar(errResp.errorMessage.replaceAll("founds", "funds"), context); _overlay?.remove(); Navigator.of(context).pop(); } else { _overlay?.remove(); try { OperationsResponse resp = result; PascalOperation op = resp.operations[0]; if (op.valid == null || op.valid) { // Update name walletState.updateAccountName(widget.account, widget.newName); Navigator.of(context).popUntil(RouteUtils.withNameLike("/account")); AppSheets.showBottomSheet( context: context, closeOnTap: true, widget: ChangedNameSheet( newName: widget.newName, fee: fee, )); } else { if (op.errors.contains("zero fee") && widget.fee == walletState.NO_FEE) { UIUtil.showFeeDialog( context: context, onConfirm: () async { Navigator.of(context).pop(); doChange(fee: walletState.MIN_FEE); }); } else { UIUtil.showSnackbar("${op.errors}", context); } } } catch (e) { UIUtil.showSnackbar( AppLocalization.of(context).somethingWentWrongError, context); } } } catch (e) { _overlay?.remove(); UIUtil.showSnackbar( AppLocalization.of(context).somethingWentWrongError, context); } } Future _authenticateBiometrics(AuthUtil authUtil, String message) async { // Biometric auth bool authenticated = await authUtil.authenticateWithBiometrics(message); if (authenticated) { HapticUtil.fingerprintSucess(); } return authenticated; } Future _authenticatePin(String message) async { String expectedPin = await sl.get().getPin(); bool result = await Navigator.of(context) .push(MaterialPageRoute(builder: (BuildContext context) { return PinScreen( type: PinOverlayType.ENTER_PIN, onSuccess: (pin) { Navigator.of(context).pop(true); }, expectedPin: expectedPin, description: message, ); })); await Future.delayed(Duration(milliseconds: 200)); return result != null && result; } Future authenticate() async { String message = AppLocalization.of(context) .authenticateToChangeNameParagraph .replaceAll("%1", widget.newName.toString()); // Authenticate AuthUtil authUtil = AuthUtil(); if (await authUtil.useBiometrics()) { // Biometric auth try { return await _authenticateBiometrics(authUtil, message); } catch (e) { return await _authenticatePin(message); } } else { return await _authenticatePin(message); } } } ================================================ FILE: lib/ui/account/other_operations/delist_for_sale/delisted_for_sale.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:pascaldart/pascaldart.dart'; class DelistedForSaleSheet extends StatefulWidget { final AccountNumber account; final Currency fee; DelistedForSaleSheet({@required this.account, @required this.fee}) : super(); _DelistedForSaleSheetState createState() => _DelistedForSaleSheetState(); } class _DelistedForSaleSheetState extends State { @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( width: double.maxFinite, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: [ 0.0, 0.7, 0.7, 1.0 ], colors: [ StateContainer.of(context).curTheme.success, StateContainer.of(context).curTheme.success, StateContainer.of(context).curTheme.backgroundPrimary, StateContainer.of(context).curTheme.backgroundPrimary, ]), borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Container( margin: EdgeInsetsDirectional.only(top: 8), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Sized Box SizedBox( height: 50, width: 65, ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .delistedSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), // Tick Container( margin: EdgeInsetsDirectional.only(top: 8), height: 110, width: 110, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: StateContainer.of(context).curTheme.success, boxShadow: [ StateContainer.of(context) .curTheme .shadowTextDarkTwo ], ), child: Icon( AppIcons.tick, size: 40, color: StateContainer.of(context) .curTheme .backgroundPrimary, ), ), ], ), ), ), Expanded( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .delistedFromSaleParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), // "Account" header Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).accountTextFieldHeader, style: AppStyles.textFieldLabelSuccess(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the account number Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: AutoSizeText( widget.account.toString(), maxLines: 1, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyTextDark(context), ), ), // "Fee" header widget.fee != Currency("0") ? Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .feeTextFieldHeader, style: AppStyles.textFieldLabelSuccess(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ) : SizedBox(), // Container for the fee widget.fee != Currency("0") ? Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .success15), color: StateContainer.of(context) .curTheme .success10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontSuccessBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.fee.toStringOpt(), style: AppStyles.balanceSmallSuccess( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ) : SizedBox(), // Bottom Margin SizedBox(height: 24), ], ), ), ), // "CANCEL" button Row( children: [ AppButton( type: AppButtonType.SuccessOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } } ================================================ FILE: lib/ui/account/other_operations/delist_for_sale/delisting_for_sale.dart ================================================ import 'dart:async'; import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/events.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/store/account/account.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/delist_for_sale/delisted_for_sale.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/routes.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/pin_screen.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/util/authentication.dart'; import 'package:blaise_wallet_flutter/util/haptic_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:flare_flutter/flare_actor.dart'; import 'package:flutter/material.dart'; import 'package:pascaldart/pascaldart.dart'; class DelistingForSaleSheet extends StatefulWidget { final PascalAccount account; final Currency fee; DelistingForSaleSheet({@required this.account, @required this.fee}) : super(); _DelistingForSaleSheetState createState() => _DelistingForSaleSheetState(); } class _DelistingForSaleSheetState extends State { OverlayEntry _overlay; Account accountState; StreamSubscription _authSub; void _registerBus() { _authSub = EventTaxiImpl.singleton() .registerTo() .listen((event) { if (event.authType == AUTH_EVENT_TYPE.DELIST_FORSALE) { doDelist(); } }); } void _destroyBus() { if (_authSub != null) { _authSub.cancel(); } } @override void dispose() { _destroyBus(); super.dispose(); } @override void initState() { super.initState(); _registerBus(); this.accountState = walletState.getAccountState(widget.account); } void showOverlay(BuildContext context) { OverlayState overlayState = Overlay.of(context); _overlay = OverlayEntry( builder: (context) => BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container( width: double.maxFinite, height: double.maxFinite, color: StateContainer.of(context).curTheme.overlay20, child: Center( child: //Container for the animation Container( margin: EdgeInsetsDirectional.only( top: MediaQuery.of(context).padding.top), //Width/Height ratio for the animation is needed because BoxFit is not working as expected width: double.maxFinite, height: MediaQuery.of(context).size.width, child: Center( child: FlareActor( StateContainer.of(context).curTheme.animationSale, animation: "main", fit: BoxFit.contain, ), ), ), ), ), ), ); overlayState.insert(_overlay); } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .delistingSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).delistFromSaleParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), // "Account" header Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).accountTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the account number Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: AutoSizeText( widget.account.account.toString(), maxLines: 1, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyTextDark(context), ), ), // "Fee" header widget.fee != Currency("0") ? Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).feeTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ) : SizedBox(), // Container for the fee widget.fee != Currency("0") ? Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .primary15), color: StateContainer.of(context) .curTheme .primary10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontPrimaryBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.fee.toStringOpt(), style: AppStyles.balanceSmall(context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ) : SizedBox(), // Bottom Margin SizedBox(height: 24), ], ), ), // "CONFIRM" button Row( children: [ AppButton( type: AppButtonType.Primary, text: toUppercase(AppLocalization.of(context) .confirmButton ,context), buttonTop: true, onPressed: () async { if (await authenticate()) { EventTaxiImpl.singleton().fire(AuthenticatedEvent( AUTH_EVENT_TYPE.DELIST_FORSALE)); } }, ), ], ), // "CANCEL" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: toUppercase(AppLocalization.of(context) .cancelButton ,context), onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } Future doDelist({Currency fee}) async { fee = fee == null ? widget.fee : fee; try { showOverlay(context); RPCResponse result = await accountState.delistAccountForSale(fee: fee); if (result.isError) { ErrorResponse errResp = result; UIUtil.showSnackbar(errResp.errorMessage.replaceAll("founds", "funds"), context); _overlay?.remove(); Navigator.of(context).pop(); } else { _overlay?.remove(); try { OperationsResponse resp = result; PascalOperation op = resp.operations[0]; if (op.valid == null || op.valid) { // Update state accountState.changeAccountState(AccountState.NORMAL); Navigator.of(context).popUntil(RouteUtils.withNameLike("/account")); ; AppSheets.showBottomSheet( context: context, closeOnTap: true, widget: DelistedForSaleSheet( account: widget.account.account, fee: fee, )); } else { if (op.errors.contains("zero fee") && widget.fee == walletState.NO_FEE) { UIUtil.showFeeDialog( context: context, onConfirm: () async { Navigator.of(context).pop(); doDelist(fee: walletState.MIN_FEE); }); } else { UIUtil.showSnackbar("${op.errors}", context); } } } catch (e) { UIUtil.showSnackbar( "Something went wrong, try again later.", context); } } } catch (e) { _overlay?.remove(); UIUtil.showSnackbar("Something went wrong, try again later.", context); } } Future _authenticateBiometrics(AuthUtil authUtil, String message) async { // Biometric auth bool authenticated = await authUtil.authenticateWithBiometrics(message); if (authenticated) { HapticUtil.fingerprintSucess(); } return authenticated; } Future _authenticatePin(String message) async { String expectedPin = await sl.get().getPin(); bool result = await Navigator.of(context) .push(MaterialPageRoute(builder: (BuildContext context) { return PinScreen( type: PinOverlayType.ENTER_PIN, onSuccess: (pin) { Navigator.of(context).pop(true); }, expectedPin: expectedPin, description: message, ); })); await Future.delayed(Duration(milliseconds: 200)); return result != null && result; } Future authenticate() async { String message = AppLocalization.of(context).authenticateToDelistParagraph; // Authenticate AuthUtil authUtil = AuthUtil(); if (await authUtil.useBiometrics()) { // Biometric auth try { return await _authenticateBiometrics(authUtil, message); } catch (e) { return await _authenticatePin(message); } } else { return await _authenticatePin(message); } } } ================================================ FILE: lib/ui/account/other_operations/list_for_sale/list_for_sale_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/list_for_sale/listing_for_sale_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/formatters.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/error_container.dart'; import 'package:blaise_wallet_flutter/ui/widgets/fee_container.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/number_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/user_data_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:pascaldart/pascaldart.dart'; class ListForSaleSheet extends StatefulWidget { final PascalAccount account; ListForSaleSheet({@required this.account}) : super(); _ListForSaleSheetState createState() => _ListForSaleSheetState(); } class _ListForSaleSheetState extends State { FocusNode priceFocusNode; FocusNode receiverFocusNode; TextEditingController priceController; TextEditingController receiverController; String _priceErr; String _receiverErr; // Fee bool _hasFee; @override void initState() { super.initState(); this.priceFocusNode = FocusNode(); this.receiverFocusNode = FocusNode(); this.priceController = TextEditingController(); this.receiverController = TextEditingController(); this._hasFee = walletState.shouldHaveFee(); this.receiverFocusNode.addListener(() { if (!this.receiverFocusNode.hasFocus) { try { AccountNumber numberFormatted = AccountNumber(this.receiverController.text); this.receiverController.text = numberFormatted.toString(); } catch (e) {} } }); } @override Widget build(BuildContext context) { return TapOutsideUnfocus( child: Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase( AppLocalization.of(context) .listForSaleSheetHeader, context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 20), child: AutoSizeText( AppLocalization.of(context).listForSaleParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), Expanded( child: KeyboardAvoider( duration: Duration(milliseconds: 0), autoScroll: true, focusPadding: 40, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Container for price field Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 10, 30, 0), child: AppTextField( label: AppLocalization.of(context) .priceTextFieldHeader, style: AppStyles.paragraphPrimary(context), maxLines: 1, inputType: TextInputType.numberWithOptions( decimal: true), prefix: Icon( AppIcons.pascalsymbol, size: 15, color: StateContainer.of(context) .curTheme .primary, ), focusNode: priceFocusNode, controller: priceController, onChanged: (text) { if (_priceErr != null) { setState(() { _priceErr = null; }); } }, inputFormatters: [ LengthLimitingTextInputFormatter(13), CurrencyFormatter( maxDecimalDigits: NumberUtil.maxDecimalDigits) ], textInputAction: TextInputAction.next, onSubmitted: (text) { receiverFocusNode.requestFocus(); }, ), ), _hasFee ? FeeContainer( feeText: walletState.MIN_FEE.toStringOpt()) : SizedBox(), ErrorContainer(errorText: _priceErr ?? ""), // Container for receving account field Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 0), child: AppTextField( label: AppLocalization.of(context) .receivingAccountTextFieldHeader, style: AppStyles.privateKeyTextDark(context), maxLines: 1, firstButton: TextFieldButton( icon: AppIcons.paste, onPressed: () async { String text = await UserDataUtil.getClipboardText( DataType.ACCOUNT); if (text != null) { receiverFocusNode.unfocus(); receiverController.text = text; } }, ), secondButton: TextFieldButton( icon: AppIcons.scan, onPressed: () async { String text = await UserDataUtil.getQRData( DataType.ACCOUNT, StateContainer.of(context) .curTheme .scannerTheme); if (text != null) { receiverFocusNode.unfocus(); receiverController.text = text; } }, ), inputFormatters: [ FilteringTextInputFormatter.allow( RegExp("[0-9-]")), PascalAccountFormatter() ], onChanged: (text) { if (_receiverErr != null) { setState(() { _receiverErr = null; }); } }, focusNode: receiverFocusNode, controller: receiverController, ), ), Container( margin: EdgeInsetsDirectional.only(bottom: 40), child: ErrorContainer( errorText: _receiverErr ?? ""), ), // Bottom Margin SizedBox(height: 24), ], ), ), ), ], ), ), // "List for Sale" button Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).listForSaleButton, onPressed: () { if (validateFormData()) { AppSheets.showBottomSheet( context: context, widget: ListingForSaleSheet( account: widget.account, price: Currency(priceController.text), receiver: AccountNumber(receiverController.text), fee: _hasFee ? walletState.MIN_FEE : walletState.NO_FEE), noBlur: true); } }, ), ], ), ], ), ), ), ], )); } bool validateFormData() { bool isValid = true; // Validate receiver if (receiverController.text.trim().length == 0) { isValid = false; setState(() { _receiverErr = AppLocalization.of(context).invalidReceivingAccountError; }); } else { try { AccountNumber(receiverController.text); } catch (e) { isValid = false; setState(() { _receiverErr = AppLocalization.of(context).invalidReceivingAccountError; }); } } // Validate price if (priceController.text.trim().length == 0) { isValid = false; setState(() { _priceErr = AppLocalization.of(context).priceRequiredError; }); } else { Currency price = Currency(priceController.text); if (price == Currency('0')) { isValid = false; setState(() { _priceErr = AppLocalization.of(context).zeroPriceError; }); } } return isValid; } } ================================================ FILE: lib/ui/account/other_operations/list_for_sale/listed_for_sale_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:pascaldart/pascaldart.dart'; class ListedForSaleSheet extends StatefulWidget { final Currency price; final AccountNumber receiver; final Currency fee; ListedForSaleSheet( {@required this.price, @required this.receiver, @required this.fee}) : super(); _ListedForSaleSheetState createState() => _ListedForSaleSheetState(); } class _ListedForSaleSheetState extends State { @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( width: double.maxFinite, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: [ 0.0, 0.7, 0.7, 1.0 ], colors: [ StateContainer.of(context).curTheme.success, StateContainer.of(context).curTheme.success, StateContainer.of(context).curTheme.backgroundPrimary, StateContainer.of(context).curTheme.backgroundPrimary, ]), borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Container( margin: EdgeInsetsDirectional.only(top: 8), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Sized Box SizedBox( height: 50, width: 65, ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .listedForSaleSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), // Tick Container( margin: EdgeInsetsDirectional.only(top: 8), height: 110, width: 110, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: StateContainer.of(context).curTheme.success, boxShadow: [ StateContainer.of(context) .curTheme .shadowTextDarkTwo ], ), child: Icon( AppIcons.tick, size: 40, color: StateContainer.of(context) .curTheme .backgroundPrimary, ), ), ], ), ), ), Expanded( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).listedForSaleParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), // Price and fee Container( margin: EdgeInsetsDirectional.fromSTEB(30, 0, 30, 0), child: Row( children: [ // Price Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Price" header Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 0, 30, 0, 0), child: AutoSizeText( AppLocalization.of(context) .priceTextFieldHeader, style: AppStyles.textFieldLabelSuccess( context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the Price Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 0, 12, 0, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .success15), color: StateContainer.of(context) .curTheme .success10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontSuccessBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.price.toStringOpt(), style: AppStyles.balanceSmallSuccess( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ), ], ), widget.fee != Currency("0") ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Fee" header Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 16, 30, 0, 0), child: AutoSizeText( AppLocalization.of(context) .feeTextFieldHeader, style: AppStyles.textFieldLabelSuccess( context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the fee Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 16, 12, 0, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .success15), color: StateContainer.of(context) .curTheme .success10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontSuccessBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.fee .toStringOpt(), style: AppStyles .balanceSmallSuccess( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ), ], ) : SizedBox(), ], ), ), // "Receving Account" header Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .receivingAccountTextFieldHeader, style: AppStyles.textFieldLabelSuccess(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the account number Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: AutoSizeText( widget.receiver.toString(), maxLines: 1, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyTextDark(context), ), ), // Bottom Margin SizedBox(height: 24), ], ), ), ), // "CANCEL" button Row( children: [ AppButton( type: AppButtonType.SuccessOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } } ================================================ FILE: lib/ui/account/other_operations/list_for_sale/listing_for_sale_sheet.dart ================================================ import 'dart:async'; import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/events.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/store/account/account.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/list_for_sale/listed_for_sale_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/routes.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/pin_screen.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/util/authentication.dart'; import 'package:blaise_wallet_flutter/util/haptic_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:flare_flutter/flare_actor.dart'; import 'package:flutter/material.dart'; import 'package:pascaldart/pascaldart.dart'; class ListingForSaleSheet extends StatefulWidget { final PascalAccount account; final Currency price; final AccountNumber receiver; final Currency fee; ListingForSaleSheet( {@required this.account, @required this.price, @required this.receiver, @required this.fee}) : super(); _ListingForSaleSheetState createState() => _ListingForSaleSheetState(); } class _ListingForSaleSheetState extends State { OverlayEntry _overlay; Account accountState; StreamSubscription _authSub; void _registerBus() { _authSub = EventTaxiImpl.singleton() .registerTo() .listen((event) { if (event.authType == AUTH_EVENT_TYPE.LIST_FORSALE) { doList(); } }); } void _destroyBus() { if (_authSub != null) { _authSub.cancel(); } } @override void dispose() { _destroyBus(); super.dispose(); } @override void initState() { super.initState(); _registerBus(); this.accountState = walletState.getAccountState(widget.account); } void showOverlay(BuildContext context) { OverlayState overlayState = Overlay.of(context); _overlay = OverlayEntry( builder: (context) => BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container( width: double.maxFinite, height: double.maxFinite, color: StateContainer.of(context).curTheme.overlay20, child: Center( child: //Container for the animation Container( margin: EdgeInsetsDirectional.only( top: MediaQuery.of(context).padding.top), //Width/Height ratio for the animation is needed because BoxFit is not working as expected width: double.maxFinite, height: MediaQuery.of(context).size.width, child: Center( child: FlareActor( StateContainer.of(context).curTheme.animationSale, animation: "main", fit: BoxFit.contain, ), ), ), ), ), ), ); overlayState.insert(_overlay); } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .listingForSaleSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).listingForSaleParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), // Price and fee Container( margin: EdgeInsetsDirectional.fromSTEB(30, 0, 30, 0), child: Row( children: [ // Price Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Price" header Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 0, 30, 0, 0), child: AutoSizeText( AppLocalization.of(context) .priceTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the Price Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 0, 12, 0, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .primary15), color: StateContainer.of(context) .curTheme .primary10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontPrimaryBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.price.toStringOpt(), style: AppStyles.balanceSmall( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ), ], ), widget.fee != Currency("0") ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Fee" header Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 16, 30, 0, 0), child: AutoSizeText( AppLocalization.of(context) .feeTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the fee Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 16, 12, 0, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .primary15), color: StateContainer.of(context) .curTheme .primary10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontPrimaryBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.fee.toStringOpt(), style: AppStyles.balanceSmall( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ), ], ) : SizedBox(), // Bottom Margin SizedBox(height: 24), ], ), ), // "Receving Account" header Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .receivingAccountTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the account number Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: AutoSizeText( widget.receiver.toString(), maxLines: 1, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyTextDark(context), ), ), ], ), ), // "CONFIRM" button Row( children: [ AppButton( type: AppButtonType.Primary, text: toUppercase(AppLocalization.of(context) .confirmButton ,context), buttonTop: true, onPressed: () async { if (await authenticate()) { EventTaxiImpl.singleton().fire( AuthenticatedEvent(AUTH_EVENT_TYPE.LIST_FORSALE)); } }, ), ], ), // "CANCEL" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: toUppercase(AppLocalization.of(context) .cancelButton ,context), onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } Future doList({Currency fee}) async { fee = fee == null ? widget.fee : fee; try { showOverlay(context); RPCResponse result = await accountState .listAccountForSale(widget.price, widget.receiver, fee: fee); if (result.isError) { ErrorResponse errResp = result; UIUtil.showSnackbar(errResp.errorMessage.replaceAll("founds", "funds"), context); _overlay?.remove(); Navigator.of(context).pop(); } else { _overlay?.remove(); try { OperationsResponse resp = result; PascalOperation op = resp.operations[0]; if (op.valid == null || op.valid) { // Update state accountState.changeAccountState(AccountState.LISTED); Navigator.of(context).popUntil(RouteUtils.withNameLike("/account")); AppSheets.showBottomSheet( context: context, closeOnTap: true, widget: ListedForSaleSheet( receiver: widget.receiver, price: widget.price, fee: fee, )); } else { if (op.errors.contains("zero fee") && widget.fee == walletState.NO_FEE) { UIUtil.showFeeDialog( context: context, onConfirm: () async { Navigator.of(context).pop(); doList(fee: walletState.MIN_FEE); }); } else { UIUtil.showSnackbar("${op.errors}", context); } } } catch (e) { UIUtil.showSnackbar( AppLocalization.of(context).somethingWentWrongError, context); } } } catch (e) { _overlay?.remove(); UIUtil.showSnackbar( AppLocalization.of(context).somethingWentWrongError, context); } } Future _authenticateBiometrics(AuthUtil authUtil, String message) async { // Biometric auth bool authenticated = await authUtil.authenticateWithBiometrics(message); if (authenticated) { HapticUtil.fingerprintSucess(); } return authenticated; } Future _authenticatePin(String message) async { String expectedPin = await sl.get().getPin(); bool result = await Navigator.of(context) .push(MaterialPageRoute(builder: (BuildContext context) { return PinScreen( type: PinOverlayType.ENTER_PIN, onSuccess: (pin) { Navigator.of(context).pop(true); }, expectedPin: expectedPin, description: message, ); })); await Future.delayed(Duration(milliseconds: 200)); return result != null && result; } Future authenticate() async { String message = AppLocalization.of(context).authenticateToListForSaleParagraph; // Authenticate AuthUtil authUtil = AuthUtil(); if (await authUtil.useBiometrics()) { // Biometric auth try { return await _authenticateBiometrics(authUtil, message); } catch (e) { return await _authenticatePin(message); } } else { return await _authenticatePin(message); } } } ================================================ FILE: lib/ui/account/other_operations/private_sale/create_private_sale_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/private_sale/creating_private_sale_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/formatters.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/error_container.dart'; import 'package:blaise_wallet_flutter/ui/widgets/fee_container.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/number_util.dart'; import 'package:blaise_wallet_flutter/util/pascal_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/user_data_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:pascaldart/pascaldart.dart'; class CreatePrivateSaleSheet extends StatefulWidget { final PascalAccount account; CreatePrivateSaleSheet({@required this.account}) : super(); _CreatePrivateSaleSheetState createState() => _CreatePrivateSaleSheetState(); } class _CreatePrivateSaleSheetState extends State { FocusNode priceFocusNode; FocusNode receiverFocusNode; FocusNode publicKeyFocusNode; TextEditingController priceController; TextEditingController receiverController; TextEditingController publicKeyController; String _priceErr; String _receiverErr; String _publicKeyErr; // Fee bool _hasFee; @override void initState() { super.initState(); this.priceFocusNode = FocusNode(); this.receiverFocusNode = FocusNode(); this.publicKeyFocusNode = FocusNode(); this.priceController = TextEditingController(); this.receiverController = TextEditingController(); this.publicKeyController = TextEditingController(); this._hasFee = walletState.shouldHaveFee(); this.receiverController.addListener(() { if (!this.receiverFocusNode.hasFocus) { try { AccountNumber numberFormatted = AccountNumber(this.receiverController.text); this.receiverController.text = numberFormatted.toString(); } catch (e) {} } }); } @override Widget build(BuildContext context) { return TapOutsideUnfocus( child: Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase( AppLocalization.of(context) .creatingPrivateSaleSheetHeader, context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 20), child: AutoSizeText( AppLocalization.of(context) .creatingPrivateSaleParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), Expanded( child: KeyboardAvoider( duration: Duration(milliseconds: 0), autoScroll: true, focusPadding: 40, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Container for price field Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 10, 30, 0), child: AppTextField( label: AppLocalization.of(context) .priceTextFieldHeader, style: AppStyles.paragraphPrimary(context), maxLines: 1, inputType: TextInputType.numberWithOptions( decimal: true), prefix: Icon( AppIcons.pascalsymbol, size: 15, color: StateContainer.of(context) .curTheme .primary, ), focusNode: priceFocusNode, controller: priceController, onChanged: (text) { if (_priceErr != null) { setState(() { _priceErr = null; }); } }, inputFormatters: [ LengthLimitingTextInputFormatter(13), CurrencyFormatter( maxDecimalDigits: NumberUtil.maxDecimalDigits) ], textInputAction: TextInputAction.next, onSubmitted: (text) { receiverFocusNode.requestFocus(); }, ), ), _hasFee ? FeeContainer( feeText: walletState.MIN_FEE.toStringOpt()) : SizedBox(), ErrorContainer(errorText: _priceErr ?? ""), // Container for receving account field Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 0), child: AppTextField( label: AppLocalization.of(context) .receivingAccountTextFieldHeader, style: AppStyles.privateKeyTextDark(context), maxLines: 1, firstButton: TextFieldButton( icon: AppIcons.paste, onPressed: () async { String text = await UserDataUtil.getClipboardText( DataType.ACCOUNT); if (text != null) { receiverFocusNode.unfocus(); receiverController.text = text; } }, ), secondButton: TextFieldButton( icon: AppIcons.scan, onPressed: () async { String text = await UserDataUtil.getQRData( DataType.ACCOUNT, StateContainer.of(context) .curTheme .scannerTheme); if (text != null) { receiverFocusNode.unfocus(); receiverController.text = text; } }, ), inputFormatters: [ FilteringTextInputFormatter.allow( RegExp("[0-9-]")), PascalAccountFormatter() ], onChanged: (text) { if (_receiverErr != null) { setState(() { _receiverErr = null; }); } }, focusNode: receiverFocusNode, controller: receiverController, textInputAction: TextInputAction.next, onSubmitted: (text) { publicKeyFocusNode.requestFocus(); }, ), ), ErrorContainer(errorText: _receiverErr ?? ""), // Container for public key field Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 10, 30, 0), child: AppTextField( label: AppLocalization.of(context) .publicKeyTextFieldHeader, style: AppStyles.privateKeyTextDark(context), maxLines: 4, firstButton: TextFieldButton( icon: AppIcons.paste, onPressed: () async { String text = await UserDataUtil.getClipboardText( DataType.PUBLIC_KEY); if (text != null) { publicKeyController.text = text; } }, ), secondButton: TextFieldButton( icon: AppIcons.scan, onPressed: () async { String text = await UserDataUtil.getClipboardText( DataType.PUBLIC_KEY); if (text != null) { publicKeyController.text = text; } }, ), focusNode: publicKeyFocusNode, controller: publicKeyController, onChanged: (text) { if (_publicKeyErr != null) { setState(() { _publicKeyErr = null; }); } }, ), ), ErrorContainer(errorText: _publicKeyErr ?? ""), // Bottom Margin SizedBox(height: 24), // TODO Implement duration /* Row( children: [ Container( height: 40.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100.0), color: StateContainer.of(context) .curTheme .backgroundPrimary, boxShadow: [ StateContainer.of(context) .curTheme .shadowTextDark, ], ), margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 40), child: FlatButton( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(100.0)), child: AutoSizeText( AppLocalization.of(context).addADurationButton, textAlign: TextAlign.center, maxLines: 1, stepGranularity: 0.1, style: AppStyles.buttonMiniBg(context), ), onPressed: () async { return null; }, ), ), ], )*/ ], ), ), ), ], ), ), // "Create Private Sale" button Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).createPrivateSaleButton, onPressed: () { if (validateFormData()) { AppSheets.showBottomSheet( context: context, widget: CreatingPrivateSaleSheet( account: widget.account, price: Currency(priceController.text), receiver: AccountNumber(receiverController.text), publicKey: publicKeyController.text, fee: _hasFee ? walletState.MIN_FEE : walletState.NO_FEE), noBlur: true); } }, ), ], ), ], ), ), ), ], )); } bool validateFormData() { bool isValid = true; // Validate receiver if (receiverController.text.trim().length == 0) { isValid = false; setState(() { _receiverErr = AppLocalization.of(context).invalidReceivingAccountError; }); } else { try { AccountNumber(receiverController.text); } catch (e) { isValid = false; setState(() { _receiverErr = AppLocalization.of(context).invalidReceivingAccountError; }); } } // Validate price if (priceController.text.trim().length == 0) { isValid = false; setState(() { _priceErr = AppLocalization.of(context).priceRequiredError; }); } else { Currency price = Currency(priceController.text); if (price == Currency('0')) { isValid = false; setState(() { _priceErr = AppLocalization.of(context).zeroPriceError; }); } } // Validate public key if (PascalUtil().decipherPublicKey(publicKeyController.text) == null) { isValid = false; setState(() { _publicKeyErr = AppLocalization.of(context).invalidPublicKeyError; }); } return isValid; } } ================================================ FILE: lib/ui/account/other_operations/private_sale/created_private_sale_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:pascaldart/pascaldart.dart'; class CreatedPrivateSaleSheet extends StatefulWidget { final Currency price; final AccountNumber receiver; final Currency fee; final String publicKey; CreatedPrivateSaleSheet( {@required this.price, @required this.receiver, @required this.publicKey, @required this.fee}) : super(); _CreatedPrivateSaleSheetState createState() => _CreatedPrivateSaleSheetState(); } class _CreatedPrivateSaleSheetState extends State { @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( width: double.maxFinite, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: [ 0.0, 0.7, 0.7, 1.0 ], colors: [ StateContainer.of(context).curTheme.success, StateContainer.of(context).curTheme.success, StateContainer.of(context).curTheme.backgroundPrimary, StateContainer.of(context).curTheme.backgroundPrimary, ]), borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Container( margin: EdgeInsetsDirectional.only(top: 8), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Sized Box SizedBox( height: 50, width: 65, ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .createdPrivateSaleSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), // Tick Container( margin: EdgeInsetsDirectional.only(top: 8), height: 110, width: 110, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: StateContainer.of(context).curTheme.success, boxShadow: [ StateContainer.of(context) .curTheme .shadowTextDarkTwo ], ), child: Icon( AppIcons.tick, size: 40, color: StateContainer.of(context) .curTheme .backgroundPrimary, ), ), ], ), ), ), Expanded( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .createdPrivateSaleParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), // Container for price header and price Container( margin: EdgeInsetsDirectional.fromSTEB(30, 0, 30, 0), child: Row( children: [ // Price Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Price" header Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 0, 30, 0, 0), child: AutoSizeText( AppLocalization.of(context) .priceTextFieldHeader, style: AppStyles.textFieldLabelSuccess( context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the Price Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 0, 12, 0, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .success15), color: StateContainer.of(context) .curTheme .success10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontSuccessBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.price.toStringOpt(), style: AppStyles.balanceSmallSuccess( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ), ], ), widget.fee != Currency("0") ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Fee" header Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 16, 30, 0, 0), child: AutoSizeText( AppLocalization.of(context) .feeTextFieldHeader, style: AppStyles.textFieldLabelSuccess( context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the fee Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 16, 12, 0, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .success15), color: StateContainer.of(context) .curTheme .success10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontSuccessBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.fee .toStringOpt(), style: AppStyles .balanceSmallSuccess( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ), ], ) : SizedBox(), ], ), ), // "Receving Account" header Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .receivingAccountTextFieldHeader, style: AppStyles.textFieldLabelSuccess(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the account number Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width / 2 - 36), margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: AutoSizeText( widget.receiver.toString(), maxLines: 1, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyTextDark(context), ), ), // "Public Key" header Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .publicKeyTextFieldHeader, style: AppStyles.textFieldLabelSuccess(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the name Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(24, 12, 24, 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: AutoSizeText( widget.publicKey, maxLines: 4, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyTextDark(context), ), ), // Bottom Margin SizedBox(height: 24), ], ), ), ), // "Close" button Row( children: [ AppButton( type: AppButtonType.SuccessOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } } ================================================ FILE: lib/ui/account/other_operations/private_sale/creating_private_sale_sheet.dart ================================================ import 'dart:async'; import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/events.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/store/account/account.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/private_sale/created_private_sale_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/routes.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/pin_screen.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/util/authentication.dart'; import 'package:blaise_wallet_flutter/util/haptic_util.dart'; import 'package:blaise_wallet_flutter/util/pascal_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:flare_flutter/flare_actor.dart'; import 'package:flutter/material.dart'; import 'package:pascaldart/pascaldart.dart'; class CreatingPrivateSaleSheet extends StatefulWidget { final PascalAccount account; final Currency price; final AccountNumber receiver; final String publicKey; final Currency fee; CreatingPrivateSaleSheet( {@required this.account, @required this.price, @required this.receiver, @required this.publicKey, @required this.fee}) : super(); _CreatingPrivateSaleSheetState createState() => _CreatingPrivateSaleSheetState(); } class _CreatingPrivateSaleSheetState extends State { OverlayEntry _overlay; Account accountState; StreamSubscription _authSub; void _registerBus() { _authSub = EventTaxiImpl.singleton() .registerTo() .listen((event) { if (event.authType == AUTH_EVENT_TYPE.LIST_FORSALE) { doList(); } }); } void _destroyBus() { if (_authSub != null) { _authSub.cancel(); } } @override void dispose() { _destroyBus(); super.dispose(); } @override void initState() { super.initState(); _registerBus(); this.accountState = walletState.getAccountState(widget.account); } void showOverlay(BuildContext context) { OverlayState overlayState = Overlay.of(context); _overlay = OverlayEntry( builder: (context) => BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container( width: double.maxFinite, height: double.maxFinite, color: StateContainer.of(context).curTheme.overlay20, child: Center( child: //Container for the animation Container( margin: EdgeInsetsDirectional.only( top: MediaQuery.of(context).padding.top), //Width/Height ratio for the animation is needed because BoxFit is not working as expected width: double.maxFinite, height: MediaQuery.of(context).size.width, child: Center( child: FlareActor( StateContainer.of(context).curTheme.animationSale, animation: "main", fit: BoxFit.contain, ), ), ), ), ), ), ); overlayState.insert(_overlay); } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .creatingPrivateSaleSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .creatingPrivateSaleParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 2, minFontSize: 8, ), ), // Container for price header and price Container( margin: EdgeInsetsDirectional.fromSTEB(30, 0, 30, 0), child: Row( children: [ // Price Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Price" header Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 0, 30, 0, 0), child: AutoSizeText( AppLocalization.of(context) .priceTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the Price Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 0, 12, 0, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .primary15), color: StateContainer.of(context) .curTheme .primary10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontPrimaryBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.price.toStringOpt(), style: AppStyles.balanceSmall( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ), ], ), widget.fee != Currency("0") ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Fee" header Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 16, 30, 0, 0), child: AutoSizeText( AppLocalization.of(context) .feeTextFieldHeader, style: AppStyles.textFieldLabel( context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the fee Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 16, 12, 0, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .primary15), color: StateContainer.of(context) .curTheme .primary10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontPrimaryBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.fee .toStringOpt(), style: AppStyles.balanceSmall( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ), ], ) : SizedBox(), ], ), ), // "Receving Account" header Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .receivingAccountTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the account number Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width / 2 - 36), margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: AutoSizeText( widget.receiver.toString(), maxLines: 1, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyTextDark(context), ), ), // "Public Key" header Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .publicKeyTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the name Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(24, 12, 24, 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: AutoSizeText( widget.publicKey, maxLines: 4, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyTextDark(context), ), ), // Bottom Margin SizedBox(height: 24), ], ), ), ), // "CONFIRM" button Row( children: [ AppButton( type: AppButtonType.Primary, text: toUppercase(AppLocalization.of(context) .confirmButton ,context), buttonTop: true, onPressed: () async { if (await authenticate()) { EventTaxiImpl.singleton().fire( AuthenticatedEvent(AUTH_EVENT_TYPE.LIST_FORSALE)); } }, ), ], ), // "CANCEL" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: toUppercase(AppLocalization.of(context) .cancelButton ,context), onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } Future doList({Currency fee}) async { fee = fee == null ? widget.fee : fee; try { showOverlay(context); RPCResponse result = await accountState.listAccountForSale( widget.price, widget.receiver, newPubKey: PascalUtil().decipherPublicKey(widget.publicKey), fee: fee); if (result.isError) { ErrorResponse errResp = result; UIUtil.showSnackbar(errResp.errorMessage.replaceAll("founds", "funds"), context); _overlay?.remove(); Navigator.of(context).pop(); } else { _overlay?.remove(); try { OperationsResponse resp = result; PascalOperation op = resp.operations[0]; if (op.valid == null || op.valid) { // Update state accountState.changeAccountState(AccountState.LISTED); Navigator.of(context).popUntil(RouteUtils.withNameLike("/account")); AppSheets.showBottomSheet( context: context, closeOnTap: true, widget: CreatedPrivateSaleSheet( receiver: widget.receiver, price: widget.price, publicKey: widget.publicKey, fee: fee, )); } else { if (op.errors.contains("zero fee") && widget.fee == walletState.NO_FEE) { UIUtil.showFeeDialog( context: context, onConfirm: () async { Navigator.of(context).pop(); doList(fee: walletState.MIN_FEE); }); } else { UIUtil.showSnackbar("${op.errors}", context); } } } catch (e) { UIUtil.showSnackbar( AppLocalization.of(context).somethingWentWrongError, context); } } } catch (e) { _overlay?.remove(); UIUtil.showSnackbar( AppLocalization.of(context).somethingWentWrongError, context); } } Future _authenticateBiometrics(AuthUtil authUtil, String message) async { // Biometric auth bool authenticated = await authUtil.authenticateWithBiometrics(message); if (authenticated) { HapticUtil.fingerprintSucess(); } return authenticated; } Future _authenticatePin(String message) async { String expectedPin = await sl.get().getPin(); bool result = await Navigator.of(context) .push(MaterialPageRoute(builder: (BuildContext context) { return PinScreen( type: PinOverlayType.ENTER_PIN, onSuccess: (pin) { Navigator.of(context).pop(true); }, expectedPin: expectedPin, description: message, ); })); await Future.delayed(Duration(milliseconds: 200)); return result != null && result; } Future authenticate() async { String message = AppLocalization.of(context).authenticateToCreatePrivateSaleParagraph; // Authenticate AuthUtil authUtil = AuthUtil(); if (await authUtil.useBiometrics()) { // Biometric auth try { return await _authenticateBiometrics(authUtil, message); } catch (e) { return await _authenticatePin(message); } } else { return await _authenticatePin(message); } } } ================================================ FILE: lib/ui/account/other_operations/transfer_account/transfer_account_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/store/account/account.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/transfer_account/transferring_account_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/error_container.dart'; import 'package:blaise_wallet_flutter/ui/widgets/fee_container.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/user_data_util.dart'; import 'package:flutter/material.dart'; import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:pascaldart/pascaldart.dart'; class TransferAccountSheet extends StatefulWidget { final PascalAccount account; TransferAccountSheet({@required this.account}); _TransferAccountSheetState createState() => _TransferAccountSheetState(); } class _TransferAccountSheetState extends State { FocusNode publicKeyFocusNode; TextEditingController publicKeyController; Account accountState; String pubkeyError; // Fee bool _hasFee; @override void initState() { super.initState(); publicKeyFocusNode = FocusNode(); publicKeyController = TextEditingController(); this.accountState = walletState.getAccountState(widget.account); _hasFee = walletState.shouldHaveFee(); } @override Widget build(BuildContext context) { return TapOutsideUnfocus( child: Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .transferSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 40, 30, 20), child: AutoSizeText( AppLocalization.of(context).transferParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), Expanded( child: KeyboardAvoider( duration: Duration(milliseconds: 0), autoScroll: true, focusPadding: 40, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Container for the public key text field Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 10, 30, 0), child: AppTextField( label: AppLocalization.of(context) .publicKeyTextFieldHeader, style: AppStyles.privateKeyTextDark(context), maxLines: 4, firstButton: TextFieldButton( icon: AppIcons.paste, onPressed: () async { String text = await UserDataUtil.getClipboardText( DataType.PUBLIC_KEY); if (text != null) { publicKeyController.text = text; } }, ), secondButton: TextFieldButton( icon: AppIcons.scan, onPressed: () async { String text = await UserDataUtil.getQRData( DataType.PUBLIC_KEY, StateContainer.of(context).curTheme.scannerTheme); if (text != null) { publicKeyController.text = text; } }, ), focusNode: publicKeyFocusNode, controller: publicKeyController, onChanged: (nt) { if (pubkeyError != null) { setState(() { pubkeyError = null; }); } }, ), ), // Fee container _hasFee ? FeeContainer( feeText: walletState.MIN_FEE.toStringOpt()) : SizedBox(), // Error Text ErrorContainer( errorText: pubkeyError == null ? "" : pubkeyError, ), // Bottom Margin SizedBox(height: 24), ], ), ), ), ], ), ), // "Transfer" button Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).transferButton, onPressed: () { validateAndTransfer(); }, ), ], ), ], ), ), ), ], )); } void validateAndTransfer() { // Validate pubkey // First try base58, then hex try { PublicKeyCoder().decodeFromBase58(publicKeyController.text); } catch (e) { try { PublicKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(publicKeyController.text)); } catch (e) { setState(() { pubkeyError = AppLocalization.of(context).invalidPublicKeyError; }); return; } } AppSheets.showBottomSheet( context: context, widget: TransferringAccountSheet( account: widget.account, publicKeyDisplay: publicKeyController.text, fee: _hasFee ? walletState.MIN_FEE : walletState.NO_FEE), noBlur: true); } } ================================================ FILE: lib/ui/account/other_operations/transfer_account/transferred_account_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:pascaldart/pascaldart.dart'; class TransferredAccountSheet extends StatefulWidget { final String newAccountPubkey; final Currency fee; TransferredAccountSheet( {@required this.newAccountPubkey, @required this.fee}); _TransferredAccountSheetState createState() => _TransferredAccountSheetState(); } class _TransferredAccountSheetState extends State { @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( width: double.maxFinite, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: [ 0.0, 0.7, 0.7, 1.0 ], colors: [ StateContainer.of(context).curTheme.success, StateContainer.of(context).curTheme.success, StateContainer.of(context).curTheme.backgroundPrimary, StateContainer.of(context).curTheme.backgroundPrimary, ]), borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Container( margin: EdgeInsetsDirectional.only(top: 8), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Sized Box SizedBox( height: 50, width: 65, ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .transferredSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), // Tick Container( margin: EdgeInsetsDirectional.only(top: 8), height: 110, width: 110, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: StateContainer.of(context).curTheme.success, boxShadow: [ StateContainer.of(context) .curTheme .shadowTextDarkTwo ], ), child: Icon( AppIcons.tick, size: 40, color: StateContainer.of(context) .curTheme .backgroundPrimary, ), ), ], ), ), ), Expanded( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 40, 30, 0), child: AutoSizeText( AppLocalization.of(context).transferredParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), // "Address" header Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .publicKeyTextFieldHeader, style: AppStyles.textFieldLabelSuccess(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the public key Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(24, 12, 24, 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: AutoSizeText( widget.newAccountPubkey, maxLines: 4, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyTextDark(context), ), ), // "Fee" header widget.fee != Currency('0') ? Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .feeTextFieldHeader, style: AppStyles.textFieldLabelSuccess(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ) : SizedBox(), // Container for the fee widget.fee != Currency("0") ? Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .success15), color: StateContainer.of(context) .curTheme .success10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontSuccessBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.fee.toStringOpt(), style: AppStyles.balanceSmallSuccess( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ) : SizedBox(), // Bottom Margin SizedBox(height: 24), ], ), ), ), // "CANCEL" button Row( children: [ AppButton( type: AppButtonType.SuccessOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } } ================================================ FILE: lib/ui/account/other_operations/transfer_account/transferring_account_sheet.dart ================================================ import 'dart:async'; import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/authenticated_event.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/store/account/account.dart'; import 'package:blaise_wallet_flutter/ui/account/other_operations/transfer_account/transferred_account_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/routes.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/pin_screen.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/util/authentication.dart'; import 'package:blaise_wallet_flutter/util/haptic_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:flare_flutter/flare_actor.dart'; import 'package:flutter/material.dart'; import 'package:pascaldart/pascaldart.dart'; class TransferringAccountSheet extends StatefulWidget { final String publicKeyDisplay; final PascalAccount account; final Currency fee; TransferringAccountSheet( {@required this.publicKeyDisplay, @required this.account, @required this.fee}); _TransferringAccountSheetState createState() => _TransferringAccountSheetState(); } class _TransferringAccountSheetState extends State { Account accountState; OverlayEntry _overlay; StreamSubscription _authSub; void _registerBus() { _authSub = EventTaxiImpl.singleton() .registerTo() .listen((event) { if (event.authType == AUTH_EVENT_TYPE.TRANSFER) { doTransfer(); } }); } void _destroyBus() { if (_authSub != null) { _authSub.cancel(); } } @override void dispose() { _destroyBus(); super.dispose(); } @override void initState() { super.initState(); _registerBus(); this.accountState = walletState.getAccountState(widget.account); } void showOverlay(BuildContext context) { OverlayState overlayState = Overlay.of(context); _overlay = OverlayEntry( builder: (context) => BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container( width: double.maxFinite, height: double.maxFinite, color: StateContainer.of(context).curTheme.overlay20, child: Center( child: //Container for the animation Container( margin: EdgeInsetsDirectional.only( top: MediaQuery.of(context).padding.top), //Width/Height ratio for the animation is needed because BoxFit is not working as expected width: double.maxFinite, height: MediaQuery.of(context).size.width, child: Center( child: FlareActor( StateContainer.of(context).curTheme.animationTransfer, animation: "main", fit: BoxFit.contain, ), ), ), ), ), ), ); overlayState.insert(_overlay); } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .transferringSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 40, 30, 0), child: AutoSizeText( AppLocalization.of(context).transferringParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), // "Public Key" header Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).publicKeyTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the public key Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(24, 12, 24, 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: AutoSizeText( widget.publicKeyDisplay, maxLines: 4, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyTextDark(context), ), ), // "Fee" header widget.fee != Currency('0') ? Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).feeTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ) : SizedBox(), // Container for the fee widget.fee != Currency("0") ? Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .primary15), color: StateContainer.of(context) .curTheme .primary10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontPrimaryBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.fee.toStringOpt(), style: AppStyles.balanceSmall(context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ) : SizedBox(), // Bottom Margin SizedBox(height: 24), ], ), ), // "CONFIRM" button Row( children: [ AppButton( type: AppButtonType.Primary, text: toUppercase(AppLocalization.of(context) .confirmButton , context), buttonTop: true, onPressed: () async { if (await authenticate()) { EventTaxiImpl.singleton().fire( AuthenticatedEvent(AUTH_EVENT_TYPE.TRANSFER)); } }, ), ], ), // "CANCEL" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: toUppercase(AppLocalization.of(context) .cancelButton ,context), onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } Future doTransfer({Currency fee}) async { fee = fee == null ? widget.fee : fee; try { showOverlay(context); RPCResponse result = await accountState.transferAccount(widget.publicKeyDisplay, fee: fee); if (result.isError) { ErrorResponse errResp = result; UIUtil.showSnackbar(errResp.errorMessage.replaceAll("founds", "funds"), context); _overlay?.remove(); Navigator.of(context).pop(); } else { _overlay?.remove(); try { OperationsResponse resp = result; PascalOperation op = resp.operations[0]; if (op.valid == null || op.valid) { // Remove all traces of this account walletState.removeAccount(widget.account); Navigator.of(context) .popUntil(RouteUtils.withNameLike("/overview")); AppSheets.showBottomSheet( context: context, closeOnTap: true, widget: TransferredAccountSheet( newAccountPubkey: widget.publicKeyDisplay, fee: fee, )); } else { if (op.errors.contains("zero fee") && widget.fee == walletState.NO_FEE) { UIUtil.showFeeDialog( context: context, onConfirm: () async { Navigator.of(context).pop(); doTransfer(fee: walletState.MIN_FEE); }); } else { UIUtil.showSnackbar("${op.errors}", context); } } } catch (e) { UIUtil.showSnackbar( AppLocalization.of(context).somethingWentWrongError, context); } } } catch (e) { _overlay?.remove(); UIUtil.showSnackbar( AppLocalization.of(context).somethingWentWrongError, context); } } Future _authenticateBiometrics(AuthUtil authUtil, String message) async { // Biometric auth bool authenticated = await authUtil.authenticateWithBiometrics(message); if (authenticated) { HapticUtil.fingerprintSucess(); } return authenticated; } Future _authenticatePin(String message) async { String expectedPin = await sl.get().getPin(); bool result = await Navigator.of(context) .push(MaterialPageRoute(builder: (BuildContext context) { return PinScreen( type: PinOverlayType.ENTER_PIN, onSuccess: (pin) { Navigator.of(context).pop(true); }, expectedPin: expectedPin, description: message, ); })); await Future.delayed(Duration(milliseconds: 200)); return result != null && result; } Future authenticate() async { String message = AppLocalization.of(context).authenticateToTransferParagraph; // Authenticate AuthUtil authUtil = AuthUtil(); if (await authUtil.useBiometrics()) { // Biometric auth try { return await _authenticateBiometrics(authUtil, message); } catch (e) { return await _authenticatePin(message); } } else { return await _authenticatePin(message); } } } ================================================ FILE: lib/ui/account/receive/receive_sheet.dart ================================================ import 'dart:async'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/account/receive/request_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:qr/qr.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:quiver/strings.dart'; class ReceiveSheet extends StatefulWidget { final String accountName; final AccountNumber accountNumber; ReceiveSheet({this.accountName, this.accountNumber}); _ReceiveSheetState createState() => _ReceiveSheetState(); } class _ReceiveSheetState extends State { bool _addressCopied; Timer _addressCopiedTimer; @override void initState() { super.initState(); _addressCopied = false; } @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SizedBox(width: 65, height: 50), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( child: isEmpty(widget.accountName) ? SizedBox() : Container( margin: EdgeInsets.only(bottom: 2), width: MediaQuery.of(context).size.width - 130, child: AutoSizeText( widget.accountName, style: AppStyles.accountCardName(context), maxLines: 1, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, ), ), ), Container( width: MediaQuery.of(context).size.width - 130, child: AutoSizeText( widget.accountNumber.toString(), style: AppStyles.accountCardAddress(context), maxLines: 1, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, ), ), ], ), ), // Share Button is removed for beta /*Container( margin: EdgeInsetsDirectional.only(start: 10, end: 5), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { return null; }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.shareaddress, color: StateContainer.of(context).curTheme.textLight, size: 22)), ),*/ SizedBox(width: 65, height: 50), ], ), ), // QR Code Container( margin: EdgeInsetsDirectional.only(top: 30, bottom: 10), child: Stack( alignment: Alignment(0, 0), children: [ // Gradient Container( width: 180, height: 180, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), gradient: StateContainer.of(context).curTheme.gradientPrimary, ), ), // White overlay Container( width: 172, height: 172, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Colors.white), ), // QR Code QrImage( data: widget.accountNumber.toString(), size: 180.0, errorCorrectionLevel: QrErrorCorrectLevel.Q, version: 4, gapless: false, ), // Logo background Container( width: 58, height: 58, decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: Colors.white), ), // Logo Container( width: 48, height: 48, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), gradient: StateContainer.of(context).curTheme.gradientPrimary, ), child: Icon(AppIcons.pascalsymbol, color: StateContainer.of(context) .curTheme .backgroundPrimary, size: 30), ), ], ), ), //"Copy Address" and "Request Amount" buttons Row( children: [ AppButton( type: _addressCopied ? AppButtonType.Success : AppButtonType.Primary, text: _addressCopied ? AppLocalization.of(context).copiedAddressButton : AppLocalization.of(context).copyAddressButton, onPressed: () { Clipboard.setData( ClipboardData(text: widget.accountNumber.toString())); setState(() { _addressCopied = true; }); if (_addressCopiedTimer != null) { _addressCopiedTimer.cancel(); } _addressCopiedTimer = Timer(const Duration(milliseconds: 1500), () { if (mounted) { setState(() { _addressCopied = false; }); } }); }, ), ], ), // "Request" button /*Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).requestButton, onPressed: () { Navigator.of(context).pop(); AppSheets.showBottomSheet( context: context, widget: RequestSheet(address: widget.accountNumber.toString())); }, ), ], ),*/ ], ), ), ], ); } } ================================================ FILE: lib/ui/account/receive/request_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:qr/qr.dart'; import 'package:qr_flutter/qr_flutter.dart'; class RequestSheet extends StatefulWidget { final String address; RequestSheet({this.address}); _RequestSheetState createState() => _RequestSheetState(); } class _RequestSheetState extends State { FocusNode amountFocusNode; TextEditingController amountController; FocusNode payloadFocusNode; TextEditingController payloadController; String amount = ''; String payload = ''; @override void initState() { super.initState(); this.amountFocusNode = FocusNode(); this.payloadFocusNode = FocusNode(); this.amountController = TextEditingController(); this.payloadController = TextEditingController(); } @override Widget build(BuildContext context) { return TapOutsideUnfocus( child: Column( mainAxisSize: MainAxisSize.min, children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // SizedBox SizedBox(width: 65, height: 50), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .requestSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // SizedBox SizedBox(width: 65, height: 50), ], ), ), Expanded( child: KeyboardAvoider( duration: Duration(milliseconds: 0), autoScroll: true, focusPadding: 40, child: Column( children: [ // QR Code Container( margin: EdgeInsetsDirectional.only(top: 30), child: Stack( alignment: Alignment(0, 0), children: [ // Gradient Container( width: 180, height: 180, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), gradient: StateContainer.of(context) .curTheme .gradientPrimary, ), ), // White overlay Container( width: 172, height: 172, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Colors.white), ), // QR Code QrImage( data: "Address=" + widget.address + " / Amount=" + amount.toString() + " / Payload=" + payload, size: 180.0, version: 6, errorCorrectionLevel: QrErrorCorrectLevel.Q, gapless: false, ), // Logo background Container( width: 58, height: 58, decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: Colors.white), ), // Logo Container( width: 48, height: 48, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), gradient: StateContainer.of(context) .curTheme .gradientPrimary, ), child: Icon(AppIcons.pascalsymbol, color: StateContainer.of(context) .curTheme .backgroundPrimary, size: 30), ), ], ), ), // Container for the amount text field Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AppTextField( onChanged: (String newText) { setState(() { amount = newText; }); }, focusNode: amountFocusNode, controller: amountController, label: AppLocalization.of(context) .amountTextFieldHeader, style: AppStyles.paragraphPrimary(context), maxLines: 1, inputType: TextInputType.numberWithOptions( decimal: true), prefix: Icon( AppIcons.pascalsymbol, size: 15, color: StateContainer.of(context).curTheme.primary, ), firstButton: TextFieldButton(icon: AppIcons.max), secondButton: TextFieldButton( icon: AppIcons.currencyswitch), textInputAction: TextInputAction.next, onSubmitted: (text) { payloadFocusNode.requestFocus(); }, ), ), // Container for the payload text field Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 40), child: AppTextField( onChanged: (String newText) { setState(() { payload = newText; }); }, focusNode: payloadFocusNode, controller: payloadController, label: AppLocalization.of(context) .payloadTextFieldHeader, style: AppStyles.paragraphMedium(context), maxLines: 1, firstButton: TextFieldButton(icon: AppIcons.paste), secondButton: TextFieldButton(icon: AppIcons.scan), ), ), ], ), ), ), // "Close" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ), ); } } ================================================ FILE: lib/ui/account/send/send_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/available_currency.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/bus/events.dart'; import 'package:blaise_wallet_flutter/model/db/appdb.dart'; import 'package:blaise_wallet_flutter/model/db/contact.dart'; import 'package:blaise_wallet_flutter/store/account/account.dart'; import 'package:blaise_wallet_flutter/ui/account/send/sending_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/formatters.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/error_container.dart'; import 'package:blaise_wallet_flutter/ui/widgets/fee_container.dart'; import 'package:blaise_wallet_flutter/ui/widgets/payload.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/number_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/user_data_util.dart'; import 'package:flare_flutter/flare_actor.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:intl/intl.dart'; import 'package:decimal/decimal.dart'; import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:quiver/strings.dart'; import 'package:event_taxi/event_taxi.dart'; class SendSheet extends StatefulWidget { final PascalAccount account; final Contact contact; final bool fromOverview; final AvailableCurrency localCurrency; SendSheet({@required this.account, @required this.localCurrency, this.contact, this.fromOverview = false}); _SendSheetState createState() => _SendSheetState(); } class _SendSheetState extends State { String addressControllerText = ""; TextEditingController addressController; TextEditingController amountController; FocusNode addressFocusNode; FocusNode amountFocusNode; // Local currency mode/fiat conversion bool _localCurrencyMode = false; String _lastLocalCurrencyAmount = ""; String _lastCryptoAmount = ""; NumberFormat _localCurrencyFormat; // Errors String destinationError; String amountError; // Payload String _payload; bool _encryptedPayload; // Fee bool _hasFee; // Contacts list List _contacts; bool _isValidContactAndUnfocused; // Account name list PascalAccount _selectedAccountName; List _accountNames; List _accountNamesUnfocused; bool _accountNamesLoading; // Account State Account accountState; // Switch to Contacts field bool _isDestinationFieldTypeContact; String _lastContactFieldValue = ""; String _lastNameFieldValue = ""; bool isDigit(String s, int idx) => (s.codeUnitAt(idx) ^ 0x30) <= 9; @override void initState() { super.initState(); this.addressController = TextEditingController(); this.addressController.addListener(() { if (mounted) { setState(() { addressControllerText = addressController.text; }); } }); this.amountController = TextEditingController(); this.addressFocusNode = FocusNode(); this.amountFocusNode = FocusNode(); this._payload = ""; this._hasFee = walletState.shouldHaveFee(); this._isValidContactAndUnfocused = false; this._isDestinationFieldTypeContact = false; this._contacts = []; this._accountNames = []; this._accountNamesUnfocused = []; this._accountNamesLoading = false; this._encryptedPayload = false; this.accountState = walletState.getAccountState(widget.account); _localCurrencyFormat = NumberFormat.currency(locale: widget.localCurrency.getLocale().toString(), symbol: widget.localCurrency.getCurrencySymbol()); this.addressFocusNode.addListener(() { if (!this.addressFocusNode.hasFocus) { // When unfocused, add checksum to account if applicable if (this.addressController.text.length > 0 && isDigit(this.addressController.text, 0)) { try { AccountNumber numberFormatted = AccountNumber(this.addressController.text); this.addressController.text = numberFormatted.toString(); } catch (e) {} } // Reset contacts list and check if contact is valid if (_isDestinationFieldTypeContact) { if (mounted) { setState(() { _contacts = []; }); } sl .get() .getContactWithName(this.addressController.text) .then((contact) { if (contact != null && mounted) { this.addressController.text = this.addressController.text; setState(() { _isValidContactAndUnfocused = true; _payload = contact.payload; _encryptedPayload = false; }); EventTaxiImpl.singleton() .fire(PayloadChangedEvent(payload: contact.payload)); } }); } else { // Hide the account name list if (_accountNames.isNotEmpty) { setState(() { _accountNames = []; }); } // Change text for selected account name if (_selectedAccountName != null) { addressController.text = "${_selectedAccountName.name.toString()} (${_selectedAccountName.account.toString()})"; } } } else { // When focused if (this._isValidContactAndUnfocused) { setState(() { _isValidContactAndUnfocused = false; }); } if (this.addressController.text.length == 0 && _isDestinationFieldTypeContact) { // Show contacts list sl.get().getContacts().then((contacts) { if (mounted) { setState(() { _contacts = contacts; }); } }); } else if (_isDestinationFieldTypeContact) { sl .get() .getContactsWithNameLike(this.addressController.text) .then((contacts) { if (mounted) { setState(() { _contacts = contacts; }); } }); } else { // Show account names list if (_accountNamesUnfocused.isNotEmpty) { setState(() { _accountNames = _accountNamesUnfocused; }); } // Change text for selected account name if (_selectedAccountName != null) { addressController.text =_selectedAccountName.name.toString(); addressController.selection = TextSelection.fromPosition( TextPosition(offset: addressController.text.length)); } } } }); // Initial contact information if (widget.contact != null) { this.addressController.text = widget.contact.name.toString(); this._payload = widget.contact.payload; this._isValidContactAndUnfocused = true; this._isDestinationFieldTypeContact = true; } } @override Widget build(BuildContext context) { return TapOutsideUnfocus( child: Column( children: [ Expanded( // Stack for everything else & account search button child: Stack( alignment: Alignment.bottomCenter, children: [ Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context) .curTheme .gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context) .curTheme .textLight15, splashColor: StateContainer.of(context) .curTheme .textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .sendSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 16, 30, 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ // Account name isEmpty(widget.account.name.toString()) ? SizedBox() : Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width / 2 - 45), child: AutoSizeText( widget.account.name.toString(), style: AppStyles .settingsItemHeader( context), maxLines: 1, minFontSize: 8, stepGranularity: 0.1, ), ), // Acccount address Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width / 2 - 45), margin: EdgeInsetsDirectional.only(top: 2), child: AutoSizeText( widget.account.account.toString(), style: AppStyles.monoTextDarkSmall400( context), maxLines: 1, minFontSize: 8, stepGranularity: 0.1, ), ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.center, children: [ // Account balance Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width / 2 - 45), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontPrimaryBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 7)), TextSpan( text: widget.account.balance .toStringOpt(), style: AppStyles.balanceSmall( context)), ], ), textAlign: TextAlign.end, maxLines: 1, minFontSize: 8, stepGranularity: 0.1, style: TextStyle( fontSize: 14, ), ), ), // Balance in fiat Observer( builder: (BuildContext context) { if (walletState.localCurrencyPrice != null) { return Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width / 2 - 45), margin: EdgeInsetsDirectional.only( top: 2), child: AutoSizeText( "(${walletState.getLocalCurrencyDisplay(currency: StateContainer.of(context).curCurrency, amount: accountState.accountBalance)})", style: AppStyles .primarySmallest400( context), maxLines: 1, minFontSize: 8, stepGranularity: 0.1, textAlign: TextAlign.end, ), ); } return SizedBox(); }, ) ], ), ], ), ), Expanded( child: KeyboardAvoider( duration: Duration(milliseconds: 0), autoScroll: true, focusPadding: 40, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Container for the address text field Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 10, 30, 0), child: _isDestinationFieldTypeContact ? AppTextField( label: AppLocalization.of(context) .contactNameTextFieldHeader, style: AppStyles.contactsItemName( context), prefix: _isValidContactAndUnfocused ? Text( " ", style: AppStyles .iconFontPrimarySmall( context), ) : null, maxLines: 1, onChanged: (text) async { if (destinationError != null && mounted) { setState(() { destinationError = null; }); } // Handle contacts await _checkAndUpdateContacts(); }, focusNode: addressFocusNode, controller: addressController, firstButton: TextFieldButton( icon: Icons .account_balance_wallet, onPressed: () { setState(() { destinationError = null; _isDestinationFieldTypeContact = false; _lastContactFieldValue = addressController.text; addressController.text = _lastNameFieldValue; }); if (_selectedAccountName == null) { addressFocusNode .requestFocus(); addressController.selection = TextSelection.fromPosition( TextPosition(offset: addressController.text.length)); } else { addressFocusNode.unfocus(); } }, ), textInputAction: TextInputAction.next, onSubmitted: (text) { amountFocusNode.requestFocus(); }, ) : AppTextField( label: AppLocalization.of(context) .addressTextFieldHeader, style: _isValidContactAndUnfocused ? AppStyles.contactsItemName( context) : AppStyles.paragraphMedium( context), prefix: _isValidContactAndUnfocused ? Text( " ", style: AppStyles .iconFontPrimarySmall( context), ) : null, maxLines: 1, onChanged: (text) async { if (destinationError != null && mounted) { setState(() { destinationError = null; }); } // Reset selected name if (mounted && _selectedAccountName != null) { setState(() { _selectedAccountName = null; }); } }, focusNode: addressFocusNode, controller: addressController, secondButton: TextFieldButton( icon: AppIcons.paste, onPressed: () { Clipboard.getData( "text/plain") .then((data) { try { AccountNumber num = AccountNumber( data.text); addressController.text = num.toString(); } catch (e) { checkAndValidateContact( name: data.text); } }); }, ), firstButton: TextFieldButton( icon: AppIcons.contacts, onPressed: () { setState(() { destinationError = null; _isDestinationFieldTypeContact = true; _lastNameFieldValue = addressController.text; addressController.text = _lastContactFieldValue; }); addressFocusNode .requestFocus(); addressController.selection = TextSelection.fromPosition( TextPosition(offset: addressController.text.length)); }, ), textInputAction: TextInputAction.next, onSubmitted: (text) { amountFocusNode.requestFocus(); }, ), ), // A stack to display contacts pop up Stack( children: [ // Column for everything else except contacts pop up Column( children: [ // Error Text ErrorContainer( errorText: destinationError == null ? "" : destinationError, ), // Container for the amount text field Container( margin: EdgeInsetsDirectional .fromSTEB(30, 30, 30, 0), child: Observer( builder: (context) { bool showCurrencySwitch = walletState.localCurrencyPrice != null; return AppTextField( label: AppLocalization.of( context) .amountTextFieldHeader, style: AppStyles .paragraphPrimary( context), maxLines: 1, inputType: TextInputType .numberWithOptions( decimal: true), prefix: _localCurrencyMode ? Text( widget.localCurrency.getCurrencySymbol(), style: AppStyles.iconFontPrimarySmall( context), ) : Icon( AppIcons.pascalsymbol, size: 15, color: StateContainer.of( context) .curTheme .primary, ), onChanged: (text) { if (amountError != null) { setState(() { amountError = null; }); } }, inputFormatters: [ LengthLimitingTextInputFormatter( 13), _localCurrencyMode ? CurrencyFormatter( decimalSeparator: _localCurrencyFormat .symbols .DECIMAL_SEP, commaSeparator: _localCurrencyFormat .symbols .GROUP_SEP, maxDecimalDigits: 2) : CurrencyFormatter( maxDecimalDigits: NumberUtil .maxDecimalDigits), LocalCurrencyFormatter( active: _localCurrencyMode, currencyFormat: _localCurrencyFormat), ], focusNode: amountFocusNode, controller: amountController, firstButton: TextFieldButton( icon: AppIcons.max, onPressed: () { amountController.text = widget.account.balance .toStringOpt(); amountFocusNode.unfocus(); }, ), secondButton: showCurrencySwitch ? TextFieldButton( icon: AppIcons .currencyswitch, onPressed: () { toggleLocalCurrency(); } ) : null ); } ) ), // Fee container _hasFee ? FeeContainer( feeText: walletState.MIN_FEE .toStringOpt()) : SizedBox(), // Error Text ErrorContainer( errorText: amountError == null ? "" : amountError, ), Payload( initialPayload: _payload, onPayloadChanged: (newPayload, encrypted) { setState(() { _payload = newPayload; _encryptedPayload = encrypted; }); }, ), // Bottom Margin SizedBox(height: 24), ], ), // Contacts pop up _getContactsPopup(), // Account name pop up _getAccountNameList(), ], ), ], ), ), ), ], ), ), // "Send" button Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).sendButton, buttonTop: true, onPressed: () async { await validateAndSend(); }, ), ], ), // "Scan QR Code" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).scanQRCodeButton, onPressed: () async { String text = await UserDataUtil.getQRData( DataType.ACCOUNT, StateContainer.of(context).curTheme.scannerTheme); if (text != null) { addressController.text = text; } }, ), ], ), ], ), ), // Account search button this.addressFocusNode.hasFocus && !_isDestinationFieldTypeContact && (this.addressControllerText.length > 2 && !isDigit(this.addressControllerText, 0)) && !_accountNamesLoading && _selectedAccountName == null ? Container( width: double.maxFinite, height: 50, margin: EdgeInsetsDirectional.only( bottom: MediaQuery.of(context).viewInsets.bottom, ), decoration: BoxDecoration( gradient: StateContainer.of(context) .curTheme .gradientPrimary, ), child: FlatButton( onPressed: () async { if (mounted) { setState(() { _accountNamesLoading = true; }); } List accounts = await walletState.findAccountsWithNameLike(this.addressController.text); if (accounts == null) { UIUtil.showSnackbar(AppLocalization.of(context).somethingWentWrongError, context); setState(() { _accountNamesLoading = false; }); } else { if (mounted) { if (accounts.isEmpty) { UIUtil.showSnackbar(AppLocalization.of(context).noResultsFound, context); } setState(() { _accountNames = accounts; _accountNamesUnfocused = accounts; _accountNamesLoading = false; }); } } }, highlightColor: StateContainer.of(context) .curTheme .backgroundPrimary15, splashColor: StateContainer.of(context) .curTheme .backgroundPrimary30, padding: EdgeInsets.all(0), child: Text(AppLocalization.of(context).searchAccountNameButton, style: AppStyles.buttonPrimary(context)), ), ) : SizedBox() ], ), ), ], ), ); } Future validateAndSend() async { bool hasError = false; Contact contact; Currency sendAmount = _localCurrencyMode ? Currency(_convertLocalCurrencyToCrypto()) :Currency(amountController.text); if (amountController.text.length == 0) { hasError = true; setState(() { amountError = AppLocalization.of(context).amountRequiredError; }); } else if (accountState.accountBalance < sendAmount) { hasError = true; setState(() { amountError = AppLocalization.of(context).insufficientBalanceError; }); } else if (sendAmount <= Currency("0")) { hasError = true; setState(() { amountError = AppLocalization.of(context).zeroAmountError; }); } String contactNameToCheck = addressController.text; if (contactNameToCheck != null && _isDestinationFieldTypeContact) { contact = await sl.get().getContactWithName(contactNameToCheck); if (contact == null) { hasError = true; setState(() { destinationError = AppLocalization.of(context).contactDoesntExistError; }); } } else if (!_isDestinationFieldTypeContact && _selectedAccountName == null) { try { AccountNumber destination = AccountNumber(addressController.text); if (destination == accountState.account.account) { hasError = true; setState(() { destinationError = AppLocalization.of(context).cantSendToYourselfError; }); } } catch (e) { hasError = true; setState(() { destinationError = AppLocalization.of(context).invalidDestinationError; }); } } if (!hasError) { AppSheets.showBottomSheet( context: context, widget: SendingSheet( destination: _selectedAccountName != null && !_isDestinationFieldTypeContact ? _selectedAccountName.account.toString() : addressController.text, amount: sendAmount.toStringOpt(), localCurrencyAmount: _localCurrencyMode ? amountController.text : null, localCurrency: widget.localCurrency, source: widget.account, fee: _hasFee ? walletState.MIN_FEE : walletState.NO_FEE, payload: _payload, fromOverview: widget.fromOverview, contact: contact, encryptPayload: _encryptedPayload, accountName: _selectedAccountName != null && !_isDestinationFieldTypeContact ? _selectedAccountName.name : null), noBlur: true); } } void toggleLocalCurrency() { // Keep a cache of previous amounts because, it's kinda nice to see approx what nano is worth // this way you can tap button and tap back and not end up with X.9993451 NANO if (_localCurrencyMode) { // Switching to crypto-mode String cryptoAmountStr; // Check out previous state if (amountController.text == _lastLocalCurrencyAmount) { cryptoAmountStr = _lastCryptoAmount; } else { _lastLocalCurrencyAmount = amountController.text; _lastCryptoAmount = _convertLocalCurrencyToCrypto(); cryptoAmountStr = _lastCryptoAmount; } setState(() { _localCurrencyMode = false; }); Future.delayed(Duration(milliseconds: 50), () { amountController.text = cryptoAmountStr; amountController.selection = TextSelection.fromPosition( TextPosition(offset: cryptoAmountStr.length)); }); } else { // Switching to local-currency mode String localAmountStr; // Check our previous state if (amountController.text == _lastCryptoAmount) { localAmountStr = _lastLocalCurrencyAmount; } else { _lastCryptoAmount = amountController.text; _lastLocalCurrencyAmount = _convertCryptoToLocalCurrency(); localAmountStr = _lastLocalCurrencyAmount; } setState(() { _localCurrencyMode = true; }); Future.delayed(Duration(milliseconds: 50), () { amountController.text = localAmountStr; amountController.selection = TextSelection.fromPosition( TextPosition(offset: localAmountStr.length)); }); } } String _convertLocalCurrencyToCrypto() { String convertedAmt = amountController.text.replaceAll(",", "."); convertedAmt = NumberUtil.sanitizeNumber(convertedAmt); if (convertedAmt.isEmpty) { return ""; } Decimal valueLocal = Decimal.parse(convertedAmt); Decimal conversion = Decimal.parse(walletState.localCurrencyPrice.toString()); return NumberUtil.truncateDecimal(valueLocal / conversion).toString(); } String _convertCryptoToLocalCurrency() { String convertedAmt = NumberUtil.sanitizeNumber(amountController.text, maxDecimalDigits: 2); if (convertedAmt.isEmpty) { return ""; } Decimal valueCrypto = Decimal.parse(convertedAmt); Decimal conversion = Decimal.parse(walletState.localCurrencyPrice.toString()); convertedAmt = NumberUtil.truncateDecimal(valueCrypto * conversion, digits: 2) .toString(); convertedAmt = convertedAmt.replaceAll(".", _localCurrencyFormat.symbols.DECIMAL_SEP); convertedAmt = convertedAmt; return convertedAmt; } Widget _getContactsPopup() { return _contacts.length > 0 && _isDestinationFieldTypeContact ? Material( color: StateContainer.of(context).curTheme.backgroundPrimary, child: Container( constraints: BoxConstraints(maxHeight: 138), width: MediaQuery.of(context).size.width - 60, margin: EdgeInsetsDirectional.only(start: 30, end: 30, top: 4), decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, boxShadow: [ StateContainer.of(context).curTheme.shadowAccountCard ]), child: ListView.builder( physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsets.zero, shrinkWrap: true, itemCount: _contacts.length, itemBuilder: (context, index) { return _buildContactItem(_contacts[index]); }, ), )) : SizedBox(); } Widget _buildContactItem(Contact contact) { return Container( width: double.maxFinite, height: 46, child: FlatButton( padding: EdgeInsets.all(0), onPressed: () { checkAndValidateContact(contact: contact); }, child: Container( alignment: Alignment(-1, 0), margin: EdgeInsetsDirectional.only(start: 16, end: 16), child: AutoSizeText.rich( TextSpan(children: [ TextSpan( text: " ", style: AppStyles.iconFontPrimarySmall(context), ), TextSpan( text: contact.name, style: AppStyles.contactsItemName(context), ), ]), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, style: TextStyle(fontSize: 14), ), ), ), ); } Widget _getAccountNameList() { return !_isDestinationFieldTypeContact && !_accountNamesLoading && _accountNames.isNotEmpty ? Material( color: StateContainer.of(context).curTheme.backgroundPrimary, child: Container( constraints: BoxConstraints(maxHeight: 138), width: MediaQuery.of(context).size.width - 60, margin: EdgeInsetsDirectional.only(start: 30, end: 30, top: 4), decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, boxShadow: [ StateContainer.of(context).curTheme.shadowAccountCard ]), child: ListView.builder( physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsets.zero, shrinkWrap: true, itemCount: _accountNames.length, itemBuilder: (context, index) { return _buildAccountNameItem(_accountNames[index]); }, ), )) : _accountNamesLoading ? Material( color: StateContainer.of(context).curTheme.backgroundPrimary, child: Container( height: 46, width: MediaQuery.of(context).size.width - 60, margin: EdgeInsetsDirectional.only(start: 30, end: 30, top: 4), decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, boxShadow: [ StateContainer.of(context).curTheme.shadowAccountCard ]), child: Center( child: Padding( padding: EdgeInsets.all(10.0), child: FlareActor( StateContainer.of(context).curTheme.animationSearch, animation: "main", fit: BoxFit.contain, color: StateContainer.of(context).curTheme.primary, ), ), ) ) ) : SizedBox(); } Widget _buildAccountNameItem(PascalAccount account) { return Container( width: double.maxFinite, height: 46, child: FlatButton( padding: EdgeInsets.all(0), onPressed: () { setState(() { _selectedAccountName = account; _accountNames = []; _accountNamesUnfocused = []; }); addressFocusNode.unfocus(); }, child: Container( alignment: Alignment(-1, 0), margin: EdgeInsetsDirectional.only(start: 16, end: 16), child: AutoSizeText.rich( TextSpan(children: [ TextSpan( text: account.name.toString(), style: AppStyles.contactsItemName(context), ), TextSpan( text: ' (${account.account.toString()})', style: AppStyles.privateKeyTextDarkFaded(context) ) ]), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, style: TextStyle(fontSize: 14), ), ), ), ); } /// When address text field is changed Future _checkAndUpdateContacts() async { if (_isDestinationFieldTypeContact) { List matches = await sl .get() .getContactsWithNameLike(addressController.text); if (mounted) { setState(() { _contacts = matches; }); } } else if (addressController.text.isEmpty) { List allContacts = await sl.get().getContacts(); if (mounted) { setState(() { _contacts = allContacts; }); } } else { if (mounted) { setState(() { _contacts = []; }); } } } /// When checking and validating a contact string (e.g. from paste button) Future checkAndValidateContact({String name, Contact contact}) async { Contact c = name != null ? await sl.get().getContactWithName(name) : contact; if (c != null && mounted) { addressFocusNode.unfocus(); addressController.text = c.name; setState(() { _isDestinationFieldTypeContact = true; _isValidContactAndUnfocused = true; _payload = c.payload; _encryptedPayload = false; }); EventTaxiImpl.singleton().fire(PayloadChangedEvent(payload: c.payload)); } } } ================================================ FILE: lib/ui/account/send/sending_sheet.dart ================================================ import 'dart:async'; import 'dart:typed_data'; import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/authenticated_event.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/available_currency.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/account/send/sent_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/routes.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/pin_screen.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/util/authentication.dart'; import 'package:blaise_wallet_flutter/util/haptic_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:blaise_wallet_flutter/model/db/contact.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:flare_flutter/flare_actor.dart'; import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:quiver/strings.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; class SendingSheet extends StatefulWidget { final String destination; final String amount; final String localCurrencyAmount; final AvailableCurrency localCurrency; final String payload; final PascalAccount source; final Currency fee; final bool fromOverview; final Contact contact; final bool encryptPayload; final AccountName accountName; SendingSheet( {@required this.destination, @required this.amount, @required this.source, @required this.fee, this.localCurrencyAmount, this.localCurrency, this.contact, this.payload = "", this.fromOverview = false, this.encryptPayload = false, this.accountName}); _SendingSheetState createState() => _SendingSheetState(); } class _SendingSheetState extends State { final Logger log = sl.get(); OverlayEntry _overlay; StreamSubscription _authSub; Uint8List encryptedPayload; void _registerBus() { _authSub = EventTaxiImpl.singleton() .registerTo() .listen((event) { if (event.authType == AUTH_EVENT_TYPE.SEND) { doSend(); } }); } void _destroyBus() { if (_authSub != null) { _authSub.cancel(); } } @override void initState() { super.initState(); _registerBus(); } @override void dispose() { _destroyBus(); super.dispose(); } void showOverlay(BuildContext context) { OverlayState overlayState = Overlay.of(context); _overlay = OverlayEntry( builder: (context) => BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container( width: double.maxFinite, height: double.maxFinite, color: StateContainer.of(context).curTheme.overlay20, child: Center( child: //Container for the animation Container( margin: EdgeInsetsDirectional.only( top: MediaQuery.of(context).padding.top), //Width/Height ratio for the animation is needed because BoxFit is not working as expected width: double.maxFinite, height: MediaQuery.of(context).size.width, child: Center( child: FlareActor( StateContainer.of(context).curTheme.animationSend, animation: "main", fit: BoxFit.contain, ), ), ), ), ), ), ); overlayState.insert(_overlay); } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SizedBox(width: 65, height: 50), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .sendingSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).sendingConfirmParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), // "Address" header Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).addressTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the account number Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: widget.contact == null && widget.accountName == null ? AutoSizeText( widget.destination, maxLines: 1, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyTextDark(context), ) : widget.contact != null ? AutoSizeText.rich( TextSpan(children: [ TextSpan( text: " ", style: AppStyles.iconFontPrimarySmall( context), ), TextSpan( text: widget.contact.name, style: AppStyles.contactsItemName( context)), TextSpan( text: " (" + widget.contact.account.toString() + ")", style: AppStyles.privateKeyTextDarkFaded( context), ), ]), style: TextStyle( fontSize: 14, ), minFontSize: 8, stepGranularity: 0.1, ) : AutoSizeText.rich( TextSpan(children: [ TextSpan( text: widget.accountName.toString(), style: AppStyles.contactsItemName( context)), TextSpan( text: " (" + widget.destination.toString() + ")", style: AppStyles.privateKeyTextDarkFaded( context), ), ]), style: TextStyle( fontSize: 14, ), minFontSize: 8, stepGranularity: 0.1, ) , ), // Amount and Fee Container( margin: EdgeInsetsDirectional.fromSTEB(30, 0, 30, 0), child: Row( children: [ // Amount Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Amount" header Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 0, 30, 0, 0), child: AutoSizeText( AppLocalization.of(context) .amountTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the Amount Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 0, 12, 0, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .primary15), color: StateContainer.of(context) .curTheme .primary10, ), child: AutoSizeText.rich( TextSpan( children: widget.localCurrencyAmount == null ? [ TextSpan( text: "", style: AppStyles .iconFontPrimaryBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.amount, style: AppStyles.balanceSmall( context)), ] : [ TextSpan( text: "", style: AppStyles .iconFontPrimaryBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.amount, style: AppStyles.balanceSmall( context)), TextSpan( text: " (", style: AppStyles.balanceSmall( context)), TextSpan( text: widget.localCurrency.getCurrencySymbol(), style: AppStyles.balanceSmall(context) ), TextSpan( text: widget.localCurrencyAmount, style: AppStyles.balanceSmall( context)), TextSpan( text: ")", style: AppStyles.balanceSmall( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ), ], ), widget.fee != Currency("0") ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Fee" header Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 16, 30, 0, 0), child: AutoSizeText( AppLocalization.of(context) .feeTextFieldHeader, style: AppStyles.textFieldLabel( context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the fee Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 16, 12, 0, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .primary15), color: StateContainer.of(context) .curTheme .primary10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontPrimaryBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.fee .toStringOpt(), style: AppStyles.balanceSmall( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ), ], ) : SizedBox(), ], ), ), // "Payload" header isNotEmpty(widget.payload) ? Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .payloadTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ) : SizedBox(), // Container for the payload text isNotEmpty(widget.payload) ? Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context) .curTheme .textDark10, ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - (widget.encryptPayload ? 101 : 86)), child: AutoSizeText( widget.payload, maxLines: 3, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.left, style: AppStyles.paragraph(context), ), ), widget.encryptPayload ? Container( alignment: Alignment.center, margin: EdgeInsetsDirectional.only( start: 3.0), child: Icon(FontAwesomeIcons.lock, size: 12, color: StateContainer.of(context) .curTheme .textDark)) : SizedBox() ])) : SizedBox(), // Bottom Margin SizedBox(height: 24), ], ), ), ), // "CONFIRM" button Row( children: [ AppButton( type: AppButtonType.Primary, text: toUppercase(AppLocalization.of(context) .confirmButton ,context), buttonTop: true, onPressed: () async { if (await authenticate()) { EventTaxiImpl.singleton() .fire(AuthenticatedEvent(AUTH_EVENT_TYPE.SEND)); } }, ), ], ), // "CANCEL" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: toUppercase(AppLocalization.of(context) .cancelButton ,context), onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } Future doSend({Currency fee}) async { fee = fee == null ? widget.fee : fee; try { showOverlay(context); if (widget.encryptPayload && encryptedPayload == null) { // Try to encrypt the payload with the receivers public key encryptedPayload = await walletState .getAccountState(widget.source) .encryptPayloadEcies( widget.payload, widget.contact == null ? AccountNumber(widget.destination) : widget.contact.account); if (encryptedPayload == null) { _overlay?.remove(); UIUtil.showSnackbar( AppLocalization.of(context).failedToEncryptPayloadError, context); return; } } // Do send RPCResponse result = await walletState .getAccountState(widget.source) .doSend( amount: widget.amount, destination: widget.contact == null ? widget.destination : widget.contact.account.toString(), payload: widget.payload, encryptedPayload: encryptedPayload, fee: fee); if (result.isError) { ErrorResponse errResp = result; UIUtil.showSnackbar(errResp.errorMessage.replaceAll("founds", "funds"), context); _overlay?.remove(); Navigator.of(context).pop(); } else { _overlay?.remove(); OperationsResponse resp = result; PascalOperation op = resp.operations[0]; if (op.valid == null || op.valid) { Navigator.of(context).popUntil(RouteUtils.withNameLike( widget.fromOverview ? "/overview" : "/account")); AppSheets.showBottomSheet( context: context, closeOnTap: true, widget: SentSheet( destination: widget.contact == null ? widget.destination : widget.contact.account.toString(), amount: widget.amount, localCurrencyAmount: widget.localCurrencyAmount, localCurrency: widget.localCurrency, fee: fee, payload: widget.payload, contact: widget.contact, encryptedPayload: widget.encryptPayload, accountName: widget.accountName)); } else { if (op.errors.contains("zero fee") && widget.fee == walletState.NO_FEE) { UIUtil.showFeeDialog( context: context, onConfirm: () async { Navigator.of(context).pop(); doSend(fee: walletState.MIN_FEE); }); } else { UIUtil.showSnackbar("${op.errors}", context); } } } } catch (e) { log.e(e.toString()); _overlay?.remove(); UIUtil.showSnackbar( AppLocalization.of(context).somethingWentWrongError, context); } } Future _authenticateBiometrics(AuthUtil authUtil, String message) async { // Biometric auth bool authenticated = await authUtil.authenticateWithBiometrics(message); if (authenticated) { HapticUtil.fingerprintSucess(); } return authenticated; } Future _authenticatePin(String message) async { String expectedPin = await sl.get().getPin(); bool result = await Navigator.of(context) .push(MaterialPageRoute(builder: (BuildContext context) { return PinScreen( type: PinOverlayType.ENTER_PIN, onSuccess: (pin) { Navigator.of(context).pop(true); }, expectedPin: expectedPin, description: message, ); })); await Future.delayed(Duration(milliseconds: 200)); return result != null && result; } Future authenticate() async { String message = AppLocalization.of(context) .authenticateToSendParagraph .replaceAll("%1", widget.amount); // Authenticate AuthUtil authUtil = AuthUtil(); if (await authUtil.useBiometrics()) { // Biometric auth try { return await _authenticateBiometrics(authUtil, message); } catch (e) { return await _authenticatePin(message); } } else { return await _authenticatePin(message); } } } ================================================ FILE: lib/ui/account/send/sent_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/available_currency.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/model/db/contact.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:quiver/strings.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; class SentSheet extends StatefulWidget { final String destination; final String amount; final String localCurrencyAmount; final AvailableCurrency localCurrency; final String payload; final Currency fee; final Contact contact; final bool encryptedPayload; final AccountName accountName; SentSheet( {@required this.destination, @required this.amount, @required this.fee, this.localCurrencyAmount, this.localCurrency, this.contact, this.payload = "", this.encryptedPayload = false, this.accountName}); _SentSheetState createState() => _SentSheetState(); } class _SentSheetState extends State { @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( width: double.maxFinite, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: [ 0.0, 0.7, 0.7, 1.0 ], colors: [ StateContainer.of(context).curTheme.success, StateContainer.of(context).curTheme.success, StateContainer.of(context).curTheme.backgroundPrimary, StateContainer.of(context).curTheme.backgroundPrimary, ]), borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Container( margin: EdgeInsetsDirectional.only(top: 8), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Sized Box SizedBox( height: 50, width: 65, ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .sentSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), // Tick Container( margin: EdgeInsetsDirectional.only(top: 8), height: 110, width: 110, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: StateContainer.of(context).curTheme.success, boxShadow: [ StateContainer.of(context) .curTheme .shadowTextDarkTwo ], ), child: Icon( AppIcons.tick, size: 40, color: StateContainer.of(context) .curTheme .backgroundPrimary, ), ), ], ), ), ), Expanded( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 40, 30, 0), child: AutoSizeText( AppLocalization.of(context).sentParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), // "Address" header Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).addressTextFieldHeader, style: AppStyles.textFieldLabelSuccess(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the account number Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context) .curTheme .textDark10, ), child: widget.contact == null && widget.accountName == null ? AutoSizeText( widget.destination, maxLines: 1, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyTextDark(context), ) : widget.contact != null ? AutoSizeText.rich( TextSpan(children: [ TextSpan( text: " ", style: AppStyles.iconFontSuccessSmall( context), ), TextSpan( text: widget.contact.name, style: AppStyles.contactsItemName( context)), TextSpan( text: " (" + widget.contact.account .toString() + ")", style: AppStyles.privateKeyTextDarkFaded( context)), ]), style: TextStyle( fontSize: 14, ), minFontSize: 8, stepGranularity: 0.1) : AutoSizeText.rich( TextSpan(children: [ TextSpan( text: widget.accountName.toString(), style: AppStyles.contactsItemName( context)), TextSpan( text: " (" + widget.destination .toString() + ")", style: AppStyles.privateKeyTextDarkFaded( context)), ]), style: TextStyle( fontSize: 14, ), minFontSize: 8, stepGranularity: 0.1 ) ), // Amount and Fee Container( margin: EdgeInsetsDirectional.fromSTEB(30, 0, 30, 0), child: Row( children: [ // Amount Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Amount" header Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 0, 30, 0, 0), child: AutoSizeText( AppLocalization.of(context) .amountTextFieldHeader, style: AppStyles.textFieldLabelSuccess( context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the Amount Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 0, 12, 0, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .success15), color: StateContainer.of(context) .curTheme .success10, ), child: AutoSizeText.rich( TextSpan( children: widget.localCurrencyAmount == null ? [ TextSpan( text: "", style: AppStyles .iconFontSuccessBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.amount, style: AppStyles.balanceSmallSuccess( context)), ] : [ TextSpan( text: "", style: AppStyles .iconFontSuccessBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.amount, style: AppStyles.balanceSmallSuccess( context)), TextSpan( text: " (", style: AppStyles.balanceSmallSuccess( context)), TextSpan( text: widget.localCurrency.getCurrencySymbol(), style: AppStyles.balanceSmallSuccess(context) ), TextSpan( text: widget.localCurrencyAmount, style: AppStyles.balanceSmallSuccess( context)), TextSpan( text: ")", style: AppStyles.balanceSmallSuccess( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ), ], ), widget.fee != Currency("0") ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Fee" header Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 16, 30, 0, 0), child: AutoSizeText( AppLocalization.of(context) .feeTextFieldHeader, style: AppStyles.textFieldLabelSuccess( context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the fee Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 76 / 2), margin: EdgeInsetsDirectional.fromSTEB( 16, 12, 0, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .success15), color: StateContainer.of(context) .curTheme .success10, ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontSuccessBalanceSmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 8)), TextSpan( text: widget.fee .toStringOpt(), style: AppStyles .balanceSmallSuccess( context)), ], ), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ), ], ) : SizedBox(), ], ), ), // "Payload" header isNotEmpty(widget.payload) ? Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .payloadTextFieldHeader, style: AppStyles.textFieldLabelSuccess(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ) : SizedBox(), // Container for the payload text isNotEmpty(widget.payload) ? Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .textDark15), color: StateContainer.of(context) .curTheme .textDark10, ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - (widget.encryptedPayload ? 101 : 86)), child: AutoSizeText( widget.payload, maxLines: 3, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.start, style: AppStyles.paragraph(context), ), ), widget.encryptedPayload ? Container( alignment: Alignment.center, margin: EdgeInsetsDirectional.only( start: 3.0), child: Icon(FontAwesomeIcons.lock, size: 12, color: StateContainer.of(context) .curTheme .textDark)) : SizedBox() ])) : SizedBox(), // Bottom Margin SizedBox(height: 24), ], ), ), ), // "CANCEL" button Row( children: [ AppButton( type: AppButtonType.SuccessOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } } ================================================ FILE: lib/ui/intro/intro_backup_confirm.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/pin_screen.dart'; import 'package:blaise_wallet_flutter/ui/widgets/svg_repaint.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:flutter/material.dart'; class IntroBackupConfirmPage extends StatefulWidget { @override _IntroBackupConfirmPageState createState() => _IntroBackupConfirmPageState(); } class _IntroBackupConfirmPageState extends State { var _scaffoldKey = GlobalKey(); @override Widget build(BuildContext context) { // The main scaffold that holds everything return Scaffold( resizeToAvoidBottomInset: false, key: _scaffoldKey, backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, body: LayoutBuilder( builder: (context, constraints) => Column( children: [ //A widget that holds welcome animation + paragraph Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Container for the header Container( padding: EdgeInsetsDirectional.only( top: (MediaQuery.of(context).padding.top) + (24 - (MediaQuery.of(context).padding.top) / 2), ), decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, ), // Row for back button and the header child: Row( children: [ // The header Container( width: MediaQuery.of(context).size.width - 60, margin: EdgeInsetsDirectional.fromSTEB(30, 24, 30, 24), child: AutoSizeText( AppLocalization.of(context).backUpKeyHeader, style: AppStyles.header(context), maxLines: 3, stepGranularity: 0.1, ), ), ], ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Container for the illustration Container( child: SvgRepaintAsset( asset: StateContainer.of(context) .curTheme .illustrationBackup, width: MediaQuery.of(context).size.width * 0.6, height: MediaQuery.of(context).size.width * 0.6 * (183 / 230), ), ), //Container for the paragraph Container( alignment: Alignment(-1, 0), margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 12), child: AutoSizeText( AppLocalization.of(context) .newKeyBackUpConfirmParagraph, maxLines: 5, stepGranularity: 0.1, style: AppStyles.paragraph(context), ), ), ], ), ), ], ), ), //"YES, I'M SURE" and "NO, GO BACK" buttons // "YES, I'M SURE" button Row( children: [ AppButton( type: AppButtonType.Primary, text: toUppercase( AppLocalization.of(context).yesImSureButton, context), buttonTop: true, onPressed: () { sl .get() .setPrivateKeyBackedUp(true) .then((_) { Navigator.of(context).push( MaterialPageRoute(builder: (BuildContext context) { return PinScreen( type: PinOverlayType.NEW_PIN, onSuccess: (pin) { sl.get().setPin(pin).then((_) { walletState.requestUpdate(); Navigator.of(context).pushNamedAndRemoveUntil( '/overview', (Route route) => false); }); }); })); }); }, ), ], ), // "No, Go Back" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: toUppercase( AppLocalization.of(context).noGoBackButton, context), onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ); } } ================================================ FILE: lib/ui/intro/intro_decrypt_and_import_private_key.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/util/routes.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/overlay_dialog.dart'; import 'package:blaise_wallet_flutter/ui/widgets/pin_screen.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:flutter/material.dart'; import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:pascaldart/pascaldart.dart'; class IntroDecryptAndImportPrivateKeyPage extends StatefulWidget { final String encryptedKey; IntroDecryptAndImportPrivateKeyPage({@required this.encryptedKey}); @override _IntroDecryptAndImportPrivateKeyPageState createState() => _IntroDecryptAndImportPrivateKeyPageState(); } class _IntroDecryptAndImportPrivateKeyPageState extends State { FocusNode _passwordFocusNode; TextEditingController _passwordController; String _passwordError; @override void initState() { super.initState(); _passwordFocusNode = FocusNode(); _passwordController = TextEditingController(); } @override Widget build(BuildContext context) { // The main scaffold that holds everything return Scaffold( resizeToAvoidBottomInset: false, backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, body: TapOutsideUnfocus( child: LayoutBuilder( builder: (context, constraints) => Column( children: [ //A widget that holds welcome animation + paragraph Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Container for the header Container( padding: EdgeInsetsDirectional.only( top: (MediaQuery.of(context).padding.top) + (24 - (MediaQuery.of(context).padding.top) / 2), ), decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, ), // Row for back button and the header child: Row( children: [ // The header Container( width: MediaQuery.of(context).size.width - 60, margin: EdgeInsetsDirectional.fromSTEB(30, 24, 30, 24), child: AutoSizeText( AppLocalization.of(context) .decryptAndImportKeyHeader, style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, ), ), ], ), ), //Container for the paragraph Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 20), alignment: Alignment(-1, 0), child: AutoSizeText( AppLocalization.of(context) .looksLikeEncryptedKeyParagraph, maxLines: 3, stepGranularity: 0.1, style: AppStyles.paragraph(context), ), ), Expanded( child: KeyboardAvoider( duration: Duration(milliseconds: 0), autoScroll: true, focusPadding: 40, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Container for the text field Container( margin: EdgeInsetsDirectional.fromSTEB(30, 10, 30, 0), child: AppTextField( label: AppLocalization.of(context) .passwordTextFieldHeader, style: AppStyles.privateKeyPrimary(context), passwordField: true, focusNode: _passwordFocusNode, controller: _passwordController, onChanged: onPasswordChanged, maxLines: 1, )), // Error Text Container( margin: EdgeInsetsDirectional.only( start: 30, end: 30, top: 4, bottom: 40), child: Text( _passwordError == null ? "" : _passwordError, style: AppStyles.paragraphPrimary(context), textAlign: TextAlign.start, ), ), ], ), ), ), ], )), //"Import" and "Go Back" buttons Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).importButton, buttonTop: true, onPressed: () { decryptAndSubmit(); }, ), ], ), // "Go Back" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).goBackButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ))); } void onPasswordChanged(String newText) { setState(() { _passwordError = null; }); } void decryptAndSubmit() { if (_passwordController.text.length < 1) { setState(() { _passwordError = AppLocalization.of(context).emptyPasswordError; }); return; } PrivateKey privKey; try { privKey = PrivateKeyCrypt.decrypt( PDUtil.hexToBytes(widget.encryptedKey), _passwordController.text); } catch (e) { setState(() { _passwordError = AppLocalization.of(context).invalidPasswordError; }); return; } if (!privKey.curve.supported) { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization.of(context).keyTypeNotSupportedHeader, confirmButtonText: toUppercase( AppLocalization.of(context).okayGoBackButton, context), feeDialog: true, body: TextSpan( children: [ TextSpan( text: AppLocalization.of(context) .keyTypeNotSupportedParagraph, style: AppStyles.paragraph(context), ) ], ), onConfirm: () { Navigator.of(context) .popUntil(RouteUtils.withNameLike('/intro_welcome')); }, )); return; } sl.get().setPrivateKey(privKey).then((_) { sl.get().setPrivateKeyBackedUp(true).then((_) { Navigator.of(context) .push(MaterialPageRoute(builder: (BuildContext context) { return PinScreen( type: PinOverlayType.NEW_PIN, onSuccess: (pin) { sl.get().setPin(pin).then((_) { walletState.requestUpdate(); Navigator.of(context).pushNamedAndRemoveUntil( '/overview', (Route route) => false); }); }); })); }); }); } } ================================================ FILE: lib/ui/intro/intro_import_private_key.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/formatters.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/util/routes.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/overlay_dialog.dart'; import 'package:blaise_wallet_flutter/ui/widgets/pin_screen.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:blaise_wallet_flutter/util/user_data_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:pascaldart/pascaldart.dart'; class IntroImportPrivateKeyPage extends StatefulWidget { @override _IntroImportPrivateKeyPageState createState() => _IntroImportPrivateKeyPageState(); } class _IntroImportPrivateKeyPageState extends State { FocusNode privateKeyFocusNode; TextEditingController privateKeyController; bool _showPrivateKeyError; bool _privateKeyValid; @override void initState() { super.initState(); privateKeyFocusNode = FocusNode(); privateKeyController = TextEditingController(); _showPrivateKeyError = false; _privateKeyValid = false; } @override Widget build(BuildContext context) { // The main scaffold that holds everything return Scaffold( resizeToAvoidBottomInset: false, backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, body: TapOutsideUnfocus( child: LayoutBuilder( builder: (context, constraints) => Column( children: [ //A widget that holds welcome animation + paragraph Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Container for the header Container( padding: EdgeInsetsDirectional.only( top: (MediaQuery.of(context).padding.top) + (24 - (MediaQuery.of(context).padding.top) / 2), ), decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, ), // Row for back button and the header child: Row( children: [ // The header Container( width: MediaQuery.of(context).size.width - 60, margin: EdgeInsetsDirectional.fromSTEB(30, 24, 30, 24), child: AutoSizeText( AppLocalization.of(context) .importPrivateKeyHeader, style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, ), ), ], ), ), //Container for the paragraph Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 20), alignment: Alignment(-1, 0), child: AutoSizeText( AppLocalization.of(context).importPrivateKeyParagraph, maxLines: 2, stepGranularity: 0.1, style: AppStyles.paragraph(context), ), ), Expanded( child: KeyboardAvoider( duration: Duration(milliseconds: 0), autoScroll: true, focusPadding: 40, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Container for the text field Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 10, 30, 0), child: AppTextField( label: AppLocalization.of(context) .privateKeyTextFieldHeader, style: _privateKeyValid ? AppStyles.privateKeyPrimary(context) : AppStyles.privateKeyTextDark(context), focusNode: privateKeyFocusNode, controller: privateKeyController, maxLines: 1, firstButton: TextFieldButton( icon: AppIcons.paste, onPressed: () { Clipboard.getData("text/plain") .then((cdata) { if (privateKeyIsValid(cdata.text) || privateKeyIsEncrypted(cdata.text)) { privateKeyController.text = cdata.text; onKeyTextChanged( privateKeyController.text); } }); }, ), secondButton: TextFieldButton( icon: AppIcons.scan, onPressed: () async { String text = await UserDataUtil.getQRData( DataType.RAW, StateContainer.of(context) .curTheme .scannerTheme); if (text != null) { if (privateKeyIsValid(text) || privateKeyIsEncrypted(text)) { privateKeyController.text = text; onKeyTextChanged( privateKeyController.text); } } }, ), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp( "[a-fA-F0-9]")), // Hex characters UpperCaseTextFormatter() ], textCapitalization: TextCapitalization.characters, onChanged: onKeyTextChanged, )), // Error text Container( margin: EdgeInsetsDirectional.only( start: 30, end: 30, top: 4, bottom: 40), child: AutoSizeText( _showPrivateKeyError ? AppLocalization.of(context) .invalidPrivateKeyError : "", style: AppStyles.paragraphPrimary(context), textAlign: TextAlign.start, ), ) ], ), ), ), ], ), ), // "Import" button Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).importButton, buttonTop: true, onPressed: () { validateAndSubmit(); }, ), ], ), // "Go Back" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).goBackButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ))); } bool privateKeyIsValid(String pkText) { try { PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes(pkText)); return true; } catch (e) { return false; } } bool privateKeyIsEncrypted(String pkText, {bool lengthCheck = true}) { int minLength = lengthCheck ? 100 : 8; if (pkText == null || pkText.length < minLength) { return false; } try { String salted = PDUtil.bytesToUtf8String(PDUtil.hexToBytes(pkText.substring(0, 16))); if (salted == "Salted__") { return true; } return false; } catch (e) { return false; } } void onKeyTextChanged(String newText) { if (privateKeyIsValid(newText)) { setState(() { _privateKeyValid = true; _showPrivateKeyError = false; }); } else { setState(() { _privateKeyValid = false; _showPrivateKeyError = false; }); } } void validateAndSubmit() { if (privateKeyIsValid(privateKeyController.text)) { if (!PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(privateKeyController.text)) .curve .supported) { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization.of(context).keyTypeNotSupportedHeader, warningStyle: true, confirmButtonText: AppLocalization.of(context).okayGoBackButton, body: TextSpan( children: [ TextSpan( text: AppLocalization.of(context) .keyTypeNotSupportedParagraph, style: AppStyles.paragraph(context), ) ], ), onConfirm: () { Navigator.of(context) .popUntil(RouteUtils.withNameLike('/intro_welcome')); }, )); return; } sl .get() .setPrivateKey(PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(privateKeyController.text))) .then((_) { sl.get().setPrivateKeyBackedUp(true).then((_) { Navigator.of(context) .push(MaterialPageRoute(builder: (BuildContext context) { return PinScreen( type: PinOverlayType.NEW_PIN, onSuccess: (pin) { sl.get().setPin(pin).then((_) { walletState.requestUpdate(); Navigator.of(context).pushNamedAndRemoveUntil( '/overview', (Route route) => false); }); }); })); }); }); } else if (privateKeyIsEncrypted(privateKeyController.text)) { Navigator.of(context).pushNamed('/intro_decrypt_and_import_private_key', arguments: privateKeyController.text); } else { setState(() { _showPrivateKeyError = true; }); } } } ================================================ FILE: lib/ui/intro/intro_new_private_key.dart ================================================ import 'dart:async'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/util/user_data_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class IntroNewPrivateKeyPage extends StatefulWidget { @override _IntroNewPrivateKeyPageState createState() => _IntroNewPrivateKeyPageState(); } class _IntroNewPrivateKeyPageState extends State { bool _keyCopied; Timer _keyCopiedTimer; var _scaffoldKey = GlobalKey(); @override void initState() { super.initState(); _keyCopied = false; } @override Widget build(BuildContext context) { // The main scaffold that holds everything return Scaffold( resizeToAvoidBottomInset: false, key: _scaffoldKey, backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, body: LayoutBuilder( builder: (context, constraints) => Column( children: [ //A widget that holds welcome animation + paragraph Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Container for the header Container( padding: EdgeInsetsDirectional.only( top: (MediaQuery.of(context).padding.top) + (24 - (MediaQuery.of(context).padding.top) / 2), ), decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, ), // Row for back button and the header child: Row( children: [ // The header Container( width: MediaQuery.of(context).size.width - 60, margin: EdgeInsetsDirectional.fromSTEB(30, 24, 30, 24), child: AutoSizeText( AppLocalization.of(context).newPrivateKeyHeader, style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, ), ), ], ), ), //Container for the paragraph Container( alignment: Alignment(-1, 0), margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).newPrivateKeyParagraph, maxLines: 5, stepGranularity: 0.1, style: AppStyles.paragraph(context), ), ), // Container for the private key Container( margin: EdgeInsetsDirectional.fromSTEB(30, 24, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(32, 16, 32, 16), width: MediaQuery.of(context).size.width - 60, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: _keyCopied ? StateContainer.of(context).curTheme.success15 : StateContainer.of(context).curTheme.primary15), color: _keyCopied ? StateContainer.of(context).curTheme.success10 : StateContainer.of(context).curTheme.primary10, ), child: FutureBuilder( future: sl.get().getPrivateKey(), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData && snapshot.data != null) { return AutoSizeText( snapshot.data, textAlign: TextAlign.center, maxLines: 3, stepGranularity: 0.5, style: _keyCopied ? AppStyles.privateKeySuccess(context) : AppStyles.privateKeyPrimary(context), ); } else { return SizedBox(); } }), ), // Container for the "Copy to Clipboard" button Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(100.0), color: _keyCopied ? StateContainer.of(context).curTheme.success : StateContainer.of(context) .curTheme .backgroundPrimary, boxShadow: [ StateContainer.of(context).curTheme.shadowTextDark, ], ), margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), height: 40, child: FlatButton( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(100.0)), child: AutoSizeText( _keyCopied ? AppLocalization.of(context).copiedButton : AppLocalization.of(context).copyButton, textAlign: TextAlign.center, maxLines: 1, stepGranularity: 0.1, style: _keyCopied ? AppStyles.buttonMiniSuccess(context) : AppStyles.buttonMiniBg(context), ), splashColor: StateContainer.of(context) .curTheme .backgroundPrimary60, highlightColor: StateContainer.of(context) .curTheme .backgroundPrimary30, onPressed: () { sl.get().getPrivateKey().then((key) { UserDataUtil.setSecureClipboardItem(key); }); setState(() { _keyCopied = true; }); if (_keyCopiedTimer != null) { _keyCopiedTimer.cancel(); } _keyCopiedTimer = Timer(const Duration(milliseconds: 1500), () { if (mounted) { setState(() { _keyCopied = false; }); } }); }, ), ), ], ), ], ), ), //"I've Backed It Up" and "Go Back" buttons Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).iHaveBackedItUpButton, buttonTop: true, onPressed: () { Navigator.of(context).pushNamed('/intro_backup_confirm'); }, ), ], ), // "Go Back" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).goBackButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ); } } ================================================ FILE: lib/ui/intro/intro_security_first.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/svg_repaint.dart'; import 'package:blaise_wallet_flutter/util/pascal_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:flutter/material.dart'; class IntroSecurityFirstPage extends StatefulWidget { @override _IntroSecurityFirstPageState createState() => _IntroSecurityFirstPageState(); } class _IntroSecurityFirstPageState extends State { var _scaffoldKey = GlobalKey(); @override Widget build(BuildContext context) { // The main scaffold that holds everything return Scaffold( resizeToAvoidBottomInset: false, key: _scaffoldKey, backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, body: LayoutBuilder( builder: (context, constraints) => Column( children: [ //A widget that holds welcome animation + paragraph Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Container for the header Container( padding: EdgeInsetsDirectional.only( top: (MediaQuery.of(context).padding.top) + (24 - (MediaQuery.of(context).padding.top) / 2), ), decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, ), // Row for back button and the header child: Row( children: [ // The header Container( width: MediaQuery.of(context).size.width - 60, margin: EdgeInsetsDirectional.fromSTEB(30, 24, 30, 24), child: AutoSizeText( AppLocalization.of(context).securityFirstHeader, style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, ), ), ], ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Container for the illustration Container( margin: EdgeInsetsDirectional.only(top: 10), child: SvgRepaintAsset( asset: StateContainer.of(context) .curTheme .illustrationSecurity, width: MediaQuery.of(context).size.width * (UIUtil.smallScreen(context) ? 0.35 : 0.5), height: MediaQuery.of(context).size.width * (UIUtil.smallScreen(context) ? 0.35 : 0.5) * (213 / 230), ), ), //Container for the paragraph Container( alignment: Alignment(-1, 0), margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).newKeySecurityParagraph, maxLines: 4, stepGranularity: 0.1, style: AppStyles.paragraph(context), ), ), //Container for the paragraph Container( alignment: Alignment(-1, 0), margin: EdgeInsetsDirectional.fromSTEB(30, 10, 30, 0), child: AutoSizeText( AppLocalization.of(context) .uninstallDisclaimerParagraph, maxLines: 3, stepGranularity: 0.1, style: AppStyles.paragraphPrimary(context), ), ), ], ), ), ], ), ), //"I've Backed It Up" and "Go Back" buttons Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).gotItButton, buttonTop: true, onPressed: () { sl .get() .setPrivateKey( sl.get().generateKeyPair().privateKey) .then((key) { Navigator.of(context).pushNamed('/intro_new_private_key'); }); }, ), ], ), // "Go Back" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).goBackButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ); } } ================================================ FILE: lib/ui/intro/intro_welcome.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/available_languages.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/overlay_dialog.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:flutter/material.dart'; import 'package:flare_flutter/flare_actor.dart'; class IntroWelcomePage extends StatefulWidget { @override _IntroWelcomePageState createState() => _IntroWelcomePageState(); } class _IntroWelcomePageState extends State { List getLanguageList() { List ret = []; AvailableLanguage.values.forEach((AvailableLanguage value) { LanguageSetting setting = LanguageSetting(value); ret.add(DialogListItem( option: setting.getDisplayName(context), action: () { if (setting != StateContainer.of(context).curLanguage) { sl.get().setLanguage(setting).then((result) { StateContainer.of(context).updateLanguage(setting); }); } Navigator.of(context).pop(); })); }); return ret; } @override void initState() { super.initState(); } @override Widget build(BuildContext context) { // The main scaffold that holds everything return Scaffold( resizeToAvoidBottomInset: false, backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, body: LayoutBuilder( builder: (context, constraints) => Column( children: [ //A widget that holds welcome animation + paragraph Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // A stack for background gradient and the animation Stack( children: [ // Container for the gradient background Container( height: (MediaQuery.of(context).padding.top + (MediaQuery.of(context).size.width * 262 / 400)) - (MediaQuery.of(context).size.width * 80 / 400), decoration: BoxDecoration( gradient: StateContainer.of(context) .curTheme .gradientPrimary, ), ), //Container for the animation Container( margin: EdgeInsetsDirectional.only( top: MediaQuery.of(context).padding.top), //Width/Height ratio for the animation is needed because BoxFit is not working as expected width: double.maxFinite, height: MediaQuery.of(context).size.width * 262 / 400, child: Center( child: FlareActor( StateContainer.of(context) .curTheme .animationWelcome, animation: "main", fit: BoxFit.contain, ), ), ), ], ), //Container for the paragraph Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( margin: EdgeInsets.symmetric(horizontal: 30), child: AutoSizeText( AppLocalization.of(context).welcomeParagraph, maxLines: 4, stepGranularity: 0.1, style: AppStyles.paragraph(context), ), ), // "Language" button Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( height: 40.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100.0), color: StateContainer.of(context) .curTheme .backgroundPrimary, boxShadow: [ StateContainer.of(context) .curTheme .shadowTextDark, ], ), margin: EdgeInsetsDirectional.fromSTEB(30, 20, 30, 0), child: FlatButton( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(120.0)), child: Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 100), padding: EdgeInsetsDirectional.only( start: 4, end: 4), child: AutoSizeText.rich( TextSpan(children: [ TextSpan( text: AppLocalization.of(context) .languageColonHeader, style: TextStyle( fontSize: 14, color: StateContainer.of(context) .curTheme .primary, fontWeight: FontWeight.w600), ), TextSpan( text: " ", style: TextStyle( fontSize: 14, color: StateContainer.of(context) .curTheme .primary, fontWeight: FontWeight.w600), ), TextSpan( text: StateContainer.of(context) .curLanguage .getDisplayName(context), style: TextStyle( fontSize: 14, color: StateContainer.of(context) .curTheme .textDark, fontWeight: FontWeight.w400), ), ]), textAlign: TextAlign.center, maxLines: 1, stepGranularity: 0.1, style: TextStyle(fontSize: 14), ), ), onPressed: () { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization.of(context) .languageHeader, optionsList: getLanguageList())); }, ), ), ], ), ], ), ), ], ), ), //"New Private Key" and "Import Private Key" buttons Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).newPrivateKeyButton, buttonTop: true, onPressed: () { Navigator.of(context).pushNamed('/intro_security_first'); }, ), ], ), // "Import Private Key" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).importPrivateKeyButton, onPressed: () { Navigator.of(context) .pushNamed('/intro_import_private_key'); }, ), ], ), ], ), ), ); } } ================================================ FILE: lib/ui/lockscreen/lock_screen.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/authentication_method.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/routes.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/overlay_dialog.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/pin_screen.dart'; import 'package:blaise_wallet_flutter/util/authentication.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:flutter/material.dart'; class LockScreenPage extends StatefulWidget { @override _LockScreenPageState createState() => _LockScreenPageState(); } class _LockScreenPageState extends State { bool _showUnlockButton = false; bool _lockedOut = true; String _countDownTxt = ""; void _goHome() { walletState.requestUpdate(); Navigator.of(context) .pushNamedAndRemoveUntil('/overview', (Route route) => false); } String _formatCountDisplay(int count) { if (count <= 60) { // Seconds only String secondsStr = count.toString(); if (count < 10) { secondsStr = "0" + secondsStr; } return "00:" + secondsStr; } else if (count > 60 && count <= 3600) { // Minutes:Seconds String minutesStr = ""; int minutes = count ~/ 60; if (minutes < 10) { minutesStr = "0" + minutes.toString(); } else { minutesStr = minutes.toString(); } String secondsStr = ""; int seconds = count % 60; if (seconds < 10) { secondsStr = "0" + seconds.toString(); } else { secondsStr = seconds.toString(); } return minutesStr + ":" + secondsStr; } else { // Hours:Minutes:Seconds String hoursStr = ""; int hours = count ~/ 3600; if (hours < 10) { hoursStr = "0" + hours.toString(); } else { hoursStr = hours.toString(); } count = count % 3600; String minutesStr = ""; int minutes = count ~/ 60; if (minutes < 10) { minutesStr = "0" + minutes.toString(); } else { minutesStr = minutes.toString(); } String secondsStr = ""; int seconds = count % 60; if (seconds < 10) { secondsStr = "0" + seconds.toString(); } else { secondsStr = seconds.toString(); } return hoursStr + ":" + minutesStr + ":" + secondsStr; } } Future _runCountdown(int count) async { if (count >= 1) { if (mounted) { setState(() { _showUnlockButton = true; _lockedOut = true; _countDownTxt = _formatCountDisplay(count); }); } Future.delayed(Duration(seconds: 1), () { _runCountdown(count - 1); }); } else { if (mounted) { setState(() { _lockedOut = false; }); } } } Future _authenticateBiometrics() async { setState(() { _showUnlockButton = true; }); bool authenticated = await AuthUtil().authenticateWithBiometrics( AppLocalization.of(context).authenticateToUnlockParagraph); if (authenticated) { _goHome(); } else { setState(() { _showUnlockButton = true; }); } } Future _authenticatePin({bool transitions = false}) async { // PIN authentication String expectedPin = await sl.get().getPin(); if (transitions) { Navigator.of(context).push( MaterialPageRoute(builder: (BuildContext context) { return PinScreen( type: PinOverlayType.ENTER_PIN, expectedPin: expectedPin, description: AppLocalization.of(context).enterPINToUnlockParagraph, onSuccess: (pin) { _goHome(); }); }), ); } else { Navigator.of(context).push( NoPushTransitionRoute(builder: (BuildContext context) { return PinScreen( type: PinOverlayType.ENTER_PIN, expectedPin: expectedPin, description: AppLocalization.of(context).enterPINToUnlockParagraph, onSuccess: (pin) { _goHome(); }); }), ); } Future.delayed(Duration(milliseconds: 200), () { if (mounted) { setState(() { _showUnlockButton = true; }); } }); } Future _authenticate({bool transitions = false}) async { // Test if user is locked out // Get duration of lockout DateTime lockUntil = await sl.get().getLockDate(); if (lockUntil == null) { await sl.get().resetLockAttempts(); } else { int countDown = lockUntil.difference(DateTime.now().toUtc()).inSeconds; // They're not allowed to attempt if (countDown > 0) { _runCountdown(countDown); return; } } setState(() { _lockedOut = false; }); bool useBiometrics = await AuthUtil().useBiometrics(); if (useBiometrics) { try { await _authenticateBiometrics(); } catch (e) { await _authenticatePin(transitions: transitions); } } else { await _authenticatePin(transitions: transitions); } } @override void initState() { super.initState(); _authenticate(); } void logoutPressed() { showAppDialog( context: context, builder: (_) => DialogOverlay( title: toUppercase( AppLocalization.of(context).warningHeader, context), warningStyle: true, confirmButtonText: toUppercase( AppLocalization.of(context).deletePrivateKeyAndLogoutButton, context), body: TextSpan( children: formatLocalizedColorsDanger(context, AppLocalization.of(context).logoutFirstDisclaimerParagraph), ), onConfirm: () { Navigator.of(context).pop(); showAppDialog( context: context, builder: (_) => DialogOverlay( title: toUppercase( AppLocalization.of(context).areYouSureHeader, context), warningStyle: true, confirmButtonText: toUppercase( AppLocalization.of(context).yesImSureButton, context), body: TextSpan( children: formatLocalizedColorsDanger( context, AppLocalization.of(context) .logoutSecondDisclaimerParagraph), ), onConfirm: () { // Handle logging out walletState.reset(); sl.get().deleteAll().then((_) { sl.get().deleteAll().then((_) { Navigator.of(context).pushNamedAndRemoveUntil( '/', (Route route) => false); }); }); })); }, )); } @override Widget build(BuildContext context) { // The main scaffold that holds everything return Scaffold( resizeToAvoidBottomInset: false, backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, body: LayoutBuilder( builder: (context, constraints) => Column( children: [ //A widget that holds lock icon and background Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // A stack for background gradient and the lock icon Stack( children: [ // Container for the gradient background Container( height: (MediaQuery.of(context).padding.top + MediaQuery.of(context).size.width * 0.4), decoration: BoxDecoration( gradient: StateContainer.of(context) .curTheme .gradientPrimary, ), ), // "Locked" text Container( margin: EdgeInsetsDirectional.only( top: MediaQuery.of(context).padding.top + MediaQuery.of(context).size.width * 0.08), alignment: Alignment(0, 0), child: Text( toUppercase(AppLocalization.of(context).lockedHeader, context), textAlign: TextAlign.center, style: TextStyle( fontSize: 30, fontFamily: "Metropolis", fontWeight: FontWeight.w700), ), ), // Lock Icon Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( margin: EdgeInsetsDirectional.only( top: MediaQuery.of(context).padding.top + MediaQuery.of(context).size.width * 0.2), height: MediaQuery.of(context).size.width * 0.4, width: MediaQuery.of(context).size.width * 0.4, decoration: BoxDecoration( gradient: StateContainer.of(context) .curTheme .gradientPrimary, borderRadius: BorderRadius.circular(400)), child: Icon(Icons.lock, color: StateContainer.of(context) .curTheme .backgroundPrimary, size: MediaQuery.of(context).size.width * 0.2), ), ], ), // Logout button Container( margin: EdgeInsetsDirectional.only(start: 5, top: 30), alignment: Alignment.topLeft, child: Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ FlatButton( splashColor: StateContainer.of(context) .curTheme .backgroundPrimary30, highlightColor: StateContainer.of(context) .curTheme .backgroundPrimary15, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8)), onPressed: () { logoutPressed(); }, padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), // A row for logout icon and text child: Row( mainAxisSize: MainAxisSize.min, children: [ // log out icon Container( margin: EdgeInsetsDirectional.only(end: 8), child: Icon(AppIcons.logout, size: 20, color: StateContainer.of(context) .curTheme .backgroundPrimary), ), // Support text Container( constraints: BoxConstraints( maxWidth: (MediaQuery.of(context).size.width - 100) * 0.4), child: AutoSizeText( AppLocalization.of(context).logoutHeader, style: TextStyle( fontWeight: FontWeight.w700, fontSize: 14, fontFamily: "Metropolis", color: StateContainer.of(context) .curTheme .backgroundPrimary), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 0.1, ), ), ], ), ), ], ), ), ], ), ], ), ), _lockedOut ? Align( alignment: Alignment.bottomCenter, child: Container( width: MediaQuery.of(context).size.width - 60, child: Text( AppLocalization.of(context).manyFailedAttemptsParagraph, style: AppStyles.paragraphDanger(context), textAlign: TextAlign.center, ), ), ) : SizedBox(), // "Unlock" button _showUnlockButton ? Row( children: [ AppButton( type: AppButtonType.Primary, text: _lockedOut ? _countDownTxt : AppLocalization.of(context).unlockButton, onPressed: () async { if (!_lockedOut) { await _authenticate(transitions: true); } }, disabled: _lockedOut, ), ], ) : SizedBox(), ], ), ), ); } } ================================================ FILE: lib/ui/overview/buy_account_sheet.dart ================================================ import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/routes.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/svg_repaint.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flare_flutter/flare_actor.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:pascaldart/pascaldart.dart'; class BuyAccountSheet extends StatefulWidget { _BuyAccountSheetState createState() => _BuyAccountSheetState(); } class _BuyAccountSheetState extends State { String accountPrice = "0.25"; String fiatPrice = ""; String returnInDays = "3"; OverlayEntry _overlay; void showOverlay(BuildContext context) { OverlayState overlayState = Overlay.of(context); _overlay = OverlayEntry( builder: (context) => BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container( width: double.maxFinite, height: double.maxFinite, color: StateContainer.of(context).curTheme.overlay20, child: Center( child: //Container for the animation Container( margin: EdgeInsetsDirectional.only( top: MediaQuery.of(context).padding.top), //Width/Height ratio for the animation is needed because BoxFit is not working as expected width: double.maxFinite, height: MediaQuery.of(context).size.width, child: Center( child: FlareActor( StateContainer.of(context).curTheme.animationGetAccount, animation: "main", fit: BoxFit.contain, ), ), ), ), ), ), ); overlayState.insert(_overlay); } @override Widget build(BuildContext context) { fiatPrice = walletState.getLocalCurrencyDisplay( currency: StateContainer.of(context).curCurrency, amount: Currency(accountPrice), decimalDigits: 2); return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Sized Box SizedBox( height: 50, width: 65, ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .buyAccountSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( children: [ // Container for the illustration Container( margin: EdgeInsetsDirectional.only( top: 30, ), child: SvgRepaintAsset( asset: StateContainer.of(context) .curTheme .illustrationBorrowed, width: MediaQuery.of(context).size.width * (UIUtil.smallScreen(context) ? 0.6 : 0.8), height: MediaQuery.of(context).size.width * (UIUtil.smallScreen(context) ? 0.6 : 0.8) * 132 / 295), ), // Paragraph Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText.rich( TextSpan( children: formatLocalizedColors( context, AppLocalization.of(context) .borrowAccountParagraph.replaceAll("%1", accountPrice).replaceAll("%2", "~" + fiatPrice).replaceAll("%3", returnInDays)), ), stepGranularity: 0.5, maxLines: 9, minFontSize: 8, style: TextStyle(fontSize: 14), ), ), ], ), ), //"Borrow an Account" and "Cancel" buttons Row( children: [ Observer( builder: (context) { bool disabled = !walletState.isBorrowEligible || walletState.hasExceededBorrowLimit; return AppButton( type: AppButtonType.Primary, text: toUppercase(AppLocalization.of(context) .borrowAnAccountButton ,context), disabled: disabled, buttonTop: true, onPressed: () async { if (!walletState.isBorrowEligible) { return; } showOverlay(context); dynamic resp = await walletState.initiateBorrow(); if (walletState.borrowedAccount != null) { _overlay?.remove(); walletState.loadWallet(); if (resp is PascalAccount) { Navigator.of(context).pushReplacementNamed('/account', arguments: resp); } else { Navigator.of(context).popUntil(RouteUtils.withNameLike('/overview')); UIUtil.showSnackbar(AppLocalization.of(context).borrowStarted.replaceAll('%1', walletState.borrowedAccount.account.toString()), context); } } else { _overlay?.remove(); Navigator.of(context).popUntil(RouteUtils.withNameLike('/overview')); UIUtil.showSnackbar(AppLocalization.of(context).somethingWentWrongError, context); } }, ); }, ), ], ), // "Close" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: toUppercase(AppLocalization.of(context) .cancelButton ,context), onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } } ================================================ FILE: lib/ui/overview/confirm_free_account_sheet.dart ================================================ import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/network/http_client.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/util/routes.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flare_flutter/flare_actor.dart'; import 'package:flutter/material.dart'; import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:logger/logger.dart'; import 'package:pascaldart/pascaldart.dart'; class ConfirmFreeAccountSheet extends StatefulWidget { final String requestId; ConfirmFreeAccountSheet({@required this.requestId}) : super(); _ConfirmFreeAccountSheetState createState() => _ConfirmFreeAccountSheetState(); } class _ConfirmFreeAccountSheetState extends State { final Logger log = sl.get(); OverlayEntry _overlay; TextEditingController codeController; @override void initState() { super.initState(); this.codeController = TextEditingController(); log.d("confirming with request ID '${widget.requestId}'"); } void showOverlay(BuildContext context) { OverlayState overlayState = Overlay.of(context); _overlay = OverlayEntry( builder: (context) => BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container( width: double.maxFinite, height: double.maxFinite, color: StateContainer.of(context).curTheme.overlay20, child: Center( child: //Container for the animation Container( margin: EdgeInsetsDirectional.only( top: MediaQuery.of(context).padding.top), //Width/Height ratio for the animation is needed because BoxFit is not working as expected width: double.maxFinite, height: MediaQuery.of(context).size.width, child: Center( child: FlareActor( StateContainer.of(context).curTheme.animationGetAccount, animation: "main", fit: BoxFit.contain, ), ), ), ), ), ), ); overlayState.insert(_overlay); } @override Widget build(BuildContext context) { return TapOutsideUnfocus( child: Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Sized Box SizedBox( height: 50, width: 65, ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( AppLocalization.of(context).freeAccountSheetHeader.toUpperCase(), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( children: [ //Container for the paragraph Container( alignment: Alignment(-1, 0), margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).enterConfirmationCodeParagraph, maxLines: 3, stepGranularity: 0.1, style: AppStyles.paragraph(context), ), ), Expanded( child: KeyboardAvoider( duration: Duration(milliseconds: 0), autoScroll: true, focusPadding: 40, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Container for SMS confirmation code Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 30), child: AppTextField( label: AppLocalization.of(context).confirmationCodeTextFieldHeader, style: AppStyles.paragraphMedium(context), inputType: TextInputType.number, maxLines: 1, controller: codeController, ), ), ], ), ), ), ], ), ), //"CONFIRM" and "Go Back" buttons Row( children: [ AppButton( type: AppButtonType.Primary, text: toUppercase(AppLocalization.of(context).confirmButton, context), buttonTop: true, onPressed: () async { showOverlay(context); bool success = await verifyFreepasaAccount(); _overlay?.remove(); if (success) { walletState.loadWallet(); Navigator.popUntil(context, RouteUtils.withNameLike('/overview')); UIUtil.showSnackbar(AppLocalization.of(context).freepasaComplete, context); } }, ), ], ), // "Close" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: toUppercase(AppLocalization.of(context).goBackButton, context), onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ), ); } /// Verify a freepasa request, return true if valid, false if not Future verifyFreepasaAccount() async { try { log.d("Sending request to verify freePASA rid: '${widget.requestId}', code ${codeController.text}"); int response = await HttpAPI.verifyFreePASA( widget.requestId, codeController.text ); if (response == null) { log.w("Null response from HttpAPI.verifyFreePASA"); UIUtil.showSnackbar(AppLocalization.of(context).somethingWentWrongError, context); return false; } else if (response < 0) { log.d("Invalid confirmation code entered at freepasa verify"); UIUtil.showSnackbar(AppLocalization.of(context).confirmationCodeError, context); return false; } AccountNumber account = AccountNumber.fromInt(response); await sl.get().setFreepasaAccount(account); return true; } catch (e) { log.e("Caught exception at freepasa verify ${e.toString()}", e); return false; } } } ================================================ FILE: lib/ui/overview/get_account_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/available_currency.dart'; import 'package:blaise_wallet_flutter/ui/overview/buy_account_sheet.dart'; import 'package:blaise_wallet_flutter/ui/overview/get_free_account_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/ui/widgets/svg_repaint.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:pascaldart/pascaldart.dart'; class GetAccountSheet extends StatefulWidget { _GetAccountSheetState createState() => _GetAccountSheetState(); } class _GetAccountSheetState extends State { String accountPrice = "0.25"; @override void initState() { super.initState(); } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .getAccountSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: // Container for the illustration Container( margin: EdgeInsetsDirectional.only(top: 24, bottom: 16), child: SvgRepaintAsset( asset: StateContainer.of(context) .curTheme .illustrationTwoOptions, width: MediaQuery.of(context).size.width * 0.6, height: MediaQuery.of(context).size.width * (142 / 180) * 0.6, ), ), ), // Paragraph Container( margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 24), child: AutoSizeText.rich( TextSpan( children: getAccountSheetParagraphs(StateContainer.of(context).curCurrency), ), stepGranularity: 0.1, maxLines: 9, minFontSize: 8, ), ), //"Get a Free Account" and "Buy An Account" buttons Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).getAFreeAccountButton, buttonTop: true, onPressed: () { Navigator.of(context).pop(); AppSheets.showBottomSheet( context: context, widget: GetFreeAccountSheet() ); }, ), ], ), // "Buy An Account" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).buyAnAccountButton, onPressed: () { Navigator.of(context).pop(); AppSheets.showBottomSheet( context: context, widget: BuyAccountSheet()); }, ), ], ), ], ), ), ), ], ); } getAccountSheetParagraphs(AvailableCurrency curCurrency) { List ret = []; ret.add( TextSpan( text: AppLocalization.of(context).getAccountFirstParagraph + "\n\n", style: AppStyles.paragraph(context), ), ); ret = ret + [] ..addAll(formatLocalizedColors( context, AppLocalization.of(context).getAccountSecondParagraph)); ret.add( TextSpan( text: "\n\n", style: AppStyles.paragraph(context), ), ); ret = ret + [] ..addAll(formatLocalizedColors( context, AppLocalization.of(context) .getAccountThirdParagraphAlternative2 .replaceAll("%1", accountPrice).replaceAll("%2",curCurrency == null || walletState.localCurrencyPrice == null ? "N/A" : "~" + walletState.getLocalCurrencyDisplay( currency: curCurrency, amount: Currency(accountPrice), decimalDigits: 2)).replaceAll("%3", "2"))); return ret; } } ================================================ FILE: lib/ui/overview/get_free_account_sheet.dart ================================================ import 'dart:convert'; import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/network/http_client.dart'; import 'package:blaise_wallet_flutter/ui/overview/confirm_free_account_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/formatters.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/error_container.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flare_flutter/flare_actor.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:pascaldart/pascaldart.dart'; class CountryCode { String isoCode; String displayName; CountryCode({@required this.isoCode, @required this.displayName}); static List fromJsonList(List jsonList) { return jsonList.map((e) => CountryCode.fromJson(e)).toList(); } factory CountryCode.fromJson(Map json) { return CountryCode(isoCode: json['iso'], displayName: json['text']); } } class GetFreeAccountSheet extends StatefulWidget { _GetFreeAccountSheetState createState() => _GetFreeAccountSheetState(); } class _GetFreeAccountSheetState extends State { List _countryCodes; CountryCode _selectedCountry; FixedExtentScrollController _cupertinoPickerController; TextEditingController _phoneNumberController; bool _showPhoneError; OverlayEntry _overlay; String requestId; Future> readCountryCodeFromAssets() async { return CountryCode.fromJsonList(json.decode( await DefaultAssetBundle.of(context) .loadString("assets/country_phone_map.json"))); } CountryCode getDefaultCountryCode(List list) { String locale = Localizations.localeOf(context).countryCode.toUpperCase(); return list.firstWhere((cc) => cc.isoCode == locale, orElse: () => list.firstWhere((cc) => cc.isoCode == 'US')); } List _getCountryCodeForPicker() { List ret = []; _countryCodes.forEach((value) { ret.add(Center( child: Text( value?.displayName, style: AppStyles.paragraphMedium(context), ))); }); return ret; } @override void initState() { super.initState(); _phoneNumberController = TextEditingController(); this._showPhoneError = false; _cupertinoPickerController = FixedExtentScrollController(); this.requestId = null; readCountryCodeFromAssets().then((values) { setState(() { _countryCodes = values; _selectedCountry = getDefaultCountryCode(values); _cupertinoPickerController = FixedExtentScrollController( initialItem: _countryCodes .indexWhere((cc) => cc.isoCode == _selectedCountry.isoCode)); }); }); } @override Widget build(BuildContext context) { return TapOutsideUnfocus( child: Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Sized Box SizedBox( height: 50, width: 65, ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase( AppLocalization.of(context) .freeAccountSheetHeader, context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( children: [ //Container for the paragraph Container( alignment: Alignment(-1, 0), margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .enterPhoneNumberParagraph, maxLines: 3, stepGranularity: 0.1, style: AppStyles.paragraph(context), ), ), Expanded( child: KeyboardAvoider( duration: Duration(milliseconds: 0), autoScroll: true, focusPadding: 40, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Country Code" header Container( width: MediaQuery.of(context).size.width - 60, margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context) .countryCodeTextFieldHeader, style: AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), Container( margin: EdgeInsetsDirectional.only( start: 30, end: 30), child: FlatButton( onPressed: () { if (_countryCodes == null) { return; } showCountryCodePicker(); }, padding: EdgeInsets.all(0), child: Column( children: [ // Container for country code Container( width: MediaQuery.of(context) .size .width - 60, margin: EdgeInsetsDirectional.fromSTEB( 0, 14, 0, 0), child: AutoSizeText.rich( TextSpan(children: [ TextSpan( text: _selectedCountry ?.displayName, style: AppStyles.paragraphMedium( context), ), ]), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the underline Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB( 0, 10, 0, 0), height: 1, color: StateContainer.of(context) .curTheme .primary, ), ], ), )), // Container for phone number field Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 40), child: AppTextField( label: AppLocalization.of(context) .phoneNumberTextFieldHeader, style: AppStyles.paragraphMedium(context), controller: _phoneNumberController, maxLines: 1, inputType: TextInputType.phone, inputFormatters: [ PhoneNumberFormatter(), FilteringTextInputFormatter.allow( RegExp("[0-9-]")) ], onChanged: (text) { if (mounted && this._showPhoneError) { setState(() { this._showPhoneError = false; }); } }, ), ), // Error Text ErrorContainer( errorText: this._showPhoneError ? AppLocalization.of(context) .invalidPhoneNumberParagraph : "", ), ], ), ), ), ], ), ), //"Send Confirmation" and "Cancel" buttons Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).sendConfirmationButton, buttonTop: true, onPressed: () async { if (this.requestId == null) { showOverlay(context); String result = await onSubmitted(); _overlay?.remove(); if (result != null) { this.requestId = result; AppSheets.showBottomSheet( context: context, widget: ConfirmFreeAccountSheet( requestId: result, ), noBlur: true); } } else { AppSheets.showBottomSheet( context: context, widget: ConfirmFreeAccountSheet( requestId: this.requestId), noBlur: true); } }, ), ], ), // "Close" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).cancelButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ), ); } void showOverlay(BuildContext context) { OverlayState overlayState = Overlay.of(context); _overlay = OverlayEntry( builder: (context) => BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container( width: double.maxFinite, height: double.maxFinite, color: StateContainer.of(context).curTheme.overlay20, child: Center( child: //Container for the animation Container( margin: EdgeInsetsDirectional.only( top: MediaQuery.of(context).padding.top), //Width/Height ratio for the animation is needed because BoxFit is not working as expected width: double.maxFinite, height: MediaQuery.of(context).size.width, child: Center( child: FlareActor( StateContainer.of(context).curTheme.animationGetAccount, animation: "main", fit: BoxFit.contain, ), ), ), ), ), ), ); overlayState.insert(_overlay); } Future showCountryCodePicker() async { CountryCode countrySelection; await showCupertinoModalPopup( context: context, builder: (context) { return Container( height: MediaQuery.of(context).size.height * 0.4, child: Material( child: CupertinoPicker( backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, useMagnifier: true, magnification: 1.5, scrollController: _cupertinoPickerController, onSelectedItemChanged: (index) { countrySelection = _countryCodes[index]; }, itemExtent: 30, children: _getCountryCodeForPicker()), ), ); }, ); if (countrySelection != null) { setState(() { _selectedCountry = countrySelection; _cupertinoPickerController = FixedExtentScrollController( initialItem: _countryCodes .indexWhere((cc) => cc.isoCode == _selectedCountry.isoCode)); }); } } // Submit request, return request ID if successful Future onSubmitted() async { // Validate phone number if (_phoneNumberController.text.replaceAll(RegExp(r"[^0-9]"), "").length < 5) { if (mounted) { setState(() { _showPhoneError = true; }); } return null; } // Make free pasa request String response = await HttpAPI.getFreePASA( _selectedCountry.isoCode, _phoneNumberController.text, PublicKeyCoder().encodeToBase58(walletState.publicKey)); // Error occured if null, otherwise move on to verification screen if (response == null) { UIUtil.showSnackbar( AppLocalization.of(context).somethingWentWrongError, context); return null; } return response; } } ================================================ FILE: lib/ui/overview/overview.dart ================================================ import 'dart:async'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/events.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/overview/buy_account_sheet.dart'; import 'package:blaise_wallet_flutter/ui/overview/get_account_sheet.dart'; import 'package:blaise_wallet_flutter/ui/overview/public_key_overview_sheet.dart'; import 'package:blaise_wallet_flutter/ui/settings/settings.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/routes.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/account_card.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/placeholder_account_card.dart'; import 'package:blaise_wallet_flutter/ui/widgets/reactive_refresh.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/ui/widgets/svg_repaint.dart'; import 'package:blaise_wallet_flutter/util/haptic_util.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:pascaldart/pascaldart.dart'; class OverviewPage extends StatefulWidget { OverviewPage(); @override _OverviewPageState createState() => _OverviewPageState(); } class _OverviewPageState extends State with SingleTickerProviderStateMixin, WidgetsBindingObserver { GlobalKey _scaffoldKey = GlobalKey(); // Opacity Animation Animation _opacityAnimation; AnimationController _opacityAnimationController; // Pull to refresh bool _isRefreshing; // Whether to lock app bool _lockDisabled; int accountToLogin; // Account to login after a notification bool walletLoaded; Future walletLoad() async { await walletState?.loadWallet(); if (accountToLogin != null && !walletState.walletLoading) { _switchToAccount(accountToLogin); accountToLogin = null; } if (!walletLoaded) { walletLoaded = true; walletState.fcmUpdateBulk(); } } StreamSubscription _daemonChangeSub; StreamSubscription _disableLockSub; void _registerBus() { _daemonChangeSub = EventTaxiImpl.singleton() .registerTo() .listen((event) { walletLoad(); }); // Hackish event to block auto-lock functionality _disableLockSub = EventTaxiImpl.singleton() .registerTo() .listen((event) { if (event.disable) { cancelLockEvent(); } _lockDisabled = event.disable; }); } void _destroyBus() { if (_daemonChangeSub != null) { _daemonChangeSub.cancel(); } if (_disableLockSub != null) { _disableLockSub.cancel(); } } // To lock and unlock the app StreamSubscription lockStreamListener; Future setAppLockEvent() async { if (await sl.get().getLock() && !_lockDisabled) { if (lockStreamListener != null) { lockStreamListener.cancel(); } Future delayed = new Future.delayed( (await sl.get().getLockTimeout()).getDuration()); delayed.then((_) { return true; }); lockStreamListener = delayed.asStream().listen((_) { Navigator.of(context) .pushNamedAndRemoveUntil('/', (Route route) => false); }); } } Future cancelLockEvent() async { if (lockStreamListener != null) { lockStreamListener.cancel(); } } @override void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { case AppLifecycleState.paused: setAppLockEvent(); walletState.disconnect(); break; case AppLifecycleState.resumed: cancelLockEvent(); walletState.reconnect(); super.didChangeAppLifecycleState(state); break; default: super.didChangeAppLifecycleState(state); break; } } void _switchToAccount(int account) { bool exists = false; walletState.walletAccounts.forEach((acct) { if (acct.account.account == account) { exists = true; if (walletState.activeAccount != AccountNumber.fromInt(account)) { Navigator.popUntil(context, RouteUtils.withNameLike('/overview')); Navigator.pushNamed(context, '/account', arguments: acct); } } }); // Disable notifications for this acct if (!exists) { walletState.fcmDeleteAccount(AccountNumber.fromInt(account)); } } void _chooseCorrectAccountFromNotification(dynamic message) { if (message.containsKey("account")) { int account = int.tryParse(message['account']); if (account != null) { if (!walletState.walletLoading) { _switchToAccount(account); } else { accountToLogin = account; } } } } void getNotificationPermissions() async { FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async { try { _chooseCorrectAccountFromNotification(message.data); } catch (e) {} }); try { NotificationSettings settings = await FirebaseMessaging.instance .requestPermission(sound: true, badge: true, alert: true); if (settings.alert == AppleNotificationSetting.enabled || settings.badge == AppleNotificationSetting.enabled || settings.sound == AppleNotificationSetting.enabled || settings.authorizationStatus == AuthorizationStatus.authorized) { sl.get().getNotificationsSet().then((beenSet) { if (!beenSet) { sl.get().setNotificationsOn(true); } }); } else { sl.get().setNotificationsOn(false).then((_) {}); } } catch (e) { sl.get().setNotificationsOn(false); } } @override void initState() { super.initState(); walletLoaded = false; _registerBus(); getNotificationPermissions(); WidgetsBinding.instance.addObserver(this); _isRefreshing = false; _lockDisabled = false; // Load the wallet, total balance, etc. walletLoad(); // Opacity Animation _opacityAnimationController = AnimationController( duration: const Duration(milliseconds: 500), vsync: this, ); _opacityAnimation = Tween(begin: 1.0, end: 0.4).animate( CurvedAnimation( parent: _opacityAnimationController, curve: Curves.easeIn, reverseCurve: Curves.easeOut, ), ); _startAnimation(); } void _animationStatusListener(AnimationStatus status) { switch (status) { case AnimationStatus.dismissed: _opacityAnimationController.forward(); break; case AnimationStatus.completed: _opacityAnimationController.reverse(); break; default: return null; } } void _animationControllerListener() { if (walletState.walletLoading) { setState(() {}); } else { _disposeAnimations(); } } void _disposeAnimations() { try { _opacityAnimation?.removeStatusListener(_animationStatusListener); _opacityAnimationController?.removeListener(_animationControllerListener); _opacityAnimationController?.dispose(); } catch (e) {} } @override void dispose() { WidgetsBinding.instance.removeObserver(this); _disposeAnimations(); _destroyBus(); super.dispose(); } void _startAnimation() { _opacityAnimationController.addListener(_animationControllerListener); _opacityAnimation.addStatusListener(_animationStatusListener); _opacityAnimationController.forward(); } // Refresh list Future _refresh() async { setState(() { _isRefreshing = true; }); HapticUtil.success(); walletState.requestUpdate(); // Hide refresh indicator after 3 seconds if no server response Future.delayed(new Duration(seconds: 3), () { if (mounted) { setState(() { _isRefreshing = false; }); } }); walletLoad().whenComplete(() { if (mounted) { setState(() { _isRefreshing = false; }); } }); } @override Widget build(BuildContext context) { // The main scaffold that holds everything return Scaffold( drawerEdgeDragWidth: 200, key: _scaffoldKey, resizeToAvoidBottomInset: false, endDrawer: SizedBox( width: double.infinity, child: Drawer(child: SettingsPage())), backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, body: LayoutBuilder( builder: (context, constraints) => Column( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // A stack for the main card and the background gradient Stack( children: [ // Container for the gradient background Container( height: 65 + (MediaQuery.of(context).padding.top) + (20 - (MediaQuery.of(context).padding.bottom) / 2), decoration: BoxDecoration( gradient: StateContainer.of(context) .curTheme .gradientPrimary, ), ), //Container for the main card Container( height: 130, margin: EdgeInsetsDirectional.fromSTEB( 12, (MediaQuery.of(context).padding.top) + (20 - (MediaQuery.of(context).padding.bottom) / 2), 12, 0), width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context) .curTheme .gradientPrimary, borderRadius: BorderRadius.circular(12), boxShadow: [ StateContainer.of(context) .curTheme .shadowMainCard, ]), child: ClipRRect( borderRadius: BorderRadius.circular(12), child: Material( color: Colors.transparent, child: InkWell( onTap: () { AppSheets.showBottomSheet( context: context, widget: PublicKeyOverviewSheet()); }, highlightColor: StateContainer.of(context) .curTheme .textLight15, splashColor: StateContainer.of(context) .curTheme .textLight30, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ // Column for balance texts Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Container for "TOTAL BALANCE" text Container( margin: EdgeInsetsDirectional.fromSTEB( 24, 0, 24, 0), child: AutoSizeText( toUppercase( AppLocalization.of(context) .totalBalanceHeader, context), style: AppStyles.paragraphTextLightSmall( context), ), ), // Container for the balance Container( width: MediaQuery.of(context) .size .width - 160, margin: EdgeInsetsDirectional.fromSTEB( 24, 4, 24, 4), child: Observer( builder: (BuildContext context) { if (walletState.walletLoading) { return Opacity( opacity: _opacityAnimation.value, child: Align( alignment: Alignment(-1, 0), child: Container( decoration: BoxDecoration( color: StateContainer.of( context) .curTheme .textLight .withOpacity(0.75), borderRadius: BorderRadius.circular( 100), ), child: AutoSizeText( " ", style: AppStyles.header( context), maxLines: 1, minFontSize: 8, stepGranularity: 1, ), ), ), ); } else { return AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontTextLightPascal( context), ), TextSpan( text: " ", style: TextStyle( fontSize: 12)), TextSpan( text: walletState .totalWalletBalance .toStringOpt(), style: AppStyles.header( context)), ], ), maxLines: 1, minFontSize: 8, stepGranularity: 1, style: TextStyle( fontSize: 28, ), ); } })), // Container for the fiat conversion Container( margin: EdgeInsetsDirectional.fromSTEB( 24, 0, 24, 0), child: Observer( builder: (BuildContext context) { if (walletState.walletLoading || walletState .localCurrencyPrice == null || walletState .totalWalletBalance == null) { return Opacity( opacity: _opacityAnimation.value, child: Container( decoration: BoxDecoration( color: StateContainer.of( context) .curTheme .textLight .withOpacity(0.75), borderRadius: BorderRadius.circular( 100), ), child: AutoSizeText( " ", style: AppStyles .paragraphTextLightSmall( context), ), ), ); } else { return AutoSizeText( "(${walletState.getLocalCurrencyDisplay(currency: StateContainer.of(context).curCurrency, amount: walletState.totalWalletBalance)})", style: AppStyles .paragraphTextLightSmall( context), ); } }, ), ) ], ), // Column for settings icon and price text Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.end, children: [ // Settings Icon Container( margin: EdgeInsetsDirectional.only( top: 2, end: 2), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context) .curTheme .textLight15, splashColor: StateContainer.of(context) .curTheme .textLight30, onPressed: () { _scaffoldKey.currentState .openEndDrawer(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( 50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.settings, color: StateContainer.of(context) .curTheme .textLight, size: 24)), ), Container( margin: EdgeInsetsDirectional.only( end: 16, bottom: 12), child: Observer( builder: (BuildContext context) { if (walletState .localCurrencyPrice == null) { return Opacity( opacity: _opacityAnimation.value, child: Container( decoration: BoxDecoration( color: StateContainer.of( context) .curTheme .textLight .withOpacity(0.75), borderRadius: BorderRadius.circular( 100), ), child: AutoSizeText( " ", style: AppStyles .paragraphTextLightSmallSemiBold( context), ), ), ); } else { return AutoSizeText( "${walletState.getLocalCurrencyDisplay(currency: StateContainer.of(context).curCurrency, amount: Currency('1'), decimalDigits: 3)}", style: AppStyles .paragraphTextLightSmallSemiBold( context), ); } }, ), ), ], ), ], ), ), ), ), ), ], ), Observer(builder: (BuildContext context) { // Wallet loaded with no accounts if (walletState.walletLoading) { return Expanded( child: Column( children: [ // Accounts text Container( margin: EdgeInsetsDirectional.fromSTEB(24, 18, 24, 4), alignment: Alignment(-1, 0), child: AutoSizeText( toUppercase( AppLocalization.of(context).accountsHeader, context), style: AppStyles.headerSmall(context), textAlign: TextAlign.left, stepGranularity: 0.5, maxLines: 1, ), ), // Accounts List Expanded( child: Stack( children: [ // The list ReactiveRefreshIndicator( backgroundColor: StateContainer.of(context) .curTheme .backgroundPrimary, onRefresh: _refresh, isRefreshing: _isRefreshing, child: ListView( padding: EdgeInsetsDirectional.fromSTEB( 0, 3, 0, 19), children: _getPlaceholderAccountCards(), )), // The gradient at the top Container( height: 8, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context) .curTheme .gradientListTop), ), ], ), ), ], ), ); } else if (walletState.walletAccounts.isEmpty) { return Expanded( child: ReactiveRefreshIndicator( backgroundColor: StateContainer.of(context) .curTheme .backgroundPrimary, onRefresh: _refresh, isRefreshing: _isRefreshing, child: SingleChildScrollView( physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsets.all(0), child: Container( height: MediaQuery.of(context).size.height - (130 + ((MediaQuery.of(context) .padding .top) + (20 - (MediaQuery.of(context) .padding .bottom) / 2))) - ((MediaQuery.of(context).padding.bottom) + (24 - (MediaQuery.of(context) .padding .bottom) / 2)) - 50 - 20, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 0, 30, 0), child: AutoSizeText.rich( TextSpan( children: formatLocalizedColors( context, AppLocalization.of(context) .newWalletGreetingParagraph)), stepGranularity: 0.5, maxLines: 10, minFontSize: 8, textAlign: TextAlign.center, style: TextStyle(fontSize: 14), ), ), // Container for the illustration Container( margin: EdgeInsetsDirectional.only( top: 24, ), child: SvgRepaintAsset( asset: StateContainer.of(context) .curTheme .illustrationNewWallet, width: MediaQuery.of(context) .size .width * 0.55, height: MediaQuery.of(context) .size .width * 0.55), ) ], ), ), ), ))); } else { // Wallet Cards return Expanded( child: Column( children: [ // Accounts text Container( margin: EdgeInsetsDirectional.fromSTEB(24, 18, 24, 4), alignment: Alignment(-1, 0), child: AutoSizeText( toUppercase( AppLocalization.of(context).accountsHeader, context), style: AppStyles.headerSmall(context), textAlign: TextAlign.left, stepGranularity: 0.5, maxLines: 1, ), ), // Accounts List Expanded( child: Stack( children: [ // The list ReactiveRefreshIndicator( backgroundColor: StateContainer.of(context) .curTheme .backgroundPrimary, onRefresh: _refresh, isRefreshing: _isRefreshing, child: ListView.builder( physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsetsDirectional.fromSTEB( 0, 3, 0, 19), itemCount: walletState.walletAccounts.length, itemBuilder: (context, index) { return AccountCard( account: walletState .walletAccounts[index]); })), // The gradient at the top Container( height: 8, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context) .curTheme .gradientListTop), ), ], ), ), ], ), ); } }), // Bottom bar Observer(builder: (BuildContext context) { if (walletState.walletLoading) { // Animated button when loading return Container( width: double.maxFinite, decoration: BoxDecoration( color: StateContainer.of(context) .curTheme .backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), boxShadow: [ StateContainer.of(context).curTheme.shadowBottomBar, ], ), child: Container( margin: EdgeInsetsDirectional.only(top: 4), child: Opacity( opacity: _opacityAnimation.value, child: Row( children: [ AppButton( text: " ", type: AppButtonType.Primary, onPressed: () { return null; }, placeholder: true, ), ], ), ), ), ); } else if (walletState.walletAccounts.length > 0) { if (walletState.isBorrowEligible && !walletState.hasExceededBorrowLimit) { return Container( width: double.maxFinite, decoration: BoxDecoration( color: StateContainer.of(context) .curTheme .backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), boxShadow: [ StateContainer.of(context) .curTheme .shadowBottomBar, ], ), child: Container( margin: EdgeInsetsDirectional.only(top: 4), child: Row( children: [ AppButton( text: AppLocalization.of(context) .getAnAccountButton, type: AppButtonType.Primary, onPressed: () { AppSheets.showBottomSheet( context: context, widget: BuyAccountSheet()); }, ), ], ), ), ); } else { return Container( width: double.maxFinite, decoration: BoxDecoration( color: StateContainer.of(context) .curTheme .backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), boxShadow: [ StateContainer.of(context) .curTheme .shadowBottomBar, ], ), child: Container( margin: EdgeInsetsDirectional.only(top: 4), child: Row( children: [ AppButton( text: AppLocalization.of(context) .viewPublicKeyHeader, type: AppButtonType.Primary, onPressed: () { AppSheets.showBottomSheet( context: context, widget: PublicKeyOverviewSheet()); }, ), ], ), ), ); } } else { return Container( width: double.maxFinite, decoration: BoxDecoration( color: StateContainer.of(context) .curTheme .backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), boxShadow: [ StateContainer.of(context).curTheme.shadowBottomBar, ], ), child: Container( margin: EdgeInsetsDirectional.only(top: 4), child: Row( children: [ AppButton( text: AppLocalization.of(context) .getAnAccountButton, type: AppButtonType.Primary, onPressed: () { if (walletState.walletLoading) { return null; } else { AppSheets.showBottomSheet( context: context, widget: GetAccountSheet()); } }, ), ], ), ), ); } }) ], ), ), ], ), ), ); } List _getPlaceholderAccountCards() { List ret = []; for (var i = 0; i < 7; i++) { ret.add(Opacity( opacity: _opacityAnimation.value, child: PlaceholderAccountCard())); } return ret; } } ================================================ FILE: lib/ui/overview/public_key_overview_sheet.dart ================================================ import 'dart:async'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:qr/qr.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:quiver/strings.dart'; class PublicKeyOverviewSheet extends StatefulWidget { PublicKeyOverviewSheet(); _PublicKeyOverviewSheetState createState() => _PublicKeyOverviewSheetState(); } class _PublicKeyOverviewSheetState extends State { bool _keyCopied; Timer _keyCopiedTimer; @override void initState() { super.initState(); _keyCopied = false; } @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Sized Box SizedBox( height: 50, width: 65, ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context).publicKeySheetHeader, context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), // QR Code Container( margin: EdgeInsetsDirectional.only(top: 30, bottom: 10), child: Stack( alignment: Alignment(0, 0), children: [ // Gradient Container( width: 198, height: 198, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), gradient: StateContainer.of(context).curTheme.gradientPrimary, ), ), // White overlay Container( width: 189, height: 189, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Colors.white), ), // QR Code QrImage( data: PublicKeyCoder() .encodeToBase58(walletState.publicKey), size: 198, errorCorrectionLevel: QrErrorCorrectLevel.Q, gapless: false, version: 9, ), // Logo background Container( width: 64, height: 64, decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: Colors.white), ), // Logo Container( width: 53, height: 53, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), gradient: StateContainer.of(context).curTheme.gradientPrimary, ), child: Icon(AppIcons.pascalsymbol, color: StateContainer.of(context) .curTheme .backgroundPrimary, size: 33), ), ], ), ), //"Copy Address" and "Request Amount" buttons Row( children: [ AppButton( type: _keyCopied ? AppButtonType.Success : AppButtonType.Primary, text: _keyCopied ? AppLocalization.of(context).keyCopiedButton : AppLocalization.of(context).copyPublicKeyButton, onPressed: () { if (walletState.publicKey != null) { Clipboard.setData(ClipboardData( text: PublicKeyCoder() .encodeToBase58(walletState.publicKey))); } setState(() { _keyCopied = true; }); if (_keyCopiedTimer != null) { _keyCopiedTimer.cancel(); } _keyCopiedTimer = Timer(const Duration(milliseconds: 1500), () { if (mounted) { setState(() { _keyCopied = false; }); } }); }, ), ], ), // "Request" button /*Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).requestButton, onPressed: () { Navigator.of(context).pop(); AppSheets.showBottomSheet( context: context, widget: RequestSheet(address: widget.accountNumber.toString())); }, ), ], ),*/ ], ), ), ], ); } } ================================================ FILE: lib/ui/settings/backup_private_key/backup_private_key_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/settings/backup_private_key/encrypt_private_key_sheet.dart'; import 'package:blaise_wallet_flutter/ui/settings/backup_private_key/unencrypted_private_key_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/pin_screen.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/util/authentication.dart'; import 'package:blaise_wallet_flutter/util/haptic_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:flutter/material.dart'; class BackupPrivateKeySheet extends StatefulWidget { _BackupPrivateKeySheetState createState() => _BackupPrivateKeySheetState(); } class _BackupPrivateKeySheetState extends State { @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .privateKeySheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: AppLocalization.of(context) .backupKeyFirstParagraph + "\n\n", style: AppStyles.paragraph(context), ), TextSpan( text: AppLocalization.of(context) .backupKeySecondParagraph + "\n\n", style: AppStyles.paragraphPrimary(context), ), TextSpan( text: AppLocalization.of(context) .backupKeyThirdParagraph + "\n\n", style: AppStyles.paragraphPrimary(context), ), TextSpan( text: AppLocalization.of(context) .backupKeyFourthParagraph, style: AppStyles.paragraph(context), ), ], ), stepGranularity: 0.5, maxLines: 14, minFontSize: 8, style: TextStyle(fontSize: 14), ), ), ], ), ), //"Encrypted Key" and "Unencrypted Key" buttons Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).encryptedKeyButton, buttonTop: true, onPressed: () async { await authenticate(true); }, ), ], ), // "Close" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).unencryptedKeyButton, onPressed: () async { await authenticate(false); }, ), ], ), ], ), ), ), ], ); } Future _authenticateBiometrics(AuthUtil authUtil, String message, bool encrypted) async { bool authenticated = await authUtil.authenticateWithBiometrics(message); if (authenticated) { HapticUtil.fingerprintSucess(); Navigator.of(context).pop(); if (encrypted) { AppSheets.showBottomSheet( context: context, widget: EncryptPrivateKeySheet()); } else { AppSheets.showBottomSheet( context: context, widget: UnencryptedPrivateKeySheet()); } } } Future _authenticatePin(bool encrypted, String message) async { String expectedPin = await sl.get().getPin(); await Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) { return PinScreen( type: PinOverlayType.ENTER_PIN, onSuccess: (pin) { Navigator.of(context).pop(); Navigator.of(context).pop(); if (encrypted) { AppSheets.showBottomSheet( context: context, widget: EncryptPrivateKeySheet()); } else { AppSheets.showBottomSheet( context: context, widget: UnencryptedPrivateKeySheet()); } }, expectedPin: expectedPin, description: message, ); }, )); } Future authenticate(bool encrypted) async { String message = AppLocalization.of(context).authenticateToBackUpParagraph; // Authenticate AuthUtil authUtil = AuthUtil(); if (await authUtil.useBiometrics()) { // Biometric auth try { await _authenticateBiometrics(authUtil, message, encrypted); } catch (e) { await _authenticatePin(encrypted, message); } } else { await _authenticatePin(encrypted, message); } } } ================================================ FILE: lib/ui/settings/backup_private_key/encrypt_private_key_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/settings/backup_private_key/encrypted_private_key_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:flutter/material.dart'; import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:pascaldart/pascaldart.dart'; class EncryptPrivateKeySheet extends StatefulWidget { _EncryptPrivateKeySheetState createState() => _EncryptPrivateKeySheetState(); } class _EncryptPrivateKeySheetState extends State { FocusNode passwordFocusNode; TextEditingController passwordController; FocusNode confirmPasswordFocusNode; TextEditingController confirmPasswordController; String passwordError; bool passwordsMatch; @override void initState() { super.initState(); this.passwordsMatch = false; this.passwordFocusNode = FocusNode(); this.confirmPasswordFocusNode = FocusNode(); this.passwordController = TextEditingController(); this.confirmPasswordController = TextEditingController(); } @override Widget build(BuildContext context) { return TapOutsideUnfocus( child: Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .encryptSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 20), child: AutoSizeText( AppLocalization.of(context).encryptKeyParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), Expanded( child: KeyboardAvoider( duration: Duration(milliseconds: 0), autoScroll: true, focusPadding: 40, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Container for new password field Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 10, 30, 0), child: AppTextField( label: AppLocalization.of(context) .newPasswordTextFieldHeader, style: this.passwordsMatch ? AppStyles.paragraphMediumPrimary(context) : AppStyles.paragraphMedium(context), maxLines: 1, passwordField: true, focusNode: passwordFocusNode, controller: passwordController, onChanged: (String newText) { if (passwordError != null) { setState(() { passwordError = null; }); } if (confirmPasswordController.text == passwordController.text) { if (mounted) { setState(() { passwordsMatch = true; }); } } else { if (mounted) { setState(() { passwordsMatch = false; }); } } }, textInputAction: TextInputAction.next, onSubmitted: (text) { confirmPasswordFocusNode.requestFocus(); }, ), ), // Container for confirm password field Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 40), child: AppTextField( label: AppLocalization.of(context) .confirmPasswordTextFieldHeader, style: this.passwordsMatch ? AppStyles.paragraphMediumPrimary(context) : AppStyles.paragraphMedium(context), maxLines: 1, passwordField: true, focusNode: confirmPasswordFocusNode, controller: confirmPasswordController, onChanged: (String newText) { if (passwordError != null) { setState(() { passwordError = null; }); } if (confirmPasswordController.text == passwordController.text) { if (mounted) { setState(() { passwordsMatch = true; }); } } else { if (mounted) { setState(() { passwordsMatch = false; }); } } }, ), ), // Error text Container( margin: EdgeInsetsDirectional.only( start: 30, end: 30, top: 4, bottom: 40), child: AutoSizeText( passwordError == null ? "" : passwordError, style: AppStyles.paragraphPrimary(context), textAlign: TextAlign.start, ), ) ], ), ), ), ], ), ), // "Encrypt" button Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).encryptButton, buttonTop: true, onPressed: () { validatePasswordMatchAndEncrypt(); }, ), ], ), // "Close" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ) ], )); } void validatePasswordMatchAndEncrypt() { if (passwordController.text != confirmPasswordController.text) { setState(() { passwordError = AppLocalization.of(context).noMatchPasswordError; }); } else if (passwordController.text.isEmpty) { setState(() { passwordError = AppLocalization.of(context).emptyPasswordError; }); } else { sl.get().getPrivateKey().then((pkStr) { PrivateKey pk = PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes(pkStr)); String encrypted = PDUtil.byteToHex( PrivateKeyCrypt.encrypt(pk, passwordController.text)); Navigator.of(context).pop(); AppSheets.showBottomSheet( context: context, widget: EncryptedPrivateKeySheet(encryptedKey: encrypted)); }); } } } ================================================ FILE: lib/ui/settings/backup_private_key/encrypted_private_key_sheet.dart ================================================ import 'dart:async'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/user_data_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class EncryptedPrivateKeySheet extends StatefulWidget { final String encryptedKey; EncryptedPrivateKeySheet({this.encryptedKey}); _EncryptedPrivateKeySheetState createState() => _EncryptedPrivateKeySheetState(); } class _EncryptedPrivateKeySheetState extends State { bool _keyCopied; bool _showingKey; Timer _keyCopiedTimer; @override void initState() { super.initState(); _keyCopied = false; _showingKey = false; } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .privateKeySheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: AppLocalization.of(context) .backupEncryptedKeyFirstParagraph + "\n\n", style: AppStyles.paragraph(context), ), TextSpan( text: AppLocalization.of(context) .backupEncryptedKeySecondParagraph, style: AppStyles.paragraphPrimary(context), ), ], ), stepGranularity: 0.5, maxLines: 10, minFontSize: 8, style: TextStyle(fontSize: 14), ), ), // Container for the private key Container( margin: EdgeInsetsDirectional.fromSTEB(30, 24, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(24, 12, 24, 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .primary15), color: StateContainer.of(context).curTheme.primary10, ), child: AutoSizeText( _showingKey ? widget.encryptedKey : '•' * widget.encryptedKey.length, maxLines: 4, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyPrimary(context), ), ), // Container for the Show/Hide button Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(100.0), color: StateContainer.of(context) .curTheme .backgroundPrimary, boxShadow: [ StateContainer.of(context) .curTheme .shadowTextDark, ], ), margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), height: 40, child: FlatButton( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(100.0)), child: AutoSizeText( _showingKey ? AppLocalization.of(context).hideButton : AppLocalization.of(context).showButton, textAlign: TextAlign.center, maxLines: 1, stepGranularity: 0.1, style: AppStyles.buttonMiniBg(context), ), onPressed: () { setState(() { _showingKey = !_showingKey; }); }, ), ), ], ), ], ), ), //"Copy Encrypted Key" and "Close" buttons Row( children: [ AppButton( type: _keyCopied ? AppButtonType.Success : AppButtonType.Primary, text: _keyCopied ? AppLocalization.of(context).keyCopiedButton : AppLocalization.of(context).copyEncryptedKeyButton, buttonTop: true, onPressed: () { UserDataUtil.setSecureClipboardItem(widget.encryptedKey); setState(() { _keyCopied = true; }); if (_keyCopiedTimer != null) { _keyCopiedTimer.cancel(); } _keyCopiedTimer = Timer(const Duration(milliseconds: 1500), () { if (mounted) { setState(() { _keyCopied = false; }); } }); }, ), ], ), // "Close" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } } ================================================ FILE: lib/ui/settings/backup_private_key/unencrypted_private_key_sheet.dart ================================================ import 'dart:async'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/user_data_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class UnencryptedPrivateKeySheet extends StatefulWidget { _UnencryptedPrivateKeySheetState createState() => _UnencryptedPrivateKeySheetState(); } class _UnencryptedPrivateKeySheetState extends State { bool _keyCopied; bool _showingKey; Timer _keyCopiedTimer; @override void initState() { super.initState(); _keyCopied = false; _showingKey = false; } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .privateKeySheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText.rich( TextSpan( children: formatLocalizedColors( context, AppLocalization.of(context) .backupUnencryptedKeyParagraph), ), stepGranularity: 0.5, maxLines: 6, minFontSize: 8, style: TextStyle(fontSize: 14), ), ), // Container for the private key Container( margin: EdgeInsetsDirectional.fromSTEB(30, 24, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .primary15), color: StateContainer.of(context).curTheme.primary10, ), child: FutureBuilder( future: sl.get().getPrivateKey(), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData && snapshot.data != null) { return AutoSizeText( _showingKey ? snapshot.data : '•' * snapshot.data.length, maxLines: 4, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyPrimary(context)); } else { return SizedBox(); } })), // Container for the Show/Hide button Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(100.0), color: StateContainer.of(context) .curTheme .backgroundPrimary, boxShadow: [ StateContainer.of(context) .curTheme .shadowTextDark, ], ), margin: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 0), height: 40, child: FlatButton( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(100.0)), child: AutoSizeText( _showingKey ? AppLocalization.of(context).hideButton : AppLocalization.of(context).showButton, textAlign: TextAlign.center, maxLines: 1, stepGranularity: 0.1, style: AppStyles.buttonMiniBg(context), ), onPressed: () { setState(() { _showingKey = !_showingKey; }); }, ), ), ], ), ], ), ), //"Copy Unencrypted Key" and "Close" buttons Row( children: [ AppButton( type: _keyCopied ? AppButtonType.Success : AppButtonType.Primary, text: _keyCopied ? AppLocalization.of(context).keyCopiedButton : AppLocalization.of(context) .copyUnencryptedKeyButton, buttonTop: true, onPressed: () { sl.get().getPrivateKey().then((key) { UserDataUtil.setSecureClipboardItem(key); }); setState(() { _keyCopied = true; }); if (_keyCopiedTimer != null) { _keyCopiedTimer.cancel(); } _keyCopiedTimer = Timer(const Duration(milliseconds: 1500), () { if (mounted) { setState(() { _keyCopied = false; }); } }); }, ), ], ), // "Close" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } } ================================================ FILE: lib/ui/settings/change_daemon_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/constants.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/error_container.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/user_data_util.dart'; import 'package:flutter/material.dart'; import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:validators/validators.dart'; class ChangeDaemonSheet extends StatefulWidget { final Function onChanged; ChangeDaemonSheet({this.onChanged}) : super(); _ChangeDaemonSheetState createState() => _ChangeDaemonSheetState(); } class _ChangeDaemonSheetState extends State { FocusNode daemonFocusNode; TextEditingController daemonController; String daemonError; @override void initState() { super.initState(); this.daemonFocusNode = FocusNode(); this.daemonController = TextEditingController(); sl.get().getRpcUrl().then((val) { if (mounted) { daemonController.text = val; } }); this.daemonFocusNode.addListener(() { if (!this.daemonFocusNode.hasFocus) { if (isIP(daemonController.text)) { if (!daemonController.text.contains(":")) { daemonController.text = daemonController.text + ":4003"; } } } }); } @override Widget build(BuildContext context) { return TapOutsideUnfocus( child: Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Close Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.close, color: StateContainer.of(context) .curTheme .textLight, size: 20)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .changeDaemonSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(30, 40, 30, 20), child: AutoSizeText( AppLocalization.of(context).changeDaemonParagraph, style: AppStyles.paragraph(context), stepGranularity: 0.1, maxLines: 3, minFontSize: 8, ), ), Expanded( child: KeyboardAvoider( duration: Duration(milliseconds: 0), autoScroll: true, focusPadding: 40, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Container for the name text field Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 10, 30, 0), child: AppTextField( label: AppLocalization.of(context) .addressTextFieldHeader, style: AppStyles.paragraphMedium(context), maxLines: 1, firstButton: TextFieldButton( icon: AppIcons.paste, onPressed: () async { String text = await UserDataUtil.getClipboardText( DataType.URL); if (text != null) { daemonController.text = text; } }, ), controller: daemonController, focusNode: daemonFocusNode, onChanged: () { if (daemonError != null) { setState(() { daemonError = null; }); } }, ), ), // Error Container ErrorContainer( errorText: daemonError ?? "", ), // Container for the "Set to Default" button Row( children: [ Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(100.0), color: StateContainer.of(context) .curTheme .backgroundPrimary, boxShadow: [ StateContainer.of(context) .curTheme .shadowTextDark, ], ), margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 40), height: 40, child: FlatButton( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(100.0)), child: AutoSizeText( AppLocalization.of(context) .setToDefaultButton, textAlign: TextAlign.center, maxLines: 1, stepGranularity: 0.1, style: AppStyles.buttonMiniBg(context), ), onPressed: () { daemonFocusNode.unfocus(); daemonController.text = AppConstants.DEFAULT_RPC_HTTP_URL; }, ), ), ], ), ], ), ), ), ], ), ), // "CHANGE DAEMON" button Row( children: [ AppButton( type: AppButtonType.Primary, text: toUppercase(AppLocalization.of(context) .changeDaemonButton ,context), onPressed: () async { if (validateFormData()) { String daemon = daemonController.text; if (daemon == AppConstants.DEFAULT_RPC_HTTP_URL) { await sl.get().resetRpcUrl(); } else { await sl.get().setRpcUrl(daemon); } walletState.changeRpcUrl(daemon); if (widget.onChanged != null) { widget.onChanged(daemon); } UIUtil.showSnackbar( AppLocalization.of(context) .urlChangedToParagraph .replaceAll("%1", daemon), context); Navigator.of(context).pop(); } }, buttonTop: true, ), ], ), // "CANCEL" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: toUppercase(AppLocalization.of(context) .cancelButton ,context), onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], )); } bool validateFormData() { if (!isIP(daemonController.text) && !isURL(daemonController.text)) { setState(() { daemonError = AppLocalization.of(context).invalidAddressError; }); return false; } return true; } } ================================================ FILE: lib/ui/settings/contacts/add_contact_sheet.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/events.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/db/appdb.dart'; import 'package:blaise_wallet_flutter/model/db/contact.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/formatters.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/error_container.dart'; import 'package:blaise_wallet_flutter/ui/widgets/payload.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:blaise_wallet_flutter/util/user_data_util.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:pascaldart/common.dart'; import 'package:quiver/strings.dart'; class AddContactSheet extends StatefulWidget { final AccountNumber account; AddContactSheet({this.account}); _AddContactSheetState createState() => _AddContactSheetState(); } class _AddContactSheetState extends State { FocusNode nameFocusNode; FocusNode addressFocusNode; TextEditingController nameController; TextEditingController addressController; String payload; String nameError; String accountError; @override void initState() { super.initState(); this.nameFocusNode = FocusNode(); this.addressFocusNode = FocusNode(); this.nameController = TextEditingController(); this.addressController = TextEditingController(); this.payload = ""; if (widget.account == null) { this.addressFocusNode.addListener(() { if (!this.addressFocusNode.hasFocus) { try { AccountNumber numberFormatted = AccountNumber(this.addressController.text); this.addressController.text = numberFormatted.toString(); } catch (e) {} } }); } else { this.addressController.text = widget.account.toString(); } } @override Widget build(BuildContext context) { return TapOutsideUnfocus( child: Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Sized Box SizedBox( height: 50, width: 65, ), // Container for the address text field Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase( AppLocalization.of(context).addContactSheetHeader, context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: KeyboardAvoider( duration: Duration(milliseconds: 0), autoScroll: true, focusPadding: 40, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Container for the name text field Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AppTextField( label: AppLocalization.of(context) .contactNameTextFieldHeader, style: AppStyles.contactsItemName(context), prefix: Text( " ", style: AppStyles.iconFontPrimarySmall(context), ), maxLines: 1, focusNode: nameFocusNode, controller: nameController, inputFormatters: [ LengthLimitingTextInputFormatter(20) ], onChanged: (newText) { if (isNotEmpty(nameError)) { setState(() { nameError = null; }); } }, textInputAction: TextInputAction.newline, onSubmitted: (text) { addressFocusNode.requestFocus(); }, )), ErrorContainer( errorText: nameError ?? "", ), // Container for the address text field Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AppTextField( label: AppLocalization.of(context) .addressTextFieldHeader, style: AppStyles.contactsItemAddress(context), firstButton: widget.account == null ? TextFieldButton( icon: AppIcons.paste, onPressed: () { UserDataUtil.getClipboardText( DataType.ACCOUNT) .then((account) { if (account != null) { addressController.text = account; } }); }, ) : null, secondButton: widget.account == null ? TextFieldButton( icon: AppIcons.scan, onPressed: () async { String text = await UserDataUtil.getQRData( DataType.ACCOUNT, StateContainer.of(context) .curTheme .scannerTheme); if (text != null) { addressController.text = text; } }) : null, maxLines: 1, textCapitalization: TextCapitalization.characters, controller: addressController, focusNode: addressFocusNode, inputFormatters: [ FilteringTextInputFormatter.allow( RegExp("[0-9-]")), PascalAccountFormatter() ], onChanged: (newText) { if (isNotEmpty(accountError)) { setState(() { accountError = null; }); } }, readOnly: widget.account != null, ), ), ErrorContainer( errorText: accountError ?? "", ), // Container for the "Add Payload" button Payload( onPayloadChanged: (newPayload, encrypted) { setState(() { payload = newPayload; }); }, allowEncryption: false, ) ], ), ), ), //"Add Contact" and "Close" buttons Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).addContactButton, buttonTop: true, onPressed: () async { if (await validateForm()) { Contact newContact = Contact( account: widget.account == null ? AccountNumber(addressController.text) : widget.account, name: nameController.text, payload: payload); await sl.get().saveContact(newContact); EventTaxiImpl.singleton() .fire(ContactAddedEvent(contact: newContact)); UIUtil.showSnackbar( AppLocalization.of(context) .addedToContactsParagraph .replaceAll("%1", newContact.name), context); EventTaxiImpl.singleton() .fire(ContactModifiedEvent(contact: newContact)); Navigator.of(context).pop(); } }, ), ], ), // "Close" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], )); } Future validateForm() async { bool isValid = true; // Address Validations // Don't validate address if it came pre-filled in if (widget.account == null) { if (addressController.text.isEmpty) { isValid = false; setState(() { accountError = AppLocalization.of(context).invalidAccountError; }); } else { try { AccountNumber acctNum = AccountNumber(addressController.text); addressFocusNode.unfocus(); bool exists = await sl.get().contactExistsWithAccount(acctNum); if (exists) { isValid = false; setState(() { accountError = AppLocalization.of(context).contactAlreadyExistsError; }); } } catch (e) { isValid = false; setState(() { accountError = AppLocalization.of(context).invalidAccountError; }); } } } // Name Validations if (nameController.text.isEmpty) { isValid = false; setState(() { nameError = AppLocalization.of(context).nameRequiredError; }); } else { bool nameExists = await sl.get().contactExistsWithName(nameController.text); if (nameExists) { isValid = false; setState(() { nameError = AppLocalization.of(context).contactAlreadyExistsError; }); } } return isValid; } } ================================================ FILE: lib/ui/settings/contacts/contact_detail_sheet.dart ================================================ import 'dart:async'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/events.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/db/appdb.dart'; import 'package:blaise_wallet_flutter/model/db/contact.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/store/account/account.dart'; import 'package:blaise_wallet_flutter/ui/account/send/send_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/overlay_dialog.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/ui/widgets/webview.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:quiver/strings.dart'; class ContactDetailSheet extends StatefulWidget { final Contact contact; final Account account; ContactDetailSheet({this.contact, this.account}); _ContactDetailSheetState createState() => _ContactDetailSheetState(); } class _ContactDetailSheetState extends State { bool contactNameCopied; Timer contactNameCopiedTimer; bool contactAddressCopied; Timer contactAddressCopiedTimer; bool contactPayloadCopied; Timer contactPayloadCopiedTimer; @override void initState() { super.initState(); contactNameCopied = false; contactAddressCopied = false; contactPayloadCopied = false; } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Trashcan Button Container( margin: EdgeInsetsDirectional.only(start: 5, end: 10), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () async { bool deleted = await sl .get() .deleteContact(widget.contact); if (deleted) { EventTaxiImpl.singleton().fire( ContactRemovedEvent( contact: widget.contact)); EventTaxiImpl.singleton().fire( ContactModifiedEvent( contact: widget.contact)); UIUtil.showSnackbar( AppLocalization.of(context) .removedFromContactsParagraph .replaceAll("%1", widget.contact.name), context); Navigator.of(context).pop(); } else { UIUtil.showSnackbar( AppLocalization.of(context) .failedToRemoveFromContactsParagraph .replaceAll("%1", widget.contact.name), context); } }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.trashcan, color: StateContainer.of(context) .curTheme .textLight, size: 24)), ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .contactSheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Search Button Container( margin: EdgeInsetsDirectional.only(start: 10, end: 5), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { AppWebView.showWebView(context, 'http://explorer.pascalcoin.org/account.php?account=${widget.contact.account.toString()}'); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.search, color: StateContainer.of(context) .curTheme .textLight, size: 24)), ), ], ), ), Expanded( child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Contact name gesture detector GestureDetector( onTapDown: (details) { _copyToClipboard("Contact Name"); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Contact Name" header Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB( 30, 40, 30, 0), child: AutoSizeText( contactNameCopied ? AppLocalization.of(context).copiedButton : AppLocalization.of(context) .contactNameTextFieldHeader, style: contactNameCopied ? AppStyles.textFieldLabelSuccess(context) : AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the Contact Name Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: contactNameCopied ? StateContainer.of(context) .curTheme .success15 : StateContainer.of(context) .curTheme .textDark15), color: contactNameCopied ? StateContainer.of(context) .curTheme .success10 : StateContainer.of(context) .curTheme .textDark10, ), child: AutoSizeText.rich( TextSpan(children: [ TextSpan( text: " ", style: contactNameCopied ? AppStyles.iconFontSuccessSmall( context) : AppStyles.iconFontPrimarySmall( context), ), TextSpan( text: widget.contact.name, style: contactNameCopied ? AppStyles.contactsItemNameSuccess( context) : AppStyles.contactsItemName(context), ), ]), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, style: TextStyle(fontSize: 14.0), ), ), ], ), ), GestureDetector( onTapDown: (details) { _copyToClipboard("Address"); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Address" header Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 0), child: AutoSizeText( contactAddressCopied ? AppLocalization.of(context).copiedButton : AppLocalization.of(context) .addressTextFieldHeader, style: contactAddressCopied ? AppStyles.textFieldLabelSuccess(context) : AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the address Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: contactAddressCopied ? StateContainer.of(context) .curTheme .success15 : StateContainer.of(context) .curTheme .textDark15), color: contactAddressCopied ? StateContainer.of(context) .curTheme .success10 : StateContainer.of(context) .curTheme .textDark10, ), child: AutoSizeText( widget.contact.account.toString(), maxLines: 1, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: contactAddressCopied ? AppStyles.privateKeySuccess(context) : AppStyles.privateKeyTextDark(context), ), ), ], ), ), isNotEmpty(widget.contact.payload) ? GestureDetector( onTapDown: (details) { _copyToClipboard("Payload"); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // "Payload" header Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB( 30, 30, 30, 0), child: AutoSizeText( contactPayloadCopied ? AppLocalization.of(context) .copiedButton : AppLocalization.of(context) .payloadTextFieldHeader, style: contactPayloadCopied ? AppStyles.textFieldLabelSuccess( context) : AppStyles.textFieldLabel(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.start, ), ), // Container for the payload text Container( margin: EdgeInsetsDirectional.fromSTEB( 30, 12, 30, 0), padding: EdgeInsetsDirectional.fromSTEB( 12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: contactPayloadCopied ? StateContainer.of(context) .curTheme .success15 : StateContainer.of(context) .curTheme .textDark15), color: contactPayloadCopied ? StateContainer.of(context) .curTheme .success10 : StateContainer.of(context) .curTheme .textDark10, ), child: Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 86), child: AutoSizeText( widget.contact.payload, maxLines: 3, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: contactPayloadCopied ? AppStyles.paragraphSuccess( context) : AppStyles.paragraph(context), ), ), ) ], ), ) : SizedBox(), ], ), ), ), //"Add Contact" and "Close" buttons _showSendButton() ? Row( children: [ AppButton( type: AppButtonType.Primary, text: AppLocalization.of(context).sendButton, buttonTop: true, onPressed: () { if (widget.account != null) { Navigator.pop(context); AppSheets.showBottomSheet( context: context, widget: SendSheet( account: widget.account.account, contact: widget.contact, localCurrency: StateContainer.of(context).curCurrency), ); } else { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization.of(context) .accountToSendFromHeader, optionsList: _getAccountsList())); } }, ), ], ) : SizedBox(), // "Close" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } bool _showSendButton() { return ((widget.account != null && widget.account.accountBalance > Currency('0')) || (widget.account == null && !walletState.walletLoading && walletState.totalWalletBalance > Currency('0') && walletState.getNonzeroBalanceAccounts().length > 0)); } List _getAccountsList() { /// Get a list of accounts we can send from List ret = []; walletState.getNonzeroBalanceAccounts().forEach((acct) { ret.add(DialogListItem( option: "${acct.account.toString()} (${acct.balance.toStringOpt()} PASC)", action: () { Navigator.of(context).pop(); Navigator.of(context).pop(); AppSheets.showBottomSheet( context: context, widget: SendSheet( account: acct, contact: widget.contact, fromOverview: true, localCurrency: StateContainer.of(context).curCurrency ), ); })); }); return ret; } void _copyToClipboard(String toCopy) { if (toCopy == "Contact Name") { Clipboard.setData( ClipboardData(text: (widget.contact.name.toString()))); setState(() { contactNameCopied = true; contactAddressCopied = false; contactPayloadCopied = false; }); if (contactNameCopiedTimer != null) { contactNameCopiedTimer.cancel(); } contactNameCopiedTimer = Timer(const Duration(milliseconds: 1500), () { if (mounted) { setState(() { contactNameCopied = false; }); } }); } if (toCopy == "Address") { Clipboard.setData(ClipboardData(text: widget.contact.account.toString())); setState(() { contactAddressCopied = true; contactNameCopied = false; contactPayloadCopied = false; }); if (contactAddressCopiedTimer != null) { contactAddressCopiedTimer.cancel(); } contactAddressCopiedTimer = Timer(const Duration(milliseconds: 1500), () { if (mounted) { setState(() { contactAddressCopied = false; }); } }); } if (toCopy == "Payload") { Clipboard.setData(ClipboardData(text: widget.contact.payload)); setState(() { contactPayloadCopied = true; contactNameCopied = false; contactAddressCopied = false; }); if (contactPayloadCopiedTimer != null) { contactPayloadCopiedTimer.cancel(); } contactPayloadCopiedTimer = Timer(const Duration(milliseconds: 1500), () { if (mounted) { setState(() { contactPayloadCopied = false; }); } }); } } } ================================================ FILE: lib/ui/settings/contacts/contacts.dart ================================================ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/events.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/db/appdb.dart'; import 'package:blaise_wallet_flutter/model/db/contact.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/store/account/account.dart'; import 'package:blaise_wallet_flutter/ui/settings/contacts/add_contact_sheet.dart'; import 'package:blaise_wallet_flutter/ui/settings/contacts/contact_detail_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/ui/widgets/settings_list_item.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:path_provider/path_provider.dart'; import 'package:share/share.dart'; class ContactsPage extends StatefulWidget { final Account account; ContactsPage({this.account}) : super(); @override _ContactsPageState createState() => _ContactsPageState(); } class _ContactsPageState extends State { final Logger log = sl.get(); List _contacts; String documentsDirectory; StreamSubscription _contactAddedSub; StreamSubscription _contactRemovedSub; void _registerBus() { // Contact added bus event _contactAddedSub = EventTaxiImpl.singleton() .registerTo() .listen((event) { setState(() { _contacts.add(event.contact); //Sort by name _contacts.sort( (a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase())); }); // Full update which includes downloading new monKey _updateContacts(); }); // Contact removed bus event _contactRemovedSub = EventTaxiImpl.singleton() .registerTo() .listen((event) { setState(() { _contacts.remove(event.contact); }); }); } void _destroyBus() { if (_contactAddedSub != null) { _contactAddedSub.cancel(); } if (_contactRemovedSub != null) { _contactRemovedSub.cancel(); } } @override void initState() { super.initState(); _registerBus(); // Populate contacts _contacts = []; _updateContacts(); // Get FS directory for export getApplicationDocumentsDirectory().then((directory) { documentsDirectory = directory.path; setState(() { documentsDirectory = directory.path; }); _updateContacts(); }); } @override void dispose() { _destroyBus(); super.dispose(); } void _updateContacts() { sl.get().getContacts().then((contacts) { for (Contact c in contacts) { if (!_contacts.contains(c)) { setState(() { _contacts.add(c); }); } } // Re-sort list setState(() { _contacts.sort( (a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase())); }); }); } Future _exportContacts() async { List contacts = await sl.get().getContacts(); if (contacts.length == 0) { UIUtil.showSnackbar( AppLocalization.of(context).noContactsToExportError, context); return; } List> jsonList = List(); contacts.forEach((contact) { jsonList.add(contact.toJson()); }); DateTime exportTime = DateTime.now(); String filename = "blaisecontacts_${exportTime.year}${exportTime.month}${exportTime.day}${exportTime.hour}${exportTime.minute}${exportTime.second}.txt"; Directory baseDirectory = await getApplicationDocumentsDirectory(); File contactsFile = File("${baseDirectory.path}/$filename"); await contactsFile.writeAsString(json.encode(jsonList)); UIUtil.cancelLockEvent(); Share.shareFile(contactsFile); } Future _importContacts() async { UIUtil.cancelLockEvent(); FilePickerResult result = await FilePicker.platform.pickFiles( allowMultiple: false, type: FileType.custom, allowedExtensions: ["txt"]); if (result != null) { File f = File(result.files.single.path); if (!await f.exists()) { UIUtil.showSnackbar( AppLocalization.of(context).failedToImportContactsError, context); return; } try { String contents = await f.readAsString(); Iterable contactsJson = json.decode(contents); List contacts = List.empty(); List contactsToAdd = List.empty(); contactsJson.forEach((contact) { contacts.add(Contact.fromJson(contact)); }); for (Contact contact in contacts) { if (!await sl.get().contactExistsWithName(contact.name) && !await sl .get() .contactExistsWithAccount(contact.account)) { // Contact doesnt exist, make sure name and address are valid if (contact.account != null && contact.account.toString().length > 0) { if (contact.name.length <= 20) { contactsToAdd.add(contact); } } } } // Save all the new contacts and update states int numSaved = await sl.get().saveContacts(contactsToAdd); if (numSaved > 0) { _updateContacts(); EventTaxiImpl.singleton().fire(ContactModifiedEvent( contact: Contact(name: "", account: AccountNumber.fromInt(0)))); UIUtil.showSnackbar( AppLocalization.of(context) .successfullyImportedContactsParagraph .replaceAll("%1", numSaved.toString()), context); } else { UIUtil.showSnackbar( AppLocalization.of(context).noContactsToImportError, context); } } catch (e) { log.e(e.toString()); UIUtil.showSnackbar( AppLocalization.of(context).failedToImportContactsError, context); return; } } else { // Cancelled by user log.e("FilePicker cancelled by user"); UIUtil.showSnackbar( AppLocalization.of(context).failedToImportContactsError, context); return; } } @override Widget build(BuildContext context) { // The main scaffold that holds everything return Scaffold( resizeToAvoidBottomInset: false, backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, body: LayoutBuilder( builder: (context, constraints) => Stack( children: [ // Container for the gradient background Container( height: 104 + (MediaQuery.of(context).padding.top) + (36 - (MediaQuery.of(context).padding.top) / 2), decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, ), ), // Column for the rest Column( children: [ // Container for the header and the buttons Container( margin: EdgeInsetsDirectional.only( top: (MediaQuery.of(context).padding.top) + (36 - (MediaQuery.of(context).padding.top) / 2), bottom: 8, ), // Row for the header and the buttons child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Back button and header Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Back Button Container( margin: EdgeInsetsDirectional.only(start: 2), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context) .curTheme .textLight15, splashColor: StateContainer.of(context) .curTheme .textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.back, color: StateContainer.of(context) .curTheme .textLight, size: 24)), ), // The header Container( width: MediaQuery.of(context).size.width - 200, margin: EdgeInsetsDirectional.fromSTEB(4, 0, 24, 0), child: AutoSizeText( AppLocalization.of(context).contactsHeader, style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, ), ), ], ), // Import and export buttons Row( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.center, children: [ // Import Button Container( height: 40, width: 40, child: FlatButton( highlightColor: StateContainer.of(context) .curTheme .textLight15, splashColor: StateContainer.of(context) .curTheme .textLight30, onPressed: () { _importContacts(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.import_icon, color: StateContainer.of(context) .curTheme .textLight, size: 24)), ), // Export Button Container( margin: EdgeInsetsDirectional.only(start: 4, end: 12), height: 40, width: 40, child: FlatButton( highlightColor: StateContainer.of(context) .curTheme .textLight15, splashColor: StateContainer.of(context) .curTheme .textLight30, onPressed: () { _exportContacts(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.export_icon, color: StateContainer.of(context) .curTheme .textLight, size: 24)), ), ], ), ], ), ), // Expanded list Expanded( // Container for the list child: Container( margin: EdgeInsetsDirectional.fromSTEB(12, 0, 12, 0), width: double.maxFinite, decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), boxShadow: [ StateContainer.of(context).curTheme.shadowSettingsList, ], ), // Settings List child: ClipRRect( borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12)), child: Stack( children: [ ListView.builder( physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsetsDirectional.only( bottom: MediaQuery.of(context).padding.bottom + 12), itemCount: _contacts.length, itemBuilder: (context, index) { return SettingsListItem( contactName: _contacts[index].name, contactAddress: _contacts[index].account.toString(), contact: true, onPressed: () { AppSheets.showBottomSheet( context: context, widget: ContactDetailSheet( contact: _contacts[index], account: widget.account)); }, ); }, ), ], ), ), ), ), // Bottom bar Align( alignment: Alignment.bottomCenter, child: Container( width: double.maxFinite, decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), boxShadow: [ StateContainer.of(context).curTheme.shadowBottomBar, ], ), child: Container( margin: EdgeInsetsDirectional.only(top: 4), child: Row( children: [ AppButton( text: AppLocalization.of(context).addContactButton, type: AppButtonType.Primary, onPressed: () { AppSheets.showBottomSheet( context: context, widget: AddContactSheet()); }, ), ], ), ), ), ), ], ), ], ), ), ); } } ================================================ FILE: lib/ui/settings/public_key_sheet.dart ================================================ import 'dart:async'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:pascaldart/pascaldart.dart'; class PublicKeySheet extends StatefulWidget { _PublicKeySheetState createState() => _PublicKeySheetState(); } class _PublicKeySheetState extends State { bool _keyCopied; Timer _keyCopiedTimer; @override void initState() { super.initState(); _keyCopied = false; } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Column( children: [ // Sheet header Container( height: 60, width: double.maxFinite, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Sized Box SizedBox( height: 50, width: 65, ), // Header Container( width: MediaQuery.of(context).size.width - 130, alignment: Alignment(0, 0), child: AutoSizeText( toUppercase(AppLocalization.of(context) .publicKeySheetHeader ,context), style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.center, ), ), // Sized Box SizedBox( height: 50, width: 65, ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Paragraph Container( margin: EdgeInsetsDirectional.fromSTEB(30, 30, 30, 0), child: AutoSizeText( AppLocalization.of(context).publicKeyParagraph, stepGranularity: 0.5, maxLines: 6, minFontSize: 8, style: AppStyles.paragraph(context), ), ), // Container for the private key Container( margin: EdgeInsetsDirectional.fromSTEB(30, 24, 30, 0), padding: EdgeInsetsDirectional.fromSTEB(30, 12, 30, 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context) .curTheme .primary15), color: StateContainer.of(context).curTheme.primary10, ), child: Observer(builder: (BuildContext context) { if (walletState.publicKey != null) { return AutoSizeText( PublicKeyCoder() .encodeToBase58(walletState.publicKey), maxLines: 6, stepGranularity: 0.1, minFontSize: 8, textAlign: TextAlign.center, style: AppStyles.privateKeyPrimary(context), ); } else { return SizedBox(); } })), ], ), ), //"Copy Unencrypted Key" and "Close" buttons Row( children: [ AppButton( type: _keyCopied ? AppButtonType.Success : AppButtonType.Primary, text: _keyCopied ? AppLocalization.of(context).keyCopiedButton : AppLocalization.of(context).copyPublicKeyButton, buttonTop: true, onPressed: () { if (walletState.publicKey != null) { Clipboard.setData(ClipboardData( text: PublicKeyCoder() .encodeToBase58(walletState.publicKey))); } setState(() { _keyCopied = true; }); if (_keyCopiedTimer != null) { _keyCopiedTimer.cancel(); } _keyCopiedTimer = Timer(const Duration(milliseconds: 1500), () { if (mounted) { setState(() { _keyCopied = false; }); } }); }, ), ], ), // "Close" button Row( children: [ AppButton( type: AppButtonType.PrimaryOutline, text: AppLocalization.of(context).closeButton, onPressed: () { Navigator.of(context).pop(); }, ), ], ), ], ), ), ), ], ); } } ================================================ FILE: lib/ui/settings/security.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/authentication_method.dart'; import 'package:blaise_wallet_flutter/model/lock_timeout.dart'; import 'package:blaise_wallet_flutter/model/unlock_setting.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/overlay_dialog.dart'; import 'package:blaise_wallet_flutter/ui/widgets/settings_list_item.dart'; import 'package:blaise_wallet_flutter/util/authentication.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:flutter/material.dart'; class SecurityPage extends StatefulWidget { @override _SecurityPageState createState() => _SecurityPageState(); } class _SecurityPageState extends State { var _scaffoldKey = GlobalKey(); bool _hasBiometricsEnrolled; AuthenticationMethod curAuthMethod = AuthenticationMethod(AuthMethod.BIOMETRICS); LockTimeoutSetting curTimeoutSetting = LockTimeoutSetting(LockTimeoutOption.ONE); UnlockSetting curAuthOnLaunch = UnlockSetting(UnlockOption.NO); List _getAuthMethods() { List ret = []; AuthMethod.values.forEach((AuthMethod value) { AuthenticationMethod method = AuthenticationMethod(value); ret.add(DialogListItem( option: method.getDisplayName(context), action: () async { await sl.get().setAuthMethod(method); if (mounted) { setState(() { curAuthMethod = method; }); } Navigator.of(context).pop(); })); }); return ret; } List _authOnLaunchOptions() { List ret = []; UnlockOption.values.forEach((UnlockOption value) { UnlockSetting setting = UnlockSetting(value); ret.add(DialogListItem( option: setting.getDisplayName(context), action: () async { await sl .get() .setLock(setting.setting == UnlockOption.YES); if (mounted) { setState(() { curAuthOnLaunch = setting; }); } Navigator.of(context).pop(); })); }); return ret; } List _getLockTimeoutList() { List ret = []; LockTimeoutOption.values.forEach((LockTimeoutOption value) { LockTimeoutSetting setting = LockTimeoutSetting(value); ret.add(DialogListItem( option: setting.getDisplayName(context), action: () async { await sl.get().setLockTimeout(setting); if (mounted) { setState(() { curTimeoutSetting = setting; }); } Navigator.of(context).pop(); })); }); return ret; } @override void initState() { super.initState(); _hasBiometricsEnrolled = false; AuthUtil().hasBiometrics().then((hasBiometrics) { if (mounted) { setState(() { _hasBiometricsEnrolled = hasBiometrics; }); } }); sl.get().getAuthMethod().then((authMethod) { if (mounted) { setState(() { curAuthMethod = authMethod; }); } }); sl.get().getLockTimeout().then((result) { if (mounted) { setState(() { curTimeoutSetting = result; }); } }); sl.get().getLock().then((result) { if (result) { if (mounted) { setState(() { curAuthOnLaunch = UnlockSetting(UnlockOption.YES); }); } } else { if (mounted) { setState(() { curAuthOnLaunch = UnlockSetting(UnlockOption.NO); }); } } }); } @override Widget build(BuildContext context) { // The main scaffold that holds everything return Scaffold( resizeToAvoidBottomInset: false, key: _scaffoldKey, backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, body: LayoutBuilder( builder: (context, constraints) => Stack( children: [ // Container for the gradient background Container( height: 104 + (MediaQuery.of(context).padding.top) + (36 - (MediaQuery.of(context).padding.top) / 2), decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, ), ), // Column for the rest Column( children: [ // Container for the header and button Container( margin: EdgeInsetsDirectional.only( top: (MediaQuery.of(context).padding.top) + (36 - (MediaQuery.of(context).padding.top) / 2), bottom: 8, ), // Row for back button and the header child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Back Button Container( margin: EdgeInsetsDirectional.only(start: 2), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.back, color: StateContainer.of(context) .curTheme .textLight, size: 24)), ), // The header Container( width: MediaQuery.of(context).size.width - 100, margin: EdgeInsetsDirectional.fromSTEB(4, 0, 24, 0), child: AutoSizeText( AppLocalization.of(context).securityHeader, style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, ), ), ], ), ), // Expanded list Expanded( // Container for the list child: Container( margin: EdgeInsetsDirectional.fromSTEB(12, 0, 12, 0), width: double.maxFinite, decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), boxShadow: [ StateContainer.of(context).curTheme.shadowSettingsList, ], ), // Settings List child: ListView( padding: EdgeInsetsDirectional.only( bottom: MediaQuery.of(context).padding.bottom + 12), children: [ // Preferences text Container( alignment: Alignment(-1, 0), margin: EdgeInsetsDirectional.only( start: 24, end: 24, top: 16, bottom: 8), child: AutoSizeText( AppLocalization.of(context).preferencesHeader, style: AppStyles.settingsHeader(context), maxLines: 1, stepGranularity: 0.1, ), ), // Divider Container( width: double.maxFinite, height: 1, color: StateContainer.of(context).curTheme.textDark10, ), // List Items _hasBiometricsEnrolled ? SettingsListItem( header: AppLocalization.of(context) .authenticationMethodHeader, subheader: curAuthMethod.getDisplayName(context), icon: AppIcons.fingerprint, onPressed: () { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization.of(context) .authenticationMethodHeader, optionsList: _getAuthMethods())); }, ) : SizedBox(), _hasBiometricsEnrolled ? Container( width: double.maxFinite, height: 1, color: StateContainer.of(context) .curTheme .textDark10, ) : SizedBox(), SettingsListItem( header: AppLocalization.of(context) .authenticateOnLaunchHeader, subheader: curAuthOnLaunch.getDisplayName(context), icon: AppIcons.lock, onPressed: () { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization.of(context) .authenticateOnLaunchHeader, optionsList: _authOnLaunchOptions())); }, ), SettingsListItem( header: AppLocalization.of(context) .automaticallyLockHeader, subheader: curTimeoutSetting.getDisplayName(context), icon: AppIcons.timer, disabled: curAuthOnLaunch.setting == UnlockOption.NO, onPressed: () { if (curAuthOnLaunch.setting == UnlockOption.YES) { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization.of(context) .automaticallyLockHeader, optionsList: _getLockTimeoutList())); } }, ), ], ), ), ), ], ), ], ), ), ); } } ================================================ FILE: lib/ui/settings/settings.dart ================================================ import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/daemon_changed_event.dart'; import 'package:blaise_wallet_flutter/constants.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/model/available_themes.dart'; import 'package:blaise_wallet_flutter/model/available_currency.dart'; import 'package:blaise_wallet_flutter/model/available_languages.dart'; import 'package:blaise_wallet_flutter/model/notification_enabled.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/store/account/account.dart'; import 'package:blaise_wallet_flutter/ui/widgets/webview.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:package_info/package_info.dart'; import 'package:share/share.dart'; import 'package:blaise_wallet_flutter/themes.dart'; import 'package:blaise_wallet_flutter/ui/settings/backup_private_key/backup_private_key_sheet.dart'; import 'package:blaise_wallet_flutter/ui/settings/change_daemon_sheet.dart'; import 'package:blaise_wallet_flutter/ui/settings/public_key_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/overlay_dialog.dart'; import 'package:blaise_wallet_flutter/ui/widgets/settings_list_item.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:blaise_wallet_flutter/util/vault.dart'; import 'package:flutter/material.dart'; class SettingsPage extends StatefulWidget { final Account account; SettingsPage({this.account}) : super(); @override _SettingsPageState createState() => _SettingsPageState(); } class _SettingsPageState extends State { var _scaffoldKey = GlobalKey(); List getThemeList() { List ret = []; ThemeOptions.values.forEach((ThemeOptions value) { ThemeSetting theme = ThemeSetting(value); ret.add(DialogListItem( option: theme.getDisplayName(context), action: () { StateContainer.of(context).updateTheme(ThemeSetting(value)); Navigator.of(context).pop(); })); }); return ret; } List getCurrencyList() { List ret = []; AvailableCurrencyEnum.values.forEach((AvailableCurrencyEnum value) { AvailableCurrency currency = AvailableCurrency(value); ret.add(DialogListItem( option: currency.getDisplayName(context), action: () { sl.get().setCurrency(currency).then((result) { if (StateContainer.of(context).curCurrency.currency != currency.currency) { setState(() { StateContainer.of(context).curCurrency = currency; }); walletState.requestUpdate(); } }); Navigator.of(context).pop(); })); }); return ret; } List getNotificationList() { List ret = []; NotificationOptions.values.forEach((NotificationOptions value) { NotificationSetting setting = NotificationSetting(value); ret.add(DialogListItem( option: setting.getDisplayName(context), action: () { if (setting != _curNotificiationSetting) { sl .get() .setNotificationsOn(setting.setting == NotificationOptions.ON) .then((result) { setState(() { _curNotificiationSetting = setting; }); walletState.fcmUpdateBulk(); }); } Navigator.of(context).pop(); })); }); return ret; } List getLanguageList() { List ret = []; AvailableLanguage.values.forEach((AvailableLanguage value) { LanguageSetting setting = LanguageSetting(value); ret.add(DialogListItem( option: setting.getDisplayName(context), action: () { if (setting != StateContainer.of(context).curLanguage) { sl.get().setLanguage(setting).then((result) { StateContainer.of(context).updateLanguage(setting); }); } Navigator.of(context).pop(); })); }); return ret; } String daemonURL; String versionString = ""; NotificationSetting _curNotificiationSetting = NotificationSetting(NotificationOptions.ON); @override void initState() { super.initState(); sl.get().getRpcUrl().then((result) { if (result != AppConstants.DEFAULT_RPC_HTTP_URL && mounted) { setState(() { daemonURL = result; }); } }); // Version string PackageInfo.fromPlatform().then((packageInfo) { setState(() { versionString = "v${packageInfo.version}"; }); }); // Get default notification setting sl.get().getNotificationsOn().then((notificationsOn) { setState(() { _curNotificiationSetting = notificationsOn ? NotificationSetting(NotificationOptions.ON) : NotificationSetting(NotificationOptions.OFF); }); }); } @override Widget build(BuildContext context) { // The main scaffold that holds everything return Scaffold( resizeToAvoidBottomInset: false, key: _scaffoldKey, backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, body: LayoutBuilder( builder: (context, constraints) => Stack( children: [ // Container for the gradient background Container( height: 104 + (MediaQuery.of(context).padding.top) + (36 - (MediaQuery.of(context).padding.top) / 2), decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, ), ), // Column for the rest Column( children: [ // Container for the header and button Container( margin: EdgeInsetsDirectional.only( top: (MediaQuery.of(context).padding.top) + (36 - (MediaQuery.of(context).padding.top) / 2), bottom: 8, ), // Row for back button and the header child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Back Button Container( margin: EdgeInsetsDirectional.only(start: 2), height: 50, width: 50, child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.of(context).pop(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(50.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.back, color: StateContainer.of(context) .curTheme .textLight, size: 24)), ), // The header Container( width: MediaQuery.of(context).size.width - 100, margin: EdgeInsetsDirectional.fromSTEB(4, 0, 24, 0), child: AutoSizeText( AppLocalization.of(context).settingsHeader, style: AppStyles.header(context), maxLines: 1, stepGranularity: 0.1, ), ), ], ), ), // Expanded list Expanded( child: Container( margin: EdgeInsetsDirectional.fromSTEB(12, 0, 12, 0), width: double.maxFinite, decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), boxShadow: [ StateContainer.of(context).curTheme.shadowSettingsList, ], ), // Settings List child: ClipRRect( borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12)), child: ListView( padding: EdgeInsetsDirectional.only( bottom: MediaQuery.of(context).padding.bottom + 24), children: [ // Preferences text Container( alignment: Alignment(-1, 0), margin: EdgeInsetsDirectional.only( start: 24, end: 24, top: 18, bottom: 8), child: AutoSizeText( AppLocalization.of(context).preferencesHeader, style: AppStyles.settingsHeader(context), maxLines: 1, stepGranularity: 0.1, ), ), // Divider Container( width: double.maxFinite, height: 1, color: StateContainer.of(context).curTheme.textDark10, ), // List Items SettingsListItem( header: AppLocalization.of(context).currencyHeader, subheader: StateContainer.of(context) .curCurrency .getDisplayName(context), icon: AppIcons.currency, onPressed: () { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization.of(context) .currencyHeader, optionsList: getCurrencyList())); }, ), SettingsListItem( header: AppLocalization.of(context).languageHeader, subheader: StateContainer.of(context) .curLanguage .getDisplayName(context), icon: AppIcons.language, onPressed: () { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization.of(context) .languageHeader, optionsList: getLanguageList())); }, ), SettingsListItem( header: AppLocalization.of(context).themeHeader, subheader: StateContainer.of(context) .curTheme .toString() == BlaiseLightTheme().toString() ? AppLocalization.of(context).themeLightHeader : StateContainer.of(context) .curTheme .toString() == BlaiseDarkTheme().toString() ? AppLocalization.of(context) .themeDarkHeader : AppLocalization.of(context) .themeCopperHeader, icon: AppIcons.theme, onPressed: () { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization.of(context) .themeHeader, optionsList: getThemeList())); }, ), SettingsListItem( header: AppLocalization.of(context).notificationsHeader, subheader: _curNotificiationSetting .getDisplayName(context), icon: AppIcons.notifications, onPressed: () { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization.of(context) .notificationsHeader, optionsList: getNotificationList())); }, ), SettingsListItem( header: AppLocalization.of(context).securityHeader, icon: AppIcons.security, onPressed: () { Navigator.pushNamed(context, '/security'); }, ), SettingsListItem( header: AppLocalization.of(context).daemonHeader, subheader: daemonURL ?? AppLocalization.of(context).defaultHeader, icon: AppIcons.changedaemon, onPressed: () { AppSheets.showBottomSheet( context: context, widget: ChangeDaemonSheet(onChanged: (newDaemon) { EventTaxiImpl.singleton().fire( DaemonChangedEvent( newDaemon: newDaemon)); if (newDaemon != AppConstants.DEFAULT_RPC_HTTP_URL) { setState(() { daemonURL = newDaemon; }); } else { setState(() { daemonURL = null; }); } })); }, ), // Manage text Container( alignment: Alignment(-1, 0), margin: EdgeInsetsDirectional.only( start: 24, end: 24, top: 18, bottom: 8), child: AutoSizeText( AppLocalization.of(context).manageHeader, style: AppStyles.settingsHeader(context), maxLines: 1, stepGranularity: 0.1, ), ), // Divider Container( width: double.maxFinite, height: 1, color: StateContainer.of(context).curTheme.textDark10, ), SettingsListItem( header: AppLocalization.of(context).contactsHeader, icon: AppIcons.contacts, onPressed: () { Navigator.pushNamed(context, '/contacts', arguments: widget.account); }, ), SettingsListItem( header: AppLocalization.of(context) .backUpPrivateKeyHeader, icon: AppIcons.backupprivatekey, onPressed: () { AppSheets.showBottomSheet( context: context, widget: BackupPrivateKeySheet()); }, ), SettingsListItem( header: AppLocalization.of(context).viewPublicKeyHeader, icon: Icons.public, onPressed: () { AppSheets.showBottomSheet( context: context, widget: PublicKeySheet()); }, ), SettingsListItem( header: AppLocalization.of(context).shareHeader, icon: AppIcons.share, onPressed: () { UIUtil.cancelLockEvent(); Share.share(AppLocalization.of(context) .checkOutBlaiseParagraph); }), SettingsListItem( header: AppLocalization.of(context).logoutHeader, icon: AppIcons.logout, onPressed: () { logoutPressed(); }, ), Padding( padding: EdgeInsets.only(top: 10.0, bottom: 10.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(versionString, style: AppStyles.textStyleVersion(context)), Text(" | ", style: AppStyles.textStyleVersion(context)), GestureDetector( onTap: () { AppWebView.showWebView(context, AppConstants.PRIVACY_POLICY_URL); }, child: Text( AppLocalization.of(context) .privacyPolicyHeader, style: AppStyles.textStyleVersionUnderline( context))), Text(" | ", style: AppStyles.textStyleVersion(context)), GestureDetector( onTap: () { AppWebView.showWebView(context, AppConstants.PRIVACY_POLICY_URL); }, child: Text("EULA", style: AppStyles.textStyleVersionUnderline( context))), ], ), ), ], ), ), ), ), ], ), ], ), ), ); } void logoutPressed() { showAppDialog( context: context, builder: (_) => DialogOverlay( title: toUppercase( AppLocalization.of(context).warningHeader, context), warningStyle: true, confirmButtonText: toUppercase( AppLocalization.of(context).deletePrivateKeyAndLogoutButton, context), body: TextSpan( children: formatLocalizedColorsDanger(context, AppLocalization.of(context).logoutFirstDisclaimerParagraph), ), onConfirm: () { Navigator.of(context).pop(); showAppDialog( context: context, builder: (_) => DialogOverlay( title: toUppercase( AppLocalization.of(context).areYouSureHeader, context), warningStyle: true, confirmButtonText: toUppercase( AppLocalization.of(context).yesImSureButton, context), body: TextSpan( children: formatLocalizedColorsDanger( context, AppLocalization.of(context) .logoutSecondDisclaimerParagraph), ), onConfirm: () { // Handle logging out walletState.reset(); sl.get().deleteAll().then((_) { sl.get().deleteAll().then((_) { Navigator.of(context).pushNamedAndRemoveUntil( '/', (Route route) => false); }); }); })); }, )); } } ================================================ FILE: lib/ui/util/app_icons.dart ================================================ /// Flutter icons AppIcons /// Copyright (C) 2019 by original authors @ fluttericon.com, fontello.com /// This font was generated by FlutterIcon.com, which is derived from Fontello. /// /// To use this font, place it in your fonts/ directory and include the /// following in your pubspec.yaml /// /// flutter: /// fonts: /// - family: AppIcons /// fonts: /// - asset: fonts/AppIcons.ttf /// /// /// import 'package:flutter/widgets.dart'; class AppIcons { AppIcons._(); static const _kFontFam = 'AppIcons'; static const IconData addcontact = const IconData(0xe800, fontFamily: _kFontFam); static const IconData back = const IconData(0xe801, fontFamily: _kFontFam); static const IconData backupprivatekey = const IconData(0xe802, fontFamily: _kFontFam); static const IconData close = const IconData(0xe803, fontFamily: _kFontFam); static const IconData contacts = const IconData(0xe804, fontFamily: _kFontFam); static const IconData currency = const IconData(0xe805, fontFamily: _kFontFam); static const IconData currencyswitch = const IconData(0xe806, fontFamily: _kFontFam); static const IconData downarrow = const IconData(0xe807, fontFamily: _kFontFam); static const IconData edit = const IconData(0xe808, fontFamily: _kFontFam); static const IconData export_icon = const IconData(0xe809, fontFamily: _kFontFam); static const IconData fingerprint = const IconData(0xe80a, fontFamily: _kFontFam); static const IconData hide = const IconData(0xe80b, fontFamily: _kFontFam); static const IconData import_icon = const IconData(0xe80c, fontFamily: _kFontFam); static const IconData language = const IconData(0xe80d, fontFamily: _kFontFam); static const IconData lock = const IconData(0xe80e, fontFamily: _kFontFam); static const IconData support = const IconData(0xe80f, fontFamily: _kFontFam); static const IconData notifications = const IconData(0xe810, fontFamily: _kFontFam); static const IconData contact = const IconData(0xe811, fontFamily: _kFontFam); static const IconData pascalcurrency = const IconData(0xe812, fontFamily: _kFontFam); static const IconData paste = const IconData(0xe813, fontFamily: _kFontFam); static const IconData scan = const IconData(0xe814, fontFamily: _kFontFam); static const IconData search = const IconData(0xe815, fontFamily: _kFontFam); static const IconData security = const IconData(0xe816, fontFamily: _kFontFam); static const IconData settings = const IconData(0xe817, fontFamily: _kFontFam); static const IconData share = const IconData(0xe818, fontFamily: _kFontFam); static const IconData shareaddress = const IconData(0xe819, fontFamily: _kFontFam); static const IconData show = const IconData(0xe81a, fontFamily: _kFontFam); static const IconData theme = const IconData(0xe81b, fontFamily: _kFontFam); static const IconData tick = const IconData(0xe81c, fontFamily: _kFontFam); static const IconData timer = const IconData(0xe81d, fontFamily: _kFontFam); static const IconData trashcan = const IconData(0xe81e, fontFamily: _kFontFam); static const IconData uparrow = const IconData(0xe81f, fontFamily: _kFontFam); static const IconData changedaemon = const IconData(0xe820, fontFamily: _kFontFam); static const IconData pascalsymbol = const IconData(0xe821, fontFamily: _kFontFam); static const IconData chatbubble = const IconData(0xe822, fontFamily: _kFontFam); static const IconData max = const IconData(0xe823, fontFamily: _kFontFam); static const IconData logout = const IconData(0xe824, fontFamily: _kFontFam); } ================================================ FILE: lib/ui/util/formatters.dart ================================================ import 'package:blaise_wallet_flutter/util/number_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; /// For Pascal Accounts class PascalAccountFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { // Ensure only one - is present if (newValue.text.isNotEmpty && newValue.text.allMatches('-').length > 1) { return oldValue; } else if (newValue.text.isNotEmpty && newValue.text.contains('-')) { // Ensure only two digits after "-" if (newValue.text.length - newValue.text.indexOf('-') > 3) { return oldValue; } } return newValue; } } /// For Pascal Names class PascalNameFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { // Ensure first character is not a number if (newValue.text.startsWith(RegExp(r"[0-9]"))) { return oldValue; } return newValue; } } /// For phone numbers /// Ensures no more than 20 digits /// Ensures hyphens only occur after a number class PhoneNumberFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { // Check digit count, 20 is max if (newValue.text.replaceAll(RegExp(r"[^0-9]"), "").length > 20) { return oldValue; } // Ensure only 1 hyphen after a number if (newValue.text.endsWith("-") && oldValue.text.endsWith("-")) { return oldValue; } return newValue; } } /// Input formatter for Crpto/Fiat amounts class CurrencyFormatter extends TextInputFormatter { String commaSeparator; String decimalSeparator; int maxDecimalDigits; CurrencyFormatter({this.commaSeparator = ",", this.decimalSeparator = ".", this.maxDecimalDigits = NumberUtil.maxDecimalDigits}); TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { bool returnOriginal = true; if (newValue.text.contains(decimalSeparator) || newValue.text.contains(commaSeparator)) { returnOriginal = false; } // If no text, or text doesnt contain a period of comma, no work to do here if(newValue.selection.baseOffset == 0 || returnOriginal) { return newValue; } String workingText = newValue.text.replaceAll(commaSeparator, decimalSeparator); // if contains more than 2 decimals in newValue, return oldValue if (decimalSeparator.allMatches(workingText).length > 1) { return newValue.copyWith( text: oldValue.text, selection: new TextSelection.collapsed(offset: oldValue.text.length)); } else if (workingText.startsWith(decimalSeparator)) { workingText = "0" + workingText; } List splitStr = workingText.split(decimalSeparator); // If this string contains more than 1 decimal, move all characters to after the first decimal if (splitStr.length > 2) { returnOriginal = false; splitStr.forEach((val) { if (splitStr.indexOf(val) > 1) { splitStr[1] += val; } }); } if (splitStr[1].length <= maxDecimalDigits) { if (workingText == newValue.text) { return newValue; } else { return newValue.copyWith( text: workingText, selection: new TextSelection.collapsed(offset: workingText.length)); } } String newText = splitStr[0] + decimalSeparator + splitStr[1].substring(0, maxDecimalDigits); return newValue.copyWith( text: newText, selection: new TextSelection.collapsed(offset: newText.length)); } } class LocalCurrencyFormatter extends TextInputFormatter { NumberFormat currencyFormat; bool active; LocalCurrencyFormatter({this.currencyFormat, this.active}); TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { if (newValue.text.isEmpty) { // Return empty string return newValue.copyWith( text: "", selection: new TextSelection.collapsed(offset: 0)); } // Ensure our input is in the right formatting here if (active) { // Make local currency = amount with correct decimal separator String curText = newValue.text; String shouldBeText = NumberUtil.sanitizeNumber(curText.replaceAll(",", ".")); shouldBeText = shouldBeText.replaceAll(".", currencyFormat.symbols.DECIMAL_SEP); if (shouldBeText != curText) { return newValue.copyWith( text: shouldBeText, selection: TextSelection.collapsed(offset: shouldBeText.length)); } } else { // Make crypto amount formatted as US locale String curText = newValue.text; String shouldBeText = NumberUtil.sanitizeNumber(curText.replaceAll(",", ".")); if (shouldBeText != curText) { return newValue.copyWith( text: shouldBeText, selection: TextSelection.collapsed(offset: shouldBeText.length)); } } return newValue; } } /// Ensures input is always uppercase class UpperCaseTextFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { return TextEditingValue( text: newValue.text?.toUpperCase(), selection: newValue.selection, ); } } /// Ensures input is always lowercase class LowerCaseTextFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { return TextEditingValue( text: newValue.text?.toLowerCase(), selection: newValue.selection, ); } } ================================================ FILE: lib/ui/util/margins.dart ================================================ import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; class AppMarginSizes { static const m0 = 0.0; static const m1 = 4.0; static const m2 = 16.0; static const m3 = 18.0; static const m4 = 24.0; static const m5 = 30.0; static const m6 = 40.0; } class AppMargins { // Text Field Header Margins static EdgeInsetsDirectional textFieldHeader(BuildContext context) { return UIUtil.smallScreen(context) ? EdgeInsetsDirectional.fromSTEB(AppMarginSizes.m5, AppMarginSizes.m2, AppMarginSizes.m5, AppMarginSizes.m0) : EdgeInsetsDirectional.fromSTEB(AppMarginSizes.m5, AppMarginSizes.m5, AppMarginSizes.m5, AppMarginSizes.m0); } } ================================================ FILE: lib/ui/util/routes.dart ================================================ import 'package:flutter/material.dart'; enum TransitionOption { NONE, NOPUSH, NOPOP } /// NoTransitionRoute /// Custom route which has no transitions class NoTransitionRoute extends MaterialPageRoute { NoTransitionRoute( {WidgetBuilder builder, RouteSettings settings}) : super(builder: builder, settings: settings); @override Widget buildTransitions(BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { return child; } } /// NoPushTransitionRoute /// Custom route which has no transition when pushed, but has a pop animation class NoPushTransitionRoute extends MaterialPageRoute { NoPushTransitionRoute({ WidgetBuilder builder, RouteSettings settings }) : super(builder: builder, settings: settings); @override Widget buildTransitions(BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { // is popping if (animation.status == AnimationStatus.reverse) { return super.buildTransitions(context, animation, secondaryAnimation, child); } return child; } } /// NoPopTransitionRoute /// Custom route which has no transition when popped, but has a push animation class NoPopTransitionRoute extends MaterialPageRoute { NoPopTransitionRoute({ WidgetBuilder builder, RouteSettings settings }) : super(builder: builder, settings: settings); @override Widget buildTransitions(BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { // is pushing if (animation.status == AnimationStatus.forward) { return super.buildTransitions(context, animation, secondaryAnimation, child); } return child; } } class RouteUtils { static RoutePredicate withNameLike(String name) { return (Route route) { return !route.willHandlePopInternally && route is ModalRoute && route.settings.name != null && route.settings.name.contains(name); }; } } ================================================ FILE: lib/ui/util/text_styles.dart ================================================ import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:flutter/material.dart'; class AppStyles { // For snackbar/Toast text static TextStyle snackbar(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.small, fontWeight: FontWeight.w700, color: StateContainer.of(context).curTheme.backgroundPrimary); } // For headers static TextStyle header(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.largest, fontWeight: FontWeight.w700, color: StateContainer.of(context).curTheme.textLight); } // For small headers static TextStyle headerSmall(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.medium, fontWeight: FontWeight.w500, color: StateContainer.of(context).curTheme.primary); } static TextStyle headerSmallBold(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.medium, fontWeight: FontWeight.w700, color: StateContainer.of(context).curTheme.primary); } static TextStyle headerSmallBoldSuccess(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.medium, fontWeight: FontWeight.w700, color: StateContainer.of(context).curTheme.success); } // For paragraphs static TextStyle paragraph(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textDark, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w400); } // For paragraphs static TextStyle paragraphSuccess(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.success, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w400); } static TextStyle paragraphMedium(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textDark, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w500); } static TextStyle paragraphMediumPrimary(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w500); } static TextStyle paragraphMediumSuccess(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.success, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w500); } // For paragraphs static TextStyle paragraphPrimary(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w700); } // For paragraphs static TextStyle primarySmall600(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: AppFontSizes.small, height: 1.3, fontWeight: FontWeight.w600); } static TextStyle primarySmallest400(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: 12.0, height: 1.3, fontWeight: FontWeight.w400); } static TextStyle primarySmallest500(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: 12.0, height: 1.3, fontWeight: FontWeight.w500); } static TextStyle primarySmallest600(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: 12.0, height: 1.3, fontWeight: FontWeight.w600); } static TextStyle dangerSmallest600(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.danger, fontSize: 12.0, height: 1.3, fontWeight: FontWeight.w600); } static TextStyle paragraphDanger(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.danger, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w700); } static TextStyle balanceMedium(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: 18.0, fontWeight: FontWeight.w700); } static TextStyle balanceSmall(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: 14.0, fontWeight: FontWeight.w600); } static TextStyle balanceSmallSuccess(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.success, fontSize: 14.0, fontWeight: FontWeight.w600); } static TextStyle balanceSmallTextDark(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textDark, fontSize: 14.0, fontWeight: FontWeight.w600); } static TextStyle balanceSmallSecondary(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.secondary, fontSize: 14.0, fontWeight: FontWeight.w600); } static TextStyle paragraphBig(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textDark, fontSize: 16.0, height: 1.3, fontWeight: FontWeight.w500); } static TextStyle paragraphBigDisabled(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textDark30, fontSize: 16.0, height: 1.3, fontWeight: FontWeight.w500); } static TextStyle paragraphTextLightSmall(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textLight, fontSize: 12.0, height: 1.3, fontWeight: FontWeight.w300); } static TextStyle textLightSmall600(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textLight, fontSize: 14.0, fontWeight: FontWeight.w600); } static TextStyle paragraphTextLightSmallSemiBold(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textLight, fontSize: 12.0, height: 1.3, fontWeight: FontWeight.w600); } // Modal header static TextStyle snackBar(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: 14.0, fontWeight: FontWeight.w700); } // Modal header static TextStyle modalHeader(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textLight, fontSize: 18.0, height: 1.3, fontWeight: FontWeight.w700); } // Settings item header static TextStyle settingsItemHeader(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textDark, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w700); } static TextStyle settingsItemHeaderDisabled(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textDark60, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w700); } // Settings item subheader static TextStyle settingsItemSubHeader(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textDark, fontSize: 12.0, height: 1.3, fontWeight: FontWeight.w400); } static TextStyle settingsItemSubHeaderDisabled(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textDark60, fontSize: 12.0, height: 1.3, fontWeight: FontWeight.w400); } // Date on operations list items static TextStyle operationDate(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textDark50, fontSize: 12.0, fontWeight: FontWeight.w400); } // Operation type static TextStyle operationType(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textLight, fontSize: 13.0, fontWeight: FontWeight.w600); } // Settings header static TextStyle settingsHeader(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: 16.0, fontWeight: FontWeight.w700); } static TextStyle iconFontPrimaryMedium(BuildContext context) { return TextStyle( fontFamily: 'AppIcons', color: StateContainer.of(context).curTheme.primary, fontSize: 16.0); } static TextStyle iconFontSuccessMedium(BuildContext context) { return TextStyle( fontFamily: 'AppIcons', color: StateContainer.of(context).curTheme.success, fontSize: 16.0); } static TextStyle iconFontPrimarySmall(BuildContext context) { return TextStyle( fontFamily: 'AppIcons', color: StateContainer.of(context).curTheme.primary, fontSize: 14.0); } static TextStyle iconFontSuccessSmall(BuildContext context) { return TextStyle( fontFamily: 'AppIcons', color: StateContainer.of(context).curTheme.success, fontSize: 14.0); } static TextStyle emptySpaceSmallest(BuildContext context) { return TextStyle(fontSize: 12); } static TextStyle emptySpaceTiny(BuildContext context) { return TextStyle(fontSize: 10); } static TextStyle accountsItemName(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textDark, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w700); } static TextStyle accountsItemNumber(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textDark60, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w500); } // Contacts item name static TextStyle contactsItemName(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textDark, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w700); } // Contacts item name primary static TextStyle contactsItemNamePrimary(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w700); } // Contacts item name primary static TextStyle contactsItemNameSuccess(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.success, fontSize: 14.0, height: 1.3, fontWeight: FontWeight.w700); } // Contacts item address static TextStyle contactsItemAddress(BuildContext context) { return TextStyle( fontFamily: 'SourceCodePro', color: StateContainer.of(context).curTheme.textDark, fontSize: 14.0, fontWeight: FontWeight.w400); } // Contacts item address static TextStyle monoTextDarkSmall400(BuildContext context) { return TextStyle( fontFamily: 'SourceCodePro', color: StateContainer.of(context).curTheme.textDark, fontSize: 14.0, fontWeight: FontWeight.w400); } static TextStyle contactsItemAddressPrimary(BuildContext context) { return TextStyle( fontFamily: 'Metropolis', fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: 16.0, fontWeight: FontWeight.w400); } // For text field labels static TextStyle textFieldLabel(BuildContext context) { return TextStyle( color: StateContainer.of(context).curTheme.primary, fontFamily: 'Metropolis', fontFamilyFallback: ["RobotoRegular"], fontSize: 16.0, height: 1.3, fontWeight: FontWeight.w600); } static TextStyle textFieldLabelSuccess(BuildContext context) { return TextStyle( color: StateContainer.of(context).curTheme.success, fontFamily: 'Metropolis', fontFamilyFallback: ["RobotoRegular"], fontSize: 16.0, height: 1.3, fontWeight: FontWeight.w600); } // For primary Private Key static TextStyle privateKeyPrimary(BuildContext context) { return TextStyle( color: StateContainer.of(context).curTheme.primary, fontSize: 14.0, height: 1, fontWeight: FontWeight.w500, fontFamily: 'SourceCodePro'); } // For neutral Private Key static TextStyle privateKeyTextDark(BuildContext context) { return TextStyle( color: StateContainer.of(context).curTheme.textDark, fontSize: 14.0, height: 1, fontWeight: FontWeight.w500, fontFamily: 'SourceCodePro'); } // For neutral Private Key static TextStyle privateKeyTextDarkFaded(BuildContext context) { return TextStyle( fontFamily: 'SourceCodePro', color: StateContainer.of(context).curTheme.textDark.withOpacity(0.5), fontSize: 14.0, height: 1, fontWeight: FontWeight.w500, ); } // For success Private Key static TextStyle privateKeySuccess(BuildContext context) { return TextStyle( color: StateContainer.of(context).curTheme.success, fontSize: 14.0, height: 1, fontWeight: FontWeight.w500, fontFamily: 'SourceCodePro'); } // Primary Button Text static TextStyle buttonPrimary(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textLight, fontSize: 18, fontWeight: FontWeight.w700); } // Outline Button Text static TextStyle buttonPrimaryOutline(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: 18, fontWeight: FontWeight.w700); } static TextStyle buttonSuccessOutline(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.success, fontSize: 18, fontWeight: FontWeight.w700); } // Danger Button Text static TextStyle buttonDanger(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.textLight, fontSize: 12, fontWeight: FontWeight.w700, height: 1.3, ); } // Danger Outline Button Text static TextStyle buttonDangerOutline(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.danger, fontSize: 12, fontWeight: FontWeight.w700, height: 1.3, ); } // Bg Colored Mini Button Text static TextStyle buttonMiniBg(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.primary, fontSize: 14, fontWeight: FontWeight.w600); } // Success Colored Mini Button Text static TextStyle buttonMiniSuccess(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], color: StateContainer.of(context).curTheme.backgroundPrimary, fontSize: 14, fontWeight: FontWeight.w600); } // For header pascal icon static TextStyle iconFontTextLightPascal(BuildContext context) { return TextStyle( fontSize: 28, color: StateContainer.of(context).curTheme.textLight, fontFamily: 'AppIcons', ); } // For pascal icon on the account card balance static TextStyle iconFontPrimaryBalanceMediumPascal(BuildContext context) { return TextStyle( fontSize: 18, color: StateContainer.of(context).curTheme.primary, fontFamily: 'AppIcons', ); } // For pascal icon on the operation items static TextStyle iconFontPrimaryBalanceSmallPascal(BuildContext context) { return TextStyle( fontSize: 14, color: StateContainer.of(context).curTheme.primary, fontFamily: 'AppIcons', ); } static TextStyle iconFontSuccessBalanceSmallPascal(BuildContext context) { return TextStyle( fontSize: 14, color: StateContainer.of(context).curTheme.success, fontFamily: 'AppIcons', ); } static TextStyle iconFontTextDarkBalanceSmallPascal(BuildContext context) { return TextStyle( fontSize: 14, color: StateContainer.of(context).curTheme.textDark, fontFamily: 'AppIcons', ); } static TextStyle iconFontSecondarySmallPascal(BuildContext context) { return TextStyle( fontSize: 14, color: StateContainer.of(context).curTheme.secondary, fontFamily: 'AppIcons', ); } // For chat bubble icon static TextStyle iconFontPrimaryBalanceSmallest(BuildContext context) { return TextStyle( fontSize: 12, color: StateContainer.of(context).curTheme.primary50, fontFamily: 'AppIcons', ); } static TextStyle iconFontTextDarkBalanceSmallest(BuildContext context) { return TextStyle( fontSize: 12, color: StateContainer.of(context).curTheme.textDark50, fontFamily: 'AppIcons', ); } // Account Card Name static TextStyle accountCardName(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: 14, color: StateContainer.of(context).curTheme.textLight, fontWeight: FontWeight.w700, ); } // Account Card Address static TextStyle accountCardAddress(BuildContext context) { return TextStyle( fontSize: 14, color: StateContainer.of(context).curTheme.textLight, fontFamily: 'SourceCodePro', ); } // PIN Description static TextStyle pinDescription(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.small, color: StateContainer.of(context).curTheme.textLight, ); } // PIN Description static TextStyle pinNumberPad(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.larger, color: StateContainer.of(context).curTheme.primary, fontWeight: FontWeight.w700, ); } // Text Dark Small 400 static TextStyle textDarkSmall400(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.small, color: StateContainer.of(context).curTheme.textDark, fontWeight: FontWeight.w400, ); } // Text Dark Large 400 static TextStyle textDarkLarge700(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.large, color: StateContainer.of(context).curTheme.textDark, fontWeight: FontWeight.w700, ); } // Text Light Small 400 static TextStyle textLightSmall400(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.small, color: StateContainer.of(context).curTheme.textLight, fontWeight: FontWeight.w400, ); } // Text Light Large 400 static TextStyle textLightLarge700(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.large, color: StateContainer.of(context).curTheme.textLight, fontWeight: FontWeight.w700, ); } // Text Light Small 700 static TextStyle textLightSmall700(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.small, color: StateContainer.of(context).curTheme.textLight, fontWeight: FontWeight.w700, ); } // Version info in settings static TextStyle textStyleVersion(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.small, fontWeight: FontWeight.w100, color: StateContainer.of(context).curTheme.textDark60); } static TextStyle textStyleVersionUnderline(BuildContext context) { return TextStyle( fontFamily: "Metropolis", fontFamilyFallback: ["RobotoRegular"], fontSize: AppFontSizes.small, fontWeight: FontWeight.w100, color: StateContainer.of(context).curTheme.textDark60, decoration: TextDecoration.underline); } } class AppFontSizes { static const smallest = 12.0; static const small = 14.0; static const medium = 16.0; static const large = 18.0; static const larger = 24.0; static const largest = 28.0; } ================================================ FILE: lib/ui/widgets/account_card.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/account/receive/receive_sheet.dart'; import 'package:blaise_wallet_flutter/ui/account/send/send_sheet.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/overlay_dialog.dart'; import 'package:blaise_wallet_flutter/ui/widgets/sheets.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:quiver/strings.dart'; /// A widget for buttons class AccountCard extends StatefulWidget { final PascalAccount account; AccountCard({@required this.account}); _AccountCardState createState() => _AccountCardState(); } class _AccountCardState extends State { @override Widget build(BuildContext context) { // Account Card return Slidable( actionPane: SlidableBehindActionPane(), actionExtentRatio: 0.175, movementDuration: Duration(milliseconds: 150), actions: _getButtons(), child: Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(12, 5, 12, 5), height: 70, decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.circular(12), boxShadow: [StateContainer.of(context).curTheme.shadowAccountCard]), child: Stack( children: [ // Left gradient background Container( height: double.maxFinite, width: MediaQuery.of(context).size.width * 0.47 - 24, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), bottomLeft: Radius.circular(12))), ), FlatButton( onPressed: () { if (!widget.account.isFreepasa) { Navigator.pushNamed(context, '/account', arguments: widget.account); } else { showAppDialog( context: context, builder: (_) => DialogOverlay( title: toUppercase( AppLocalization.of(context).unconfirmedAccountHeader, context), confirmButtonText: AppLocalization.of(context).okayButton, singleButton: true, onConfirm: ()=>Navigator.pop(context), body: TextSpan( children: formatLocalizedColors(context, AppLocalization.of(context).unconfirmedAccountParagraph), ), ) ); } }, padding: EdgeInsets.all(0), shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(12)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Left part of the card with account name and number Container( height: double.maxFinite, width: MediaQuery.of(context).size.width * 0.47 - 24, padding: EdgeInsetsDirectional.only(start: 16, end: 16), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ isNotEmpty(widget.account.name.accountName) ? Container( margin: EdgeInsetsDirectional.only(bottom: 2), child: AutoSizeText( widget.account.name.accountName, style: AppStyles.accountCardName(context), textAlign: TextAlign.left, stepGranularity: 0.5, maxLines: 1, minFontSize: 8, ), ) : SizedBox(), AutoSizeText( widget.account.account.toString(), style: AppStyles.accountCardAddress(context), textAlign: TextAlign.left, stepGranularity: 0.5, maxLines: 1, ) ], ), ), // Right part of the card with balance Container( height: double.maxFinite, width: MediaQuery.of(context).size.width * 0.53 - 24, padding: EdgeInsetsDirectional.only(end: 16), alignment: Alignment(1, 0), child: Observer( builder: (context) { String status = ""; if (widget.account.isFreepasa) { status = AppLocalization.of(context).pendingHeader; } else if (walletState.borrowedAccount != null && walletState.borrowedAccount.account == widget.account.account) { if (walletState.borrowedAccount.paid) { status = AppLocalization.of(context).borrowedTransferredHeader; } else { status = AppLocalization.of(context).borrowedHeader; } } else if (widget.account.state == AccountState.LISTED) { status = AppLocalization.of(context).forSaleHeader; } if (!widget.account.isBorrowed && !widget.account.isFreepasa && !(widget.account.state == AccountState.LISTED)) { return AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: AppStyles .iconFontPrimaryBalanceMediumPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 7)), TextSpan( text: widget.account.balance.toStringOpt(), style: AppStyles.balanceMedium(context)), ], ), textAlign: TextAlign.end, maxLines: 1, minFontSize: 4, stepGranularity: 1, style: TextStyle( fontSize: 18, ), ); } else { return Container( decoration: BoxDecoration( gradient: StateContainer.of(context) .curTheme .gradientPrimary, borderRadius: BorderRadius.circular(100)), padding: EdgeInsetsDirectional.fromSTEB(10, 5, 10, 5), child: AutoSizeText( status, style: AppStyles.textLightSmall600(context), textAlign: TextAlign.center, maxLines: 1, minFontSize: 8, stepGranularity: 1, ), ); } }, ) ), ], ), ), ], ), ), ); } // The function that does nothing void _doNothing() { return null; } // Widget that returns the hidden buttons List _getButtons() { List ret = []; if (widget.account.isFreepasa) { return ret; } ret.add( // Receive Icon Container( color: Colors.transparent, margin: EdgeInsetsDirectional.only( start: MediaQuery.of(context).size.width * 0.05, ), child: ClipRRect( borderRadius: BorderRadius.circular(12), child: Container( height: 48, width: 50, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, boxShadow: [ StateContainer.of(context).curTheme.shadowPrimaryOne ]), child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { Navigator.pushNamed(context, '/account', arguments: widget.account); AppSheets.showBottomSheet( context: context, widget: ReceiveSheet( accountName: widget.account.name.accountName, accountNumber: widget.account.account, )); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.downarrow, color: StateContainer.of(context).curTheme.textLight, size: 24)), ), ), ), ); widget.account.balance == Currency('0') ? _doNothing() : ret.add( // Send icon Container( color: Colors.transparent, padding: EdgeInsetsDirectional.only( start: (MediaQuery.of(context).size.width * 0.05) * 2 / 3, end: (MediaQuery.of(context).size.width * 0.05) * 1 / 3, ), child: ClipRRect( borderRadius: BorderRadius.circular(12), child: Container( margin: EdgeInsetsDirectional.only(top: 0, end: 0), height: 48, width: 50, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, boxShadow: [ StateContainer.of(context).curTheme.shadowPrimaryOne ]), child: FlatButton( highlightColor: StateContainer.of(context).curTheme.textLight15, splashColor: StateContainer.of(context).curTheme.textLight30, onPressed: () { if (widget.account.balance > Currency('0')) { Navigator.pushNamed(context, '/account', arguments: widget.account); AppSheets.showBottomSheet( context: context, widget: SendSheet(account: widget.account, localCurrency: StateContainer.of(context).curCurrency)); } }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0)), padding: EdgeInsets.all(0.0), child: Icon(AppIcons.uparrow, color: StateContainer.of(context).curTheme.textLight, size: 24)), ), ), )); return ret; } } ================================================ FILE: lib/ui/widgets/app_text_field.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; /// TextField button class TextFieldButton extends StatelessWidget { final IconData icon; final Function onPressed; TextFieldButton({@required this.icon, this.onPressed}); @override Widget build(BuildContext context) { return Container( height: 38, width: 38, decoration: BoxDecoration( shape: BoxShape.circle, gradient: StateContainer.of(context).curTheme.gradientPrimary, ), child: FlatButton( padding: EdgeInsets.all(0), shape: CircleBorder(), onPressed: () { return onPressed != null ? onPressed() : null; }, splashColor: StateContainer.of(context).curTheme.backgroundPrimary30, highlightColor: StateContainer.of(context).curTheme.backgroundPrimary15, child: Icon( icon, size: 22, color: StateContainer.of(context).curTheme.backgroundPrimary, ), ), ); } } /// A widget for our custom textfields class AppTextField extends StatefulWidget { final String label; final TextFieldButton firstButton; final TextFieldButton secondButton; final TextEditingController controller; final FocusNode focusNode; final TextCapitalization textCapitalization; final TextInputType inputType; final TextStyle style; final bool passwordField; final Widget prefix; final int maxLines; final List inputFormatters; final Function onChanged; final Function onTap; final bool readOnly; final TextInputAction textInputAction; final Function onSubmitted; AppTextField( {@required this.label, @required this.style, this.firstButton, this.secondButton, this.controller, this.focusNode, this.textCapitalization, this.inputType = TextInputType.text, this.prefix, this.maxLines, this.inputFormatters, this.onChanged, this.onTap, this.onSubmitted, this.passwordField = false, this.readOnly = false, this.textInputAction = TextInputAction.done}); _AppTextFieldState createState() => _AppTextFieldState(); } class _AppTextFieldState extends State { @override Widget build(BuildContext context) { return Column( children: [ Container( alignment: Alignment(-1, 0), child: AutoSizeText( widget.label, style: AppStyles.textFieldLabel(context), ), ), Container( child: Theme( data: ThemeData( primaryColor: StateContainer.of(context).curTheme.primary, hintColor: StateContainer.of(context).curTheme.primary, splashColor: StateContainer.of(context).curTheme.primary30, highlightColor: StateContainer.of(context).curTheme.primary15, textSelectionColor: StateContainer.of(context).curTheme.primary30, ), child: Stack( alignment: AlignmentDirectional.center, children: [ TextField( readOnly: widget.readOnly, controller: widget.controller, focusNode: widget.focusNode, obscureText: widget.passwordField, style: widget.style, cursorColor: StateContainer.of(context).curTheme.primary, keyboardType: widget.inputType, autocorrect: false, textCapitalization: widget.textCapitalization ?? TextCapitalization.none, textInputAction: widget.textInputAction, maxLines: widget.maxLines, minLines: 1, onSubmitted: (text) { if (widget.textInputAction == TextInputAction.done && widget.onSubmitted == null) { FocusScope.of(context).unfocus(); } else if (widget.onSubmitted != null) { widget.onSubmitted(); } }, inputFormatters: widget.inputFormatters, onTap: widget.onTap, onChanged: (String newValue) { if (widget.onChanged != null) { widget.onChanged(newValue); } }, decoration: InputDecoration( enabledBorder: UnderlineInputBorder( borderSide: BorderSide( color: StateContainer.of(context).curTheme.primary), ), focusedBorder: UnderlineInputBorder( borderSide: BorderSide( color: StateContainer.of(context).curTheme.primary, width: 2), ), errorBorder: UnderlineInputBorder( borderSide: BorderSide( color: StateContainer.of(context).curTheme.danger, width: 2), ), prefix: widget.prefix, suffixIcon: widget.firstButton == null && widget.secondButton == null ? Container( width: 0, height: 0, ) : Container( width: widget.firstButton == null || widget.secondButton == null ? 50 : 100, height: 38, ), ), ), // Buttons Column( mainAxisSize: MainAxisSize.max, children: [ Row( mainAxisAlignment: MainAxisAlignment.end, children: [ widget.secondButton != null ? Container( margin: EdgeInsetsDirectional.only(start: 12), width: 38, height: 38, child: widget.secondButton) : SizedBox(), widget.firstButton != null ? Container( margin: EdgeInsetsDirectional.only(start: 12), width: 38, height: 38, child: widget.firstButton) : SizedBox() ], ), ], ) ], )), ), ], ); } } ================================================ FILE: lib/ui/widgets/buttons.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:flutter/material.dart'; enum AppButtonType { Primary, PrimaryLeft, PrimaryRight, PrimaryOutline, Success, SuccessOutline, Danger, DangerOutline, } /// A widget for buttons class AppButton extends StatefulWidget { final AppButtonType type; final String text; final Function onPressed; final bool disabled; final bool buttonTop; final bool buttonMiddle; final bool placeholder; AppButton( {this.type, this.text, this.onPressed, this.disabled = false, this.buttonTop = false, this.placeholder = false, this.buttonMiddle = false}); _AppButtonState createState() => _AppButtonState(); } class _AppButtonState extends State { @override Widget build(BuildContext context) { return Expanded( child: Opacity( opacity: widget.disabled ? 0.4 : 1.0, child: Container( margin: widget.buttonTop ? EdgeInsetsDirectional.fromSTEB(20, 20, 20, 0) : widget.buttonMiddle ? EdgeInsetsDirectional.fromSTEB(20, 16, 20, 0) : EdgeInsetsDirectional.fromSTEB( widget.type == AppButtonType.PrimaryRight ? 10 : 20, 16, widget.type == AppButtonType.PrimaryLeft ? 10 : 20, (MediaQuery.of(context).padding.bottom) + (24 - (MediaQuery.of(context).padding.bottom) / 2), ), height: 50, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12.0), gradient: (widget.type == AppButtonType.Primary || widget.type == AppButtonType.PrimaryOutline || widget.type == AppButtonType.PrimaryLeft || widget.type == AppButtonType.PrimaryRight) ? StateContainer.of(context).curTheme.gradientPrimary : (widget.type == AppButtonType.Danger || widget.type == AppButtonType.DangerOutline || widget.type == AppButtonType.Success || widget.type == AppButtonType.SuccessOutline) ? null : StateContainer.of(context) .curTheme .gradientPrimary, // Success color placeholder color: (widget.type == AppButtonType.Danger || widget.type == AppButtonType.DangerOutline) ? StateContainer.of(context).curTheme.danger : widget.type == AppButtonType.Success || widget.type == AppButtonType.SuccessOutline ? StateContainer.of(context).curTheme.success : null, boxShadow: [ widget.type == AppButtonType.Primary || widget.type == AppButtonType.PrimaryLeft || widget.type == AppButtonType.PrimaryRight ? StateContainer.of(context).curTheme.shadowPrimaryOne : widget.type == AppButtonType.PrimaryOutline ? StateContainer.of(context).curTheme.shadowPrimaryTwo : widget.type == AppButtonType.Success ? StateContainer.of(context).curTheme.shadowSuccessOne : widget.type == AppButtonType.SuccessOutline ? StateContainer.of(context) .curTheme .shadowSuccessTwo : widget.type == AppButtonType.Danger ? StateContainer.of(context) .curTheme .shadowDangerOne : StateContainer.of(context) .curTheme .shadowDangerTwo, ], ), child: widget.type == AppButtonType.Primary || widget.type == AppButtonType.PrimaryLeft || widget.type == AppButtonType.PrimaryRight || widget.type == AppButtonType.Success || widget.type == AppButtonType.Danger // Primary Button ? FlatButton( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0)), child: !widget.placeholder ? AutoSizeText( widget.text, textAlign: TextAlign.center, maxLines: widget.type == AppButtonType.Danger ? 2 : 1, stepGranularity: 0.1, style: widget.type == AppButtonType.Danger ? AppStyles.buttonDanger(context) : AppStyles.buttonPrimary(context), ) // Placeholder button rectangle : Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.textLight.withOpacity(0.75), borderRadius: BorderRadius.circular(100), ), child: AutoSizeText( widget.text, textAlign: TextAlign.center, maxLines: widget.type == AppButtonType.Danger ? 2 : 1, stepGranularity: 0.1, style: widget.type == AppButtonType.Danger ? AppStyles.buttonDanger(context) : AppStyles.buttonPrimary(context), ), ), splashColor: StateContainer.of(context).curTheme.backgroundPrimary30, highlightColor: StateContainer.of(context).curTheme.backgroundPrimary15, onPressed: () { if (widget.onPressed != null && !widget.disabled) { widget.onPressed(); } return; }, ) // Primary Outlined Button : Stack( children: [ Container( margin: EdgeInsets.all(2), height: 46, decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: StateContainer.of(context).curTheme.backgroundPrimary, ), ), Container( height: 50, width: double.maxFinite, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), color: Colors.transparent, ), child: FlatButton( child: AutoSizeText( widget.text, textAlign: TextAlign.center, maxLines: 1, stepGranularity: 0.1, style: widget.type == AppButtonType.DangerOutline ? AppStyles.buttonDangerOutline(context) : widget.type == AppButtonType.SuccessOutline ? AppStyles.buttonSuccessOutline(context) : AppStyles.buttonPrimaryOutline(context), ), color: Colors.transparent, splashColor: widget.type == AppButtonType.SuccessOutline ? StateContainer.of(context).curTheme.success30 : widget.type == AppButtonType.DangerOutline ? StateContainer.of(context).curTheme.danger30 : StateContainer.of(context).curTheme.primary30, highlightColor: widget.type == AppButtonType.SuccessOutline ? StateContainer.of(context).curTheme.success15 : widget.type == AppButtonType.DangerOutline ? StateContainer.of(context).curTheme.danger15 : StateContainer.of(context).curTheme.primary15, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0)), onPressed: () { if (widget.onPressed != null && !widget.disabled) { widget.onPressed(); } return; }, ), ), ], ), ), )); } } ================================================ FILE: lib/ui/widgets/error_container.dart ================================================ import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:flutter/material.dart'; class ErrorContainer extends StatelessWidget { final String errorText; ErrorContainer({@required this.errorText}); @override Widget build(BuildContext context) { return Container( alignment: Alignment(-1, 0), margin: EdgeInsetsDirectional.only(start: 30, end: 30, top: 4, bottom: 0), child: Text(this.errorText, style: AppStyles.dangerSmallest600(context), textAlign: TextAlign.start), ); } } ================================================ FILE: lib/ui/widgets/fee_container.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:flutter/material.dart'; class FeeContainer extends StatelessWidget { final String feeText; FeeContainer({@required this.feeText}); @override Widget build(BuildContext context) { return Container( margin: EdgeInsetsDirectional.fromSTEB(30, 4, 30, 0), alignment: Alignment(-1, 0), child: AutoSizeText.rich( TextSpan(children: [ TextSpan( text: AppLocalization.of(context).feeColonHeader + " ", style: AppStyles.primarySmall600(context), ), TextSpan( text: "", style: AppStyles.iconFontPrimaryBalanceSmallPascal(context), ), TextSpan(text: " ", style: TextStyle(fontSize: 6)), TextSpan( text: this.feeText, style: AppStyles.primarySmall600(context)), ]), ), ); } } ================================================ FILE: lib/ui/widgets/operation_list_item.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:quiver/strings.dart'; enum OperationType { Received, Sent, NameChanged, ListedForSale, DelistedForSale, Welcome } /// A widget for displaying a mnemonic phrase class OperationListItem extends StatefulWidget { final OperationType type; final String amount; final String address; final String date; final String payload; final Function onPressed; final String name; final String price; final bool isContact; OperationListItem({ this.type, this.amount, this.address, this.date, this.payload, this.onPressed, this.name, this.price, this.isContact = false }); _OperationListItemState createState() => _OperationListItemState(); } class _OperationListItemState extends State { @override Widget build(BuildContext context) { return Column(children: [ Container( width: double.maxFinite, height: widget.type == OperationType.Welcome ? null : 74, child: FlatButton( padding: EdgeInsetsDirectional.only(start: 24, end: 24), onPressed: () { if (widget.onPressed != null) { widget.onPressed(); } return; }, splashColor: StateContainer.of(context).curTheme.primary30, highlightColor: StateContainer.of(context).curTheme.primary15, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)), child: widget.type == OperationType.Welcome ? Container( margin: EdgeInsetsDirectional.fromSTEB(30, 16, 30, 16), child: AutoSizeText.rich( TextSpan( children: formatLocalizedColors(context, AppLocalization.of(context).newAccountParagraph)), stepGranularity: 0.5, maxLines: 3, minFontSize: 8, textAlign: TextAlign.center, style: TextStyle(fontSize: 14), ), ) : Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Operation Type Container( padding: EdgeInsetsDirectional.fromSTEB(8, 4, 8, 4), decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: widget.type == OperationType.Received ? StateContainer.of(context).curTheme.primary : widget.type == OperationType.Sent ? StateContainer.of(context) .curTheme .textDark : StateContainer.of(context) .curTheme .secondary), child: AutoSizeText( widget.type == OperationType.Received ? AppLocalization.of(context).receivedHeader : widget.type == OperationType.Sent ? AppLocalization.of(context).sentHeader : widget.type == OperationType.NameChanged ? AppLocalization.of(context) .nameChangedHeader : widget.type == OperationType.ListedForSale ? AppLocalization.of(context) .listedForSaleHeader : widget.type == OperationType .DelistedForSale ? AppLocalization.of(context) .delistedHeader : AppLocalization.of(context) .undefinedHeader, style: AppStyles.operationType(context), ), ), // Amount & Payload indicator or New Account Name widget.type == OperationType.DelistedForSale ? SizedBox() : Container( width: MediaQuery.of(context).size.width / 2 - 72, margin: EdgeInsetsDirectional.only(top: 4), child: widget.type == OperationType.Received || widget.type == OperationType.Sent || widget.type == OperationType.ListedForSale ? AutoSizeText.rich( TextSpan( children: [ TextSpan( text: "", style: widget.type == OperationType.Received ? AppStyles .iconFontPrimaryBalanceSmallPascal( context) : widget.type == OperationType.Sent ? AppStyles .iconFontTextDarkBalanceSmallPascal( context) : AppStyles .iconFontSecondarySmallPascal( context), ), TextSpan( text: " ", style: TextStyle(fontSize: 7)), TextSpan( text: widget.type == OperationType .Received || widget.type == OperationType.Sent ? widget.amount : widget.price, style: widget.type == OperationType.Received ? AppStyles.balanceSmall( context) : widget.type == OperationType.Sent ? AppStyles .balanceSmallTextDark( context) : AppStyles .balanceSmallSecondary( context)), isNotEmpty(widget.payload) ? TextSpan( text: " ", style: TextStyle(fontSize: 14)) : TextSpan(), isNotEmpty(widget.payload) ? TextSpan( text: "", style: widget.type == OperationType .Received ? AppStyles .iconFontPrimaryBalanceSmallest( context) : AppStyles .iconFontTextDarkBalanceSmallest( context), ) : TextSpan(), ], ), textAlign: TextAlign.start, maxLines: 1, minFontSize: 4, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ) : widget.type == OperationType.NameChanged ? AutoSizeText( widget.name, textAlign: TextAlign.start, maxLines: 1, minFontSize: 4, stepGranularity: 1, style: AppStyles.balanceSmallSecondary( context), ) : SizedBox(), ), ], ), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ widget.type == OperationType.Received || widget.type == OperationType.Sent ? // Address widget.isContact ? Container( width: MediaQuery.of(context).size.width / 2 - 72, margin: EdgeInsetsDirectional.only(bottom: 4), alignment: Alignment(1, 0), child: AutoSizeText.rich( TextSpan(children: [ TextSpan( text: " ", style: AppStyles.iconFontPrimarySmall( context), ), TextSpan( text: widget.address, style: AppStyles.contactsItemName( context), ), ]), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.end, style: TextStyle( fontSize: 14, ), ), ) : Container( width: MediaQuery.of(context).size.width / 2 - 72, margin: EdgeInsetsDirectional.only(bottom: 4), alignment: Alignment(1, 0), child: AutoSizeText( widget.address, style: AppStyles.monoTextDarkSmall400( context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.end, ), ) : SizedBox(), // Date Container( width: MediaQuery.of(context).size.width / 2 - 72, alignment: Alignment(1, 0), child: AutoSizeText( widget.date, style: AppStyles.operationDate(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.end, ), ), ], ), ], ), ), ), Container( width: double.maxFinite, height: 1, color: StateContainer.of(context).curTheme.textDark10, ) ]); } } ================================================ FILE: lib/ui/widgets/overlay_dialog.dart ================================================ import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/buttons.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; /// An overlay that supports a list of options as well as text/confirm actions class DialogOverlay extends StatefulWidget { final String title; final List optionsList; final bool warningStyle; final TextSpan body; final String confirmButtonText; final Function onConfirm; final bool feeDialog; final bool singleButton; DialogOverlay( {this.title, this.optionsList, this.body, this.confirmButtonText, this.onConfirm, this.warningStyle = false, this.singleButton = false, this.feeDialog = false}); @override State createState() => _DialogOverlayState(); } class _DialogOverlayState extends State with SingleTickerProviderStateMixin { AnimationController _controller; Animation _scaleAnimation; Animation _opacityAnimation; @override void initState() { super.initState(); _controller = AnimationController(vsync: this, duration: Duration(milliseconds: 200)); _scaleAnimation = Tween(begin: 0.75, end: 1.0).animate( CurvedAnimation(parent: _controller, curve: Curves.decelerate)); _opacityAnimation = Tween(begin: 0.25, end: 1.0).animate( CurvedAnimation(parent: _controller, curve: Curves.decelerate)); _controller.addListener(() { setState(() {}); }); _controller.forward(); } @override void dispose() { _controller?.dispose(); super.dispose(); } Widget buildListItems(List optionsList) { List widgets = []; for (var option in optionsList) { widgets.add( // Single Option Container( width: double.maxFinite, height: 50, child: FlatButton( onPressed: () { if (option.action == null || option.disabled) { return; } option.action(); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0), ), padding: EdgeInsets.all(0), child: Container( alignment: Alignment(-1, 0), margin: EdgeInsetsDirectional.only(start: 24, end: 24), child: AutoSizeText( option.option, style: option.disabled ? AppStyles.paragraphBigDisabled(context) : AppStyles.paragraphBig(context), textAlign: TextAlign.start, maxLines: 1, stepGranularity: 0.1, ), )), ), ); } return Column(children: widgets); } @override Widget build(BuildContext context) { return Center( child: Material( color: Colors.transparent, child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: FadeTransition( opacity: _opacityAnimation, child: ScaleTransition( scale: _scaleAnimation, child: Container( margin: EdgeInsetsDirectional.only(start: 20, end: 20), constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.8, maxWidth: 280, ), decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: StateContainer.of(context).curTheme.shadow50, offset: Offset(0, 30), blurRadius: 60, spreadRadius: -10, ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(12), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: double.maxFinite, decoration: BoxDecoration( gradient: widget.warningStyle ? null : StateContainer.of(context) .curTheme .gradientPrimary, color: widget.warningStyle ? StateContainer.of(context).curTheme.danger : null, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12)), ), child: Container( margin: EdgeInsetsDirectional.fromSTEB(24, 16, 24, 16), child: AutoSizeText( widget.title, style: AppStyles.modalHeader(context), maxLines: 1, stepGranularity: 0.1, ), ), ), // Options container widget.body != null ? Column( children: [ Container( margin: EdgeInsetsDirectional.fromSTEB( 24, 16, 24, 16), child: Column( children: [ AutoSizeText.rich( widget.body, stepGranularity: 0.1, maxLines: 8, minFontSize: 8, ) ], ), ), Row( children: [ AppButton( type: widget.feeDialog || widget.singleButton ? AppButtonType.Primary : AppButtonType.Danger, text: widget.confirmButtonText, buttonTop: widget.singleButton ? false : true, onPressed: () { if (widget.onConfirm != null) { widget.onConfirm(); } }, ), ], ), !widget.singleButton ? Row( children: [ AppButton( type: widget.feeDialog ? AppButtonType.PrimaryOutline : AppButtonType.DangerOutline, text: toUppercase( AppLocalization.of(context) .cancelButton, context), onPressed: () { Navigator.of(context).pop(); }, ), ], ) : SizedBox(), ], ) : Container( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.6 - 60, minHeight: 0), // Options list child: SingleChildScrollView( padding: EdgeInsetsDirectional.only( top: 8, bottom: 8), child: buildListItems(widget.optionsList), ), ), ], ), ), ), ), ), ), ), ); } } class DialogListItem { final String option; final Function action; final bool disabled; DialogListItem({@required this.option, this.action, this.disabled = false}); } /// Modified dialog function from flutter source. Modified for the backdrop blur effect Widget _buildMaterialDialogTransitions( BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { return FadeTransition( opacity: CurvedAnimation( parent: animation, curve: Curves.easeOut, ), child: child, ); } Future showAppDialog({ @required BuildContext context, bool barrierDismissible = true, @Deprecated( 'Instead of using the "child" argument, return the child from a closure ' 'provided to the "builder" argument. This will ensure that the BuildContext ' 'is appropriate for widgets built in the dialog.') Widget child, WidgetBuilder builder, }) { assert(child == null || builder == null); assert(debugCheckHasMaterialLocalizations(context)); final ThemeData theme = Theme.of(context); return showGeneralDialog( context: context, pageBuilder: (BuildContext buildContext, Animation animation, Animation secondaryAnimation) { final Widget pageChild = child ?? Builder(builder: builder); return SafeArea( child: Builder(builder: (BuildContext context) { return theme != null ? Theme(data: theme, child: pageChild) : pageChild; }), ); }, barrierDismissible: barrierDismissible, barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, barrierColor: StateContainer.of(context).curTheme.overlay10, transitionDuration: const Duration(milliseconds: 0), transitionBuilder: _buildMaterialDialogTransitions, ); } ================================================ FILE: lib/ui/widgets/payload.dart ================================================ import 'dart:async'; import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/events.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/app_icons.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/app_text_field.dart'; import 'package:blaise_wallet_flutter/ui/widgets/overlay_dialog.dart'; import 'package:blaise_wallet_flutter/ui/widgets/tap_outside_unfocus.dart'; import 'package:blaise_wallet_flutter/util/user_data_util.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:quiver/strings.dart'; import 'package:event_taxi/event_taxi.dart'; class Payload extends StatefulWidget { final Function onPayloadChanged; final String initialPayload; final bool allowEncryption; Payload( {@required this.onPayloadChanged, this.initialPayload = "", this.allowEncryption = true}) : super(); @override _PayloadState createState() => _PayloadState(); } class _PayloadState extends State { bool _hasPayload; String _payload; bool _encrypted; StreamSubscription _payloadSub; void _registerBus() { _payloadSub = EventTaxiImpl.singleton() .registerTo() .listen((event) { setState(() { _payload = event.payload; _encrypted = false; _hasPayload = isNotEmpty(event.payload); }); }); } void _destroyBus() { if (_payloadSub != null) { _payloadSub.cancel(); } } @override void initState() { super.initState(); _registerBus(); this._hasPayload = isNotEmpty(widget.initialPayload); this._payload = widget.initialPayload; this._encrypted = false; } @override void dispose() { _destroyBus(); super.dispose(); } void handlePayloadChange(String newPayload, bool encrypted) { if (isNotEmpty(newPayload) && mounted) { widget.onPayloadChanged(newPayload, encrypted); setState(() { _payload = newPayload; _hasPayload = true; _encrypted = encrypted; }); } else if (mounted) { widget.onPayloadChanged(newPayload, encrypted); setState(() { _payload = ""; _hasPayload = false; _encrypted = encrypted; }); } } @override Widget build(BuildContext context) { // Payload text and edit button return this._hasPayload ? Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Container for the payload Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 110), margin: EdgeInsetsDirectional.fromSTEB(30, 20, 12, 0), padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( width: 1, color: StateContainer.of(context).curTheme.textDark15), color: StateContainer.of(context).curTheme.textDark10, ), child: Row(mainAxisSize: MainAxisSize.min, children: [ Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - (_encrypted ? 151 : 136)), child: AutoSizeText( this._payload, maxLines: 3, stepGranularity: 0.1, minFontSize: 6, textAlign: TextAlign.left, style: AppStyles.paragraphMedium(context), ), ), _encrypted ? Container( alignment: Alignment.center, margin: EdgeInsetsDirectional.only(start: 3.0), child: Icon(FontAwesomeIcons.lock, size: 12, color: StateContainer.of(context) .curTheme .textDark)) : SizedBox() ])), // Container for the edit button Container( margin: EdgeInsetsDirectional.fromSTEB(0, 20, 30, 0), child: TextFieldButton( icon: Icons.edit, onPressed: () async { await showAppDialog( context: context, builder: (_) => PayloadDialog( initialPayload: _payload, encrypted: _encrypted, onPayloadChanged: (newPayload, encrypted) { handlePayloadChange(newPayload, encrypted); }, )); }, )) ], ) : // "Add Payload" button Row( children: [ Container( height: 40.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100.0), color: StateContainer.of(context).curTheme.backgroundPrimary, boxShadow: [ StateContainer.of(context).curTheme.shadowTextDark, ], ), margin: EdgeInsetsDirectional.fromSTEB(30, 20, 30, 40), child: FlatButton( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(100.0)), child: AutoSizeText( AppLocalization.of(context).addAPayloadButton, textAlign: TextAlign.center, maxLines: 1, stepGranularity: 0.1, style: AppStyles.buttonMiniBg(context), ), onPressed: () async { await showAppDialog( context: context, builder: (_) => PayloadDialog( onPayloadChanged: (newPayload, encrypted) { handlePayloadChange(newPayload, encrypted); }, allowEncryption: widget.allowEncryption, )); }, ), ), ], ); } } /// An overlay that supports a list of options as well as text/confirm actions class PayloadDialog extends StatefulWidget { final Function onPayloadChanged; final String initialPayload; final bool encrypted; final bool allowEncryption; PayloadDialog( {@required this.onPayloadChanged, this.initialPayload = "", this.encrypted = false, this.allowEncryption = true}) : super(); @override _PayloadDialogState createState() => _PayloadDialogState(); } class _PayloadDialogState extends State with SingleTickerProviderStateMixin { AnimationController _controller; Animation _scaleAnimation; Animation _opacityAnimation; TextEditingController payloadController; bool _encrypted; @override void initState() { super.initState(); // Set initial value this.payloadController = TextEditingController(); this.payloadController.text = widget.initialPayload; this._encrypted = widget.encrypted; _controller = AnimationController(vsync: this, duration: Duration(milliseconds: 200)); _scaleAnimation = Tween(begin: 0.75, end: 1.0).animate( CurvedAnimation(parent: _controller, curve: Curves.decelerate)); _opacityAnimation = Tween(begin: 0.25, end: 1.0).animate( CurvedAnimation(parent: _controller, curve: Curves.decelerate)); _controller.addListener(() { setState(() {}); }); _controller.forward(); } @override void dispose() { _controller?.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return TapOutsideUnfocus( child: Align( alignment: Alignment(0, 1), child: Material( color: Colors.transparent, child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: FadeTransition( opacity: _opacityAnimation, child: ScaleTransition( scale: _scaleAnimation, child: AnimatedContainer( duration: Duration(milliseconds: 200), curve: Curves.easeOutQuad, margin: EdgeInsetsDirectional.only( start: 20, end: 20, bottom: MediaQuery.of(context).viewInsets.bottom + 50), constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.4, maxWidth: MediaQuery.of(context).size.width - 40), decoration: BoxDecoration( color: StateContainer.of(context) .curTheme .backgroundPrimary, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: StateContainer.of(context).curTheme.shadow50, offset: Offset(0, 30), blurRadius: 60, spreadRadius: -10, ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(12), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.6 - 60, minHeight: 0), // Options list child: Column( mainAxisSize: MainAxisSize.min, children: [ // Container for the amount text field Container( margin: EdgeInsetsDirectional.fromSTEB( 20, 24, 20, 0), child: AppTextField( label: AppLocalization.of(context) .payloadTextFieldHeader, controller: payloadController, style: AppStyles.paragraph(context), maxLines: 1, firstButton: TextFieldButton( icon: AppIcons.paste, onPressed: () { Clipboard.getData("text/plain") .then((clipboardData) { if (clipboardData.text.length <= 80) { widget.onPayloadChanged( clipboardData.text, _encrypted); payloadController.text = clipboardData.text; } }); }), secondButton: TextFieldButton( icon: AppIcons.scan, onPressed: () async { String text = await UserDataUtil.getQRData( DataType.RAW, StateContainer.of(context).curTheme.scannerTheme); if (text != null && text.length <= 80) { widget.onPayloadChanged( text, _encrypted); payloadController.text = text; } }), onChanged: (payload) { widget.onPayloadChanged( payload, _encrypted); }, inputFormatters: [ LengthLimitingTextInputFormatter(80) ], ), ), widget.allowEncryption ? Row( children: [ Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context) .size .width - 152), margin: EdgeInsetsDirectional .fromSTEB(20, 16, 0, 20), child: AutoSizeText( AppLocalization.of(context) .encryptPayloadHeader, style: AppStyles .textFieldLabel( context), minFontSize: 8, stepGranularity: 0.1, maxLines: 1, )), Container( margin: EdgeInsetsDirectional .fromSTEB(4, 16, 30, 18), child: Switch( value: _encrypted, onChanged: (bool) { widget.onPayloadChanged( this .payloadController .text, !_encrypted); setState(() { _encrypted = !_encrypted; }); }, inactiveThumbColor: StateContainer.of(context) .curTheme .switchKnob, inactiveTrackColor: StateContainer.of(context) .curTheme .switchTrack, activeColor: StateContainer.of(context) .curTheme .primary, ), ), ], ) : SizedBox(height: 34), ], )), ], ), ), ), ), ), ), ))); } } ================================================ FILE: lib/ui/widgets/pin_screen.dart ================================================ import 'dart:math'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/util/haptic_util.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; enum PinOverlayType { NEW_PIN, ENTER_PIN } class ShakeCurve extends Curve { @override double transform(double t) { //t from 0.0 to 1.0 return sin(t * 3 * pi); } } class PinScreen extends StatefulWidget { final PinOverlayType type; final String expectedPin; final String description; final Function onSuccess; PinScreen( {@required this.type, @required this.onSuccess, this.description = "", this.expectedPin = ""}); @override _PinScreenState createState() => _PinScreenState(); } class _PinScreenState extends State with SingleTickerProviderStateMixin { static const int MAX_ATTEMPTS = 5; static const int PIN_LENGTH = 6; String pinEnterTitle = ""; String pinCreateTitle = ""; // Stateful data List _dotStates; String _pin; String _pinConfirmed; bool _awaitingConfirmation; // true if pin has been entered once, false if not entered once String _header; int _failedAttempts = 0; // Invalid animation AnimationController _controller; Animation _animation; @override void initState() { super.initState(); // Initialize list all empty if (widget.type == PinOverlayType.ENTER_PIN) { _header = pinEnterTitle; } else { _header = pinCreateTitle; } _dotStates = List.filled(PIN_LENGTH, FontAwesomeIcons.circle); _awaitingConfirmation = false; _pin = ""; _pinConfirmed = ""; // Get adjusted failed attempts sl.get().getLockAttempts().then((attempts) { setState(() { _failedAttempts = attempts % MAX_ATTEMPTS; }); }); // Set animation _controller = AnimationController( duration: const Duration(milliseconds: 350), vsync: this); final Animation curve = CurvedAnimation(parent: _controller, curve: ShakeCurve()); _animation = Tween(begin: 0.0, end: 25.0).animate(curve) ..addStatusListener((status) { if (status == AnimationStatus.completed) { if (widget.type == PinOverlayType.ENTER_PIN) { sl.get().incrementLockAttempts().then((_) { _failedAttempts++; if (_failedAttempts >= MAX_ATTEMPTS) { setState(() { _controller.value = 0; }); sl.get().updateLockDate().then((_) { Navigator.of(context).pushNamedAndRemoveUntil( '/lock_screen', (Route route) => false); }); } else { setState(() { _pin = ""; _header = AppLocalization.of(context).invalidPINParagraph; _dotStates = List.filled(PIN_LENGTH, FontAwesomeIcons.circle); _controller.value = 0; }); } }); } else { setState(() { _awaitingConfirmation = false; _dotStates = List.filled(PIN_LENGTH, FontAwesomeIcons.circle); _pin = ""; _pinConfirmed = ""; _header = AppLocalization.of(context).noMatchPINParagraph; _controller.value = 0; }); } } }) ..addListener(() { setState(() { // the animation object’s value is the changed state }); }); } /// Set next character in the pin set /// return true if all characters entered bool _setCharacter(String character) { if (_awaitingConfirmation) { setState(() { _pinConfirmed = _pinConfirmed + character; }); } else { setState(() { _pin = _pin + character; }); } for (int i = 0; i < _dotStates.length; i++) { if (_dotStates[i] == FontAwesomeIcons.circle) { setState(() { _dotStates[i] = FontAwesomeIcons.solidCircle; }); break; } } if (_dotStates.last == FontAwesomeIcons.solidCircle) { return true; } return false; } void _backSpace() { if (_dotStates[0] != FontAwesomeIcons.circle) { int lastFilledIndex; for (int i = 0; i < _dotStates.length; i++) { if (_dotStates[i] == FontAwesomeIcons.solidCircle) { if (i == _dotStates.length || _dotStates[i + 1] == FontAwesomeIcons.circle) { lastFilledIndex = i; break; } } } setState(() { _dotStates[lastFilledIndex] = FontAwesomeIcons.circle; if (_awaitingConfirmation) { _pinConfirmed = _pinConfirmed.substring(0, _pinConfirmed.length - 1); } else { _pin = _pin.substring(0, _pin.length - 1); } }); } } Widget buildPINButton(String text) { return Container( width: MediaQuery.of(context).size.width / 4, height: MediaQuery.of(context).size.width / 4, child: text == "" ? SizedBox() : InkWell( // Real tap function onTapDown: (details) { if (_controller.status == AnimationStatus.forward || _controller.status == AnimationStatus.reverse) { return; } if (text == "-") { _backSpace(); } else { if (_setCharacter(text)) { // Mild delay so they can actually see the last dot get filled Future.delayed(Duration(milliseconds: 50), () { if (widget.type == PinOverlayType.ENTER_PIN) { // Pin is not what was expected if (_pin != widget.expectedPin) { HapticUtil.error(); _controller.forward(); } else { sl .get() .resetLockAttempts() .then((_) { widget.onSuccess(_pin); }); } } else { if (!_awaitingConfirmation) { // Switch to confirm pin setState(() { _awaitingConfirmation = true; _dotStates = List.filled( PIN_LENGTH, FontAwesomeIcons.circle); _header = AppLocalization.of(context).confirmPINParagraph; }); } else { // First and second pins match if (_pin == _pinConfirmed) { widget.onSuccess(_pin); } else { HapticUtil.error(); _controller.forward(); } } } }); } } }, // For the splash effect onTap: () {}, borderRadius: BorderRadius.circular(MediaQuery.of(context).size.width / 8), child: Center( child: text == "-" ? Icon( Icons.backspace, color: StateContainer.of(context).curTheme.primary, ) : Text( text, style: AppStyles.pinNumberPad(context), ), ), ), ); } List buildPINScreenDots() { List ret = List(); for (int i = 0; i < PIN_LENGTH; i++) { ret.add( Container( margin: EdgeInsetsDirectional.fromSTEB(4, 0, 4, 0), child: Icon(_dotStates[i], size: 18, color: StateContainer.of(context).curTheme.backgroundPrimary), ), ); } return ret; } @override Widget build(BuildContext context) { if (pinEnterTitle.isEmpty) { setState(() { pinEnterTitle = AppLocalization.of(context).enterPINParagraph; if (widget.type == PinOverlayType.ENTER_PIN) { _header = pinEnterTitle; } }); } if (pinCreateTitle.isEmpty) { setState(() { pinCreateTitle = AppLocalization.of(context).createPINParagraph; if (widget.type == PinOverlayType.NEW_PIN) { _header = pinCreateTitle; } }); } // Main scaffold that holds everything return Scaffold( resizeToAvoidBottomInset: false, backgroundColor: StateContainer.of(context).curTheme.backgroundPrimary, // A Stack to achive the overlap between background gradient and number pad body: LayoutBuilder( builder: (context, constraints) => Stack( children: [ // Pin Text & Dots Container( height: MediaQuery.of(context).size.height - (MediaQuery.of(context).size.width * 5 / 4), width: double.maxFinite, padding: EdgeInsetsDirectional.fromSTEB( 0, MediaQuery.of(context).padding.top, 0, MediaQuery.of(context).padding.top), decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // PIN header Container( margin: EdgeInsetsDirectional.fromSTEB(30, 0, 30, 0), child: AutoSizeText( _header, style: AppStyles.modalHeader(context), textAlign: TextAlign.center, maxLines: 1, minFontSize: 10, stepGranularity: 0.1, ), ), // PIN description widget.description == null ? Container( margin: EdgeInsetsDirectional.fromSTEB(30, 4, 30, 0), child: AutoSizeText( widget.description, textAlign: TextAlign.center, style: AppStyles.pinDescription(context), maxLines: 2, minFontSize: 10, stepGranularity: 0.1, ), ) : SizedBox(), // Dots Container( margin: EdgeInsetsDirectional.only( start: MediaQuery.of(context).size.width * 0.25 + _animation.value, end: MediaQuery.of(context).size.width * 0.25 - _animation.value, top: 16), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: buildPINScreenDots(), ), ), ], ), ), ), // Number Pad Align( alignment: Alignment.bottomCenter, child: Container( height: MediaQuery.of(context).size.width * 5 / 4 + 16, width: double.maxFinite, padding: EdgeInsetsDirectional.fromSTEB( MediaQuery.of(context).size.width * 0.075, MediaQuery.of(context).size.width * 0.075, MediaQuery.of(context).size.width * 0.075, MediaQuery.of(context).padding.bottom + MediaQuery.of(context).size.width * 0.075), decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), boxShadow: [ StateContainer.of(context).curTheme.shadowBottomBar, ], ), child: Material( color: Colors.transparent, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ buildPINButton("1"), buildPINButton("2"), buildPINButton("3"), ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ buildPINButton("4"), buildPINButton("5"), buildPINButton("6"), ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ buildPINButton("7"), buildPINButton("8"), buildPINButton("9"), ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ buildPINButton(""), buildPINButton("0"), buildPINButton("-"), ], ), ], ), ), ), ), ), ], ), ), ); } } ================================================ FILE: lib/ui/widgets/placeholder_account_card.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:flutter/material.dart'; /// A widget for buttons class PlaceholderAccountCard extends StatefulWidget { _PlaceholderAccountCardState createState() => _PlaceholderAccountCardState(); } class _PlaceholderAccountCardState extends State { @override Widget build(BuildContext context) { // Account Card return Container( width: double.maxFinite, margin: EdgeInsetsDirectional.fromSTEB(12, 5, 12, 5), height: 70, decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.circular(12), boxShadow: [StateContainer.of(context).curTheme.shadowAccountCard]), child: Stack( children: [ // Left gradient background Container( height: double.maxFinite, width: MediaQuery.of(context).size.width * 0.47 - 24, decoration: BoxDecoration( gradient: StateContainer.of(context).curTheme.gradientPrimary, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), bottomLeft: Radius.circular(12))), ), FlatButton( onPressed: () { return null; }, padding: EdgeInsets.all(0), shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(12)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Left part of the card with account name and number Container( height: double.maxFinite, width: MediaQuery.of(context).size.width * 0.47 - 24, padding: EdgeInsetsDirectional.only(start: 16, end: 16), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( margin: EdgeInsetsDirectional.only(bottom: 5), decoration: BoxDecoration( color: StateContainer.of(context).curTheme.textLight.withOpacity(0.75), borderRadius: BorderRadius.circular(100), ), child: AutoSizeText( " ", style: AppStyles.accountCardName(context), textAlign: TextAlign.left, stepGranularity: 0.5, maxLines: 1, minFontSize: 8, ), ), Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.textLight.withOpacity(0.75), borderRadius: BorderRadius.circular(100), ), child: AutoSizeText( " ", style: AppStyles.accountCardAddress(context), textAlign: TextAlign.left, stepGranularity: 0.5, maxLines: 1, ), ) ], ), ), // Right part of the card with balance Container( height: double.maxFinite, width: MediaQuery.of(context).size.width * 0.53 - 24, padding: EdgeInsetsDirectional.only(end: 16), alignment: Alignment(1, 0), child: Container( decoration: BoxDecoration( color: StateContainer.of(context).curTheme.primary.withOpacity(0.75), borderRadius: BorderRadius.circular(100), ), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: " ", style: AppStyles.balanceMedium(context)), ], ), textAlign: TextAlign.end, maxLines: 1, minFontSize: 4, stepGranularity: 1, style: TextStyle( fontSize: 18, ), ), ), ), ], ), ), ], ), ); } } ================================================ FILE: lib/ui/widgets/placeholder_operation_list_item.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:flutter/material.dart'; enum PlaceholderOperationType { Received, Sent, NameChanged, ListedForSale, Welcome } /// A widget for displaying a mnemonic phrase class PlaceholderOperationListItem extends StatefulWidget { final PlaceholderOperationType type; PlaceholderOperationListItem({ this.type, }); _PlaceholderOperationListItemState createState() => _PlaceholderOperationListItemState(); } class _PlaceholderOperationListItemState extends State { @override Widget build(BuildContext context) { return Column(children: [ Container( width: double.maxFinite, height: widget.type == PlaceholderOperationType.Welcome ? null : 74, child: FlatButton( padding: EdgeInsetsDirectional.only(start: 24, end: 24), onPressed: () { return null; }, splashColor: StateContainer.of(context).curTheme.primary30, highlightColor: StateContainer.of(context).curTheme.primary15, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Operation Type Container( padding: EdgeInsetsDirectional.fromSTEB(8, 4, 8, 4), decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: widget.type == PlaceholderOperationType.Received ? StateContainer.of(context) .curTheme .primary .withOpacity(0.75) : widget.type == PlaceholderOperationType.Sent ? StateContainer.of(context) .curTheme .textDark .withOpacity(0.75) : StateContainer.of(context) .curTheme .secondary .withOpacity(0.75)), child: AutoSizeText( widget.type == PlaceholderOperationType.Received ? " " : widget.type == PlaceholderOperationType.Sent ? " " : widget.type == PlaceholderOperationType.NameChanged ? " " : widget.type == PlaceholderOperationType.ListedForSale ? " " : " ", style: AppStyles.operationType(context), ), ), // Amount & Payload indicator or New Account Name Container( width: MediaQuery.of(context).size.width / 2 - 72, margin: EdgeInsetsDirectional.only(top: 4), child: widget.type == PlaceholderOperationType.Received || widget.type == PlaceholderOperationType.Sent || widget.type == PlaceholderOperationType.NameChanged || widget.type == PlaceholderOperationType.ListedForSale ? Align( alignment: Alignment(-1, 0), child: Container( decoration: BoxDecoration( color: widget.type == PlaceholderOperationType.Received ? StateContainer.of(context) .curTheme .primary .withOpacity(0.5) : widget.type == PlaceholderOperationType.Sent ? StateContainer.of(context) .curTheme .textDark .withOpacity(0.5) : StateContainer.of(context) .curTheme .secondary .withOpacity(0.5), borderRadius: BorderRadius.circular(100)), child: AutoSizeText.rich( TextSpan( children: [ TextSpan( text: " ", style: AppStyles .iconFontTextDarkBalanceSmallPascal( context)), TextSpan( text: " ", style: TextStyle(fontSize: 7)), TextSpan( text: " ", style: widget.type == PlaceholderOperationType .Received ? AppStyles.balanceSmall(context) : widget.type == PlaceholderOperationType .Sent ? AppStyles .balanceSmallTextDark( context) : AppStyles .balanceSmallSecondary( context)), ], ), textAlign: TextAlign.start, maxLines: 1, minFontSize: 4, stepGranularity: 1, style: TextStyle( fontSize: 14, ), ), ), ) : SizedBox(), ), ], ), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ widget.type == PlaceholderOperationType.Received || widget.type == PlaceholderOperationType.Sent ? // Address Container( width: MediaQuery.of(context).size.width / 2 - 72, alignment: Alignment(1, 0), child: Container( decoration: BoxDecoration( color: StateContainer.of(context) .curTheme .textDark .withOpacity(0.25), borderRadius: BorderRadius.circular(100), ), child: AutoSizeText( " ", style: AppStyles.contactsItemAddress(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.end, ), )) : SizedBox(), // Date Container( width: MediaQuery.of(context).size.width / 2 - 72, margin: EdgeInsetsDirectional.only(top: 4), alignment: Alignment(1, 0), child: Container( decoration: BoxDecoration( color: StateContainer.of(context) .curTheme .textDark .withOpacity(0.125), borderRadius: BorderRadius.circular(100), ), child: AutoSizeText( " ", style: AppStyles.operationDate(context), maxLines: 1, stepGranularity: 0.1, textAlign: TextAlign.end, ), ), ), ], ), ], ), ), ), Container( width: double.maxFinite, height: 1, color: StateContainer.of(context).curTheme.textDark10, ) ]); } } ================================================ FILE: lib/ui/widgets/reactive_refresh.dart ================================================ // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:async'; import 'dart:math' as math; import 'package:flutter/widgets.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; // The over-scroll distance that moves the indicator to its maximum // displacement, as a percentage of the scrollable's container extent. const double _kDragContainerExtentPercentage = 0.25; // How much the scroll's drag gesture can overshoot the RefreshIndicator's // displacement; max displacement = _kDragSizeFactorLimit * displacement. const double _kDragSizeFactorLimit = 1.5; // When the scroll ends, the duration of the refresh indicator's animation // to the RefreshIndicator's displacement. const Duration _kIndicatorSnapDuration = Duration(milliseconds: 150); // The duration of the ScaleTransition that starts when the refresh action // has completed. const Duration _kIndicatorScaleDuration = Duration(milliseconds: 200); /// The signature for a function that's called when the user has dragged a /// [RefreshIndicator] far enough to demonstrate that they want the app to /// refresh. The returned [Future] must complete when the refresh operation is /// finished. /// /// Used by [RefreshIndicator.onRefresh]. typedef RefreshCallback = Future Function(); // The state machine moves through these modes only when the scrollable // identified by scrollableKey has been scrolled to its min or max limit. enum _RefreshIndicatorMode { drag, // Pointer is down. armed, // Dragged far enough that an up event will run the onRefresh callback. snap, // Animating to the indicator's final "displacement". refresh, // Running the refresh callback. done, // Animating the indicator's fade-out after refreshing. canceled, // Animating the indicator's fade-out after not arming. } /// A widget that supports the Material "swipe to refresh" idiom. /// /// When the child's [Scrollable] descendant overscrolls, an animated circular /// progress indicator is faded into view. When the scroll ends, if the /// indicator has been dragged far enough for it to become completely opaque, /// the [onRefresh] callback is called. The callback is expected to update the /// scrollable's contents and then complete the [Future] it returns. The refresh /// indicator disappears after the callback's [Future] has completed. /// /// If the [Scrollable] might not have enough content to overscroll, consider /// settings its `physics` property to [AlwaysScrollableScrollPhysics]: /// /// ```dart /// ListView( /// physics: const AlwaysScrollableScrollPhysics(), /// children: ... // ) /// ``` /// /// Using [AlwaysScrollableScrollPhysics] will ensure that the scroll view is /// always scrollable and, therefore, can trigger the [RefreshIndicator]. /// /// A [RefreshIndicator] can only be used with a vertical scroll view. /// /// See also: /// /// * /// * [RefreshIndicatorState], can be used to programmatically show the refresh indicator. /// * [RefreshProgressIndicator], widget used by [RefreshIndicator] to show /// the inner circular progress spinner during refreshes. /// * [CupertinoSliverRefreshControl], an iOS equivalent of the pull-to-refresh pattern. /// Must be used as a sliver inside a [CustomScrollView] instead of wrapping /// around a [ScrollView] because it's a part of the scrollable instead of /// being overlaid on top of it. class ReactiveRefreshIndicator extends StatefulWidget { /// Creates a refresh indicator. /// /// The [onRefresh], [child], and [notificationPredicate] arguments must be /// non-null. The default /// [displacement] is 40.0 logical pixels. /// /// The [semanticsLabel] is used to specify an accessibility label for this widget. /// If it is null, it will be defaulted to [MaterialLocalizations.refreshIndicatorSemanticLabel]. /// An empty string may be passed to avoid having anything read by screen reading software. /// The [semanticsValue] may be used to specify progress on the widget. The const ReactiveRefreshIndicator({ Key key, @required this.child, this.displacement = 40.0, @required this.onRefresh, @required this.isRefreshing, this.color, this.backgroundColor, this.notificationPredicate = defaultScrollNotificationPredicate, this.semanticsLabel, this.semanticsValue, }) : assert(child != null), assert(onRefresh != null), assert(isRefreshing != null), assert(notificationPredicate != null), super(key: key); /// The widget below this widget in the tree. /// /// The refresh indicator will be stacked on top of this child. The indicator /// will appear when child's Scrollable descendant is over-scrolled. /// /// Typically a [ListView] or [CustomScrollView]. final Widget child; /// The distance from the child's top or bottom edge to where the refresh /// indicator will settle. During the drag that exposes the refresh indicator, /// its actual displacement may significantly exceed this value. final double displacement; /// A function that's called when the user has dragged the refresh indicator /// far enough to demonstrate that they want the app to refresh. The returned /// [Future] must complete when the refresh operation is finished. final RefreshCallback onRefresh; /// The progress indicator's foreground color. The current theme's /// [ThemeData.accentColor] by default. final Color color; /// The progress indicator's background color. The current theme's /// [ThemeData.canvasColor] by default. final Color backgroundColor; /// A check that specifies whether a [ScrollNotification] should be /// handled by this widget. /// /// By default, checks whether `notification.depth == 0`. Set it to something /// else for more complicated layouts. final ScrollNotificationPredicate notificationPredicate; /// {@macro flutter.material.progressIndicator.semanticsLabel} /// /// This will be defaulted to [MaterialLocalizations.refreshIndicatorSemanticLabel] /// if it is null. final String semanticsLabel; /// {@macro flutter.material.progressIndicator.semanticsValue} final String semanticsValue; final bool isRefreshing; @override ReactiveRefreshIndicatorState createState() => ReactiveRefreshIndicatorState(); } /// Contains the state for a [RefreshIndicator]. This class can be used to /// programmatically show the refresh indicator, see the [show] method. class ReactiveRefreshIndicatorState extends State with TickerProviderStateMixin { AnimationController _positionController; AnimationController _scaleController; Animation _positionFactor; Animation _scaleFactor; Animation _value; Animation _valueColor; _RefreshIndicatorMode _mode; Future _pendingRefreshFuture; bool _isIndicatorAtTop; double _dragOffset; static final Animatable _threeQuarterTween = Tween(begin: 0.0, end: 0.75); static final Animatable _kDragSizeFactorLimitTween = Tween(begin: 0.0, end: _kDragSizeFactorLimit); static final Animatable _oneToZeroTween = Tween(begin: 1.0, end: 0.0); @override void initState() { super.initState(); _positionController = AnimationController(vsync: this); _positionFactor = _positionController.drive(_kDragSizeFactorLimitTween); _value = _positionController.drive(_threeQuarterTween); // The "value" of the circular progress indicator during a drag. _scaleController = AnimationController(vsync: this); _scaleFactor = _scaleController.drive(_oneToZeroTween); } @override void didChangeDependencies() { final ThemeData theme = Theme.of(context); _valueColor = _positionController.drive( ColorTween( begin: (widget.color ?? theme.accentColor).withOpacity(0.0), end: (widget.color ?? theme.accentColor).withOpacity(1.0) ).chain(CurveTween( curve: const Interval(0.0, 1.0 / _kDragSizeFactorLimit) )), ); super.didChangeDependencies(); } @override void dispose() { _positionController.dispose(); _scaleController.dispose(); super.dispose(); } @override void didUpdateWidget(ReactiveRefreshIndicator oldWidget) { if (!widget.isRefreshing && oldWidget.isRefreshing) { if (_mode != null && _mode != _RefreshIndicatorMode.done) { _dismiss(_RefreshIndicatorMode.done); } } super.didUpdateWidget(oldWidget); } bool _handleScrollNotification(ScrollNotification notification) { if (!widget.notificationPredicate(notification)) return false; if (notification is ScrollStartNotification && notification.metrics.extentBefore == 0.0 && _mode == null && _start(notification.metrics.axisDirection)) { setState(() { _mode = _RefreshIndicatorMode.drag; }); return false; } bool indicatorAtTopNow; switch (notification.metrics.axisDirection) { case AxisDirection.down: indicatorAtTopNow = true; break; case AxisDirection.up: indicatorAtTopNow = false; break; case AxisDirection.left: case AxisDirection.right: indicatorAtTopNow = null; break; } if (indicatorAtTopNow != _isIndicatorAtTop) { if (_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed) _dismiss(_RefreshIndicatorMode.canceled); } else if (notification is ScrollUpdateNotification) { if (_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed) { if (notification.metrics.extentBefore > 0.0) { _dismiss(_RefreshIndicatorMode.canceled); } else { _dragOffset -= notification.scrollDelta; _checkDragOffset(notification.metrics.viewportDimension); } } if (_mode == _RefreshIndicatorMode.armed && notification.dragDetails == null) { // On iOS start the refresh when the Scrollable bounces back from the // overscroll (ScrollNotification indicating this don't have dragDetails // because the scroll activity is not directly triggered by a drag). _show(); } } else if (notification is OverscrollNotification) { if (_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed) { _dragOffset -= notification.overscroll / 2.0; _checkDragOffset(notification.metrics.viewportDimension); } } else if (notification is ScrollEndNotification) { switch (_mode) { case _RefreshIndicatorMode.drag: _dismiss(_RefreshIndicatorMode.canceled); break; default: // do nothing break; } } return false; } bool _handleGlowNotification(OverscrollIndicatorNotification notification) { if (notification.depth != 0 || !notification.leading) return false; if (_mode == _RefreshIndicatorMode.drag) { notification.disallowGlow(); return true; } return false; } bool _start(AxisDirection direction) { assert(_mode == null); assert(_isIndicatorAtTop == null); assert(_dragOffset == null); switch (direction) { case AxisDirection.down: _isIndicatorAtTop = true; break; case AxisDirection.up: _isIndicatorAtTop = false; break; case AxisDirection.left: case AxisDirection.right: _isIndicatorAtTop = null; // we do not support horizontal scroll views. return false; } _dragOffset = 0.0; _scaleController.value = 0.0; _positionController.value = 0.0; return true; } void _checkDragOffset(double containerExtent) { assert(_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed); double newValue = _dragOffset / (containerExtent * _kDragContainerExtentPercentage); if (_mode == _RefreshIndicatorMode.armed) newValue = math.max(newValue, 1.0 / _kDragSizeFactorLimit); _positionController.value = newValue.clamp(0.0, 1.0); // this triggers various rebuilds if (_mode == _RefreshIndicatorMode.drag && newValue >= 1.0 / _kDragSizeFactorLimit) //_mode = _RefreshIndicatorMode.armed; _show(); } // Stop showing the refresh indicator. Future _dismiss(_RefreshIndicatorMode newMode) async { await Future.value(); // This can only be called from _show() when refreshing and // _handleScrollNotification in response to a ScrollEndNotification or // direction change. assert(newMode == _RefreshIndicatorMode.canceled || newMode == _RefreshIndicatorMode.done); setState(() { _mode = newMode; }); switch (_mode) { case _RefreshIndicatorMode.done: await _scaleController.animateTo(1.0, duration: _kIndicatorScaleDuration); break; case _RefreshIndicatorMode.canceled: await _positionController.animateTo(0.0, duration: _kIndicatorScaleDuration); break; default: assert(false); } if (mounted && _mode == newMode) { _dragOffset = null; _isIndicatorAtTop = null; setState(() { _mode = null; }); } } void _show() { assert(_mode != _RefreshIndicatorMode.refresh); assert(_mode != _RefreshIndicatorMode.snap); _mode = _RefreshIndicatorMode.snap; _positionController .animateTo(1.0 / _kDragSizeFactorLimit, duration: _kIndicatorSnapDuration) .then((void value) { if (mounted && _mode == _RefreshIndicatorMode.snap) { assert(widget.onRefresh != null); setState(() { // Show the indeterminate progress indicator. _mode = _RefreshIndicatorMode.refresh; }); widget.onRefresh(); } }); } /// Show the refresh indicator and run the refresh callback as if it had /// been started interactively. If this method is called while the refresh /// callback is running, it quietly does nothing. /// /// Creating the [RefreshIndicator] with a [GlobalKey] /// makes it possible to refer to the [RefreshIndicatorState]. /// /// The future returned from this method completes when the /// [RefreshIndicator.onRefresh] callback's future completes. /// /// If you await the future returned by this function from a [State], you /// should check that the state is still [mounted] before calling [setState]. /// /// When initiated in this manner, the refresh indicator is independent of any /// actual scroll view. It defaults to showing the indicator at the top. To /// show it at the bottom, set `atTop` to false. Future show({ bool atTop = true }) { if (_mode != _RefreshIndicatorMode.refresh && _mode != _RefreshIndicatorMode.snap) { if (_mode == null) _start(atTop ? AxisDirection.down : AxisDirection.up); _show(); } return _pendingRefreshFuture; } void stopRefreshing() { if (_mode != null && _mode != _RefreshIndicatorMode.done) { _dismiss(_RefreshIndicatorMode.done); } } final GlobalKey _key = GlobalKey(); @override Widget build(BuildContext context) { assert(debugCheckHasMaterialLocalizations(context)); final Widget child = NotificationListener( key: _key, onNotification: _handleScrollNotification, child: NotificationListener( onNotification: _handleGlowNotification, child: widget.child, ), ); if (_mode == null) { assert(_dragOffset == null); assert(_isIndicatorAtTop == null); return child; } assert(_dragOffset != null); assert(_isIndicatorAtTop != null); final bool showIndeterminateIndicator = _mode == _RefreshIndicatorMode.refresh || _mode == _RefreshIndicatorMode.done; return Stack( children: [ child, Positioned( top: _isIndicatorAtTop ? 0.0 : null, bottom: !_isIndicatorAtTop ? 0.0 : null, left: 0.0, right: 0.0, child: SizeTransition( axisAlignment: _isIndicatorAtTop ? 1.0 : -1.0, sizeFactor: _positionFactor, // this is what brings it down child: Container( padding: _isIndicatorAtTop ? EdgeInsets.only(top: widget.displacement) : EdgeInsets.only(bottom: widget.displacement), alignment: _isIndicatorAtTop ? Alignment.topCenter : Alignment.bottomCenter, child: ScaleTransition( scale: _scaleFactor, child: AnimatedBuilder( animation: _positionController, builder: (BuildContext context, Widget child) { return RefreshProgressIndicator( semanticsLabel: widget.semanticsLabel ?? MaterialLocalizations.of(context).refreshIndicatorSemanticLabel, semanticsValue: widget.semanticsValue, value: showIndeterminateIndicator ? null : _value.value, valueColor: _valueColor, backgroundColor: widget.backgroundColor, ); }, ), ), ), ), ), ], ); } } ================================================ FILE: lib/ui/widgets/settings_list_item.dart ================================================ import 'package:auto_size_text/auto_size_text.dart'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:flutter/material.dart'; /// A widget for displaying a mnemonic phrase class SettingsListItem extends StatefulWidget { final String header; final String subheader; final String contactName; final String contactAddress; final IconData icon; final Function onPressed; final bool contact; final bool disabled; SettingsListItem({ this.header, this.subheader, this.icon, this.contactName, this.contactAddress, this.onPressed, this.disabled = false, this.contact = false, }); _SettingsListItemState createState() => _SettingsListItemState(); } class _SettingsListItemState extends State { @override Widget build(BuildContext context) { return Column( children: [ Container( width: double.maxFinite, height: 70, child: FlatButton( padding: EdgeInsetsDirectional.only(start: 24, end: 24), onPressed: () { if (widget.onPressed != null && !widget.disabled) { widget.onPressed(); } return; }, splashColor: widget.disabled ? Colors.transparent : StateContainer.of(context).curTheme.primary30, highlightColor: widget.disabled ? Colors.transparent : StateContainer.of(context).curTheme.primary15, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)), child: widget.contact ? Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( width: MediaQuery.of(context).size.width / 2, child: AutoSizeText.rich( TextSpan(children: [ TextSpan( text: " ", style: AppStyles.iconFontPrimarySmall(context), ), TextSpan( text: widget.contactName, style: AppStyles.contactsItemName(context), ), ]), maxLines: 1, stepGranularity: 0.1, style: TextStyle(fontSize: 14), ), ), Container( width: MediaQuery.of(context).size.width / 2 - 80, alignment: Alignment(1, 0), child: AutoSizeText( widget.contactAddress, style: AppStyles.contactsItemAddress(context), maxLines: 1, stepGranularity: 0.1, ), ), ], ) : Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( child: Icon(widget.icon, size: 24, color: widget.disabled ? StateContainer.of(context).curTheme.primary60 : StateContainer.of(context).curTheme.primary), ), Container( margin: EdgeInsetsDirectional.only(start: 16), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: MediaQuery.of(context).size.width - 130, child: AutoSizeText( widget.header, style: widget.disabled ? AppStyles.settingsItemHeaderDisabled(context) : AppStyles.settingsItemHeader(context), maxLines: 1, stepGranularity: 0.1, ), ), widget.subheader == null ? SizedBox() : Container( width: MediaQuery.of(context).size.width - 130, child: AutoSizeText( widget.subheader, style: widget.disabled ? AppStyles.settingsItemSubHeaderDisabled(context) : AppStyles.settingsItemSubHeader(context), maxLines: 1, stepGranularity: 0.1, ), ), ], ), ), ], ), ), ), Container( width: double.maxFinite, height: 1, color: StateContainer.of(context) .curTheme .textDark10, ), ] ); } } ================================================ FILE: lib/ui/widgets/sheets.dart ================================================ import 'dart:io'; import 'dart:ui'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:flutter/material.dart'; class AppSheets { //App Ninty Height Sheet static Future showBottomSheet( {@required BuildContext context, @required Widget widget, Color color, Color bgColor, bool noBlur = false, int animationDurationMs = 250, bool closeOnTap = false, Function onDisposed}) { if (color == null) { color = StateContainer.of(context).curTheme.backgroundPrimary; } if (bgColor == null) { bgColor = StateContainer.of(context).curTheme.overlay20; } var route = _AppBottomSheetModalRoute( builder: (BuildContext context) { return widget; }, color: color, noBlur: noBlur, barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, bgColor: bgColor, animationDurationMs: animationDurationMs, closeOnTap: closeOnTap, onDisposed: onDisposed); return Navigator.push(context, route); } } /// The constraints of this sheet class _AppBottomSheetLayout extends SingleChildLayoutDelegate { _AppBottomSheetLayout(this.progress); final double progress; @override BoxConstraints getConstraintsForChild(BoxConstraints constraints) { if (constraints.maxHeight < 667) return BoxConstraints( minWidth: constraints.maxWidth, maxWidth: constraints.maxWidth, minHeight: 0.0, maxHeight: constraints.maxHeight * 0.95); if ((constraints.maxHeight / constraints.maxWidth > 2.1 && Platform.isAndroid) || constraints.maxHeight > 812) return BoxConstraints( minWidth: constraints.maxWidth, maxWidth: constraints.maxWidth, minHeight: 0.0, maxHeight: constraints.maxHeight * 0.8); else return BoxConstraints( minWidth: constraints.maxWidth, maxWidth: constraints.maxWidth, minHeight: 0.0, maxHeight: constraints.maxHeight * 0.9); } @override Offset getPositionForChild(Size size, Size childSize) { return Offset(0.0, size.height - childSize.height * progress); } @override bool shouldRelayout(_AppBottomSheetLayout oldDelegate) { return progress != oldDelegate.progress; } } class _AppBottomSheetModalRoute extends PopupRoute { _AppBottomSheetModalRoute( {this.builder, this.barrierLabel, this.color, RouteSettings settings, this.bgColor, this.animationDurationMs, this.closeOnTap, this.noBlur, this.onDisposed}) : super(settings: settings); final WidgetBuilder builder; final Color color; final bool noBlur; final Color bgColor; final int animationDurationMs; final bool closeOnTap; final Function onDisposed; @override Color get barrierColor => bgColor; @override bool get barrierDismissible => true; @override String barrierLabel; @override void didComplete(T result) { if (onDisposed != null) { onDisposed(); } super.didComplete(result); } AnimationController _animationController; CurvedAnimation appSheetAnimation; @override AnimationController createAnimationController() { assert(_animationController == null); _animationController = BottomSheet.createAnimationController(navigator.overlay); _animationController.duration = Duration(milliseconds: animationDurationMs); this.appSheetAnimation = CurvedAnimation( parent: _animationController, curve: Curves.easeOut, reverseCurve: Curves.linear) ..addStatusListener((animationStatus) { if (animationStatus == AnimationStatus.completed) { appSheetAnimation.curve = Curves.linear; } }); return _animationController; } @override Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { return MediaQuery.removePadding( context: context, removeTop: true, child: GestureDetector( onTap: () { if (closeOnTap) { // Close when tapped anywhere Navigator.of(context).pop(); } }, child: BackdropFilter( filter: ImageFilter.blur(sigmaX: noBlur?0:5, sigmaY: noBlur?0:5), child: Theme( data: Theme.of(context).copyWith(canvasColor: Colors.transparent), child: AnimatedBuilder( animation: appSheetAnimation, builder: (context, child) => CustomSingleChildLayout( delegate: _AppBottomSheetLayout(appSheetAnimation.value), child: BottomSheet( animationController: _animationController, onClosing: () => Navigator.of(context).pop(), builder: (context) => Container( decoration: BoxDecoration( color: this.color, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), ), ), child: Builder(builder: this.builder), ), ), ), ), ), ), ), ); } @override bool get maintainState => false; @override bool get opaque => false; @override Duration get transitionDuration => Duration(milliseconds: animationDurationMs); } ================================================ FILE: lib/ui/widgets/svg_repaint.dart ================================================ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; class SvgRepaintAsset extends StatelessWidget { String asset; double width; double height; SvgRepaintAsset({this.asset, this.width, this.height}); @override Widget build(BuildContext context) { return RepaintBoundary( child: SvgPicture.asset(asset, width: width, height: height,) ); } } ================================================ FILE: lib/ui/widgets/tap_outside_unfocus.dart ================================================ import 'package:flutter/material.dart'; /// Simple wrapper that will clear focus when a tap is detected outside its boundaries class TapOutsideUnfocus extends StatelessWidget { final Widget child; TapOutsideUnfocus({@required this.child}); @override Widget build(BuildContext context) { return GestureDetector( onTap: () { // Clear focus of our fields when tapped in this empty space FocusScope.of(context).unfocus(); }, child: this.child ); } } ================================================ FILE: lib/ui/widgets/webview.dart ================================================ import 'dart:io'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; class AppWebView { static void showWebView(BuildContext context, String url) { UIUtil.cancelLockEvent(); Navigator.push( context, MaterialPageRoute( builder: (BuildContext context) => WebviewScaffold( resizeToAvoidBottomInset: Platform.isAndroid, url: url, appBar: AppBar( backgroundColor: StateContainer.of(context).curTheme.primary, brightness: StateContainer.of(context).curTheme.brightness, iconTheme: IconThemeData( color: StateContainer.of(context).curTheme.textLight))))); } } ================================================ FILE: lib/util/authentication.dart ================================================ import 'dart:io'; import 'package:blaise_wallet_flutter/model/authentication_method.dart'; import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:local_auth/local_auth.dart'; class AuthUtil { /// /// hasBiometrics() /// /// @returns [true] if device has fingerprint/faceID available and registered, [false] otherwise Future hasBiometrics() async { LocalAuthentication localAuth = new LocalAuthentication(); bool canCheck = await localAuth.canCheckBiometrics; if (canCheck) { List availableBiometrics = await localAuth.getAvailableBiometrics(); if (availableBiometrics.contains(BiometricType.face)) { return true; } else if (availableBiometrics.contains(BiometricType.fingerprint)) { return true; } } return false; } /// /// authenticateWithBiometrics() /// /// @param [message] Message shown to user in FaceID/TouchID popup /// @returns [true] if successfully authenticated, [false] otherwise Future authenticateWithBiometrics(String message) async { bool hasBiometricsEnrolled = await hasBiometrics(); if (hasBiometricsEnrolled) { LocalAuthentication localAuth = new LocalAuthentication(); return await localAuth.authenticateWithBiometrics( localizedReason: message, useErrorDialogs: false ); } return false; } Future useBiometrics() async { return await hasBiometrics() && (await sl.get().getAuthMethod()).method == AuthMethod.BIOMETRICS; } } ================================================ FILE: lib/util/haptic_util.dart ================================================ import 'dart:io'; import 'package:flutter/services.dart'; import 'package:vibrate/vibrate.dart'; import 'package:device_info/device_info.dart'; /// Utilities for haptic feedback class HapticUtil { /// Return true if this device supports taptic engine (iPhone 7+) static Future hasTapicEngine() async { if (!Platform.isIOS) { return false; } IosDeviceInfo deviceInfo = await DeviceInfoPlugin().iosInfo; String deviceIdentifier = deviceInfo.utsname.machine; switch (deviceIdentifier) { case 'iPhone5,1': // iPhone 5 case 'iPhone5,2': // iPhone 5 case 'iPhone5,3': // iPhone 5C case 'iPhone5,4': // iPhone 5C case 'iPhone6,1': // iPhone 5S case 'iPhone6,2': // iPhone 5S case 'iPhone7,2': // iPhone 6 case 'iPhone7,1': // iPhone 6 plus case 'iPhone8,1': // iPhone 6s case 'iPhone8,2': // iPhone 6s plus return false; default: return true; } } /// Feedback for error static Future error() async { if (Platform.isIOS) { // If this is simulator or this device doesnt have tapic then we can't use this if (await hasTapicEngine() && await Vibrate.canVibrate) { Vibrate.feedback(FeedbackType.error); } else { HapticFeedback.vibrate(); } } else { HapticFeedback.vibrate(); } } /// Feedback for success static Future success() async { if (Platform.isIOS) { // If this is simulator or this device doesnt have tapic then we can't use this if (await hasTapicEngine() && await Vibrate.canVibrate) { Vibrate.feedback(FeedbackType.medium); } else { HapticFeedback.mediumImpact(); } } else { HapticFeedback.mediumImpact(); } } /// Feedback for fingerprint success /// iOS-only, since Android already gives us feedback on success static Future fingerprintSucess() async { if (Platform.isIOS) { Future.delayed(Duration(milliseconds: 50), () => success()); } } } ================================================ FILE: lib/util/number_util.dart ================================================ import 'dart:math'; import 'package:decimal/decimal.dart'; class NumberUtil { static const int maxDecimalDigits = 4; /// Truncate a Decimal to a specific amount of digits /// /// @param input 1.059 /// @return double value 1.05 /// static double truncateDecimal(Decimal input, {int digits = maxDecimalDigits}) { return (input * Decimal.fromInt(pow(10, digits))).truncateToDouble() / pow(10, digits); } /// Sanitize a number as something that can actually /// be parsed. Expects "." to be decimal separator /// @param amount $1,512 /// @returns 1.512 static String sanitizeNumber(String input, {int maxDecimalDigits = maxDecimalDigits}) { String sanitized = ""; List splitStr = input.split("."); if (splitStr.length > 1) { if (splitStr[1].length > maxDecimalDigits) { splitStr[1] = splitStr[1].substring(0, maxDecimalDigits); input = splitStr[0] + "." + splitStr[1]; } } for (int i=0; i< input.length; i++) { try { if (input[i] == ".") { sanitized = sanitized + input[i]; } else { int.parse(input[i]); sanitized = sanitized + input[i]; } } catch (e) { } } return sanitized; } } ================================================ FILE: lib/util/pascal_util.dart ================================================ import 'package:pascaldart/pascaldart.dart'; /// A higher-level wrapper for pascaldart functions class PascalUtil { /// Generates a new key pair KeyPair generateKeyPair() { return Keys.generate(curve: Curve.getDefaultCurve()); } /// Turns a string into a pascal PublicKey object, returns null if invalid PublicKey decipherPublicKey(String publicKey) { try { PublicKey pubKey = PublicKeyCoder().decodeFromBase58(publicKey.trim()); return pubKey; } catch (e) { try { PublicKey pubKey = PublicKeyCoder().decodeFromBytes(PDUtil.hexToBytes(publicKey.trim())); return pubKey; } catch (e) { return null; } } } } ================================================ FILE: lib/util/salsa20crypt.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/pascaldart.dart'; import 'package:pointycastle/api.dart' show ParametersWithIV, KeyParameter; import 'package:pointycastle/stream/salsa20.dart'; /** * Encryption using Salsa20 from pointycastle */ class Salsa20Crypt { final String key; final String iv; final ParametersWithIV _params; final Salsa20Engine _cipher = Salsa20Engine(); Salsa20Crypt(this.key, this.iv) : _params = ParametersWithIV( KeyParameter(Uint8List.fromList(key.codeUnits)), Uint8List.fromList(iv.codeUnits)); String encrypt(String plainText) { _cipher ..reset() ..init(true, _params); final input = Uint8List.fromList(plainText.codeUnits); final output = _cipher.process(input); return PDUtil.byteToHex(output); } String decrypt(String cipherText) { _cipher ..reset() ..init(false, _params); final input = PDUtil.hexToBytes(cipherText); final output = _cipher.process(input); return String.fromCharCodes(output); } } ================================================ FILE: lib/util/sharedprefs_util.dart ================================================ import 'dart:convert'; import 'dart:io'; import 'dart:ui'; import 'package:blaise_wallet_flutter/constants.dart'; import 'package:blaise_wallet_flutter/model/authentication_method.dart'; import 'package:blaise_wallet_flutter/model/available_currency.dart'; import 'package:blaise_wallet_flutter/model/available_languages.dart'; import 'package:blaise_wallet_flutter/model/available_themes.dart'; import 'package:blaise_wallet_flutter/model/lock_timeout.dart'; import 'package:intl/intl.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// Singleton wrapper for shared preferences class SharedPrefsUtil { // Keys static const String first_launch_key = 'pasc_first_launch'; static const String privkey_backed_up_key = 'pasc_privkey_backedup'; static const String rpc_url_key = 'pasc_rpc_url'; static const String cur_theme = 'blaise_cur_theme_key'; // For maximum pin attempts static const String pin_attempts = 'blaise_pin_attempts'; static const String pin_lock_until = 'blaise_lock_duraton'; // Lock screen static const String lock_app = 'app_lock_screen'; static const String app_lock_timeout = 'app_lock_timeout'; // Using biometrics static const String auth_method = 'blaise_auth_method'; // Donation contact has been added static const String firstcontact_added = 'blaise_first_c_added'; // Caching price API response static const String price_api_cache = 'price_api_cache_v1'; // Local currency static const String cur_currency = 'blaise_currency_pref'; // Language setting static const String cur_language = 'blaise_lang_pref'; // UUID for our WS subscription static const String app_uuid_key = 'blaise_app_uuid'; // Push notifications static const String notification_enabled = 'blaise_notification_on'; // Freepasa account before confirmation static const String freepasa_account_pending = 'blaise_fpasa_pending_acct'; // For certain keystore incompatible androids static const String use_legacy_storage = 'blaise_legacy_storage'; // For plain-text data Future set(String key, dynamic value) async { SharedPreferences sharedPreferences = await SharedPreferences.getInstance(); if (value is bool) { sharedPreferences.setBool(key, value); } else if (value is String) { sharedPreferences.setString(key, value); } else if (value is double) { sharedPreferences.setDouble(key, value); } else if (value is int) { sharedPreferences.setInt(key, value); } } Future get(String key, {dynamic defaultValue}) async { SharedPreferences sharedPreferences = await SharedPreferences.getInstance(); return await sharedPreferences.get(key) ?? defaultValue; } /// Set a key with an expiry, expiry is in seconds Future setWithExpiry(String key, dynamic value, int expiry) async { DateTime now = DateTime.now().toUtc(); DateTime expired = now.add(Duration(seconds: expiry)); Map msg = { 'data':value, 'expiry':expired.millisecondsSinceEpoch }; String serialized = json.encode(msg); await set(key, serialized); } /// Get a key that has an expiry Future getWithExpiry(String key) async { String val = await get(key, defaultValue: null); if (val == null) { return null; } Map msg = json.decode(val); DateTime expired = DateTime.fromMillisecondsSinceEpoch(msg['expiry']); if (DateTime.now().toUtc().difference(expired).inMinutes > 0) { await remove(key); return null; } return msg['data']; } Future remove(String key) async { SharedPreferences sharedPreferences = await SharedPreferences.getInstance(); return await sharedPreferences.remove(key); } // Key-specific helpers Future setPrivateKeyBackedUp(bool value) async { return await set(privkey_backed_up_key, value); } Future getPrivateKeyBackedUp() async { return await get(privkey_backed_up_key, defaultValue: false); } Future setFirstLaunch() async { return await set(first_launch_key, false); } Future getFirstLaunch() async { return await get(first_launch_key, defaultValue: true); } Future setRpcUrl(String rpcUrl) async { return await set(rpc_url_key, rpcUrl); } Future resetRpcUrl() async { return await remove(rpc_url_key); } Future getRpcUrl() async { return await get(rpc_url_key, defaultValue: AppConstants.DEFAULT_RPC_HTTP_URL); } Future setTheme(ThemeSetting theme) async { return await set(cur_theme, theme.getIndex()); } Future getTheme() async { return ThemeSetting(ThemeOptions.values[await get(cur_theme, defaultValue: ThemeOptions.LIGHT.index)]); } Future setLock(bool value) async { return await set(lock_app, value); } Future getLock() async { return await get(lock_app, defaultValue: false); } Future setLockTimeout(LockTimeoutSetting setting) async { return await set(app_lock_timeout, setting.getIndex()); } Future getLockTimeout() async { return LockTimeoutSetting(LockTimeoutOption.values[await get(app_lock_timeout, defaultValue: LockTimeoutOption.ONE.index)]); } // Locking out when max pin attempts exceeded Future getLockAttempts() async { return await get(pin_attempts, defaultValue: 0); } Future incrementLockAttempts() async { await set(pin_attempts, await getLockAttempts() + 1); } Future resetLockAttempts() async { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.remove(pin_attempts); await prefs.remove(pin_lock_until); } Future shouldLock() async { if (await get(pin_lock_until) != null || await getLockAttempts() >= 5) { return true; } return false; } Future updateLockDate() async { int attempts = await getLockAttempts(); if (attempts >= 20) { // 4+ failed attempts await set(pin_lock_until, DateFormat.yMd().add_jms().format(DateTime.now().toUtc().add(Duration(hours: 24)))); } else if (attempts >= 15) { // 3 failed attempts await set(pin_lock_until, DateFormat.yMd().add_jms().format(DateTime.now().toUtc().add(Duration(minutes: 15)))); } else if (attempts >= 10) { // 2 failed attempts await set(pin_lock_until, DateFormat.yMd().add_jms().format(DateTime.now().toUtc().add(Duration(minutes: 5)))); } else if (attempts >= 5) { await set(pin_lock_until, DateFormat.yMd().add_jms().format(DateTime.now().toUtc().add(Duration(minutes: 1)))); } } Future getLockDate() async { String lockDateStr = await get(pin_lock_until); if (lockDateStr == null) { return null; } return DateFormat.yMd().add_jms().parseUtc(lockDateStr); } Future setAuthMethod(AuthenticationMethod method) async { return await set(auth_method, method.getIndex()); } Future getAuthMethod() async { return AuthenticationMethod(AuthMethod.values[await get(auth_method, defaultValue: AuthMethod.BIOMETRICS.index)]); } Future setFirstContactAdded(bool value) async { return await set(firstcontact_added, value); } Future getFirstContactAdded() async { return await get(firstcontact_added, defaultValue: false); } Future getPriceAPICache() async { return await get(price_api_cache, defaultValue: null); } Future setPriceAPICache(String data) async { await set(price_api_cache, data); } Future setCurrency(AvailableCurrency currency) async { return await set(cur_currency, currency.getIndex()); } Future getCurrency(Locale deviceLocale) async { return AvailableCurrency(AvailableCurrencyEnum.values[await get(cur_currency, defaultValue: AvailableCurrency.getBestForLocale(deviceLocale).currency.index)]); } Future setLanguage(LanguageSetting language) async { return await set(cur_language, language.getId()); } Future getLanguage() async { String langEnumStr = await get(cur_language, defaultValue: AvailableLanguage.DEFAULT.toString()); AvailableLanguage lang; for (AvailableLanguage aLang in AvailableLanguage.values) { if (aLang.toString() == langEnumStr) { lang = aLang; break; } } return LanguageSetting(lang); } Future setUuid(String uuid) async { return await set(app_uuid_key, uuid); } Future getUuid() async { return await get(app_uuid_key); } Future setNotificationsOn(bool value) async { return await set(notification_enabled, value); } Future getNotificationsOn() async { // Notifications off by default on iOS, bool defaultValue = Platform.isIOS ? false : true; return await get(notification_enabled, defaultValue: defaultValue); } /// If notifications have been set by user/app Future getNotificationsSet() async { return await get(notification_enabled, defaultValue: null) == null ? false : true; } Future getFreepasaAccount() async { int acct = await getWithExpiry(freepasa_account_pending); return acct == null ? null : AccountNumber.fromInt(acct); } Future setFreepasaAccount(AccountNumber account) async { await setWithExpiry(freepasa_account_pending, account.account, 2000); } Future useLegacyStorage() async { return await get(use_legacy_storage, defaultValue: false); } Future setUseLegacyStorage() async { await set(use_legacy_storage, true); } // For logging out Future deleteAll({bool firstLaunch = false}) async { SharedPreferences prefs = await SharedPreferences.getInstance(); if (firstLaunch) { await prefs.clear(); } else { await prefs.remove(privkey_backed_up_key); await prefs.remove(rpc_url_key); await prefs.remove(pin_attempts); await prefs.remove(pin_lock_until); await prefs.remove(auth_method); await prefs.remove(app_lock_timeout); await prefs.remove(lock_app); await prefs.remove(cur_currency); await prefs.remove(app_uuid_key); await prefs.remove(use_legacy_storage); } } } ================================================ FILE: lib/util/ui_util.dart ================================================ import 'dart:async'; import 'package:blaise_wallet_flutter/appstate_container.dart'; import 'package:blaise_wallet_flutter/bus/events.dart'; import 'package:blaise_wallet_flutter/localization.dart'; import 'package:blaise_wallet_flutter/ui/util/text_styles.dart'; import 'package:blaise_wallet_flutter/ui/widgets/overlay_dialog.dart'; import 'package:event_taxi/event_taxi.dart'; import 'package:intl/intl.dart'; import 'package:oktoast/oktoast.dart'; import 'package:flutter/material.dart'; class UIUtil { static bool smallScreen(BuildContext context) { if (MediaQuery.of(context).size.height < 667) return true; else return false; } static void showSnackbar(String content, BuildContext context) { showToastWidget( Align( alignment: Alignment.topCenter, child: Container( margin: EdgeInsets.symmetric( vertical: MediaQuery.of(context).size.height * 0.05, horizontal: 14), padding: EdgeInsets.symmetric(vertical: 18, horizontal: 16), width: MediaQuery.of(context).size.width - 30, decoration: BoxDecoration( color: StateContainer.of(context).curTheme.backgroundPrimary, borderRadius: BorderRadius.circular(10), boxShadow: [ BoxShadow( color: StateContainer.of(context).curTheme.overlay20, offset: Offset(0, 20), blurRadius: 40, spreadRadius: -5), ], ), child: Text( content, style: AppStyles.snackBar(context), textAlign: TextAlign.start, ), ), ), dismissOtherToast: true, duration: Duration(milliseconds: 2000), ); } static String formatDateStr(DateTime dt) { int currentYear = DateTime.now().toLocal().year; DateTime localTime = dt.toLocal(); DateFormat df; if (localTime.year != currentYear) { df = DateFormat("MMM dd, yyyy • HH:mm"); } else { df = DateFormat("MMM dd • HH:mm"); } return df.format(localTime); } static String formatDateStrLong(DateTime dt) { //"Jul 08, 2019 • 13:24:01 (1562592241)" int secondsSinceEpoch = dt.millisecondsSinceEpoch ~/ 1000; DateTime localTime = dt.toLocal(); DateFormat df; df = DateFormat("MMM dd, yyyy • HH:mm:ss"); return df.format(localTime) + "\n($secondsSinceEpoch)"; } /// Show a dialog asking if they want to confirm a fee static void showFeeDialog( {@required BuildContext context, @required Function onConfirm}) { showAppDialog( context: context, builder: (_) => DialogOverlay( title: AppLocalization.of(context).addFeeHeader, feeDialog: true, confirmButtonText: toUppercase(AppLocalization.of(context).confirmButton, context), body: TextSpan( children: [ TextSpan( text: AppLocalization.of(context).feeRequiredParagraph + "\n", style: AppStyles.paragraph(context), ), TextSpan( text: AppLocalization.of(context) .feeConfirmAmountParagraph .replaceAll("%1", "0.0001"), style: AppStyles.paragraphPrimary(context), ), ], ), onConfirm: () { onConfirm(); })); } static StreamSubscription _lockDisableSub; static Future cancelLockEvent() async { // Cancel auto-lock event, usually if we are launching another intent if (_lockDisableSub != null) { _lockDisableSub.cancel(); } EventTaxiImpl.singleton().fire(DisableLockTimeoutEvent(disable: true)); Future delayed = Future.delayed(Duration(seconds: 10)); delayed.then((_) { return true; }); _lockDisableSub = delayed.asStream().listen((_) { EventTaxiImpl.singleton().fire(DisableLockTimeoutEvent(disable: false)); }); } } List formatLocalizedColors(BuildContext context, String input) { List ret = []; if (!input.contains('') && !input.contains('')) { ret.add(TextSpan(text: input, style: AppStyles.paragraph(context))); return ret; } int i = 0; int iEnd = 0; while (input.contains('') && input.contains('')) { i = input.indexOf(''); iEnd = input.indexOf(''); ret.add(TextSpan( text: input.substring(0, i), style: AppStyles.paragraph(context))); ret.add(TextSpan( text: input.substring(i + 9, iEnd), style: AppStyles.paragraphPrimary(context))); input = input.substring(iEnd + 10); } if (input.length > 0) { ret.add(TextSpan(text: input, style: AppStyles.paragraph(context))); } return ret; } List formatLocalizedColorsDanger(BuildContext context, String input) { List ret = []; if (!input.contains('') && !input.contains('')) { ret.add(TextSpan(text: input, style: AppStyles.paragraph(context))); return ret; } int i = 0; int iEnd = 0; while (input.contains('') && input.contains('')) { i = input.indexOf(''); iEnd = input.indexOf(''); ret.add(TextSpan( text: input.substring(0, i), style: AppStyles.paragraph(context))); ret.add(TextSpan( text: input.substring(i + 9, iEnd), style: AppStyles.paragraphDanger(context))); input = input.substring(iEnd + 10); } if (input.length > 0) { ret.add(TextSpan(text: input, style: AppStyles.paragraph(context))); } return ret; } String toUppercase(String input, BuildContext context) { Locale locale = Locale(StateContainer.of(context).curLanguage.getLocaleString()); if (locale != null && locale.languageCode == 'tr') { input = input.replaceAll("i", "İ"); } else if (locale != null && locale.languageCode == 'de') { input = input.replaceAll("ß", "SS"); } return input.toUpperCase(); } ================================================ FILE: lib/util/user_data_util.dart ================================================ import 'dart:async'; import 'dart:io'; import 'package:blaise_wallet_flutter/util/ui_util.dart'; import 'package:flutter/services.dart'; import 'package:pascaldart/common.dart'; import 'package:quiver/strings.dart'; import 'package:validators/validators.dart'; import 'package:barcode_scan/barcode_scan.dart'; enum DataType { RAW, ACCOUNT, URL, PUBLIC_KEY } class UserDataUtil { static const MethodChannel _channel = const MethodChannel('fappchannel'); static StreamSubscription setStream; static String _parseData(String data, DataType type) { data = data.trim(); if (type == DataType.RAW) { return data; } else if (type == DataType.ACCOUNT) { try { AccountNumber acctNum = AccountNumber(data); return acctNum.toString(); } catch (e) {} } else if (type == DataType.URL) { if (isIP(data)) { return data; } else if (isURL(data)) { return data; } } else if (type == DataType.PUBLIC_KEY) { try { PublicKeyCoder() .decodeFromBase58(data); return data; } catch (e) { try { PublicKeyCoder() .decodeFromBytes( PDUtil.hexToBytes( data)); return data; } catch (e) {} } } return null; } static Future getClipboardText(DataType type) async { ClipboardData data = await Clipboard.getData("text/plain"); if (data == null || data.text == null) { return null; } return _parseData(data.text, type); } static Future getQRData(DataType type, OverlayTheme theme) async { UIUtil.cancelLockEvent(); try { String data = await BarcodeScanner.scan(theme); if (isEmpty(data)) { return null; } return _parseData(data, type); } catch (e) { return null; } } static Future setSecureClipboardItem(String value) async { if (Platform.isIOS) { final Map params = { 'value': value, }; await _channel.invokeMethod("setSecureClipboardItem", params); } else { // Set item in clipboard await Clipboard.setData(new ClipboardData(text: value)); // Auto clear it after 2 minutes if (setStream != null) { setStream.cancel(); } Future delayed = new Future.delayed(new Duration(minutes: 2)); delayed.then((_) { return true; }); setStream = delayed.asStream().listen((_) { Clipboard.getData("text/plain").then((data) { if (data != null && data.text != null) { if (privateKeyIsValid(data.text) || privateKeyIsEncrypted(data.text)) { Clipboard.setData(ClipboardData(text: "")); } } }); }); } } static bool privateKeyIsValid(String pkText) { try { PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes(pkText)); return true; } catch (e) { return false; } } static bool privateKeyIsEncrypted(String pkText, {bool lengthCheck = true}) { int minLength = lengthCheck ? 100 : 8; if (pkText == null || pkText.length < minLength) { return false; } try { String salted = PDUtil.bytesToUtf8String(PDUtil.hexToBytes(pkText.substring(0, 16))); if (salted == "Salted__") { return true; } return false; } catch (e) { return false; } } } ================================================ FILE: lib/util/vault.dart ================================================ import 'package:blaise_wallet_flutter/service_locator.dart'; import 'package:blaise_wallet_flutter/util/salsa20crypt.dart'; import 'package:blaise_wallet_flutter/util/sharedprefs_util.dart'; import 'package:flutter/services.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:pascaldart/common.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// Secure store implementation that stores sensitive data class Vault { static const String PRIVATEKEY_KEY = 'pascal_privatekey'; static const String PIN_KEY = 'blaise_pin'; FlutterSecureStorage _secureStorage; PrivateKeyCoder _privateKeyCoder; Future legacy() async { return await sl.get().useLegacyStorage(); } Vault() { this._secureStorage = FlutterSecureStorage(); this._privateKeyCoder = PrivateKeyCoder(); } // Generic write/read Future _write(String key, String value) async { if (await legacy()) { await setEncrypted(key, value); } else { await _secureStorage.write(key:key, value:value); } return value; } Future _read(String key, {String defaultValue}) async { if (await legacy()) { return await getEncrypted(key); } return await _secureStorage.read(key:key) ?? defaultValue; } // Private key Future getPrivateKey() async { return await _read(PRIVATEKEY_KEY); } Future setPrivateKey(PrivateKey privateKey) async { return await _write(PRIVATEKEY_KEY, _privateKeyCoder.encodeToHex(privateKey)); } // Pin Future getPin() async { return await _read(PIN_KEY); } Future setPin(String pin) async { return await _write(PIN_KEY, pin); } Future deleteAll() async { if (await legacy()) { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.remove(PIN_KEY); await prefs.remove(PRIVATEKEY_KEY); return; } await _secureStorage.deleteAll(); } // For encrypted data Future setEncrypted(String key, String value) async { String secret = await getSecret(); if (secret == null) return null; // Decrypt and return Salsa20Crypt encrypter = Salsa20Crypt(secret.substring(0, secret.length - 8), secret.substring(secret.length - 8)); // Encrypt and save SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.setString(key, encrypter.encrypt(value)); } Future getEncrypted(String key) async { String secret = await getSecret(); if (secret == null) return null; // Decrypt and return Salsa20Crypt encrypter = Salsa20Crypt(secret.substring(0, secret.length - 8), secret.substring(secret.length - 8)); SharedPreferences prefs = await SharedPreferences.getInstance(); String encrypted = prefs.get(key); if (encrypted == null) return null; return encrypter.decrypt(encrypted); } static const _channel = const MethodChannel('fappchannel'); static Future getSecret() async { return await _channel.invokeMethod('getSecret'); } } ================================================ FILE: pubspec.yaml ================================================ name: blaise_wallet_flutter description: A new Flutter project. version: 1.1.3+24 environment: sdk: ">=2.11.99 <3.0.0" dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter # PascalCoin signing/rpc/etc. pascaldart: ^2.0.0 # Localization intl: ^0.17.0 # AutoSizeText widget auto_size_text: ^2.1.0 # Flare for animations flare_flutter: ^2.0.6 # SVG flutter_svg: ^0.22.0 # Font awesome font_awesome_flutter: ^8.8.1 # Share files share: git: url: https://github.com/bbedward/plugins.git path: packages/share ref: master # Slidable flutter_slidable: ^0.5.7 # QR Code Generator qr_flutter: ^3.0.1 # Banner style alerts oktoast: ^3.0.0 # Service locator get_it: ^6.0.0 # To avoid keyboard overlap keyboard_avoider: ^0.1.2 # KeyStore/keychain flutter_secure_storage: ^5.0.2 # shared prefs shared_preferences: ^2.0.8 # Biometrics local_auth: ^1.0.0-nullsafety.3 # mobx for state mobx: ^2.0.0-nullsafety.3 flutter_mobx: ^2.0.0-nullsafety.0 # Nicer logging logger: ^1.0.0 # quiver - various Dart utilities quiver: ^3.0.1+1 # big decimal decimal: ^1.3.0 # Event Bus event_taxi: ^1.0.0 # Vibrate vibrate: git: url: https://github.com/bbedward/flutter_vibrate.git ref: master # Device-specific info device_info: ^2.0.3 # App build info package_info: ^2.0.2 # Serialization json_annotation: ^4.3.0 # SQLite sqflite: ^2.0.0+4 # OS Path Info path_provider: ^2.0.1 # Native file chooser file_picker: ^3.0.0-nullsafety.0 # Various string validations validators: 2.0.0 # Webview flutter_webview_plugin: ^0.4.0 # QR scanning barcode_scan: git: url: https://github.com/bbedward/flutter_barcode_reader.git ref: master # Push notifications firebase_messaging: ^11.1.0 firebase_core: ^1.10.0 dependency_overrides: analyzer: ^2.7.0 dart_style: ^2.0.0 dev_dependencies: flutter_test: sdk: flutter build_runner: ^2.1.5 mobx_codegen: ^2.0.0-nullsafety.1 json_serializable: ^6.0.1 intl_translation: git: url: https://github.com/bbedward/intl_translation.git ref: master # For information on the generic Dart part of this file, see the # following page: https://www.dartlang.org/tools/pub/pubspec # The following section is specific to Flutter. flutter: fonts: - family: Metropolis fonts: - asset: fonts/Metropolis-Black.ttf weight: 900 - asset: fonts/Metropolis-ExtraBold.ttf weight: 800 - asset: fonts/Metropolis-Bold.ttf weight: 700 - asset: fonts/Metropolis-SemiBold.ttf weight: 600 - asset: fonts/Metropolis-Medium.ttf weight: 500 - asset: fonts/Metropolis-Regular.ttf weight: 400 - asset: fonts/Metropolis-Light.ttf weight: 300 - asset: fonts/Metropolis-ExtraLight.ttf weight: 200 - asset: fonts/Metropolis-Thin.ttf weight: 100 - family: RobotoRegular fonts: - asset: fonts/Roboto-Regular.ttf - family: SourceCodePro fonts: - asset: fonts/SourceCodePro-Black.ttf weight: 900 - asset: fonts/SourceCodePro-Bold.ttf weight: 700 - asset: fonts/SourceCodePro-Semibold.ttf weight: 600 - asset: fonts/SourceCodePro-Medium.ttf weight: 500 - asset: fonts/SourceCodePro-Regular.ttf weight: 400 - asset: fonts/SourceCodePro-Light.ttf weight: 300 - asset: fonts/SourceCodePro-ExtraLight.ttf weight: 200 - family: AppIcons fonts: - asset: fonts/AppIcons.ttf weight: 600 assets: - assets/animation_welcome.flr - assets/animation_welcome_copper.flr - assets/animation_welcome_dark.flr - assets/animation_send.flr - assets/animation_send_copper.flr - assets/animation_send_dark.flr - assets/animation_name_change.flr - assets/animation_name_change_copper.flr - assets/animation_name_change_dark.flr - assets/animation_sale.flr - assets/animation_sale_copper.flr - assets/animation_sale_dark.flr - assets/animation_transfer.flr - assets/animation_transfer_copper.flr - assets/animation_transfer_dark.flr - assets/animation_get_account.flr - assets/animation_get_account_copper.flr - assets/animation_get_account_dark.flr - assets/animation_search.flr - assets/illustration_backup.svg - assets/illustration_new_wallet.svg - assets/illustration_two_options.svg - assets/illustration_borrowed.svg - assets/illustration_security.svg - assets/illustration_backup_dark.svg - assets/illustration_new_wallet_dark.svg - assets/illustration_two_options_dark.svg - assets/illustration_borrowed_dark.svg - assets/illustration_security_dark.svg - assets/illustration_backup_copper.svg - assets/illustration_new_wallet_copper.svg - assets/illustration_two_options_copper.svg - assets/illustration_borrowed_copper.svg - assets/illustration_security_copper.svg - assets/country_phone_map.json # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. uses-material-design: true # To add assets to your application, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. # For details regarding adding assets from package dependencies, see # https://flutter.dev/assets-and-images/#from-packages # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a # "family" key with the font family name, and a "fonts" key with a # list giving the asset and other descriptors for the font. For # example: # fonts: # - family: Schyler # fonts: # - asset: fonts/Schyler-Regular.ttf # - asset: fonts/Schyler-Italic.ttf # style: italic # - family: Trajan Pro # fonts: # - asset: fonts/TrajanPro.ttf # - asset: fonts/TrajanPro_Bold.ttf # weight: 700 # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages ================================================ FILE: test/common/base58_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('Common.Base58', () { List> testData; setUp(() { testData = [ ["", ""], ["61", "2g"], ["626262", "a3gV"], ["636363", "aPEr"], [ "73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2" ], [ "00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L" ], ["516b6fcd0f", "ABnLTmg"], ["bf4f89001e670274dd", "3SEo3LWLoPntC"], ["572e4794", "3EFU7m"], ["ecac89cad93923c02321", "EJDM8drfXA6uyA"], ["10c8511e", "Rt5zm"], ["00000000000000000000", "1111111111"], [ "000111d38e5fc9071ffcd20b4a763cc9ae4f252bb4e48fd66a835e252ada93ff480d6dd43dc62a641155a5", "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" ], [ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", "1cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY" ] ]; }); test('Encode Base58', () { testData.forEach((v) { expect(Base58.encode(PDUtil.hexToBytes(v[0])), v[1]); }); }); test('Decode Base58', () { testData.forEach((v) { expect(PDUtil.byteToHex(Base58.decode(v[1])), v[0].toUpperCase()); }); }); }); } ================================================ FILE: test/common/coding/pascal/accountname_coder_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('common.coding.pascal.accountNameCoder', () { AccountNameCoder coderBS2; AccountNameCoder coderBS1; setUp(() { coderBS2 = AccountNameCoder(); coderBS1 = AccountNameCoder(byteSize: 1); }); test('can decode a pascalcoin account name', () { expect( coderBS2 .decodeFromBytes(PDUtil.hexToBytes('040074657374')) .toString(), 'test'); expect( coderBS2.decodeFromBytes(PDUtil.hexToBytes('040074657374')) is AccountName, true); expect( coderBS1.decodeFromBytes(PDUtil.hexToBytes('0474657374')).toString(), 'test'); expect( coderBS1.decodeFromBytes(PDUtil.hexToBytes('0474657374')) is AccountName, true); }); test('can encode a pascalcoin account name', () { expect(PDUtil.byteToHex(coderBS2.encodeToBytes(AccountName('test'))), '040074657374'); expect(PDUtil.byteToHex(coderBS1.encodeToBytes(AccountName('test'))), '0474657374'); }); }); } ================================================ FILE: test/common/coding/pascal/accountnumber_coder_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('common.coding.pascal.AccountNumberCoder', () { AccountNumberCoder coder; setUp(() { coder = AccountNumberCoder(); }); test('can decode a pascalcoin account number', () { expect( coder.decodeFromBytes(PDUtil.hexToBytes('EAFA1500')) is AccountNumber, true); expect(coder.decodeFromBytes(PDUtil.hexToBytes('EAFA1500')).toString(), '1440490-43'); }); test('can encode a pascalcoin account number', () { expect( PDUtil.byteToHex(coder.encodeToBytes(AccountNumber.fromInt(1440490))), 'EAFA1500'); }); }); } ================================================ FILE: test/common/coding/pascal/currency_coder_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('common.coding.pascal.CurrencyCoder', () { CurrencyCoder coder; setUp(() { coder = CurrencyCoder(); }); test('can encode a pascalcoin currency', () { expect(PDUtil.byteToHex(coder.encodeToBytes(Currency('0.0015'))), '0F00000000000000'); }); test('can decode a pascalcoin currency', () { expect( coder .decodeFromBytes(PDUtil.hexToBytes('0F00000000000000')) .toString(), '0.0015'); expect( coder.decodeFromBytes( PDUtil.hexToBytes('0474657374'.padRight(16, '0'))) is Currency, true); }); }); } ================================================ FILE: test/common/coding/pascal/keys/curve_coder_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('common.coding.pascal.keys.CurveCoder', () { CurveCoder curveCoder; setUp(() { curveCoder = CurveCoder(); }); test('can encode a pascalcoin key curve', () { Uint8List encoded = curveCoder.encodeToBytes(Curve.fromString(Curve.CN_SECP256K1).id); expect(PDUtil.byteToHex(encoded), 'CA02'); }); test('can decode a pascalcoin key curve', () { expect( curveCoder.decodeFromBytes(PDUtil.hexToBytes('CA02')) is Curve, true); expect(curveCoder.decodeFromBytes(PDUtil.hexToBytes('CA02')).id, 714); }); }); } ================================================ FILE: test/common/coding/pascal/keys/privatekey_coder_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../../fixtures/privatekey.dart'; void main() { group('common.coding.pascal.keys.PrivateKeyCoder', () { PrivateKeyCoder coder; PrivateKeyFixtures fixtures; setUp(() { coder = PrivateKeyCoder(); fixtures = PrivateKeyFixtures(); }); test('can decode a pascalcoin private key', () { fixtures.curve714.forEach((c) { PrivateKey pk = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); expect(pk.curve.id, 714); }); fixtures.curve715.forEach((c) { PrivateKey pk = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); expect(pk.curve.id, 715); }); fixtures.curve716.forEach((c) { PrivateKey pk = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); expect(pk.curve.id, 716); }); fixtures.curve729.forEach((c) { PrivateKey pk = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); expect(pk.curve.id, 729); }); }); test('can encode a pascalcoin private key', () { fixtures.curve714.forEach((c) { PrivateKey pk = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); expect(coder.encodeToHex(pk), c['enc_privkey']); expect(PDUtil.byteToHex(coder.encodeToBytes(pk)), c['enc_privkey']); }); fixtures.curve715.forEach((c) { PrivateKey pk = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); expect(coder.encodeToHex(pk), c['enc_privkey']); expect(PDUtil.byteToHex(coder.encodeToBytes(pk)), c['enc_privkey']); }); fixtures.curve716.forEach((c) { PrivateKey pk = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); expect(coder.encodeToHex(pk), c['enc_privkey']); expect(PDUtil.byteToHex(coder.encodeToBytes(pk)), c['enc_privkey']); }); fixtures.curve729.forEach((c) { PrivateKey pk = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); expect(coder.encodeToHex(pk), c['enc_privkey']); expect(PDUtil.byteToHex(coder.encodeToBytes(pk)), c['enc_privkey']); }); }); }); } ================================================ FILE: test/common/coding/pascal/keys/publickey_coder_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../../fixtures/publickey.dart'; void main() { group('common.coding.pascal.keys.PublicKeyCoder', () { PublicKeyCoder coder; PublicKeyFixtures fixtures; setUp(() { coder = PublicKeyCoder(); fixtures = PublicKeyFixtures(); }); test('can decode a pascalcoin pubkey', () { fixtures.curve714.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(PDUtil.byteToHex(key.x), c['x']); expect(PDUtil.byteToHex(key.y), c['y']); expect(key.curve.id, c['ec_nid']); }); fixtures.curve715.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(PDUtil.byteToHex(key.x), c['x']); expect(PDUtil.byteToHex(key.y), c['y']); expect(key.curve.id, c['ec_nid']); }); fixtures.curve716.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(PDUtil.byteToHex(key.x), c['x']); expect(PDUtil.byteToHex(key.y), c['y']); expect(key.curve.id, c['ec_nid']); }); fixtures.curve729.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(PDUtil.byteToHex(key.x), c['x']); expect(PDUtil.byteToHex(key.y), c['y']); expect(key.curve.id, c['ec_nid']); }); }); test('can decode a pascalcoin pubkey from base58', () { fixtures.curve714.forEach((c) { PublicKey key = coder.decodeFromBase58(c['b58_pubkey']); expect(PDUtil.byteToHex(key.x), c['x']); expect(PDUtil.byteToHex(key.y), c['y']); expect(key.curve.id, c['ec_nid']); }); fixtures.curve715.forEach((c) { PublicKey key = coder.decodeFromBase58(c['b58_pubkey']); expect(PDUtil.byteToHex(key.x), c['x']); expect(PDUtil.byteToHex(key.y), c['y']); expect(key.curve.id, c['ec_nid']); }); fixtures.curve716.forEach((c) { PublicKey key = coder.decodeFromBase58(c['b58_pubkey']); expect(PDUtil.byteToHex(key.x), c['x']); expect(PDUtil.byteToHex(key.y), c['y']); expect(key.curve.id, c['ec_nid']); }); fixtures.curve729.forEach((c) { PublicKey key = coder.decodeFromBase58(c['b58_pubkey']); expect(PDUtil.byteToHex(key.x), c['x']); expect(PDUtil.byteToHex(key.y), c['y']); expect(key.curve.id, c['ec_nid']); }); }); test('can encode a pascalcoin pubkey', () { fixtures.curve714.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(PDUtil.byteToHex(coder.encodeToBytes(key)), c['enc_pubkey']); }); fixtures.curve715.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(PDUtil.byteToHex(coder.encodeToBytes(key)), c['enc_pubkey']); }); fixtures.curve716.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(PDUtil.byteToHex(coder.encodeToBytes(key)), c['enc_pubkey']); }); fixtures.curve729.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(PDUtil.byteToHex(coder.encodeToBytes(key)), c['enc_pubkey']); }); }); test('can encode a pascalcoin pubkey to base58', () { fixtures.curve714.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(coder.encodeToBase58(key), c['b58_pubkey']); }); fixtures.curve715.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(coder.encodeToBase58(key), c['b58_pubkey']); }); fixtures.curve716.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(coder.encodeToBase58(key), c['b58_pubkey']); }); fixtures.curve729.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(coder.encodeToBase58(key), c['b58_pubkey']); }); }); }); } ================================================ FILE: test/common/coding/pascal/ophash_coder_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../fixtures/operation_hash.dart'; void main() { group('common.coding.pascal.OperationHashCoder', () { OperationHashFixtures fixture; setUp(() { fixture = OperationHashFixtures(); }); test('can decode a pascalcoin Operationhash', () { fixture.hashes.forEach((hashData) { OperationHash oph = OperationHashCoder() .decodeFromBytes(PDUtil.hexToBytes(hashData['ophash'])); expect(oph.block, hashData['block']); expect(oph.account.account, hashData['account']); expect(oph.nOperation, hashData['n_operation']); expect( PDUtil.byteToHex(oph.md160), hashData['ophash'].substring( hashData['ophash'].length - 40, hashData['ophash'].length)); }); }); test('can encode a pascalcoin Operationhash', () { fixture.hashes.forEach((hashData) { OperationHash opHash = OperationHash( hashData['block'], hashData['account'], hashData['n_operation'], PDUtil.hexToBytes(hashData['ophash'].substring( hashData['ophash'].length - 40, hashData['ophash'].length))); String hex = PDUtil.byteToHex(OperationHashCoder().encodeToBytes(opHash)); expect(hex, hashData['ophash']); }); }); }); } ================================================ FILE: test/common/coding/pascal/optype_coder_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('common.coding.pascal.OpTypeCoder', () { test('can encode a pascalcoin opType in int8', () { expect(PDUtil.byteToHex(OpTypeCoder(1).encodeToBytes(9)), '09'); }); test('can decode a pascalcoin opType to int8', () { expect(OpTypeCoder(1).decodeFromBytes(PDUtil.hexToBytes('09')), 9); }); test('can encode a pascalcoin opType in int16', () { expect(PDUtil.byteToHex(OpTypeCoder(2).encodeToBytes(9)), '0900'); }); test('can decode a pascalcoin opType to int16', () { expect(OpTypeCoder(2).decodeFromBytes(PDUtil.hexToBytes('0900')), 9); }); test('can encode a pascalcoin opType in int32', () { expect(PDUtil.byteToHex(OpTypeCoder(4).encodeToBytes(9)), '09000000'); }); test('can decode a pascalcoin opType to int32', () { expect(OpTypeCoder(4).decodeFromBytes(PDUtil.hexToBytes('09000000')), 9); }); test('cannot handle a wrong bytesize', () { expect(() => OpTypeCoder(100), throwsException); }); }); } ================================================ FILE: test/common/fixtures/operation_hash.dart ================================================ class OperationHashFixtures { List> hashes; OperationHashFixtures() { hashes = [ { "block": 145184, "account": 725390, "ophash": "203702008E110B0001000000CED0FF597661A01924931D1DD59AC782E7148C35", "n_operation": 1 }, { "block": 133249, "account": 318389, "ophash": "81080200B5DB040006000000AF4532D33332CF689358A29466B52B7AEB07ED93", "n_operation": 6 }, { "block": 97136, "account": 285630, "ophash": "707B0100BE5B04004BEB0A00FA8C64AE68E9C3EDACD37CC57AB5EE8610A34759", "n_operation": 715595 }, { "block": 112779, "account": 285630, "ophash": "8BB80100BE5B0400DDFD1100680B88CC8FF6D6B55E3B219622C23EDDB2940989", "n_operation": 1179101 }, { "block": 132437, "account": 661655, "ophash": "5505020097180A00010000001384388202AEBD17C94FFE2D9623B76D3C31A5FD", "n_operation": 1 }, { "block": 222078, "account": 1109845, "ophash": "7E63030055EF1000010000006DE6B899C400C124196E20A45C18E9080A637E6A", "n_operation": 1 }, { "block": 88803, "account": 285630, "ophash": "E35A0100BE5B0400155C0800A72B9C62D9C6331A8D535B25B5CD8D33365336C4", "n_operation": 547861 }, { "block": 194180, "account": 274656, "ophash": "84F60200E03004000B0000000B209F2233318B7A58F79C494D52D106A182C21C", "n_operation": 11 }, { "block": 71450, "account": 168573, "ophash": "1A1701007D92020083000000E5AE8191B66E542D3D531B48FBB09F608499D8E1", "n_operation": 131 }, { "block": 94190, "account": 285630, "ophash": "EE6F0100BE5B040000F909009F899DF6688380CBB19A95F49FABB067E1920EF1", "n_operation": 653568 }, { "block": 96263, "account": 285630, "ophash": "07780100BE5B040013A70A007AB02F4EAD46C8C2998D00F753DE2BFBBCF1435E", "n_operation": 698131 }, { "block": 170823, "account": 853585, "ophash": "479B020051060D000100000034541D38FD23A54CF3A15C29EE1A4A10DFE6B07D", "n_operation": 1 }, { "block": 252072, "account": 1259770, "ophash": "A8D80300FA38130001000000DA08B38FC8F811EC48784BBE7EFE97D40090460D", "n_operation": 1 }, { "block": 123430, "account": 86646, "ophash": "26E20100765201009206000010984FE6B1C3E6FAB370A696BA8A2CBCBF00C600", "n_operation": 1682 }, { "block": 52225, "account": 250000, "ophash": "01CC000090D00300060000008A9870CB2E5E5619A42FD1EC7A636C19C08063F1", "n_operation": 6 }, { "block": 99563, "account": 285630, "ophash": "EB840100BE5B04003FC20B00666AAA11EBF1CEB03E45B6BDA1653914220A2ED8", "n_operation": 770623 }, { "block": 141094, "account": 285630, "ophash": "26270200BE5B040030712400DCE7F78F488F48A9CA9BF20B20D490C7B05B246C", "n_operation": 2388272 }, { "block": 21520, "account": 106250, "ophash": "105400000A9F01000100000044538FB5969E4408F4FD4DB4EF364A05C32DF749", "n_operation": 1 }, { "block": 71723, "account": 290760, "ophash": "2B180100C86F0400647900009624C9123D8AD699D3541C24B9917E9B24634DA5", "n_operation": 31076 }, { "block": 197456, "account": 986755, "ophash": "50030300830E0F000100000046BFA7E2D38C33F9FCE2EDF40802566E9704AADE", "n_operation": 1 }, { "block": 281842, "account": 1408660, "ophash": "F24C0400947E1500010000003C13E0389D971B1A96093CDC6885BE73C2EEB928", "n_operation": 1 }, { "block": 171733, "account": 285630, "ophash": "D59E0200BE5B04008FF5340017C8C199677E96C72BC407E03962879EAE23CE32", "n_operation": 3470735 }, { "block": 65352, "account": 285630, "ophash": "48FF0000BE5B0400995E01004092B10D2FACED75757E4B7EC1D572672554FF4B", "n_operation": 89753 }, { "block": 107122, "account": 285630, "ophash": "72A20100BE5B0400E9E20E0030E01EB1863C85116880B6FB78ECF02CC8AB661A", "n_operation": 975593 }, { "block": 188221, "account": 571630, "ophash": "3DDF0200EEB80800DB16000076206AA5E8CE273243735E5125F5B9B8529BA389", "n_operation": 5851 }, { "block": 207716, "account": 571630, "ophash": "642B0300EEB80800FD270000870874F538EAF1955AC165D4E49A6CED2F80E4D8", "n_operation": 10237 }, { "block": 127859, "account": 86646, "ophash": "73F30100765201000E0700004A7B2F31D45DE9431CE3AF944B435B7DA2A457A7", "n_operation": 1806 }, { "block": 140580, "account": 453213, "ophash": "242502005DEA06000200000086D2657961CD22C31D1896115D5717E159A8F763", "n_operation": 2 }, { "block": 163390, "account": 647858, "ophash": "3E7E0200B2E20900050000003EFE45958DDADD8FAE6F2C7F22641516590749F5", "n_operation": 5 }, { "block": 162887, "account": 813900, "ophash": "477C02004C6B0C00010000003B8FFAA508950218CC49287A7F289C0CCA00FB28", "n_operation": 1 }, { "block": 250717, "account": 285630, "ophash": "5DD30300BE5B0400A9124B005A9B9B5CBE40DBBEB596EEC47F22B055E1908245", "n_operation": 4919977 }, { "block": 137307, "account": 686010, "ophash": "5B180200BA770A0001000000C4D4ADF655617BD5055A2B9B933D2608E8219621", "n_operation": 1 }, { "block": 76273, "account": 285630, "ophash": "F1290100BE5B0400ECBA0400BA72424704CE3E70263CB2A44EC74D6A8BD901AC", "n_operation": 309996 }, { "block": 124621, "account": 143351, "ophash": "CDE60100F72F020079040000C3061A7A8FFCC4A7602A32897BCCF2C945F79E3F", "n_operation": 1145 }, { "block": 169233, "account": 845605, "ophash": "1195020025E70C0001000000249A2E8BEFA352052D2FABDF6809169EE13B9708", "n_operation": 1 }, { "block": 90199, "account": 285630, "ophash": "57600100BE5B040033BD08004A73B040F5B59DEA48AB3AEAC082242425C1CEA1", "n_operation": 572723 }, { "block": 71448, "account": 285630, "ophash": "18170100BE5B0400522E03001F425F1357D8E7A920A4CE58FB95EA7613302BEA", "n_operation": 208466 }, { "block": 112431, "account": 561565, "ophash": "2FB701009D910800010000001C80B1D02CA82F311BF317301B853FA5401CFAB4", "n_operation": 1 }, { "block": 192382, "account": 961340, "ophash": "7EEF02003CAB0E00010000004EBBACA38A5C4D0B17F01D353CE218521816C7CC", "n_operation": 1 }, { "block": 70858, "account": 168537, "ophash": "CA140100599202001F0000004E8124E483506D7DEFF310B411E1B9429172F35C", "n_operation": 31 }, { "block": 261001, "account": 1301680, "ophash": "89FB0300B0DC130001000000405368C194DCCF9006574E0795021587BB69B6C5", "n_operation": 1 }, { "block": 174259, "account": 285630, "ophash": "B3A80200BE5B0400E673360022E36363D1D9EC9D7E79F098B5AA61D1B9B41CBA", "n_operation": 3568614 }, { "block": 279728, "account": 34500, "ophash": "B0440400C4860000AC1F0000A5FAC040AC20F75B12CB9CD4D470C88A3FD5D10E", "n_operation": 8108 }, { "block": 110956, "account": 285630, "ophash": "6CB10100BE5B0400ECF51000B13946F2DEE508C1D112E28F3A214C0995BBF3C0", "n_operation": 1111532 }, { "block": 293348, "account": 819598, "ophash": "E47904008E810C00100000007330C3F25C4C112E3AC83B5D57699E7D70DCC3B4", "n_operation": 16 }, { "block": 176329, "account": 831690, "ophash": "C9B00200CAB00C001D000000698A367C0D85BC0C66322E3186A0E8057C5DF1E3", "n_operation": 29 }, { "block": 76493, "account": 285630, "ophash": "CD2A0100BE5B0400E4CF040016A722271659174361ECF5AE358123CD2220A385", "n_operation": 315364 }, { "block": 228406, "account": 1141480, "ophash": "367C0300E86A1100010000005729291FB3BB227B53CD7084B4BF91409433950E", "n_operation": 1 }, { "block": 69298, "account": 279500, "ophash": "B20E0100CC4304000A00000098F8611727E6F27484BDC1DD8F4020F6B5194A20", "n_operation": 10 }, { "block": 192904, "account": 963960, "ophash": "88F1020078B50E0001000000383AC4DFF56ECEABCAF67A780D992F2182B2D315", "n_operation": 1 }, { "block": 202057, "account": 571630, "ophash": "49150300EEB8080000230000CBD006E8FA86F458189436813CD623F32AADBE42", "n_operation": 8960 }, { "block": 266435, "account": 1331640, "ophash": "C3100400B85114000100000017F9F0144F0C0C3F67757D71E52B8EBCD5D0DC90", "n_operation": 1 }, { "block": 290745, "account": 1452690, "ophash": "B96F0400922A160001000000E9B6EC1DCCDEFC21B70743B04F1A455A9F2E0FE2", "n_operation": 1 }, { "block": 239823, "account": 1198570, "ophash": "CFA80300EA49120001000000B1AFE26A2706ADE1B098AFD11FD5AA8A939EDE89", "n_operation": 1 }, { "block": 175828, "account": 571630, "ophash": "D4AE0200EEB808009E0F0000D4670156A28528537B82C6484093C2110645BECF", "n_operation": 3998 }, { "block": 293273, "account": 1465830, "ophash": "99790400E65D1600010000006743EFCDB2969DE044F13901CD6F774690399D61", "n_operation": 1 }, { "block": 221015, "account": 300124, "ophash": "575F03005C9404003E310000BCCD047E70495F38AFFF3ADE2B03B3DB280D72D4", "n_operation": 12606 }, { "block": 75191, "account": 143351, "ophash": "B7250100F72F0200210300007A4654F2ACBF25A0B0BDD9D3563249ED950D6534", "n_operation": 801 }, { "block": 178365, "account": 359614, "ophash": "BDB80200BE7C0500020000005A9E0C4AE386F20CB1D9FF7BBEF70059EA089DB7", "n_operation": 2 }, { "block": 60105, "account": 285630, "ophash": "C9EA0000BE5B0400707D0000CE14549A0A555F5CAA568954C8AABB1B62F01D5C", "n_operation": 32112 }, { "block": 92803, "account": 285630, "ophash": "836A0100BE5B0400A57C09004723A6DE0BE9E464CAC1ED03ECB62EAC29629FA8", "n_operation": 621733 }, { "block": 167332, "account": 86646, "ophash": "A48D020076520100CB0D00000B71CC945B2211856F7C24DB3E9521E480B35B3D", "n_operation": 3531 }, { "block": 164798, "account": 823475, "ophash": "BE830200B3900C0001000000533908EEE6DD2DF06D1D19ABDF53A43DFBDCC032", "n_operation": 1 }, { "block": 40562, "account": 52405, "ophash": "729E0000B5CC000006000000E092C485BDF8F0371127E1B1A83263E5973D3AE5", "n_operation": 6 }, { "block": 258397, "account": 1291395, "ophash": "5DF1030083B4130001000000F43F9D8C32899E6D9319330B377443038287506D", "n_operation": 1 }, { "block": 18928, "account": 84585, "ophash": "F0490000694A0100010000002B1F777DFCC789CE3E5962931A136D77A2638188", "n_operation": 1 }, { "block": 187784, "account": 86646, "ophash": "88DD02007652010014130000CCF929C4F624BB0EB1954896CC9888D50A651482", "n_operation": 4884 }, { "block": 194929, "account": 974080, "ophash": "71F9020000DD0E0001000000E18CA90D602A67DA3EA12C950D4C75ADB3312BFC", "n_operation": 1 }, { "block": 93775, "account": 285630, "ophash": "4F6E0100BE5B040077D5090098C3EC32642DAC2AD38DEA6F59372A4FB39ABA92", "n_operation": 644471 }, { "block": 81932, "account": 409125, "ophash": "0C400100253E0600010000003DEEF919CDF79DC6E04E7DEB485F38F1A5C9C28C", "n_operation": 1 }, { "block": 94239, "account": 470580, "ophash": "1F700100342E0700010000009DA71AACF49B09796515A8894B7186736BC167E9", "n_operation": 1 }, { "block": 170212, "account": 850485, "ophash": "E498020035FA0C000100000017CFD48AC92E6AC56D3F3EDFF8416F158D4E1E04", "n_operation": 1 }, { "block": 173527, "account": 867105, "ophash": "D7A50200213B0D00010000004D587A8A4BDFC1EE5A85B3EF7DEA48071D6664FC", "n_operation": 1 }, { "block": 201999, "account": 86646, "ophash": "0F1503007652010084140000A2D7CDDCD804E4C5B2DFF006CCBB89A8F8B20CAF", "n_operation": 5252 }, { "block": 90868, "account": 285630, "ophash": "F4620100BE5B04009AED0800C2C081A29CE007ACFA50F1FDF8102FE6E7A09FA5", "n_operation": 585114 }, { "block": 187134, "account": 285630, "ophash": "FEDA0200BE5B04000F9D3D0015A5206A69E0CF38E35FA76DB86926A796EBC282", "n_operation": 4037903 }, { "block": 204281, "account": 1020865, "ophash": "F91D0300C1930F00010000007966FFE3C3BA795376C7B29B20BE163F081A2C13", "n_operation": 1 }, { "block": 86627, "account": 285630, "ophash": "63520100BE5B040083C90700D78FD4E7560CE92BFB92563090846F82B5028E79", "n_operation": 510339 }, { "block": 132044, "account": 86646, "ophash": "CC0302007652010074070000EB8F75D8380598D9B28A87EED7DC1865CC6C2CD3", "n_operation": 1908 }, { "block": 111237, "account": 285630, "ophash": "85B20100BE5B04003E201100ACC1299E4C04EE40A6FE27543220DDDC0F61C52F", "n_operation": 1122366 }, { "block": 147226, "account": 86646, "ophash": "1A3F02007652010084080000DE63792BAC08167C024F62FCA2F311DE6217E83A", "n_operation": 2180 }, { "block": 108113, "account": 285630, "ophash": "51A60100BE5B0400146C0F00D823419DF299490824AB38CA108B05256B9078AA", "n_operation": 1010708 }, { "block": 244497, "account": 571630, "ophash": "11BB0300EEB80800D12F0000A811D71E5523E788C0448A2833F0323A2DD29ACB", "n_operation": 12241 }, { "block": 37919, "account": 53275, "ophash": "1F9400001BD0000003000000B223226C31C4193E3D5100FA8C3F65D24986E8F3", "n_operation": 3 }, { "block": 102163, "account": 86646, "ophash": "138F01007652010057030000879342A080B1F5EF084D7EDE8147A86CB468BC11", "n_operation": 855 }, { "block": 251822, "account": 1258555, "ophash": "AED703003B34130001000000FAA7CC0063FC040603AA6C2180ACDCCEDA26F6FE", "n_operation": 1 }, { "block": 86280, "account": 285630, "ophash": "08510100BE5B0400E5B00700C0F754B00A3CD4A85B4487E10DA9D5806451FAD7", "n_operation": 504037 }, { "block": 97499, "account": 285630, "ophash": "DB7C0100BE5B04003E0B0B004A681FB2BEDF5C6140DA518C3037DFE7F627DE9B", "n_operation": 723774 }, { "block": 160692, "account": 571630, "ophash": "B4730200EEB80800F8080000FC4D04C70665735EE35009C6A4B2B4A1CE6193FE", "n_operation": 2296 }, { "block": 229189, "account": 123023, "ophash": "457F03008FE0010002000000B2B0E2400DEA5986288A4E3085CC14B03275D1B6", "n_operation": 2 }, { "block": 35168, "account": 173055, "ophash": "60890000FFA3020001000000BCE7EDFF83559295EC3970ECC62C117707379E64", "n_operation": 1 }, { "block": 231121, "account": 997350, "ophash": "D1860300E6370F00F1040000DDBBD92EAE041C2098F1CB1F3C88ED1C79F9E8DA", "n_operation": 1265 }, { "block": 67055, "account": 285630, "ophash": "EF050100BE5B040034B60100D2CA60221142050917078582FA984B99A8747567", "n_operation": 112180 }, { "block": 72983, "account": 285630, "ophash": "171D0100BE5B0400C3B20300730AFC1008446B1F90A930F89251AEE3AFC9D57D", "n_operation": 242371 }, { "block": 158892, "account": 793940, "ophash": "AC6C0200541D0C000100000088AD60A0BB3F08881D83E41B6191E2E5B118E04F", "n_operation": 1 }, { "block": 266230, "account": 1330525, "ophash": "F60F04005D4D1400010000004B7E4500CB6BC0257319DB704737DA8FDA38BC9B", "n_operation": 1 }, { "block": 140840, "account": 703685, "ophash": "28260200C5BC0A0001000000E4D3864A81B404F59DDBF9D75EABF5B788BBB5C1", "n_operation": 1 }, { "block": 190862, "account": 572489, "ophash": "8EE9020049BC080005000000A726157B53939D13B4C49A2CEF280A6E41BDF6F7", "n_operation": 5 }, { "block": 94676, "account": 285630, "ophash": "D4710100BE5B040035240A0065BB7A9F157C83C699E81306107471FA82C9E16B", "n_operation": 664629 }, { "block": 188212, "account": 571630, "ophash": "34DF0200EEB80800DA16000035A561AA5DC3F4F34E37525887DF0CD8B4D5E449", "n_operation": 5850 }, { "block": 61770, "account": 285630, "ophash": "4AF10000BE5B0400ECCD0000E07F75D4C4F46D0ABF0737A59E59998B580F5E48", "n_operation": 52716 }, { "block": 58801, "account": 285630, "ophash": "B1E50000BE5B04001542000060C3D5550E1A4A488B4AB8142B556D463732CE79", "n_operation": 16917 }, { "block": 208528, "account": 571630, "ophash": "902E0300EEB80800932800003F2F5671DE1E92AABFD355978C1D999BF93048A4", "n_operation": 10387 }, { "block": 89217, "account": 285630, "ophash": "815C0100BE5B0400E5780800E44532047B0B9212F26C302F0976163349B0B985", "n_operation": 555237 }, { "block": 90111, "account": 285630, "ophash": "FF5F0100BE5B04000AB70800B934CBCCE1386D6FCFAC2B80715FD57C0282F527", "n_operation": 571146 }, { "block": 116728, "account": 285630, "ophash": "F8C70100BE5B040090651400B57326C2ABE06BFDC3B0AB4E6570DCCA49EF79CA", "n_operation": 1336720 }, { "block": 73665, "account": 285630, "ophash": "C11F0100BE5B04009CE30300412D6968BB41DC3F258C828E9692F6C79B9387A7", "n_operation": 254876 }, { "block": 82559, "account": 285630, "ophash": "7F420100BE5B040028AE0600203D5B831220417C1753434CB78BD0A6B37A05DD", "n_operation": 437800 }, { "block": 194530, "account": 571630, "ophash": "E2F70200EEB80800EB1B0000CDF3BA49E701A45D9E25DD5A621C0CC5CA3901B4", "n_operation": 7147 }, { "block": 209658, "account": 571630, "ophash": "FA320300EEB808006029000023CF5E172CA19904C21BB4C3D91B5C2EC7F841A3", "n_operation": 10592 }, { "block": 62783, "account": 285630, "ophash": "3FF50000BE5B0400C0FC0000BFB658DD8082E4115AC1B85007AF4281F57A1554", "n_operation": 64704 }, { "block": 74807, "account": 285630, "ophash": "37240100BE5B0400783F04003769FC2D41F555965687163DE7E04209FDB0AA87", "n_operation": 278392 }, { "block": 281239, "account": 1405660, "ophash": "974A0400DC721500010000005DB200F28EDFC92CF4C9F72E81A7610A499AB653", "n_operation": 1 }, { "block": 111515, "account": 556985, "ophash": "9BB30100B97F0800010000006F465BE5DEC8FCE281575C1C7FDC29587EAAE079", "n_operation": 1 }, { "block": 263638, "account": 629030, "ophash": "D60504002699090013000000016A0C4863B33A6F69F791CACF24CA0A4F19FB26", "n_operation": 19 }, { "block": 274693, "account": 86646, "ophash": "05310400765201004318000039188CD9667B9F976B5BF2D5F30BAF90E9476418", "n_operation": 6211 }, { "block": 145877, "account": 571630, "ophash": "D5390200EEB808001A0400009116476EBE64A5934AF7E6EE2ACBAF80F4FA62C9", "n_operation": 1050 }, { "block": 232635, "account": 1162610, "ophash": "BB8C030072BD11000100000035D826FA47FBEBCC9B69A6339ECA8F289C5B0982", "n_operation": 1 }, { "block": 114056, "account": 285630, "ophash": "88BD0100BE5B0400FEB312005F36CE4BFA2A3512E881233280AFCADCF656628B", "n_operation": 1225726 }, { "block": 157764, "account": 788290, "ophash": "4468020042070C0001000000E3FAFAC8D1FBCC58DD6E8E43C3C7C5193E7E5FD1", "n_operation": 1 }, { "block": 263688, "account": 571630, "ophash": "08060400EEB808005D3200009772534E6F7709721A0A76837582F1171490EECB", "n_operation": 12893 }, { "block": 292859, "account": 1463755, "ophash": "FB770400CB55160001000000DAFD5713EDA956C3F125695A34173D6BB091B8BB", "n_operation": 1 }, { "block": 104942, "account": 285630, "ophash": "EE990100BE5B040028C00D00B6EFB26D548C06463EE3A9CB03A7C61595A5E88E", "n_operation": 901160 }, { "block": 61347, "account": 285630, "ophash": "A3EF0000BE5B0400B0B700001827E619E75FC0763CFDC8180C6B74F5DFEF8935", "n_operation": 47024 }, { "block": 69560, "account": 86646, "ophash": "B80F01007652010032010000738F97EBD7D8FEBAB90C9A2089D065BDF538CDFB", "n_operation": 306 }, { "block": 134178, "account": 285630, "ophash": "220C0200BE5B04007636200093F3B5CA5191D3877C4BE69901FAAC5569BA2713", "n_operation": 2111094 }, { "block": 232301, "account": 285630, "ophash": "6D8B0300BE5B04005F6A4900351663D952610BDC983F6008AB263C283EEEAA9D", "n_operation": 4811359 }, { "block": 290096, "account": 1449960, "ophash": "306D0400E81F160001000000FA7874E1FC3EF638E350ED8B626AB2117D9C133B", "n_operation": 1 }, { "block": 107529, "account": 285630, "ophash": "09A40100BE5B04006D1A0F0011D643A9AFCFE0A4A1A80CFB3BB6944F1B659412", "n_operation": 989805 }, { "block": 150808, "account": 285630, "ophash": "184D0200BE5B04003BE12900905C9AE5DE3E400C8C6FDAECF69807F72CDF8697", "n_operation": 2744635 }, { "block": 186914, "account": 934030, "ophash": "22DA02008E400E00010000005210BF069E762463B42948FE60F7047EDE7D3FEA", "n_operation": 1 }, { "block": 173997, "account": 869465, "ophash": "ADA7020059440D0001000000C78DE8ECC0E023C2DF12A4B5810D8165F25F825F", "n_operation": 1 }, { "block": 71162, "account": 285630, "ophash": "FA150100BE5B0400D011030058051ECD204AEACCD5BC9BA68ED97964F0F92A53", "n_operation": 201168 }, { "block": 242551, "account": 1212205, "ophash": "77B303002D7F12000100000007C0BF59D5CDC341C365E48A2D537EA8054C8B87", "n_operation": 1 }, { "block": 195974, "account": 571630, "ophash": "86FD0200EEB80800321D000058EA507FDE5F08683B412521B31F263AB8676B73", "n_operation": 7474 }, { "block": 109145, "account": 285630, "ophash": "59AA0100BE5B040077F70F00CA8BBAEE5E977C95DA4D3882D8F12E4343C8B00D", "n_operation": 1046391 }, { "block": 62303, "account": 290760, "ophash": "5FF30000C86F0400EB370000D80724773D1F5F89FB1AE6D9344C8E5F52E7B48E", "n_operation": 14315 }, { "block": 71577, "account": 285630, "ophash": "99170100BE5B0400E63A0300CE16145F8908EA85661C40C8A5AC8BDD537AE8F2", "n_operation": 211686 }, { "block": 57774, "account": 285275, "ophash": "AEE100005B5A0400010000004412B05D554B42A85E187414EA41080124C07CDB", "n_operation": 1 }, { "block": 117363, "account": 586300, "ophash": "73CA01003CF2080001000000B6EAAE86D0F1C147245F73302D02B0941CA60F76", "n_operation": 1 }, { "block": 169580, "account": 285630, "ophash": "6C960200BE5B040021BC330005C7ADFF4859D3C8EC222D6F7CEFADD61E319BFC", "n_operation": 3390497 }, { "block": 102351, "account": 285630, "ophash": "CF8F0100BE5B0400F7AE0C005196D77D6665A1601F4A6A428C19CB93F36240CF", "n_operation": 831223 }, { "block": 299368, "account": 86646, "ophash": "6891040076520100DE180000800F975D2FE6829F66CA496046CF6174A87AD02B", "n_operation": 6366 }, { "block": 218466, "account": 300124, "ophash": "625503005C940400363100009CD22D59DF15BDFA15E2EFEB241ED9C362BD5EB0", "n_operation": 12598 }, { "block": 108165, "account": 285630, "ophash": "85A60100BE5B0400FE710F002B1C3A2A502AF7D7E886E73FC921508D0BDBBE12", "n_operation": 1012222 }, { "block": 155239, "account": 571630, "ophash": "675E0200EEB80800F606000039DC724377F4183B0DAC4226DAFE4C72ABB52178", "n_operation": 1782 }, { "block": 153795, "account": 768465, "ophash": "C3580200D1B90B000100000096EAAA3EA3FBC7AD1612E8D2A6F6E682A11A8477", "n_operation": 1 }, { "block": 106682, "account": 285630, "ophash": "BAA00100BE5B04000EA50E00A133B14F6C2F7FE7AD1EF5C4A0B1C51158CBCA0E", "n_operation": 959758 }, { "block": 260844, "account": 491587, "ophash": "ECFA03004380070004000000EFD74506349E2B87492F05DAE0CC709B1A3C5E5B", "n_operation": 4 }, { "block": 232479, "account": 1161855, "ophash": "1F8C03007FBA1100010000000D10A2CC87A3B4C10C1D9CF0864D04A551042E68", "n_operation": 1 }, { "block": 133133, "account": 665135, "ophash": "0D0802002F260A0001000000DB762E45A755AA0484CFFC7E8F9EDAC72B577C1C", "n_operation": 1 }, { "block": 238342, "account": 1191135, "ophash": "06A30300DF2C120001000000E42B689A6B237BBF86FAB8E5627E72AA1AB411FB", "n_operation": 1 }, { "block": 106439, "account": 285630, "ophash": "C79F0100BE5B0400D08F0E007C26B08B76A233D462201CB27FA954912D22FA3A", "n_operation": 954320 }, { "block": 241799, "account": 300124, "ophash": "87B003005C94040098310000717C94CBA92E2947E9C75C4FD6BF9E87BCB35685", "n_operation": 12696 }, { "block": 226298, "account": 1130940, "ophash": "FA730300BC41110001000000D862785EC29CB2058DBB437DC8FCB1667384DD4A", "n_operation": 1 }, { "block": 87638, "account": 285630, "ophash": "56560100BE5B0400020F0800CA5537DDDBFABD1580A814F4C5AB1898A381B4AD", "n_operation": 528130 }, { "block": 141540, "account": 707175, "ophash": "E428020067CA0A0001000000834E56C62C04B3343E9BDBC8E0B91E2608903916", "n_operation": 1 }, { "block": 149795, "account": 748450, "ophash": "23490200A26B0B000100000004886D5E848ED49EEBE94D5F52A4A3C6EE29FB36", "n_operation": 1 }, { "block": 239296, "account": 1195940, "ophash": "C0A60300A43F12000100000038E52E64781A9A880D92A723D152000284239335", "n_operation": 1 }, { "block": 104019, "account": 277710, "ophash": "53960100CE3C040003000000B28572731F7E2987F40082D1A59F7625D78AA261", "n_operation": 3 }, { "block": 193661, "account": 571630, "ophash": "7DF40200EEB80800411B0000069C082E03E69FA45379D79CDE3EC5B03D4C3116", "n_operation": 6977 }, { "block": 278010, "account": 1389535, "ophash": "FA3D0400DF3315000100000031D4B65CDD2768A92B3BB4B4CA559FEC55CE568C", "n_operation": 1 }, { "block": 178347, "account": 891210, "ophash": "ABB802004A990D0001000000AB7D7113E4B4B0BC2653A8502C015EF0B96CCD2E", "n_operation": 1 }, { "block": 280624, "account": 1402580, "ophash": "30480400D466150001000000D343BD349CF3BAB5CBA6DAEF3A5D4C0C6B0C148F", "n_operation": 1 }, { "block": 85457, "account": 285630, "ophash": "D14D0100BE5B04001579070043877EA4B35C6DCFBB5D4D84B56BA56918884CD2", "n_operation": 489749 }, { "block": 80255, "account": 400725, "ophash": "7F390100551D060001000000A38D6E76745E9B923EEC90328FDE87CB982F6433", "n_operation": 1 }, { "block": 77342, "account": 285630, "ophash": "1E2E0100BE5B0400651005003F7C7988A5B860BAD60BB8CB77409AC41EB37A6A", "n_operation": 331877 }, { "block": 209827, "account": 571630, "ophash": "A3330300EEB80800832900005AE3863F315D46D41D5917BC0BC3F68FC876BF53", "n_operation": 10627 }, { "block": 198158, "account": 571630, "ophash": "0E060300EEB80800401F00000E55440894AF1582B5D674E615510F4C1A65A313", "n_operation": 8000 }, { "block": 80426, "account": 285630, "ophash": "2A3A0100BE5B04000802060044610EBD2D79B7C029F886148FA0E69E0F414A2C", "n_operation": 393736 }, { "block": 238840, "account": 1193670, "ophash": "F8A40300C63612000100000014880C77F57226C1F7DE931B20FCF1F90EEEA25C", "n_operation": 1 }, { "block": 68716, "account": 285630, "ophash": "6C0C0100BE5B04002831020028619D74B2B9ECEC777012B04859B0944D0047E5", "n_operation": 143656 }, { "block": 292700, "account": 1462945, "ophash": "5C770400A152160001000000B5ADA3DF99C505F2A1EB1E09B6405BB0E821E015", "n_operation": 1 }, { "block": 68986, "account": 344395, "ophash": "7A0D01004B4105000100000019CC21892FFCE87A7D234F52CA02743F379F9A5B", "n_operation": 1 }, { "block": 265638, "account": 285630, "ophash": "A60D0400BE5B0400F9F34C002EE867782E74FB8C0E8ADAD2F68FB09A33C2E27C", "n_operation": 5043193 }, { "block": 144286, "account": 720915, "ophash": "9E33020013000B0001000000FF7F863F765E476B5F682BA7655571FC2B72E915", "n_operation": 1 }, { "block": 66642, "account": 332620, "ophash": "520401004C13050001000000A4DCE7D45C9DA7EEA6E531B410FF33DE5AAA6C5B", "n_operation": 1 }, { "block": 151600, "account": 757395, "ophash": "30500200938E0B000100000052B1951288F3A830BA35D7DC9679059139058215", "n_operation": 1 }, { "block": 153023, "account": 86646, "ophash": "BF5502007652010005090000C84FAD5374E640B2988D3382E65C554692248B3E", "n_operation": 2309 }, { "block": 119180, "account": 595385, "ophash": "8CD10100B915090001000000D07732F741DDA13C9FE9310C8BDAF9288AA3EE28", "n_operation": 1 }, { "block": 73918, "account": 168536, "ophash": "BE200100589202002C010000EDD9A0C915674CE26733FF8A2701500C7419887D", "n_operation": 300 }, { "block": 59705, "account": 290760, "ophash": "39E90000C86F040050190000030BC52C9A23AC0EE0E29D43999D609309DA8B27", "n_operation": 6480 }, { "block": 108305, "account": 221465, "ophash": "11A70100196103000400000065D286BE627CBC9F9334A601CFF75C024020442D", "n_operation": 4 }, { "block": 181038, "account": 904620, "ophash": "2EC30200ACCD0D00010000003B32D1FD6CAD511E918328C33BACD8E303146015", "n_operation": 1 }, { "block": 115615, "account": 285630, "ophash": "9FC30100BE5B0400E67C1300FE0C57E6A662324C75141A1A0F011F03DA5B51E4", "n_operation": 1277158 }, { "block": 210997, "account": 285630, "ophash": "35380300BE5B0400967C4600737359BD842D7A2E7457DDF771B391A130263080", "n_operation": 4619414 }, { "block": 261690, "account": 1307920, "ophash": "3AFE030010F5130001000000BEE212DD8F2D21F441F4FBA5866EB9393F7593D5", "n_operation": 1 }, { "block": 159083, "account": 291000, "ophash": "6B6D0200B87004006C000000C492ACC1D624E7A8C57701AC96CB4865D3E8838E", "n_operation": 108 }, { "block": 64051, "account": 290760, "ophash": "33FA0000C86F040087480000225345F05F73EF6DC5E45EE3CFC758581D326236", "n_operation": 18567 }, { "block": 296695, "account": 1482925, "ophash": "F7860400ADA0160001000000079C0969F77EBDF742CFE3B72514AD8DACC7EA5A", "n_operation": 1 }, { "block": 133963, "account": 669290, "ophash": "4B0B02006A360A0001000000C7973826D40C04F6D18F064036F873CBB279D1F3", "n_operation": 1 }, { "block": 91744, "account": 285630, "ophash": "60660100BE5B0400842E0900CC55D13C97A54DA9C6D5713A6ABDCA678B80BD5E", "n_operation": 601732 }, { "block": 100817, "account": 285630, "ophash": "D1890100BE5B04008C230C001923A8F9209D16C034C705695EC39694BF2588C9", "n_operation": 795532 }, { "block": 199439, "account": 330025, "ophash": "0F0B030029090500993B0000A3519DAAAD1C0E3FDDED205B5D32275E3F7BECF1", "n_operation": 15257 }, { "block": 65986, "account": 285630, "ophash": "C2010100BE5B0400B4790100A868312C9C496B59A243853A88AE1FB57F10FD12", "n_operation": 96692 }, { "block": 160219, "account": 285630, "ophash": "DB710200BE5B040093932E005A5878DBD5D5DA502FEAC60E4686AEB068B2EEE3", "n_operation": 3052435 }, { "block": 60436, "account": 285630, "ophash": "14EC0000BE5B0400568C00001CBB76EE0452B7D7F394AB9ABAE18545F59E1EDD", "n_operation": 35926 }, { "block": 192620, "account": 962590, "ophash": "6CF002001EB00E000100000055A952B6F8D3846FE906A3C81C01174EB51D285D", "n_operation": 1 }, { "block": 182223, "account": 108004, "ophash": "CFC70200E4A5010005000000F910CB7C86F843477AE30BB1435E2C3CACCAA825", "n_operation": 5 }, { "block": 285209, "account": 760518, "ophash": "195A0400C69A0B0003000000F676409AD52C51B579A0A0A3F05571E1B40C986F", "n_operation": 3 }, { "block": 142108, "account": 571630, "ophash": "1C2B0200EEB80800FE020000B79BAC7485DCE04DA207789E24F0E203D9A4D2DF", "n_operation": 766 }, { "block": 74788, "account": 285630, "ophash": "24240100BE5B0400693D0400E7B09877122ABFC1AF0E15B23B5435F53A8272F5", "n_operation": 277865 }, { "block": 61414, "account": 290760, "ophash": "E6EF0000C86F04000F2D0000D79AA87310A9AAA0BC48B124B2762A2337BEEB2A", "n_operation": 11535 }, { "block": 97630, "account": 285630, "ophash": "5E7D0100BE5B0400D7160B007A50498A5259C3E12A7D28D363451D1E834D95BC", "n_operation": 726743 }, { "block": 111125, "account": 285630, "ophash": "15B20100BE5B04007011110054F513E2FD3340625F164B420D02EDF2152B34AD", "n_operation": 1118576 }, { "block": 147689, "account": 737900, "ophash": "E94002006C420B00010000008B679215137E561942F85307D323EAC1ABF3DEAA", "n_operation": 1 }, { "block": 269978, "account": 486913, "ophash": "9A1E0400016E070008000000DB97F36609F2F7594572BA6F4893B97351D52153", "n_operation": 8 }, { "block": 127129, "account": 635105, "ophash": "99F00100E1B0090001000000291EA616D511D331D33052F79BF5429BF394D911", "n_operation": 1 }, { "block": 72978, "account": 285630, "ophash": "121D0100BE5B0400D4B103007904A0D4A1688623ED6F90D093C56FF99FE85616", "n_operation": 242132 }, { "block": 270708, "account": 1352990, "ophash": "742104001EA51400010000009311521B68496FC38F818FEA1585926C0828D1E2", "n_operation": 1 }, { "block": 135193, "account": 285630, "ophash": "19100200BE5B04008ACE200004A62782CB17240963573B0CADF98A12DA45C842", "n_operation": 2150026 }, { "block": 70975, "account": 285630, "ophash": "3F150100BE5B04009AFE020038E9EE0011192A780757EBF01223AF3F02E7B5D5", "n_operation": 196250 }, { "block": 78820, "account": 285630, "ophash": "E4330100BE5B04008B840500EB31976BBD39522BFBADE4A53F9EB16249D13E7A", "n_operation": 361611 }, { "block": 74357, "account": 164080, "ophash": "75220100F0800200A8010000F5678895A31BFE35F9B55104D52302C74C37EB32", "n_operation": 424 }, { "block": 83024, "account": 285630, "ophash": "50440100BE5B04002FBD06007830B13DB5E0B3244CAA32910EB26434E08A51DF", "n_operation": 441647 }, { "block": 195775, "account": 825070, "ophash": "BFFC0200EE960C0048000000F3724DFE6C38C86AB864C19D294380D06519F3BE", "n_operation": 72 }, { "block": 192640, "account": 277924, "ophash": "80F00200A43D040005000000EA24689E5CAAD61985A78551C7963B68ED695D60", "n_operation": 5 }, { "block": 199528, "account": 997090, "ophash": "680B0300E2360F0001000000A3F20E1CD852DC6C5A13B59ACC59080C54442412", "n_operation": 1 }, { "block": 118128, "account": 590120, "ophash": "70CD010028010900010000002E7CD958B815D1C346EA7353BC190927E8BF4FC0", "n_operation": 1 }, { "block": 63818, "account": 285630, "ophash": "4AF90000BE5B04005E2A0100FD8B53EC95427D8A140472A3DF0F80969BD26A19", "n_operation": 76382 }, { "block": 175911, "account": 879020, "ophash": "27AF0200AC690D00010000006B3012B5398F907C7C0C4F21057C43B9C62594F3", "n_operation": 1 }, { "block": 62560, "account": 285630, "ophash": "60F40000BE5B0400AAF1000049F96C59BE2E0611C611EB3945E213FC1E0C3AD6", "n_operation": 61866 }, { "block": 154255, "account": 770745, "ophash": "8F5A0200B9C20B0001000000F6045C22F8BDD3FF69ECD6DF873819A5E5C6B7C0", "n_operation": 1 }, { "block": 141790, "account": 708410, "ophash": "DE2902003ACF0A0001000000B7351179FF015A3C23C045BD4DCB249ECE4E3CEA", "n_operation": 1 }, { "block": 129348, "account": 285630, "ophash": "44F90100BE5B0400DA3E1D0022B2996A3959FEC1F0EB3A39A76349062650DCF1", "n_operation": 1916634 }, { "block": 186339, "account": 931175, "ophash": "E3D7020067350E0001000000BA5F55D3D9F9DC7242F0AED2618F959F47321D57", "n_operation": 1 }, { "block": 188972, "account": 571630, "ophash": "2CE20200EEB80800671700004DB6DE9F3CB9B068D467012BDAEF80B620A93C1C", "n_operation": 5991 }, { "block": 115377, "account": 3000, "ophash": "B1C20100B80B00000B000000EED3BDF136A7D2E2622DA5BDF4D2518E2143C4C1", "n_operation": 11 }, { "block": 268965, "account": 606554, "ophash": "A51A04005A41090002000000BED47E723749CEC40EBD075E354E12E621F8DEF7", "n_operation": 2 }, { "block": 88544, "account": 285630, "ophash": "E0590100BE5B0400244B0800FA3C3C477EA82F11FC9D0CE2ED348C7277BFCFA6", "n_operation": 543524 }, { "block": 208259, "account": 571630, "ophash": "832D0300EEB80800652800009B28B65D634148B65371FCBAFE74122114E79ABD", "n_operation": 10341 }, { "block": 101640, "account": 285630, "ophash": "088D0100BE5B0400FD6E0C00C740C9D2E2A54276F11E5DAF54F13C16668B648C", "n_operation": 814845 }, { "block": 121985, "account": 285630, "ophash": "81DC0100BE5B04007A3C18005AD65BD739082482F86244FC7DC729DC3F516343", "n_operation": 1588346 }, { "block": 173397, "account": 866425, "ophash": "55A5020079380D0001000000C5C6739341E83B27F273810B085198D305988B92", "n_operation": 1 }, { "block": 126565, "account": 632295, "ophash": "65EE0100E7A50900010000007C027B6643F3C6B0BFF291C2AD1CDF55519CDF60", "n_operation": 1 }, { "block": 154868, "account": 288887, "ophash": "F45C0200776804000100000026646F5B9F93C150724522AECD342C438237D951", "n_operation": 1 }, { "block": 153458, "account": 766735, "ophash": "725702000FB30B0001000000FDCEA3DB20BF980BE39EF77906E5A8A24B8ED114", "n_operation": 1 }, { "block": 162925, "account": 86646, "ophash": "6D7C0200765201006B0B00001D799BD966B1E5FCBE16689AA47EF90D17D6CAA1", "n_operation": 2923 }, { "block": 260980, "account": 496961, "ophash": "74FB0300419507000600000091D2A0F5B48313B996537006DB86C216D64C6D8B", "n_operation": 6 }, { "block": 78311, "account": 76623, "ophash": "E73101004F2B010003000000D0FD1F5AB2D073A52264E04484ECDD9CE9F39689", "n_operation": 3 }, { "block": 143628, "account": 280240, "ophash": "0C310200B046040003000000A5C8EA1B98E9DAC9D60FD98A412BD6E555CEF45A", "n_operation": 3 }, { "block": 258737, "account": 332699, "ophash": "B1F203009B130500030000008C2EB0A10FFE8D9889F08DBC381AB8EB6F5DE6C6", "n_operation": 3 }, { "block": 177174, "account": 70666, "ophash": "16B402000A140100830000007EE3FE8F35E90E5617C758DFE04625617E2723D1", "n_operation": 131 }, { "block": 62168, "account": 290760, "ophash": "D8F20000C86F04004F3600000164E1B59D5FB4A50EADD29609B0B94FAF623B57", "n_operation": 13903 }, { "block": 222586, "account": 571630, "ophash": "7A650300EEB80800DC2B00001DF41A07D42C99D6A2434AB6E9FB33CD09372E17", "n_operation": 11228 }, { "block": 16288, "account": 80125, "ophash": "A03F0000FD3801000100000084069278A93E2404F22A0392B108CE72D6AD6959", "n_operation": 1 }, { "block": 87018, "account": 434445, "ophash": "EA5301000DA10600010000004B7785333181F52A375C584BA1C7A1AE21C10DE3", "n_operation": 1 }, { "block": 105851, "account": 285630, "ophash": "7B9D0100BE5B040072400E00328AE38086FF9C72903C0BE1C8F34D55E7CE9D5D", "n_operation": 934002 }, { "block": 63415, "account": 290760, "ophash": "B7F70000C86F0400D7420000233A397CA4F1098F31C4B09A911F3F17D102602B", "n_operation": 17111 }, { "block": 61140, "account": 290760, "ophash": "D4EE0000C86F0400302A00009E395ADECE28BAD24AEAE35ABE98CF797E50D797", "n_operation": 10800 }, { "block": 176694, "account": 187687, "ophash": "36B2020027DD0200070000009D9F27E63C413B9493675F96FB6900C5981ACB49", "n_operation": 7 }, { "block": 68418, "account": 290760, "ophash": "420B0100C86F0400CF6A0000A6BA0802126499CAC22A85DA720F28A9E436B0E5", "n_operation": 27343 }, { "block": 87692, "account": 285630, "ophash": "8C560100BE5B0400A012080020ACF23642B3F38EF9C4D8A60474461916BE495B", "n_operation": 529056 }, { "block": 116436, "account": 285630, "ophash": "D4C60100BE5B0400E13C140020DCAE36BC9BF08E90293ED2B821B41CCB32B998", "n_operation": 1326305 }, { "block": 67196, "account": 8777, "ophash": "7C06010049220000020000008195AF37A669F30B23966F56E1A20338674F7214", "n_operation": 2 }, { "block": 102062, "account": 5700, "ophash": "AE8E0100441600000B00000087B3EE2A8F68AFE2BA6C98A61C5367C83CCBA2C6", "n_operation": 11 }, { "block": 192490, "account": 274806, "ophash": "EAEF020076310400080000009892C1D63CA78796E521FC4D52B282EDFA990CA4", "n_operation": 8 }, { "block": 212443, "account": 571630, "ophash": "DB3D0300EEB80800462A000097FABA85B9F4502B59FED76F9B18065CC22BB610", "n_operation": 10822 }, { "block": 154862, "account": 110027, "ophash": "EE5C0200CBAD010037000000B17AE42708FADA0F019606032068A6032F176BE2", "n_operation": 55 }, { "block": 281001, "account": 1404440, "ophash": "A9490400186E1500010000003B8F699EDDD094F83E96628D9136C3F6981F08A8", "n_operation": 1 }, { "block": 278332, "account": 1391145, "ophash": "3C3F0400293A1500010000003B1B6FAF0CFDCBF62C36E5B9314C93FE37450A24", "n_operation": 1 }, { "block": 139716, "account": 698065, "ophash": "C4210200D1A60A0001000000D5B0934C694BAF6F97E9833B840D30D8ED88CE5C", "n_operation": 1 }, { "block": 111983, "account": 285630, "ophash": "6FB50100BE5B04009D881100B66EA066E986F62EE4B890533D51CFB3DE19CBC3", "n_operation": 1149085 }, { "block": 282275, "account": 819528, "ophash": "A34E040048810C0002000000532805B76DD9A990876DD3A8C45A1F8D2896A746", "n_operation": 2 }, { "block": 133039, "account": 664670, "ophash": "AF0702005E240A0001000000AB1FAFA1642BE7581FBB164E8C07416104430B1F", "n_operation": 1 }, { "block": 270592, "account": 571630, "ophash": "00210400EEB808000E330000750597722571AF2B0C09C26660032827C0A76DE2", "n_operation": 13070 }, { "block": 203372, "account": 1016345, "ophash": "6C1A030019820F0001000000132923B2846980F8A089A0337ED90FE5C3023F1C", "n_operation": 1 }, { "block": 109270, "account": 545825, "ophash": "D6AA01002154080001000000747B2FCE52876440DCB0F66E477CE024C5EDDF66", "n_operation": 1 }, { "block": 238685, "account": 1192890, "ophash": "5DA40300BA331200010000004BFA69C13A1E482D6BDFE6AEEA3DEC37CE2E288F", "n_operation": 1 }, { "block": 66953, "account": 319055, "ophash": "890501004FDE040002000000055BBB62952AA382C23A187AD5A7202C1032BDE5", "n_operation": 2 }, { "block": 268621, "account": 498985, "ophash": "4D190400299D070003000000FD96CF14F099E78A522A03E943DB03D5F212EE5E", "n_operation": 3 }, { "block": 199725, "account": 998040, "ophash": "2D0C0300983A0F00010000009EBEA6A74E12AF5993822D61C3A9AD65AE131017", "n_operation": 1 }, { "block": 272716, "account": 499126, "ophash": "4C290400B69D0700030000002BF367AA4D35AD52FD79B05FB8D1D8DD0A48A9B0", "n_operation": 3 }, { "block": 128157, "account": 640255, "ophash": "9DF40100FFC40900010000006FA26FD6D1D52BF73AB2679EAD3504D2B1E26DBB", "n_operation": 1 }, { "block": 193219, "account": 965545, "ophash": "C3F20200A9BB0E00010000005834FC26E23903935BA5B8136C38053C90B51EE4", "n_operation": 1 }, { "block": 192498, "account": 961975, "ophash": "F2EF0200B7AD0E0001000000C5B26D5018E7166B50B9D647D3EBBA162F043248", "n_operation": 1 }, { "block": 142377, "account": 711345, "ophash": "292C0200B1DA0A00010000005236B65489FBA035B1055F9D69DC929C6D614BBE", "n_operation": 1 }, { "block": 115071, "account": 273457, "ophash": "7FC10100312C04000800000003958CB6E7DFB84C8177B6709B85025A66B5BE75", "n_operation": 8 }, { "block": 100755, "account": 285630, "ophash": "93890100BE5B04002C1C0C00AFC67A612F9E5A0743722C1A66DD336695CB991C", "n_operation": 793644 }, { "block": 262472, "account": 1311815, "ophash": "480104004704140001000000E920C5A6EA14C6272CF6B7227C2D7C4D74A78E7B", "n_operation": 1 }, { "block": 65583, "account": 143351, "ophash": "2F000100F72F0200C2020000E1A120FE8294782A53018C22364C855359F88A6C", "n_operation": 706 }, { "block": 191389, "account": 86646, "ophash": "9DEB02007652010084130000BEFA71B5B4F679FD8DEA95DD9A7C6A4E812F4BD4", "n_operation": 4996 }, { "block": 209856, "account": 1048755, "ophash": "C0330300B30010000100000052161C6F0532D8967A52DABFDC4135D967745027", "n_operation": 1 }, { "block": 179459, "account": 86646, "ophash": "03BD020076520100E9110000584F21A435190ACC982EF4AEF5CFCBC3D64A65BD", "n_operation": 4585 }, { "block": 94882, "account": 285630, "ophash": "A2720100BE5B04000F360A001BA089283D45FC711FB95F745E07E7947F5CEC63", "n_operation": 669199 }, { "block": 91212, "account": 285630, "ophash": "4C640100BE5B0400E1060900225D77444FA41E3BE05618072F1FE581C2C325EB", "n_operation": 591585 }, { "block": 64008, "account": 285630, "ophash": "08FA0000BE5B0400F7310100EC7A451304C18584A764F21AF26C8AAC11E50901", "n_operation": 78327 }, { "block": 224530, "account": 381309, "ophash": "126D03007DD1050043000000FAC405D758B00AD17D50D3C00DA3406F45B8AF41", "n_operation": 67 }, { "block": 300641, "account": 1502685, "ophash": "61960400DDED16000100000007DEB18409224CDFDB87D6C0ABBC1B6A4D5B743B", "n_operation": 1 }, { "block": 142235, "account": 710665, "ophash": "9B2B020009D80A0001000000B0AFBB1C212FE9DFD2685901D1F20E01BD85BDD8", "n_operation": 1 }, { "block": 74887, "account": 290760, "ophash": "87240100C86F040090850000BCCDA654BC4FEDF51B4A092262C2E86720008884", "n_operation": 34192 }, { "block": 168152, "account": 126651, "ophash": "D8900200BBEE0100070000008E0AC8AA36B3A5813D100390E0DD2C0DA1123E7A", "n_operation": 7 }, { "block": 130854, "account": 86646, "ophash": "26FF0100765201004E0700002A84EFA0509C52B51CEA202429C052DFFC02938D", "n_operation": 1870 }, { "block": 106139, "account": 285630, "ophash": "9B9E0100BE5B040089680E005CE5E82B05DBF7EDD4FDA5FD81A413D3A6178465", "n_operation": 944265 }, { "block": 96537, "account": 89007, "ophash": "19790100AF5B010014000000BDE0DCA8F813BF5CFE789A1A7183EF4E3D7D7934", "n_operation": 20 }, { "block": 281586, "account": 1407400, "ophash": "F24B0400A8791500010000001CF8FF65A6AFE7178CE9FD88D121D2F1DD458069", "n_operation": 1 }, { "block": 184240, "account": 920670, "ophash": "B0CF02005E0C0E0001000000D5734C8DF8E624FCE463B23A3FE9ACEEA556A7CA", "n_operation": 1 }, { "block": 98683, "account": 285630, "ophash": "7B810100BE5B0400BD760B006A9BFB5B4F7653551C09DA91CD92AA25BCEDD93D", "n_operation": 751293 }, { "block": 53656, "account": 266030, "ophash": "98D100002E0F040001000000932C2A8CD9E8B262EA2D8689EC153A5E97D16DEE", "n_operation": 1 }, { "block": 207520, "account": 1037045, "ophash": "A02A0300F5D20F00010000002238E5068DCE2DE5EBEAF0CE3F7AB71A07F7CC38", "n_operation": 1 }, { "block": 18362, "account": 91200, "ophash": "BA470000406401000100000088BA572EBB52A21AC173340DD7C57FE5025CE2A3", "n_operation": 1 }, { "block": 144684, "account": 285630, "ophash": "2C350200BE5B0400F96B2600B4C55F31BF2B6B23738D659A4F630B3D6424FCE8", "n_operation": 2518009 }, { "block": 201046, "account": 1004700, "ophash": "561103009C540F000100000082FB2172B4704DD12C05C1F8074B235B7128F132", "n_operation": 1 }, { "block": 87388, "account": 436320, "ophash": "5C55010060A8060001000000EEE2226358311846A794DFF3478D6B06E7BC3DAD", "n_operation": 1 }, { "block": 242880, "account": 1213840, "ophash": "C0B4030090851200010000004D9970D1C9D3E6C2559D042DD069F08DEC7F999D", "n_operation": 1 }, { "block": 96598, "account": 285630, "ophash": "56790100BE5B04009BBC0A000D033E80E36C8430977078C2CC5FA5F146538651", "n_operation": 703643 }, { "block": 254900, "account": 1273950, "ophash": "B4E303005E70130001000000061020BA3DB9050659AB719EFF4A0B85FE7D5FA7", "n_operation": 1 }, { "block": 114649, "account": 572700, "ophash": "D9BF01001CBD080001000000FE26085E50BFC27208A7FD6A2A5BB943111E6313", "n_operation": 1 }, { "block": 146469, "account": 731830, "ophash": "253C0200B62A0B000100000029F0CFC44AAD76DE0BC8077476D1BAD668E6AEC4", "n_operation": 1 }, { "block": 273753, "account": 1368220, "ophash": "592D04009CE0140001000000B14A040D25442A5493DE14AB4A9117ED7D7AF2FC", "n_operation": 1 }, { "block": 159573, "account": 797340, "ophash": "556F02009C2A0C000100000054DDF024B531F71C7F694E62A5290D2998854569", "n_operation": 1 }, { "block": 116746, "account": 583210, "ophash": "0AC801002AE6080001000000C50B62E84A64E8EE0B4B3AC72D589F1B1DCF9D2C", "n_operation": 1 }, { "block": 252962, "account": 1264265, "ophash": "22DC0300894A13000100000091900DA53DDD120E293861C628452142D7197056", "n_operation": 1 }, { "block": 143633, "account": 273374, "ophash": "11310200DE2B040005000000F5848050FAB5557E4C69E92939D712B4386A4D7C", "n_operation": 5 }, { "block": 96054, "account": 285630, "ophash": "36770100BE5B0400B0990A00A48B953E9BFBF28B2CBC92CCDFB9334A38FCB806", "n_operation": 694704 }, { "block": 108636, "account": 285630, "ophash": "5CA80100BE5B04006AAE0F00C8C83D301BD2BD709EF663CB7C2B5434253047FA", "n_operation": 1027690 }, { "block": 191259, "account": 353958, "ophash": "1BEB0200A666050005000000B04BCDC560A1D335E6674FD3966107FDDCC096A7", "n_operation": 5 }, { "block": 84274, "account": 420805, "ophash": "32490100C56B0600010000004CA17997436AB7943D7A91AACACE4CAD3422FBA6", "n_operation": 1 }, { "block": 66264, "account": 330800, "ophash": "D8020100300C0500010000005A2A830E3ECE531282B5523417DF0791810F9386", "n_operation": 1 }, { "block": 161129, "account": 285630, "ophash": "69750200BE5B040005222F000E4C2F29B3BA06DEB710242474499C06661F069C", "n_operation": 3088901 }, { "block": 167653, "account": 285630, "ophash": "E58E0200BE5B0400ADBA3200703DF4F140DFB40A1F7233018FA60FF2340B7C10", "n_operation": 3324589 }, { "block": 248791, "account": 1243410, "ophash": "D7CB030012F91200010000007F72374C11D166C8A8DDF3EF7960489BF310B973", "n_operation": 1 }, { "block": 176927, "account": 86646, "ophash": "1FB30200765201005E110000D9EB6B29D13ABCBA3A58581FC0C0D83581763852", "n_operation": 4446 }, { "block": 35167, "account": 171705, "ophash": "5F890000B99E020001000000D48B7B0CD8C2ECFB19826E05BB1534280FB96A56", "n_operation": 1 }, { "block": 214507, "account": 285630, "ophash": "EB450300BE5B0400972C4700802EB477F4B9CBAB99FAC0F3468CDC6BC3F0207B", "n_operation": 4664471 }, { "block": 166256, "account": 86646, "ophash": "7089020076520100160D000094ADA73BAF4C3EFE0FEF79F109727374FC830252", "n_operation": 3350 }, { "block": 279837, "account": 571630, "ophash": "1D450400EEB80800C433000031B0011C8F6A9B9AC7ECB325AAE5B0C3F5613A0D", "n_operation": 13252 }, { "block": 59194, "account": 285630, "ophash": "3AE70000BE5B0400E6520000C44551ABBE8C26AC05091E2C5AA15384092DCFC4", "n_operation": 21222 }, { "block": 58144, "account": 285630, "ophash": "20E30000BE5B04000B260000AD91A5390E2CCF6CCAD06D7BFCE5B712A44FA7A9", "n_operation": 9739 }, { "block": 26078, "account": 127190, "ophash": "DE650000D6F0010001000000C4F2818096834B832692D7F97983F7EC5E8C9D82", "n_operation": 1 }, { "block": 171250, "account": 285630, "ophash": "F29C0200BE5B0400E1AE340017663710C7B766A373DE9123DE9EFF7E791F4B09", "n_operation": 3452641 }, { "block": 299487, "account": 646070, "ophash": "DF910400B6DB09000D000000256C2B6576FEBC1DA3D2F2061542C7F662C1DF90", "n_operation": 13 }, { "block": 63819, "account": 285630, "ophash": "4BF90000BE5B0400622A010075896B6495E7BD67C01B034F5348D5F471CCD6EB", "n_operation": 76386 }, { "block": 81425, "account": 285630, "ophash": "113E0100BE5B040056500600D97DDCF8926055FA732ED56CC994ED55745A22B2", "n_operation": 413782 }, { "block": 241388, "account": 1206405, "ophash": "ECAE03008568120001000000176DF43B372231B14E9E51E4BBE9E6C43D49B6E7", "n_operation": 1 }, { "block": 184690, "account": 548246, "ophash": "72D10200965D080002000000F142231D2C48997802804C59CCDDB41D9CD8F380", "n_operation": 2 }, { "block": 166748, "account": 833180, "ophash": "5C8B02009CB60C0001000000324D269A0A960FCFBAFA3FC320764F4E725A6058", "n_operation": 1 }, { "block": 279267, "account": 1395820, "ophash": "E34204006C4C15000100000013EA80DF4CAE91FA9DB01DA1B0E725CE3C263862", "n_operation": 1 }, { "block": 82196, "account": 143351, "ophash": "14410100F72F02006A03000024A514DD3B662E9E143DA1CE2FB8925A2FF4818C", "n_operation": 874 }, { "block": 175149, "account": 523548, "ophash": "2DAC02001CFD070002000000FC73953AFFD16E22867BA8094C2A9DB79DC6D470", "n_operation": 2 }, { "block": 182400, "account": 911465, "ophash": "80C8020069E80D0001000000C513A518C594719979DF6C0E7FB9D272F14D4014", "n_operation": 1 }, { "block": 109706, "account": 285630, "ophash": "8AAC0100BE5B0400CD4410005A70FFE489B5DD0547DBDF3286A8B4E55FAFF4E9", "n_operation": 1066189 }, { "block": 199336, "account": 996095, "ophash": "A80A0300FF320F00010000003FAA7CBFEE3A395F4834AEB64AFAD4773FF821CD", "n_operation": 1 }, { "block": 135797, "account": 678465, "ophash": "75120200415A0A00010000003DB061394EC0C53F46BF808077E1DE478AC5E3DF", "n_operation": 1 }, { "block": 63304, "account": 285630, "ophash": "48F70000BE5B04007A16010051553F93D91971580DF55E2C49F157B6335EFE04", "n_operation": 71290 }, { "block": 110677, "account": 285630, "ophash": "55B00100BE5B04005AD01000F9A9D20F828BACC6C49975B4D5B419D04F357682", "n_operation": 1101914 }, { "block": 56660, "account": 282565, "ophash": "54DD0000C54F0400010000004CCEF4C4092648DFF54C8149D7FEFFAB37965E2B", "n_operation": 1 }, { "block": 232091, "account": 300124, "ophash": "9B8A03005C9404007331000074A47850FD4E4343D0C692149C62A2C27EBC57C5", "n_operation": 12659 }, { "block": 56175, "account": 54280, "ophash": "6FDB000008D400000100000038D155D5E4B317A70D6E8453A9B5ACE095697459", "n_operation": 1 }, { "block": 285148, "account": 1425130, "ophash": "DC590400EABE1500010000002C4ABCC66A354E37F83A0E4B1984203CCC3E679F", "n_operation": 1 }, { "block": 171664, "account": 857805, "ophash": "909E0200CD160D000100000060F4D7C990C1CE84BF689E37E108018569A08D12", "n_operation": 1 }, { "block": 100931, "account": 285630, "ophash": "438A0100BE5B0400562F0C000172CD9AE0F2149A5DC81404EE1A39A23FCCDBE4", "n_operation": 798550 }, { "block": 208792, "account": 1043440, "ophash": "982F0300F0EB0F0001000000570B39A1F4C59C34A372F0E7D69B6EA1D6905E51", "n_operation": 1 }, { "block": 42969, "account": 205555, "ophash": "D9A70000F322030002000000F5A5C55D4D0C104A71B42CEFB877D117D33FE961", "n_operation": 2 }, { "block": 61021, "account": 285630, "ophash": "5DEE0000BE5B04006FA80000CADF26B5B364C284CD5BCD71786DB920681020C8", "n_operation": 43119 }, { "block": 218392, "account": 1091445, "ophash": "1855030075A71000010000006C5071F700911F397FF35AA1BCEE11333D99AAC7", "n_operation": 1 }, { "block": 277861, "account": 285630, "ophash": "653D0400BE5B0400CA0E4E00DB77778147D80F1EC2F01CF24FA44BE5DD8F3BAE", "n_operation": 5115594 }, { "block": 107268, "account": 285630, "ophash": "04A30100BE5B04002EF50E0032A48674A762B134F319B19148F0072FC9CDB78B", "n_operation": 980270 }, { "block": 191620, "account": 957575, "ophash": "84EC0200879C0E00010000003200769C0EC4BA046A75B41A41F10E4E0B64BE12", "n_operation": 1 }, { "block": 131444, "account": 285630, "ophash": "74010200BE5B040083791E00EA4245B88C9415366EBE59081CDC1D00F8FE638D", "n_operation": 1997187 }, { "block": 89425, "account": 285630, "ophash": "515D0100BE5B04002086080085E041562E7DBBAEFF3A420E7BDF41E0418C08B8", "n_operation": 558624 }, { "block": 7025, "account": 16295, "ophash": "711B0000A73F000001000000AA5E9D9C17729B7DAB5136497ED9FE0E8F36746E", "n_operation": 1 }, { "block": 160031, "account": 799625, "ophash": "1F71020089330C0001000000DA592B4BFC10385BB96F62D9C705E118A483C43B", "n_operation": 1 }, { "block": 137699, "account": 285630, "ophash": "E3190200BE5B0400C86D22004EAD104BA309389806C500AD0D930E232E537903", "n_operation": 2256328 }, { "block": 250695, "account": 1252925, "ophash": "47D303003D1E130001000000E5FE1B395AF54C91AC1A28C8E11380D0D325B1ED", "n_operation": 1 }, { "block": 132558, "account": 662270, "ophash": "CE050200FE1A0A0001000000B303CD1039E7B38CBEB1A918FD58E5D3403AA8B5", "n_operation": 1 }, { "block": 59717, "account": 290760, "ophash": "45E90000C86F04009B19000018A592ECD4DCE9CC4535E2493A9BF85CF51134C1", "n_operation": 6555 }, { "block": 12532, "account": 61020, "ophash": "F43000005CEE000001000000EE63CA195262F699006EEDB2A744A59130B1876B", "n_operation": 1 }, { "block": 300361, "account": 1501275, "ophash": "499504005BE8160001000000B1200433AA038FB1E83545DC8A1B17492FBAD196", "n_operation": 1 }, { "block": 228322, "account": 1141055, "ophash": "E27B03003F6911000100000098589A424012FC72E333A2F2E5F6BA7D39A22F89", "n_operation": 1 }, { "block": 92316, "account": 285630, "ophash": "9C680100BE5B04000A520900EFB5AA170F16CC10E9331F6B8F3691419526C4FE", "n_operation": 610826 }, { "block": 69833, "account": 285630, "ophash": "C9100100BE5B0400EC990200C3363791247BFB0C7B64CDDB02CB4B6D19B77A5D", "n_operation": 170476 }, { "block": 182100, "account": 571630, "ophash": "54C70200EEB80800B8120000AA1FB338DB758A398FBD8B9E530A5C75C60A9642", "n_operation": 4792 }, { "block": 119781, "account": 285630, "ophash": "E5D30100BE5B0400ECB31600092B0C0DC80C987C3287F52922F588D13B06D524", "n_operation": 1487852 }, { "block": 59197, "account": 285630, "ophash": "3DE70000BE5B04002B5300005E442F705D46FCDCF1C6DA0B3AB89893A6C267EA", "n_operation": 21291 }, { "block": 268256, "account": 1340715, "ophash": "E01704002B7514000100000003DF8C5971A7C3DBDB8194AE1F1937E7FEB05AE2", "n_operation": 1 }, { "block": 119588, "account": 285630, "ophash": "24D30100BE5B04001F90160001A4ADC7979B8B11798EC72302FB8992AC8278E2", "n_operation": 1478687 }, { "block": 275256, "account": 483701, "ophash": "38330400756107000B000000A2C9952B9A725622F66C6AD26001CAECD20ADEF2", "n_operation": 11 }, { "block": 295846, "account": 1478690, "ophash": "A68304002290160001000000B8845A89C5FFE01B8BC806F47980B24C84E3EBFF", "n_operation": 1 }, { "block": 88143, "account": 285630, "ophash": "4F580100BE5B0400203008001E41B1A7E08341B645BE3A7DB3EC1A04D1633673", "n_operation": 536608 }, { "block": 116187, "account": 70774, "ophash": "DBC501007614010004000000E939F3E20045A38113F8040614E26AE1E388A0DA", "n_operation": 4 }, { "block": 147338, "account": 285630, "ophash": "8A3F0200BE5B0400431828009DC33A5111FAFCE9C63FB30971BDF1F2A442E9C6", "n_operation": 2627651 }, { "block": 264506, "account": 285630, "ophash": "3A090400BE5B04004BAA4C003B6ACCC60739BF8915B84267155B84000512C74C", "n_operation": 5024331 }, { "block": 89496, "account": 285630, "ophash": "985D0100BE5B0400A88908006C4FDCC30B28CDD0F8A3CB0D1FC9D69C20734E10", "n_operation": 559528 }, { "block": 237753, "account": 1188235, "ophash": "B9A003008B21120001000000CCD3F800977A406F629206ED16929C476B5A8C42", "n_operation": 1 }, { "block": 168770, "account": 843315, "ophash": "4293020033DE0C0001000000D9A909CB5CF75BFBE718BE7896092FE924459192", "n_operation": 1 }, { "block": 88317, "account": 285630, "ophash": "FD580100BE5B0400D73A0800731C05374C05BD6D07748DFE4A71777E8272E112", "n_operation": 539351 }, { "block": 197918, "account": 938265, "ophash": "1E05030019510E002A000000C3F3B87DF88A68EB72590EE76A3D828C4DA9529D", "n_operation": 42 }, { "block": 65299, "account": 290760, "ophash": "13FF0000C86F04003B540000B68A5CD3955E1FC16887F85B6D0B5FA9257441BF", "n_operation": 21563 }, { "block": 144301, "account": 285630, "ophash": "AD330200BE5B0400AC2E2600A9B21C7E856520B1627CA1E59712C3463771741F", "n_operation": 2502316 }, { "block": 163042, "account": 457633, "ophash": "E27C0200A1FB060007000000CBEBB1D39BE47B65ABAE8CABCC9C24D9F7D1A167", "n_operation": 7 }, { "block": 159340, "account": 212687, "ophash": "6C6E0200CF3E0300030000006065F903E3714367BEAA33E101A2C2131CFB3419", "n_operation": 3 }, { "block": 297766, "account": 1487745, "ophash": "268B040081B3160001000000B5181A3AD8D7FC8EF903ED1787CF180722DECEE8", "n_operation": 1 }, { "block": 159991, "account": 799420, "ophash": "F7700200BC320C0001000000DBE293BE0A6A5D66CB264214E9BA0A32FF6B21A0", "n_operation": 1 }, { "block": 252144, "account": 1260170, "ophash": "F0D803008A3A130001000000290437E01599FCACF44246F044D6D3AD4260C226", "n_operation": 1 }, { "block": 49666, "account": 247820, "ophash": "02C200000CC8030001000000D0C66F0FFF7CAA838906EC000717B422B8EDACB2", "n_operation": 1 }, { "block": 155963, "account": 86646, "ophash": "3B6102007652010087090000C97E5287D7CF0E2E14906A63BE47DEB775238848", "n_operation": 2439 }, { "block": 107024, "account": 534555, "ophash": "10A201001B28080001000000CF960DB1DCBFA35BBF5D110021C28250AA6836A2", "n_operation": 1 }, { "block": 69514, "account": 285630, "ophash": "8A0F0100BE5B0400E27A0200E9252F5BF86CDCC2F964BA55A58A1D32C3DB1903", "n_operation": 162530 }, { "block": 64962, "account": 290760, "ophash": "C2FD0000C86F0400D2500000612E12A6BCB1811C20B1E2B3178EBEE45F5D9089", "n_operation": 20690 }, { "block": 110834, "account": 285630, "ophash": "F2B00100BE5B040063E61000D9E0F40F4804DBB8113BADDBADA5F4734BC2DFD4", "n_operation": 1107555 }, { "block": 134370, "account": 671300, "ophash": "E20C0200443E0A0001000000BE4E144FA1514BC947F8406B80A59F6E923D106D", "n_operation": 1 }, { "block": 205790, "account": 1028395, "ophash": "DE2303002BB10F000100000070E21C5BE29651683A199D95C76948BD7C291002", "n_operation": 1 }, { "block": 70526, "account": 285630, "ophash": "7E130100BE5B040043D3020009F831B6224C4B9FA7786C8139987E6D575BD4E7", "n_operation": 185155 }, { "block": 86270, "account": 297299, "ophash": "FE5001005389040043000000486E48648677C5CCD614A190E42C7E00653EE643", "n_operation": 67 }, { "block": 97555, "account": 285630, "ophash": "137D0100BE5B0400EF0F0B00224A0A1EC53FA14DE5D99EF72D05650A4EFC60BD", "n_operation": 724975 }, { "block": 189604, "account": 318107, "ophash": "A4E402009BDA04000300000079D23235E1C6C9AEC384C1A18D9878767834D9B7", "n_operation": 3 }, { "block": 62520, "account": 290760, "ophash": "38F40000C86F0400763A0000A7C0692153ACD74ABBF1E05B47B09F5215A10564", "n_operation": 14966 }, { "block": 261135, "account": 571630, "ophash": "0FFC0300EEB80800513200009197579C27AE1C3B39021D8DFAA289E3A5C7F5A4", "n_operation": 12881 }, { "block": 128593, "account": 578210, "ophash": "51F60100A2D2080052000000C301F09A894ADADD542D34B73D0CE61DFB4132E4", "n_operation": 82 }, { "block": 206477, "account": 1031810, "ophash": "8D26030082BE0F00010000007F55D6FD6ABC4442C74D09678837D715FFA26A2B", "n_operation": 1 }, { "block": 94284, "account": 470855, "ophash": "4C700100472F0700010000009B5239CC500603CD0C99A9CCC37AC990874FF602", "n_operation": 1 }, { "block": 152347, "account": 285630, "ophash": "1B530200BE5B0400948F2A00E9593F079298E422302235AC8A461E3344BAD0F2", "n_operation": 2789268 }, { "block": 50201, "account": 248440, "ophash": "19C4000078CA0300010000002E38F96D9561879E6B1151D69CF430A122F4D875", "n_operation": 1 }, { "block": 192157, "account": 472756, "ophash": "9DEE0200B43607000900000084C3A39A121540ADBF8B489DEB0964D59F629811", "n_operation": 9 }, { "block": 179477, "account": 571630, "ophash": "15BD0200EEB808001311000087254B22148BB741D877F0016CEB9A18AADDDFBF", "n_operation": 4371 }, { "block": 154672, "account": 771725, "ophash": "305C02008DC60B0001000000D3BC67E41A2F9B9F7C1EC63438F9DCEB7B8F410C", "n_operation": 1 }, { "block": 65032, "account": 285630, "ophash": "08FE0000BE5B040037550100723CC655A898F4F92D989052AC2A513AAC45E9AE", "n_operation": 87351 }, { "block": 91006, "account": 285630, "ophash": "7E630100BE5B0400DBF7080084A33E582DC1A195365B4A71021EF36A0BFED18A", "n_operation": 587739 }, { "block": 116194, "account": 285630, "ophash": "E2C50100BE5B0400810B1400EA6C29DF1F9228F934FBB5F45780D15F1502CA41", "n_operation": 1313665 }, { "block": 228663, "account": 546929, "ophash": "377D03007158080002000000A59958FC58040A575B466E39F7A14288B146848C", "n_operation": 2 }, { "block": 267909, "account": 1320421, "ophash": "85160400E5251400030000001AEA6931AF59BAD86C44BA32AD1572D95AF23E9F", "n_operation": 3 }, { "block": 239604, "account": 1197495, "ophash": "F4A70300B745120001000000AC4B2B944EF28E1FD3DE0302487CFF1A7636F4A7", "n_operation": 1 }, { "block": 265475, "account": 7392, "ophash": "030D0400E01C000001000000F857D53720A011CE7D4A57C7AF5EF3DF4E12A806", "n_operation": 1 }, { "block": 94908, "account": 473950, "ophash": "BC7201005E3B070001000000CDA178235153D2E1866E252EF4BAD9E43287D43D", "n_operation": 1 }, { "block": 295227, "account": 1475610, "ophash": "3B8104001A84160001000000BDC24EF12DC7673637DC6E8AD59DE093687F6053", "n_operation": 1 }, { "block": 176134, "account": 285630, "ophash": "06B00200BE5B0400679C3700BA0BCFB7588CEBB49603F01CF7DD52EDCE829953", "n_operation": 3644519 }, { "block": 159607, "account": 797460, "ophash": "776F0200142B0C000100000013A6A9EC6091A512E4C2C5C05033B16F86A93D1C", "n_operation": 1 }, { "block": 185340, "account": 926135, "ophash": "FCD30200B7210E000100000042C2A7A0FE0F6C3C27E49735E83D5AB5B24A656C", "n_operation": 1 }, { "block": 163329, "account": 285630, "ophash": "017E0200BE5B0400D2753000B3727717A58DF9E79FE366DEA5CA649B3F8CCF31", "n_operation": 3175890 }, { "block": 98630, "account": 285630, "ophash": "46810100BE5B040016720B00D987CC04AA6C1A8530B836D4063F8E63500DF50C", "n_operation": 750102 }, { "block": 205457, "account": 1026735, "ophash": "91220300AFAA0F0001000000BDB4470963FFAB86091D060519DF97377BCD365E", "n_operation": 1 }, { "block": 116182, "account": 285630, "ophash": "D6C50100BE5B0400690814002E490DB838CAD4ACC3955F00E5E6FE6C8087FAB0", "n_operation": 1312873 }, { "block": 163021, "account": 814580, "ophash": "CD7C0200F46D0C00010000009284512CED659B3F51E43D5E7B115F44FFB47C6B", "n_operation": 1 }, { "block": 163880, "account": 818850, "ophash": "28800200A27E0C00010000001065306434DF1B00C31B40E5504D5C78DD5EF75B", "n_operation": 1 }, { "block": 154370, "account": 86646, "ophash": "025B020076520100310900001E063016FF437F1AF900AE21CBC7ED73D6A76639", "n_operation": 2353 }, { "block": 164852, "account": 285630, "ophash": "F4830200BE5B0400C7333100B92376BC9DC10571660C5C6FD743E20FFE60C3AE", "n_operation": 3224519 }, { "block": 92903, "account": 463930, "ophash": "E76A01003A140700010000001AAD9B96070AC1D5D23E5C3140BE5C102897F753", "n_operation": 1 }, { "block": 61408, "account": 285630, "ophash": "E0EF0000BE5B040070BB0000BDA87567EA0E4E3C84E634CED6DA7ED5F57F35EA", "n_operation": 47984 }, { "block": 149583, "account": 285630, "ophash": "4F480200BE5B040064412900F63B430304A4CAC7F32ABB158C4657408C94A2B4", "n_operation": 2703716 }, { "block": 197244, "account": 571630, "ophash": "7C020300EEB808005A1E000090938ED64CDF6D9FB3A9B9BFA1E3041A8296BFEB", "n_operation": 7770 }, { "block": 168244, "account": 571630, "ophash": "34910200EEB80800E60B00008FD44358EDBF4E750DEBF00CE3757D925A926BB5", "n_operation": 3046 }, { "block": 127913, "account": 578210, "ophash": "A9F30100A2D208004700000099D1726DC51C41122364913F05C9557A5E9387C5", "n_operation": 71 }, { "block": 102490, "account": 285630, "ophash": "5A900100BE5B04008CBA0C0080832BA660D35DAA07F9A17BDA611FCB0D6093B9", "n_operation": 834188 }, { "block": 67565, "account": 285630, "ophash": "ED070100BE5B04009AD10100868662E4DDD3662E12CBDBB88A425D08CAEF288C", "n_operation": 119194 }, { "block": 92013, "account": 89846, "ophash": "6D670100F65E010001000000B26E49E35C5DE824B2C25E11A7A97B7DC4C370C6", "n_operation": 1 }, { "block": 72700, "account": 285630, "ophash": "FC1B0100BE5B0400B09E03006215E2FE7E85C66072712FB6CEA252325F1D9E2C", "n_operation": 237232 }, { "block": 112236, "account": 285630, "ophash": "6CB60100BE5B0400CFA71100F03E23CE320C70D5D1B896A36612F6672827A5B6", "n_operation": 1157071 }, { "block": 63842, "account": 290760, "ophash": "62F90000C86F0400484600005DC3E97D64D5E94236E37E5C256C9F7D346ABCEC", "n_operation": 17992 }, { "block": 108833, "account": 309655, "ophash": "21A9010097B9040003000000832F6D8B98B470C2815385C0204C19D1493E4289", "n_operation": 3 }, { "block": 56428, "account": 185097, "ophash": "6CDC000009D3020004000000E1A28CF520A3353393E99B4F64D0F301BFB2781F", "n_operation": 4 }, { "block": 126627, "account": 285630, "ophash": "A3EE0100BE5B04000E6C1B00350402B06FFEBFAC08A90107161FF23DDD55B0CD", "n_operation": 1797134 }, { "block": 281328, "account": 71707, "ophash": "F04A04001B1801001A00000093BFEA33BF4330D30FBA1A27DED7A22BA492D1E3", "n_operation": 26 }, { "block": 233179, "account": 1165360, "ophash": "DB8E030030C8110001000000AAA410BF5CB38188C6CFD3598E0E8B7A7DAD661A", "n_operation": 1 }, { "block": 263651, "account": 499016, "ophash": "E3050400489D070006000000BDECA36BC113DA1CB3B279C13ADC6B53C6BC9EAD", "n_operation": 6 }, { "block": 197326, "account": 67050, "ophash": "CE020300EA0501005E01000088E5F0620921D6A735922AE125A83EF5F241204B", "n_operation": 350 }, { "block": 138016, "account": 689555, "ophash": "201B020093850A0001000000C6EE7D2559CCFC7011370CA974DD7136A1D9F324", "n_operation": 1 }, { "block": 155398, "account": 86646, "ophash": "065F020076520100660900002E0A2F9BCC6940F7D39DFE2736E5E7F72DE2B33C", "n_operation": 2406 }, { "block": 263982, "account": 1317355, "ophash": "2E070400EB1914000100000051E696095A43277D76B49E9C2E59E43D146F465B", "n_operation": 1 }, { "block": 79367, "account": 396275, "ophash": "07360100F30B060001000000D7ABF325E29B2FC32D41242DB993EFCA6995F4F1", "n_operation": 1 }, { "block": 114796, "account": 573450, "ophash": "6CC001000AC0080001000000561461B4B298B227956E8BDCB3483E5ECB02AB86", "n_operation": 1 }, { "block": 234256, "account": 571630, "ophash": "10930300EEB80800112E000064F4A6A734D122E686C3C544E4FC2F0D59B9784C", "n_operation": 11793 }, { "block": 95546, "account": 285630, "ophash": "3A750100BE5B0400176F0A0091D95FBC910AECF652417B8B9312540FE9F5EB59", "n_operation": 683799 }, { "block": 204327, "account": 1021110, "ophash": "271E0300B6940F00010000000EB0E6D33101A92D62616772FA7D94DD65A6E126", "n_operation": 1 }, { "block": 121480, "account": 606875, "ophash": "88DA01009B420900010000008440E94A63EE6DA806F1A96749F45D95BAC9C6B0", "n_operation": 1 }, { "block": 276432, "account": 1381625, "ophash": "D0370400F91415000100000078A65DB47117A558C72097ED5D886AE5D1200D99", "n_operation": 1 }, { "block": 231847, "account": 86646, "ophash": "A78903007652010069160000524F93B8F669FB7DDE1A6F226AA7FE29427E8601", "n_operation": 5737 }, { "block": 224532, "account": 1122115, "ophash": "146D0300431F1100010000000D81AB49CBF8317CC85985151E693D228C57668F", "n_operation": 1 }, { "block": 62583, "account": 290760, "ophash": "77F40000C86F04002F3B000049826B2D0F92083D6EB9E8DBF686FC8211E9BF5D", "n_operation": 15151 }, { "block": 221164, "account": 571630, "ophash": "EC5F0300EEB80800312B0000F1E35F1EA9C068C6BBE5E0533251EB25D22F55F3", "n_operation": 11057 }, { "block": 56125, "account": 279195, "ophash": "3DDB00009B4204000100000086FB38FE7C6CDE50B184D969B99244E80B5A1421", "n_operation": 1 }, { "block": 283716, "account": 1418040, "ophash": "4454040038A3150001000000B7C9FCA42B0A0F04C3C91A3593B54F15217A6321", "n_operation": 1 }, { "block": 113898, "account": 285630, "ophash": "EABC0100BE5B04004CA612002E5DFFE6D0797AFE5059E4C9FBA78DA5C3BCB89C", "n_operation": 1222220 }, { "block": 129599, "account": 647465, "ophash": "3FFA010029E109000100000033A361367CC466C27D2A6286EE91CFCA02B93CDE", "n_operation": 1 }, { "block": 75734, "account": 285630, "ophash": "D6270100BE5B0400EB8D04009497D874F3A22BD540A953DDBAA3C03C772B44FA", "n_operation": 298475 }, { "block": 225746, "account": 1128190, "ophash": "D2710300FE36110001000000550296F81333B16FCCC5AA2565D1083B38585EB1", "n_operation": 1 }, { "block": 243342, "account": 1216145, "ophash": "8EB60300918E120001000000B4379D33E16E4A46D4876C9479F192C4ED064582", "n_operation": 1 }, { "block": 231112, "account": 1155015, "ophash": "C8860300C79F110001000000F0EFF94163165E934B5BB9A914DE13182DD7FABC", "n_operation": 1 }, { "block": 91589, "account": 285630, "ophash": "C5650100BE5B0400C22109007AFD9051858145A83863242C559A360CFF19B704", "n_operation": 598466 }, { "block": 62600, "account": 290760, "ophash": "88F40000C86F0400473B000011A477907C27F5FA69F93AF4095EBF4DDA659E70", "n_operation": 15175 }, { "block": 290251, "account": 1450725, "ophash": "CB6D0400E5221600010000009ED9B2DDF853C939E66A5AAC2A0EC6E037A00DDB", "n_operation": 1 }, { "block": 162641, "account": 812685, "ophash": "517B02008D660C00010000005FD3C7E557DD1B0F56CCF799FA0139578378ABD5", "n_operation": 1 }, { "block": 170175, "account": 86646, "ophash": "BF98020076520100580F0000087F22C3AAFC7F954F248883CE556BC72FC5A162", "n_operation": 3928 }, { "block": 72331, "account": 168537, "ophash": "8B1A010059920200F1000000F0034C8E42439074C11BE0BCA0CAD7FB24381185", "n_operation": 241 }, { "block": 153078, "account": 764865, "ophash": "F6550200C1AB0B00010000005F7D5B05F6AA061913050374AFFC825A03EF0C9D", "n_operation": 1 }, { "block": 62256, "account": 290760, "ophash": "30F30000C86F04007D3700008C76CB1B0DD5D3EF850F911942B0D7E6975FD1AE", "n_operation": 14205 }, { "block": 263860, "account": 285630, "ophash": "B4060400BE5B040075834C004F4B1D7144F262DA2BA35F7CBF7BB462A7999BD8", "n_operation": 5014389 }, { "block": 242385, "account": 571630, "ophash": "D1B20300EEB808004D2F0000DD8285C4B743104D51EA9CB7D77E3C7608D432DE", "n_operation": 12109 }, { "block": 227724, "account": 300124, "ophash": "8C7903005C9404005F310000F8FBC319D0CBEE46F69FC02DBF8FA4DDFC6DC8F9", "n_operation": 12639 }, { "block": 94833, "account": 473575, "ophash": "71720100E739070001000000AABDF06B4AA9CD0649B26860EB033077E13D0E10", "n_operation": 1 }, { "block": 76568, "account": 285630, "ophash": "182B0100BE5B0400A5D60400A467F1B92D619101904828AF64D7A3482AC422D3", "n_operation": 317093 }, { "block": 98123, "account": 489995, "ophash": "4B7F01000B7A070001000000E922A662DDF599F6D3E958374D32A1CC890695AD", "n_operation": 1 }, { "block": 266154, "account": 547523, "ophash": "AA0F0400C35A08002B0000005436E5A75F67E342DA0440A549A593184C168A60", "n_operation": 43 }, { "block": 199907, "account": 999005, "ophash": "E30C03005D3E0F0001000000C2582E93519F4F66008236F3B42C9FE489A624EC", "n_operation": 1 }, { "block": 101660, "account": 285630, "ophash": "1C8D0100BE5B0400D3700C00B810A8C3362E6BD47990D7827268E598D2C44EA2", "n_operation": 815315 }, { "block": 107444, "account": 285630, "ophash": "B4A30100BE5B0400140E0F00A90A70939676AB789C95E4B0B96334D77E7C6094", "n_operation": 986644 }, { "block": 114770, "account": 285630, "ophash": "52C00100BE5B040061E21200E5EDA6A775FC5D63FB910F2805F8C4501AAFD302", "n_operation": 1237601 }, { "block": 115478, "account": 576875, "ophash": "16C301006BCD080001000000FF13E97255F7EA3FDF4A3ECB91118BA07453C31C", "n_operation": 1 }, { "block": 277354, "account": 285630, "ophash": "6A3B0400BE5B0400DC054E00305C764D6B9FD5265CBF8EE0D530FEA6691D4CA3", "n_operation": 5113308 }, { "block": 189885, "account": 948880, "ophash": "BDE50200907A0E00010000000E9049747619AE445E71F17E47D4C2522C5BD11B", "n_operation": 1 }, { "block": 195398, "account": 976450, "ophash": "46FB020042E60E00010000008CED964A2B60934ED7C4F7DADE55EC70EEAEB714", "n_operation": 1 }, { "block": 96669, "account": 285630, "ophash": "9D790100BE5B0400EDC10A00DB9C7C0F4778F3F89E56055435EDCD4AD43262AC", "n_operation": 705005 }, { "block": 96102, "account": 479840, "ophash": "66770100605207000100000002761D06AC30715D7DA563E44B9CB86B403438AC", "n_operation": 1 }, { "block": 67166, "account": 86646, "ophash": "5E0601007652010005010000E90F6CD0EEF809A1697C4AAFAAA7F16731BC9523", "n_operation": 261 }, { "block": 149050, "account": 744725, "ophash": "3A460200155D0B00010000005C1FF3478ABD91A3E2D8BACD6FA0216AD5C2A373", "n_operation": 1 }, { "block": 221184, "account": 1105365, "ophash": "00600300D5DD1000010000006D8261676DC86BA08063CBF48D9F73E5F1D42157", "n_operation": 1 }, { "block": 84644, "account": 285630, "ophash": "A44A0100BE5B0400FE3407003734BC6CFE37BF0654C54006389D7DEA0275C8B1", "n_operation": 472318 }, { "block": 214369, "account": 1071315, "ophash": "61450300D358100001000000607613B22207D94684FA266A7265E453600EAF39", "n_operation": 1 }, { "block": 263872, "account": 112800, "ophash": "C0060400A0B801003B280000DA7359A7BAE3DA4989091ACFA2B2FBEB69D4A197", "n_operation": 10299 }, { "block": 218026, "account": 571630, "ophash": "AA530300EEB80800F02A00006F57F332B92ABB48AC4D10541F67460C7FCC56B8", "n_operation": 10992 }, { "block": 218372, "account": 1091340, "ophash": "045503000CA7100001000000B5EF7823A26DC2D5D52D11ACC04A8724E3DE5A74", "n_operation": 1 }, { "block": 149825, "account": 748595, "ophash": "41490200336C0B00010000003027D8DB576B3353245FDC8B4AAE9FE1C9F539DA", "n_operation": 1 }, { "block": 139167, "account": 695290, "ophash": "9F1F0200FA9B0A00010000002D1DD2D1A8ACEDDD9DACBE30211C21B5975D9D9C", "n_operation": 1 }, { "block": 282235, "account": 285630, "ophash": "7B4E0400BE5B0400235B4E0083B08A25E16721A98D84355F1B8736BDEBBF067F", "n_operation": 5135139 }, { "block": 222412, "account": 546194, "ophash": "CC64030092550800040000003115A39A5B4644E4BE62AB29B81B545C92E78219", "n_operation": 4 }, { "block": 257700, "account": 1287970, "ophash": "A4EE030022A7130001000000491876A47BCE43F6CA872CB05B8693FD739E5FC4", "n_operation": 1 }, { "block": 196343, "account": 571630, "ophash": "F7FE0200EEB80800741D00001A36BF7B5F135DDD191E71468018350F63E53C8C", "n_operation": 7540 }, { "block": 184032, "account": 86646, "ophash": "E0CE02007652010099120000A1268F8CD79065D0E10636CC1B2E21117D8F6258", "n_operation": 4761 }, { "block": 227068, "account": 1134780, "ophash": "FC760300BC5011000100000075503526DDF8EB8A6B22C086D7A5EC604A3DAFEB", "n_operation": 1 }, { "block": 284439, "account": 473232, "ophash": "17570400903807000200000017101A73BA408C79C915FED32DEDFBF95415E557", "n_operation": 2 }, { "block": 70442, "account": 285630, "ophash": "2A130100BE5B040077CB0200A582018F506FF3EBAE3E98BB40AEDC8544F1EF65", "n_operation": 183159 }, { "block": 63469, "account": 160435, "ophash": "EDF70000B372020002000000AFA04464713E97E2903105CA0349602CB5940220", "n_operation": 2 }, { "block": 28139, "account": 50001, "ophash": "EB6D000051C3000001000000D5DEBFB7FF40A3BA8B5089EA1FB53CCD5E2A0FDF", "n_operation": 1 }, { "block": 191507, "account": 957020, "ophash": "13EC02005C9A0E00010000000EB42E4161F386D9110BDC8566E0D138274E56BC", "n_operation": 1 }, { "block": 211254, "account": 566847, "ophash": "363903003FA608000600000024399D9822F02038E594161750115F5CC36E2562", "n_operation": 6 }, { "block": 70318, "account": 290760, "ophash": "AE120100C86F04008073000052D4400A2A64DE35ABC09B0111688260ACE4014D", "n_operation": 29568 }, { "block": 133467, "account": 666810, "ophash": "5B090200BA2C0A000100000080B6362F883A83B130D09D667F07B6E0FC3CF6ED", "n_operation": 1 }, { "block": 158337, "account": 791170, "ophash": "816A020082120C000100000069E60889277ACDB9C82803341D3148B3DE69F460", "n_operation": 1 }, { "block": 21526, "account": 106725, "ophash": "16540000E5A00100020000006A5FCF4C3EAEA5262DF491ABBF43F2C6E40656A4", "n_operation": 2 }, { "block": 124128, "account": 285630, "ophash": "E0E40100BE5B040037B41900807460DCBB7545F345A484A3DC0AD823C489C227", "n_operation": 1684535 }, { "block": 63396, "account": 285630, "ophash": "A4F70000BE5B0400641B010000D60319FDCF10E4CB051592A6093DED13C5D503", "n_operation": 72548 }, { "block": 38484, "account": 188120, "ophash": "54960000D8DE0200010000006B43B91F8F83349F847D3CD8CB114EAD78D19C0F", "n_operation": 1 }, { "block": 96391, "account": 86646, "ophash": "8778010076520100C20200009E14530649B0AFD91352FF01A9A47E6024FE67FB", "n_operation": 706 }, { "block": 44457, "account": 122520, "ophash": "A9AD000098DE010003000000113BE910DA846B06A99A87A6A313BC272F520A84", "n_operation": 3 }, { "block": 70983, "account": 168539, "ophash": "471501005B920200200000007BCB357D95D4B6BB00E7909BA3B5ADA301F71A19", "n_operation": 32 }, { "block": 62014, "account": 290760, "ophash": "3EF20000C86F0400CC330000E7CEF1A1ED74517DFB442CDE4B70620A381B9715", "n_operation": 13260 }, { "block": 204142, "account": 1020195, "ophash": "6E1D030023910F0001000000B7425175FD1082E5FBEBB968E6EBF998A641B716", "n_operation": 1 }, { "block": 192116, "account": 582004, "ophash": "74EE020074E10800020000000E4AD4A0A6645819B33ACE153629249D9ADD7253", "n_operation": 2 }, { "block": 103375, "account": 285630, "ophash": "CF930100BE5B0400130A0D00DECCCD5E689707AAC39B1FBF2182438839F694DC", "n_operation": 854547 }, { "block": 90560, "account": 296330, "ophash": "C06101008A8504000D0000004319B9D0E86F6D156A6083D8F8F1C5EEC0FECD0F", "n_operation": 13 }, { "block": 88451, "account": 285630, "ophash": "83590100BE5B040006440800C8FA00E867719E5D818542C7E3F7945EFC8D40F7", "n_operation": 541702 }, { "block": 129136, "account": 285630, "ophash": "70F80100BE5B0400041A1D00926C7DCBCAFC15234DE9DAF2BAC29D4030B17865", "n_operation": 1907204 }, { "block": 102172, "account": 285630, "ophash": "1C8F0100BE5B0400AF9D0C00AAA5322EFF2B135D872343CE3CE3AE8BFEDE252A", "n_operation": 826799 }, { "block": 4715, "account": 22565, "ophash": "6B1200002558000001000000D611C8913430195FA7D358DA9DCC1CF693B4D8AE", "n_operation": 1 }, { "block": 67631, "account": 290760, "ophash": "2F080100C86F0400046500001FC27BD6620BBFB4D95C2C85D368FBB900EB5986", "n_operation": 25860 }, { "block": 193441, "account": 571630, "ophash": "A1F30200EEB808000E1B0000F2EC2FBC1EEC72B06699684451B75CE69EAD380B", "n_operation": 6926 }, { "block": 276017, "account": 1379530, "ophash": "31360400CA0C1500010000002F413BA60C48F81C1F0B6B3E4FA5B865CBE8F850", "n_operation": 1 }, { "block": 269593, "account": 1347435, "ophash": "191D04006B8F14000100000077440ABA0F3284EBC1A93F044614606C2B6797F5", "n_operation": 1 }, { "block": 252748, "account": 1263180, "ophash": "4CDB03004C46130001000000A22A2793C4C9B5603A83F0F7A7B142C88CA57FDE", "n_operation": 1 }, { "block": 73819, "account": 168610, "ophash": "5B200100A292020016010000F0ADF4028A4107BAB52B9EA1D3A4DE8134FBC94C", "n_operation": 278 }, { "block": 145148, "account": 86646, "ophash": "FC360200765201006708000043553447B286FEBFC88AEA2DB72AE0448A613B4D", "n_operation": 2151 }, { "block": 105811, "account": 285630, "ophash": "539D0100BE5B0400F6390E001E9FC886DD7EFF20C1D8F7987732F4CA4634F629", "n_operation": 932342 }, { "block": 82043, "account": 285630, "ophash": "7B400100BE5B04001E8506005D7011285E996315A6494FD83E15F57C3992205B", "n_operation": 427294 }, { "block": 145069, "account": 285630, "ophash": "AD360200BE5B0400DBCA2600A5F3C213E23AE4BADF8E8AAB334026FC643B075D", "n_operation": 2542299 }, { "block": 133644, "account": 285630, "ophash": "0C0A0200BE5B040015E01F0023956D81B71FDB697B93A3A17D9DB33374093657", "n_operation": 2088981 }, { "block": 98911, "account": 285630, "ophash": "5F820100BE5B0400058D0B00BC475738C83916139E226FDAB8A6DAD297065445", "n_operation": 756997 }, { "block": 134216, "account": 670560, "ophash": "480C0200603B0A000100000055E131DF83F077AD4824543EC07008087F528F98", "n_operation": 1 }, { "block": 114609, "account": 285630, "ophash": "B1BF0100BE5B0400BFBE120036CFDF39BE75659E358AF1994F8A76B8FF2558CB", "n_operation": 1228479 }, { "block": 249844, "account": 1248660, "ophash": "F4CF0300940D130001000000D4F17CEA90152E33B376E67DDDA5D3D5FD339DC4", "n_operation": 1 }, { "block": 97888, "account": 86646, "ophash": "607E010076520100DF020000EFFCABAD5705F7D0E177FAAEAD1B783CB2FC668B", "n_operation": 735 }, { "block": 196472, "account": 571630, "ophash": "78FF0200EEB80800951D0000C3ADD86C8BFE47D592CD2F091CF4AC1E0BAF0B5A", "n_operation": 7573 }, { "block": 102366, "account": 285630, "ophash": "DE8F0100BE5B040089B00C002D240A62C802397A9E8A29BE57473C1F2C025D3F", "n_operation": 831625 }, { "block": 139485, "account": 696890, "ophash": "DD2002003AA20A0001000000A945DEEA6995ABAC819387555C886076259AF471", "n_operation": 1 }, { "block": 117719, "account": 588085, "ophash": "D7CB010035F9080001000000A8CFF36119F1135111722019E1AFEAC619FFD708", "n_operation": 1 }, { "block": 264398, "account": 1321445, "ophash": "CE080400E529140001000000240C3F41ABF23BAF63EF7EA81FFDCC991235D636", "n_operation": 1 }, { "block": 243776, "account": 1218330, "ophash": "40B803001A9712000100000046831599044268F7161D28654A94C4F7B15AB405", "n_operation": 1 }, { "block": 250713, "account": 105569, "ophash": "59D30300619C01001C00000020C59C9780389B5CB0D7C5120CD0E200EBE2CBD6", "n_operation": 28 }, { "block": 210274, "account": 571630, "ophash": "62350300EEB80800C6290000ADABAFEC2E31FC6131DF4E8B2E6D47D02EB747F4", "n_operation": 10694 }, { "block": 76653, "account": 9001, "ophash": "6D2B0100292300000800000042F55CF4771FE3C0C28029F7EEE707547026FBE2", "n_operation": 8 }, { "block": 91195, "account": 285630, "ophash": "3B640100BE5B0400CD0509005FD9DEC03856EED6C7C01291C451D347FA1EBB68", "n_operation": 591309 }, { "block": 300026, "account": 483689, "ophash": "FA9304006961070098010000BD1C9A5C6911533981830690E81D26A4BA5D92F1", "n_operation": 408 }, { "block": 129203, "account": 48210, "ophash": "B3F8010052BC00000100000099EC1491FAD22A7F29A608DF19E83D0064B1B96B", "n_operation": 1 }, { "block": 198357, "account": 86646, "ophash": "D50603007652010037140000C5211E1D130F0117CB1D9A467253579F1563ECD0", "n_operation": 5175 }, { "block": 76456, "account": 285630, "ophash": "A82A0100BE5B040011CC0400AC70B24A9889B1A38049428BCF4335CA7B118B7E", "n_operation": 314385 }, { "block": 4484, "account": 21540, "ophash": "841100002454000001000000B5898E33AED0D84BB57C8BD5844D7A3F0F4AFFD8", "n_operation": 1 }, { "block": 64710, "account": 290760, "ophash": "C6FC0000C86F0400964E0000F9336CEC5AA500D4A343DB170F11D9537DA9EABF", "n_operation": 20118 }, { "block": 192550, "account": 962215, "ophash": "26F00200A7AE0E00010000001DCB510074E146A4B5562CE00ACBF7EE9D1510E4", "n_operation": 1 }, { "block": 7017, "account": 12540, "ophash": "691B0000FC300000010000005155F3B2A5D7E27813EE6AF505C7683F6FBBB8CB", "n_operation": 1 }, { "block": 127235, "account": 635655, "ophash": "03F1010007B3090001000000BDD05399FFDE66BA9B202CE4C1E997B0F9F7B82D", "n_operation": 1 }, { "block": 143444, "account": 291000, "ophash": "54300200B87004005400000014D3164D0CA6A9F4904BB3C0FC0BA61A70662CE3", "n_operation": 84 }, { "block": 159592, "account": 285630, "ophash": "686F0200BE5B0400702B2E00A22FFF99429B4694B00B251454AEEE23D02A264F", "n_operation": 3025776 }, { "block": 98984, "account": 285630, "ophash": "A8820100BE5B040066930B000D3D119BFF19C45087DF26E7D679E64CB4202D64", "n_operation": 758630 }, { "block": 239387, "account": 106975, "ophash": "1BA70300DFA1010006000000B0234671746239D46CA10C6E0EF7EAD1E048A30A", "n_operation": 6 }, { "block": 90238, "account": 285630, "ophash": "7E600100BE5B0400BFBF0800E29B0C8B5866680FFBE6F8C29031FFA8BF8BDEBC", "n_operation": 573375 }, { "block": 66054, "account": 290760, "ophash": "06020100C86F0400FB5A0000C3A9CCE5D081DD87646BB16CA0BA692F58C558B7", "n_operation": 23291 }, { "block": 120992, "account": 604425, "ophash": "A0D801000939090001000000DEF3964051C78F42C0A9953CE54C03A57F0D27F6", "n_operation": 1 }, { "block": 105817, "account": 285630, "ophash": "599D0100BE5B04005A3A0E004E2E8A066A5142A377F80C8699C81851E7023A02", "n_operation": 932442 }, { "block": 44739, "account": 220475, "ophash": "C3AE00003B5D0300010000006003F373E9DF71C67C9D15B1D226546183C59F9B", "n_operation": 1 }, { "block": 110779, "account": 285630, "ophash": "BBB00100BE5B040017DC1000A403B4806ADE4D790DC89E16902D2F8A873C1DF7", "n_operation": 1104919 }, { "block": 156193, "account": 200119, "ophash": "21620200B70D030006000000D6A1124EDA5E4B64C513115762F3A3002BE376E4", "n_operation": 6 }, { "block": 165231, "account": 55065, "ophash": "6F85020019D70000030000006AE1AAE990DBA46C0673CC8E53DE9888B023DF53", "n_operation": 3 }, { "block": 186362, "account": 931290, "ophash": "FAD70200DA350E0001000000549B1A5CED2763EDA588CE3498CBA6BF6266B4D6", "n_operation": 1 }, { "block": 111519, "account": 285630, "ophash": "9FB30100BE5B0400FF4A11006411EEAA47DCF324FF2BBA13A40C24A17E83F202", "n_operation": 1133311 }, { "block": 107248, "account": 285630, "ophash": "F0A20100BE5B04008BF20E003E39FA92025317F408468564FC196B05A22747FD", "n_operation": 979595 }, { "block": 190669, "account": 285630, "ophash": "CDE80200BE5B0400283E3F007E14CFEF28671DF54FA056551F0C5A06BF988B0B", "n_operation": 4144680 }, { "block": 266179, "account": 1330335, "ophash": "C30F04009F4C140001000000C10EE494FD3511D44B83AA8F168258BEC764B59F", "n_operation": 1 }, { "block": 90072, "account": 285630, "ophash": "D85F0100BE5B0400E5B408006ABA4D7C2FC98BAFF046CE2AEC5FB78928B86572", "n_operation": 570597 }, { "block": 166793, "account": 467315, "ophash": "898B0200732107000800000041FE1DB3707F5558747AF981BB41E9C33DCB6AB5", "n_operation": 8 }, { "block": 124009, "account": 588621, "ophash": "69E401004DFB080014000000729C263F977EB1EEB36831C553C016045F36C9B4", "n_operation": 20 }, { "block": 98758, "account": 285630, "ophash": "C6810100BE5B0400B97E0B00376795081AE35F90D9D88C7EF1D38716A279F021", "n_operation": 753337 }, { "block": 67682, "account": 290760, "ophash": "62080100C86F04006A6500002DE7F08349F9212FB2CB33FAC4EA23141C97C400", "n_operation": 25962 }, { "block": 78957, "account": 285630, "ophash": "6D340100BE5B04006E8F0500F927DB8EC5C9B609AFB7B6934004E74DB1320C6D", "n_operation": 364398 }, { "block": 293621, "account": 86646, "ophash": "F57A040076520100B618000000D8DE5EFD1480C0F0A2F12EC25D1E90A50D389F", "n_operation": 6326 }, { "block": 73906, "account": 285630, "ophash": "B2200100BE5B040085F30300D8BB8B69ADD9DA958AB031717867305CD1F3E55E", "n_operation": 258949 }, { "block": 172908, "account": 863995, "ophash": "6CA30200FB2E0D00010000003E9BADF45BE7EE1654EBCC579384A81B78EA9B48", "n_operation": 1 }, { "block": 256395, "account": 1281415, "ophash": "8BE90300878D13000100000060C41189C5D49F5A3F689B62F3C9FB9E8A0C4065", "n_operation": 1 }, { "block": 102363, "account": 290911, "ophash": "DB8F01005F70040042000000710BC898DA4BC985CD8812FE7C7EDEBFB4DDE0F3", "n_operation": 66 }, { "block": 272747, "account": 1363215, "ophash": "6B2904000FCD1400010000004A6634A0CF2B3FE948E2B8AA8C0F3EBC1FD53736", "n_operation": 1 }, { "block": 85499, "account": 285630, "ophash": "FB4D0100BE5B0400827C07000B91E9B9CCA038EA2FCFDB701123FE1786496F99", "n_operation": 490626 }, { "block": 121919, "account": 285630, "ophash": "3FDC0100BE5B04008E301800B2F25B4DA7C0BD8DD18A63EE7323803EA92E51C3", "n_operation": 1585294 }, { "block": 217324, "account": 47805, "ophash": "EC500300BDBA00004F00000000757707E7BCF1B7CA4817BC853D876427285542", "n_operation": 79 }, { "block": 33490, "account": 3535, "ophash": "D2820000CF0D000015000000BFC5C63500E5B6A36560059698668DC1582AC6A2", "n_operation": 21 }, { "block": 77657, "account": 285630, "ophash": "592F0100BE5B0400B32705000756833F13CE363BD68885EB2A8AB0CF3D787EE7", "n_operation": 337843 }, { "block": 207107, "account": 1034980, "ophash": "03290300E4CA0F0001000000EA259C8B21C4A0DB90D6A493C8921DAE15983769", "n_operation": 1 }, { "block": 107453, "account": 160888, "ophash": "BDA3010078740200020000002DC3FD0032F9417AA7DE9FCB8A99E9835F928E31", "n_operation": 2 }, { "block": 86886, "account": 285630, "ophash": "66530100BE5B040083DD0700BB613A4740FE40784703884E992064E461EE33C3", "n_operation": 515459 }, { "block": 105422, "account": 86646, "ophash": "CE9B010076520100AB030000C70F3E3DBD570B95517875FA38072D57C608E315", "n_operation": 939 }, { "block": 107625, "account": 285630, "ophash": "69A40100BE5B0400DA260F00D4D06609054BB713E3BE2BBFC6473D3A206E6290", "n_operation": 992986 }, { "block": 205804, "account": 1028500, "ophash": "EC23030094B10F00010000002AD031EB248B8729FDB261A48CD6A36F5ADB7B37", "n_operation": 1 }, { "block": 98988, "account": 303905, "ophash": "AC82010021A30400030000008A762486C0F0CEEE4584728C4913588526CAF474", "n_operation": 3 }, { "block": 264243, "account": 1309660, "ophash": "33080400DCFB13000300000026C6F94612F5474ECE9378181FB67811356E0072", "n_operation": 3 }, { "block": 70416, "account": 285630, "ophash": "10130100BE5B0400A9C70200DECB8DC0B5E98CBFA909F22428F36D8AD8EF1550", "n_operation": 182185 }, { "block": 263474, "account": 1316835, "ophash": "32050400E31714000100000057867A698F1A48257D089B421AD7B8239D88A68F", "n_operation": 1 }, { "block": 186093, "account": 929930, "ophash": "EDD602008A300E00010000008FE3299EA49B50B44B6D9631CF506F9E7949BFBF", "n_operation": 1 }, { "block": 69173, "account": 285630, "ophash": "350E0100BE5B0400595A0200E7A8136DFB111F7E3F509E2B34CC3A9019EAC0E5", "n_operation": 154201 }, { "block": 227105, "account": 571630, "ophash": "21770300EEB80800D32C000055A6920ECDE08A89F57EE7960E6B357C893BDFA9", "n_operation": 11475 }, { "block": 254466, "account": 271959, "ophash": "02E2030057260400070000007F48E12A9B29B3EC4528E44BF77F4305220137B5", "n_operation": 7 }, { "block": 236525, "account": 1182110, "ophash": "ED9B03009E091200010000007FD692919D8E5BD4402DFC1506C067BCC6F27205", "n_operation": 1 }, { "block": 249594, "account": 285630, "ophash": "FACE0300BE5B040017FC4A0073E043E9F4EF45B19811EF5C2AFC9538B9E2A7CE", "n_operation": 4914199 }, { "block": 73391, "account": 285630, "ophash": "AF1E0100BE5B040007D00300EA1463D03B610C2305AE41695540DFC01BEA41FA", "n_operation": 249863 }, { "block": 205092, "account": 571630, "ophash": "24210300EEB80800A0250000DD059305BD3B889D707DB3386C49F3DB52B6CFEA", "n_operation": 9632 }, { "block": 45523, "account": 219045, "ophash": "D3B10000A5570300010000001E3DAC4B2B6A4541CF0CA5164B108C1C0F28C138", "n_operation": 1 }, { "block": 235809, "account": 188923, "ophash": "21990300FBE10200270000009956D57FB4913A91F19ACBCAD691FAA39F1FFC0D", "n_operation": 39 }, { "block": 107805, "account": 285630, "ophash": "1DA50100BE5B0400D8400F0042D7FD93682C8887DEEE6B1C5E6773516A4D1A37", "n_operation": 999640 }, { "block": 252125, "account": 366422, "ophash": "DDD8030056970500050000009B6AC5B177078666A5A62EA2EC7A2DD6AFF1AA08", "n_operation": 5 }, { "block": 146557, "account": 732265, "ophash": "7D3C0200692C0B0001000000496A7756DEFFFFB215BD8C22408E7FE9A7189F8F", "n_operation": 1 }, { "block": 281016, "account": 582406, "ophash": "B849040006E30800410000008D4EB81EDC2941686011645E5BAB41CD966E6275", "n_operation": 65 }, { "block": 195577, "account": 571630, "ophash": "F9FB0200EEB80800EF1C0000937E764F9E87F80F0C41B43D9A39AFB796A68D52", "n_operation": 7407 }, { "block": 219512, "account": 1097020, "ophash": "785903003CBD10000100000009A79D8A753838A5AD35961B9DF5EFD9F61719BB", "n_operation": 1 }, { "block": 65062, "account": 285630, "ophash": "26FE0000BE5B0400715501006C7DC8280B5E709CA6B1764CB137A2577CE0E6D3", "n_operation": 87409 }, { "block": 71423, "account": 285630, "ophash": "FF160100BE5B0400B52C03004ED4A1D1EC85F30E72DF33D9D83E1897912D951F", "n_operation": 208053 }, { "block": 281441, "account": 1406655, "ophash": "614B0400BF761500010000002777A707030D12D7E97C6CFB80359F7A0506685B", "n_operation": 1 }, { "block": 296584, "account": 1481985, "ophash": "88860400019D160001000000A6549B2A9CEFD4858E4D9F33E62753FFF7B7A2BF", "n_operation": 1 }, { "block": 61643, "account": 307590, "ophash": "CBF0000086B10400010000003D62699D7E0A3F5C003B6B69AE9E34E4E9D6539D", "n_operation": 1 }, { "block": 94629, "account": 285630, "ophash": "A5710100BE5B040089200A0074213C34C0AEBC1176315EB6D411481852DEC1C1", "n_operation": 663689 }, { "block": 158988, "account": 86646, "ophash": "0C6D020076520100100A00004CF669F1BA5E2169CFF3515E161C58FB2013FCAB", "n_operation": 2576 }, { "block": 89305, "account": 285630, "ophash": "D95C0100BE5B0400B6800800949AF117E34C6B332B682BC95B5C2ED281D7715A", "n_operation": 557238 }, { "block": 139351, "account": 696240, "ophash": "57200200B09F0A00010000005F4134F3EBCAF3B73989653DC2B87161F21B97CA", "n_operation": 1 }, { "block": 58445, "account": 285630, "ophash": "4DE40000BE5B04006F3500007863CC72F0CEB1409CC80EC6887E92207C410947", "n_operation": 13679 }, { "block": 268103, "account": 629030, "ophash": "4717040026990900270000001698A996DF51A1BAC86306CAB793ECAE86A2EEE3", "n_operation": 39 }, { "block": 123039, "account": 614650, "ophash": "9FE00100FA6009000100000022A0CBD4A904C612A1221A39DF24C0B8CBBEF5DF", "n_operation": 1 }, { "block": 298048, "account": 1303940, "ophash": "408C040084E5130001000000D0ADE437213AB5AD924D8ED65DC2FF0DA272C987", "n_operation": 1 }, { "block": 72404, "account": 282867, "ophash": "D41A0100F3500400140000007CB177FFEE08ABA2EB620F06E11A9B6E6DB64E57", "n_operation": 20 }, { "block": 117008, "account": 584515, "ophash": "10C9010043EB08000100000076433D989745D724786333DABA6C5613BC0B9924", "n_operation": 1 }, { "block": 146029, "account": 332823, "ophash": "6D3A0200171405005800000084DF70E3BF3F382DF78C9AFFCE9331866CB7AE71", "n_operation": 88 }, { "block": 136638, "account": 682670, "ophash": "BE150200AE6A0A0001000000E827EBDC580F1B0F2BE8B69862985500CBFE0C8D", "n_operation": 1 }, { "block": 231694, "account": 545034, "ophash": "0E8903000A51080004000000A0ABE9E03FF31DAEA6479C6167B4E272F12C48E9", "n_operation": 4 }, { "block": 122001, "account": 609480, "ophash": "91DC0100C84C0900010000005B9A2E76CCE0ECD75EA1E433BEBCA0E2B6BC2A6F", "n_operation": 1 }, { "block": 55151, "account": 273775, "ophash": "6FD700006F2D040001000000069AF1B76BE2A6B788F55E41E271BB5B6C07E2FB", "n_operation": 1 }, { "block": 235071, "account": 1174825, "ophash": "3F96030029ED110001000000F462FFC1D6145E125FF6AAD8E0BB2C9E9EA7EA52", "n_operation": 1 }, { "block": 175346, "account": 876185, "ophash": "F2AC0200995E0D000100000050424E9060CC62C72D682FB3A13A979AE0AF3E2D", "n_operation": 1 }, { "block": 99320, "account": 285630, "ophash": "F8830100BE5B04007AB20B007CFF89EE5A55906F0F73CB51C765B31128756FB1", "n_operation": 766586 }, { "block": 245202, "account": 1225460, "ophash": "D2BD0300F4B21200010000005C186120E9F13B6E235881BA3C653777EA011148", "n_operation": 1 }, { "block": 123195, "account": 285630, "ophash": "3BE10100BE5B0400D315190061C7431FCF474EBF9BCF85F6E104A646E64C8049", "n_operation": 1643987 }, { "block": 300632, "account": 1076799, "ophash": "589604003F6E1000070000002E5ACABA459FE87E05BE2AD065D5DEF933A8054A", "n_operation": 7 }, { "block": 155338, "account": 200119, "ophash": "CA5E0200B70D03000400000093AE9001B3A9EE9A69175B1162B6A2D1C8C57CAF", "n_operation": 4 }, { "block": 104892, "account": 523900, "ophash": "BC9901007CFE07000100000049B72B9C12A0E701734CB99F20FF6F0F3FCEEEA9", "n_operation": 1 }, { "block": 74516, "account": 164122, "ophash": "142301001A810200A2010000433FED94C3970462D04C2B41B285864BB043416D", "n_operation": 418 }, { "block": 25396, "account": 124860, "ophash": "34630000BCE7010001000000646D9F2A7B20CF98367574DA74DF562EE0D64A42", "n_operation": 1 }, { "block": 58106, "account": 285630, "ophash": "FAE20000BE5B0400C224000093A366F9ED1F9ACBBE1C6575864438B5C8DB03A8", "n_operation": 9410 }, { "block": 154283, "account": 285630, "ophash": "AB5A0200BE5B0400D47B2B0045A9D7393FC87CE6063E1A2FF9F60D85FEE8EF0D", "n_operation": 2849748 }, { "block": 70283, "account": 285630, "ophash": "8B120100BE5B040030BD02004132B0B9E343E72557078413FBBC30BE96B62F12", "n_operation": 179504 }, { "block": 70719, "account": 285630, "ophash": "3F140100BE5B040091E40200327BC04AD58984CD9A8B33543DDA4049528C6291", "n_operation": 189585 }, { "block": 119675, "account": 597820, "ophash": "7BD301003C1F0900010000005C98278683759B303FE7D9B53C6A2053E362F195", "n_operation": 1 }, { "block": 188078, "account": 350506, "ophash": "AEDE02002A5905002C050000A66E993E1A37F8CFC71CC94192C203B5F49CF9AE", "n_operation": 1324 }, { "block": 172242, "account": 86646, "ophash": "D2A0020076520100FC0F000045DA6E5DC61D399723E80CD3E886853FC29CE757", "n_operation": 4092 }, { "block": 202277, "account": 1010840, "ophash": "25160300986C0F0001000000A34DDD49316C6627B40EDC29C123517225823CD5", "n_operation": 1 }, { "block": 60505, "account": 290760, "ophash": "59EC0000C86F0400EA24000010031678053F223002513D3E4CEC1F55FB5396B4", "n_operation": 9450 }, { "block": 271417, "account": 1356555, "ophash": "392404000BB31400010000007265AFE4A7B9D8B4B55D7124D182121BC9198484", "n_operation": 1 }, { "block": 175549, "account": 877230, "ophash": "BDAD0200AE620D000100000083B5FDAEF2B6DEEE777D9094DE2E54D5CCEC1680", "n_operation": 1 }, { "block": 157678, "account": 787860, "ophash": "EE67020094050C000100000044CA9B2B15F45C00B527772629219D0C4DE1C7BB", "n_operation": 1 }, { "block": 266384, "account": 285630, "ophash": "90100400BE5B04004F064D00F053B2AD8E2166012285A0901D740CAB79D1F1CD", "n_operation": 5047887 }, { "block": 137856, "account": 285630, "ophash": "801A0200BE5B0400A888220037019E7BBF3640008CFF8D1DCF69EF1006431518", "n_operation": 2263208 }, { "block": 136625, "account": 682585, "ophash": "B1150200596A0A00010000004FE2E4F6FDBDE61352F49D9F991B27168AB69AF1", "n_operation": 1 }, { "block": 63969, "account": 290760, "ophash": "E1F90000C86F0400B1470000A80BDD2CEB1DC7917F13F369409035C8CF950CAC", "n_operation": 18353 }, { "block": 94915, "account": 285630, "ophash": "C3720100BE5B040025380A006BAEAC35E0305700DC880800A293DCEAF636AB50", "n_operation": 669733 }, { "block": 297004, "account": 1484475, "ophash": "2C880400BBA6160001000000B79A7B94D82226A117399FBA07DE45460F715218", "n_operation": 1 }, { "block": 254564, "account": 285630, "ophash": "64E20300BE5B0400C35F4B004E3EE134C8E8E7105280F1CFAC52F8EEF7D4FFAA", "n_operation": 4939715 }, { "block": 169764, "account": 848290, "ophash": "24970200A2F10C000100000035C25B89A11229D0BF411077FB5C91450B37382D", "n_operation": 1 }, { "block": 15146, "account": 73005, "ophash": "2A3B00002D1D01000100000059E952FEEA140BE34CB2C54BBFD1FAC86B0A2ACB", "n_operation": 1 }, { "block": 168794, "account": 3570, "ophash": "5A930200F20D0000250000006EEE8A381E26E25FD12679E950562873435E7342", "n_operation": 37 }, { "block": 120517, "account": 285630, "ophash": "C5D60100BE5B0400B9391700A18EEC9F013BA64039F1B80BB4510C4540566A2A", "n_operation": 1522105 }, { "block": 78077, "account": 285630, "ophash": "FD300100BE5B0400A14705002054A43E07063D80CBC2E4DF833E9EE49569AC73", "n_operation": 346017 }, { "block": 162396, "account": 811445, "ophash": "5C7A0200B5610C0001000000AFEEB09F9DE079D36857D5308101D4F9FA47B945", "n_operation": 1 }, { "block": 61336, "account": 290760, "ophash": "98EF0000C86F0400502C0000B93D6567A83E4307E6E21D9CA591DC4EF584E1B1", "n_operation": 11344 }, { "block": 130474, "account": 86646, "ophash": "AAFD01007652010042070000F452FF6CFE01B927E69371E058086A1C9A56B4D8", "n_operation": 1858 }, { "block": 199067, "account": 571630, "ophash": "9B090300EEB8080029200000E5D954B7E82F86305EE4887DD68002CCB4E8C178", "n_operation": 8233 }, { "block": 120758, "account": 86646, "ophash": "B6D7010076520100000600006B0B8128FA653C416F3076F797D2993D980072A8", "n_operation": 1536 }, { "block": 275591, "account": 285630, "ophash": "87340400BE5B040054E74D00E4B2092A355C42E246FEECB1E6D495CAC622B585", "n_operation": 5105492 }, { "block": 259212, "account": 195655, "ophash": "8CF4030047FC02000E000000D07CCDD94A4A923BC15917E589AEA9C6E5232774", "n_operation": 14 }, { "block": 74904, "account": 373990, "ophash": "98240100E6B405000100000033A570577C2CEBB59D2075689300898F43BB696E", "n_operation": 1 }, { "block": 140237, "account": 700665, "ophash": "CD230200F9B00A00010000003BD5AD92297250B90BA756B2BCDA560D0FD4EBC5", "n_operation": 1 }, { "block": 207176, "account": 1035320, "ophash": "4829030038CC0F0001000000E1932E805C6B64254AA3535E70B13ECD0C4F00FE", "n_operation": 1 }, { "block": 273457, "account": 1366775, "ophash": "312C0400F7DA140001000000381F85DA1DACF5B0ADBF9F13D752E3E5CA4155E3", "n_operation": 1 }, { "block": 97198, "account": 285630, "ophash": "AE7B0100BE5B0400A2EF0A00EFE09313A00271C8118D2B06251F61E2536EC4A4", "n_operation": 716706 }, { "block": 59580, "account": 285630, "ophash": "BCE80000BE5B0400DC63000065DCF77335341CA7126D035BC7ED004442CA9DD7", "n_operation": 25564 }, { "block": 147206, "account": 735490, "ophash": "063F020002390B00010000007FBAC29CE4D4B10BC2FBF5AC395E75874591D3C8", "n_operation": 1 }, { "block": 286648, "account": 1432690, "ophash": "B85F040072DC1500010000000FB8A3C06C20E65BCD3675D2700D6BD90155A247", "n_operation": 1 }, { "block": 139704, "account": 697990, "ophash": "B821020086A60A0001000000AC49048F769AE409ACD214AEBFF343D894E82CD0", "n_operation": 1 }, { "block": 218525, "account": 1092095, "ophash": "9D550300FFA9100001000000DF80BBA3139B4AD92C3AF3C0AD0FD6AEC709F01B", "n_operation": 1 }, { "block": 113998, "account": 285630, "ophash": "4EBD0100BE5B0400C5B312004ECCFF6DD628A97472A8F1A11E0C4C738B40FE92", "n_operation": 1225669 }, { "block": 1191, "account": 5010, "ophash": "A704000092130000010000006CEA38D565B3358778030C70681DEB048C71919F", "n_operation": 1 }, { "block": 103962, "account": 285630, "ophash": "1A960100BE5B0400EB3D0D00AD27A9E67FE484684A46E25FEFD29B310A59D774", "n_operation": 867819 }, { "block": 63446, "account": 285630, "ophash": "D6F70000BE5B040006200100EE90B8E3FB8EDE97DA184E65A8AECA82E97EC6F3", "n_operation": 73734 }, { "block": 201951, "account": 1009240, "ophash": "DF14030058660F00010000009328E8BB9F67BE62927F620B099CE784FE33B867", "n_operation": 1 }, { "block": 58160, "account": 285630, "ophash": "30E30000BE5B0400842600006DEDD1C1F8E691325EE96965F1D6061389E20579", "n_operation": 9860 }, { "block": 178776, "account": 893350, "ophash": "58BA0200A6A10D0001000000AA0C1D2672854CA1841A3EFE767405E62A487EFF", "n_operation": 1 }, { "block": 119769, "account": 285630, "ophash": "D9D30100BE5B04004DB016009A55D50EC4817DFADC9E8FB5B2A5A02D189CE43C", "n_operation": 1486925 }, { "block": 281710, "account": 1408005, "ophash": "6E4C0400057C150001000000B1DF169226CFA370B6D462DF13B27789BA8016ED", "n_operation": 1 }, { "block": 294215, "account": 69933, "ophash": "477D04002D1101001900000087209747EA0CCC394A5BB8E2329B9AAFB2BBCC36", "n_operation": 25 }, { "block": 176781, "account": 285630, "ophash": "8DB20200BE5B04007E0538006782521BB2A89ACEBE9F95F7F66C9E87744F989C", "n_operation": 3671422 }, { "block": 207562, "account": 571630, "ophash": "CA2A0300EEB80800E127000099D6DDD839ED7CE16878CDD120E71727168562DE", "n_operation": 10209 }, { "block": 287533, "account": 1437125, "ophash": "2D630400C5ED1500010000002E164FB8108E75C92668D1B724C2E7B4F4307EB8", "n_operation": 1 }, { "block": 99850, "account": 285630, "ophash": "0A860100BE5B040034C70B00B8E21D3FB67A624C1B661C8E053BB29B98290BD4", "n_operation": 771892 }, { "block": 251056, "account": 1254715, "ophash": "B0D403003B251300010000001DE45A8961ACF8BF0F7F62AA2B63D7BECFE8EAB6", "n_operation": 1 }, { "block": 253729, "account": 1268115, "ophash": "21DF0300935913000100000082C80D912A3429A0F62C625970F456554755D251", "n_operation": 1 }, { "block": 255192, "account": 1275345, "ophash": "D8E40300D175130001000000DF6773F6C738913FE85C2D9B8BA951D12272BC66", "n_operation": 1 }, { "block": 146609, "account": 285630, "ophash": "B13C0200BE5B04005CC527002478B7787E181522BBD61120BB906315A24A35F1", "n_operation": 2606428 }, { "block": 115476, "account": 576870, "ophash": "14C3010066CD080001000000CA463364DC83AC555FD38175E0F7F087E3FBB54B", "n_operation": 1 }, { "block": 227695, "account": 6680, "ophash": "6F790300181A000007000000D70169AE3DE6F5320DC097A3FE3F317241A796E0", "n_operation": 7 }, { "block": 263468, "account": 1316815, "ophash": "2C050400CF171400010000006C40F28E9E7F10D427D292348BF991441C3F5884", "n_operation": 1 }, { "block": 139146, "account": 274800, "ophash": "8A1F02007031040004000000583CAE29D930DCF4B4C244C5D8520AFFE38B0F20", "n_operation": 4 }, { "block": 90345, "account": 285630, "ophash": "E9600100BE5B040068C70800F2FC3549DD406F68A4638506567EAD0E57D53B7F", "n_operation": 575336 }, { "block": 81492, "account": 86646, "ophash": "543E010076520100C40100009487EB357CA0A1B362044BCF06CA8771918C5353", "n_operation": 452 }, { "block": 148934, "account": 744145, "ophash": "C6450200D15A0B00010000004B449A2E684C59E32AE0AD964893CE6B03971AB0", "n_operation": 1 }, { "block": 92617, "account": 278248, "ophash": "C9690100E83E0400030000004E2CD07FD528002361416FC0204847F2BD11A43E", "n_operation": 3 }, { "block": 149157, "account": 745265, "ophash": "A5460200315F0B000100000089975F7CA115B64E3207EB5E2DDA2E3AFF8B912D", "n_operation": 1 }, { "block": 73635, "account": 285630, "ophash": "A31F0100BE5B04007FE003004FC3E8B7D187823CAF31798FF973049D0F292025", "n_operation": 254079 }, { "block": 91331, "account": 285630, "ophash": "C3640100BE5B0400CF100900E0EA7BA14D603CA7362D96EE47A77AC7835C9B6A", "n_operation": 594127 }, { "block": 169807, "account": 86646, "ophash": "4F97020076520100270F000092BDDDF1A7423C353ACAB8D214D2F4B0BD1021E5", "n_operation": 3879 }, { "block": 141158, "account": 705225, "ophash": "66270200C9C20A0001000000196E985F399A0C90C4BB1B6B804018E45A3254E1", "n_operation": 1 }, { "block": 176604, "account": 285630, "ophash": "DCB10200BE5B04002BD53700C5B84D9CFBD5A03E4F425B942CB9E31EE6660025", "n_operation": 3659051 }, { "block": 88225, "account": 285630, "ophash": "A1580100BE5B04007A340800679A85D79A09010D2FB52FD1A3C1EAD943F10436", "n_operation": 537722 }, { "block": 138048, "account": 285630, "ophash": "401B0200BE5B04008EA822003D23A06CA163AD81F63AD1935418EC353D34BA25", "n_operation": 2271374 }, { "block": 212292, "account": 1060940, "ophash": "443D03004C30100001000000D51E7CF014AB1FDC7339023ED0C50AC1CC934824", "n_operation": 1 }, { "block": 170850, "account": 285630, "ophash": "629B0200BE5B04005C6C3400D0A7B6E25BF80CB75DC8602917E2BA1FCEDF5E04", "n_operation": 3435612 }, { "block": 91004, "account": 285630, "ophash": "7C630100BE5B040077F708001968E50B4BF65462B028EAC6672B564179B7FCD1", "n_operation": 587639 }, { "block": 73560, "account": 285630, "ophash": "581F0100BE5B040085DB03002D5DDF43C8F11C5AB7873BD9BD2AFD5D3671BED4", "n_operation": 252805 }, { "block": 245101, "account": 285630, "ophash": "6DBD0300BE5B0400E49C4A006584FAFA2C3C8E96136D7A6FD2A74AFED691DF81", "n_operation": 4889828 }, { "block": 125544, "account": 627190, "ophash": "68EA0100F691090001000000DAB106BFE581029866BCEF8D15DF667E9E5CB518", "n_operation": 1 }, { "block": 231729, "account": 1158085, "ophash": "31890300C5AB110001000000C6FD506440F78D8ABF5B606337625E49BBFBBDD8", "n_operation": 1 }, { "block": 64598, "account": 285630, "ophash": "56FC0000BE5B040092460100B3F67789B4E355297883AA293AB32838E41ABC99", "n_operation": 83602 }, { "block": 73283, "account": 285630, "ophash": "431E0100BE5B0400E6C9030084A20B98628DA32B5258DE0FAC2EC6A003C85F12", "n_operation": 248294 }, { "block": 210360, "account": 529497, "ophash": "B83503005914080003000000A9F6C9660AD322B96ADFC7A1E1B146297A13FFB5", "n_operation": 3 }, { "block": 113922, "account": 285630, "ophash": "02BD0100BE5B04001DAB12002B71936474F2846292DD675D6394BCCB6756AE67", "n_operation": 1223453 }, { "block": 117718, "account": 588070, "ophash": "D6CB010026F9080001000000D47891024BBBCB0DDACA8C03A2D57D3FDD43DC14", "n_operation": 1 }, { "block": 117033, "account": 584650, "ophash": "29C90100CAEB0800010000003E2942293C6939A950605FF7B1D0F680C530783B", "n_operation": 1 }, { "block": 185454, "account": 571630, "ophash": "6ED40200EEB808002615000031BCDDA3EABF618DED799F51CD7B9E276BEDED16", "n_operation": 5414 }, { "block": 126673, "account": 632850, "ophash": "D1EE010012A8090001000000D2EF3494DD7C13BF1BC5EC68C70C7518FC28F41D", "n_operation": 1 }, { "block": 128464, "account": 641805, "ophash": "D0F501000DCB0900010000005093F96D88F44B3B997E03E3F7EA167DF055C9AF", "n_operation": 1 }, { "block": 202622, "account": 1012595, "ophash": "7E17030073730F00010000008FD932449C23B4BAFABC47A709EC538E75FC549F", "n_operation": 1 }, { "block": 218755, "account": 1093240, "ophash": "8356030078AE100001000000CD5D00FC1B58A5E99F10BF5FE1BB8D1DCA2EEBBF", "n_operation": 1 }, { "block": 286089, "account": 547523, "ophash": "895D0400C35A080080000000E1921F23F3B2886A4097ED87C4E19EA13725022C", "n_operation": 128 }, { "block": 61645, "account": 285630, "ophash": "CDF00000BE5B0400B8C5000070FD85D5D8801B36357340B61234A5EE7E354A67", "n_operation": 50616 }, { "block": 261090, "account": 285630, "ophash": "E2FB0300BE5B04003BDE4B005488AA0D489AC55803A46F179DE2939D90A50F61", "n_operation": 4972091 }, { "block": 65096, "account": 285630, "ophash": "48FE0000BE5B0400DE550100F05997513419B7F30B4DE15A2C48ED84CA68909F", "n_operation": 87518 }, { "block": 194168, "account": 571630, "ophash": "78F60200EEB80800A61B0000F05CC08AFDEE9072A80A787146B0FC89C9BACFF2", "n_operation": 7078 }, { "block": 238927, "account": 1194105, "ophash": "4FA503007938120001000000CD34CAB2495B3ADF8ED72CC890E22B6CAF02C54F", "n_operation": 1 }, { "block": 168334, "account": 838183, "ophash": "8E91020027CA0C0006000000731EFF4111C74AE953505EB78218D5F36E3DE183", "n_operation": 6 }, { "block": 164713, "account": 86646, "ophash": "69830200765201000A0C0000731F67F94B85FD0CFE11F8F75361DECCCCDC0203", "n_operation": 3082 }, { "block": 145011, "account": 724515, "ophash": "73360200230E0B0001000000B121C049559E46DDE6E77F7DB8A9E5C6F3CBAE4D", "n_operation": 1 }, { "block": 54282, "account": 122478, "ophash": "0AD400006EDE0100170000003CD0C51F6336AF96A96D55279B928A7211CB18C4", "n_operation": 23 }, { "block": 259445, "account": 587022, "ophash": "75F503000EF50800020000003E35BFE9FB939B34596E6FB7621BC4B650522CAE", "n_operation": 2 }, { "block": 71330, "account": 168536, "ophash": "A2160100589202006C000000A2C8F5AB3B3C87C2B1A31B4651CEA146429EF186", "n_operation": 108 }, { "block": 72087, "account": 285630, "ophash": "97190100BE5B0400EE6A03007D51003B708BFB49A16168207946C8B91235262F", "n_operation": 223982 }, { "block": 82120, "account": 285630, "ophash": "C8400100BE5B0400AD8B0600D2259628B00E8A5240FA1026D4D82EBB2FCB2571", "n_operation": 428973 }, { "block": 101044, "account": 285630, "ophash": "B48A0100BE5B0400A3370C00BEE19753BBB660146868C21A05E0DD32A36888D5", "n_operation": 800675 }, { "block": 154118, "account": 770070, "ophash": "065A020016C00B0001000000AD31D8491C367E7D95ECCA5FC662CDBE49F0C23F", "n_operation": 1 }, { "block": 75494, "account": 376945, "ophash": "E626010071C0050001000000E02D765830EA9C2C13EFCF0CE24E48B8BD40A068", "n_operation": 1 }, { "block": 98228, "account": 285630, "ophash": "B47F0100BE5B0400864C0B00F797E42F8B1F8A8BF68EF6FBEE9018C523308F59", "n_operation": 740486 }, { "block": 128845, "account": 285630, "ophash": "4DF70100BE5B040013EB1C00DAAFF4035605EDF948351D09B2F312AE99D5F58C", "n_operation": 1895187 }, { "block": 165604, "account": 571630, "ophash": "E4860200EEB80800B00A00007A881B54E77DA30F4F14263103BB9983886EFBBE", "n_operation": 2736 }, { "block": 76952, "account": 285630, "ophash": "982C0100BE5B0400B9F5040067BD5658D4C2577165F4C52428B134A3E25754E0", "n_operation": 325049 }, { "block": 143439, "account": 716645, "ophash": "4F30020065EF0A0001000000AA4F4DFB5D4D5E69D97C309B5813858909EBF71F", "n_operation": 1 }, { "block": 259851, "account": 587043, "ophash": "0BF7030023F50800020000001D364AAC5B7E4DCDBE171D8D7FF9764A7DA55EDA", "n_operation": 2 }, { "block": 157514, "account": 285630, "ophash": "4A670200BE5B0400F8FC2C005ED1880D5B95E8F49FE6951B0A7C70004E9770B2", "n_operation": 2948344 }, { "block": 57227, "account": 285610, "ophash": "8BDF0000AA5B0400010000008BE28F287E74F7100FBEDBD415DEE7DD85F5336E", "n_operation": 1 }, { "block": 83245, "account": 415665, "ophash": "2D450100B15706000100000075206FE36A9B40C3963C1C983E26327647BDE20A", "n_operation": 1 }, { "block": 244096, "account": 1219960, "ophash": "80B90300789D120001000000FDB40E0B61645EB86A06E4B62BF7C619D55DE58E", "n_operation": 1 }, { "block": 175063, "account": 571630, "ophash": "D7AB0200EEB80800450F0000F224EDE0BB20831692A941FAD5CC0FBDB5B352E0", "n_operation": 3909 }, { "block": 149073, "account": 285630, "ophash": "51460200BE5B04004F002900A4A808801A76C70374C082325C835C9B818B4F51", "n_operation": 2687055 }, { "block": 238652, "account": 1192690, "ophash": "3CA40300F23212000100000048CB9170AD33F27F3389154FAD6D345FB7C1639B", "n_operation": 1 }, { "block": 256462, "account": 439015, "ophash": "CEE90300E7B20600050000004D8E4AF9F6B0ED3F66F4F5D304626FC6E5F8A25D", "n_operation": 5 }, { "block": 59878, "account": 285630, "ophash": "E6E90000BE5B04002971000066D21AF40410F66FF15E92DF92662FA907D33447", "n_operation": 28969 }, { "block": 88514, "account": 285630, "ophash": "C2590100BE5B04005F4808004E860BE21E9C9D7A55415F6B57A6A376EA01ADD6", "n_operation": 542815 }, { "block": 225698, "account": 1127955, "ophash": "A27103001336110001000000EEDDD98D2867C675C7116EFC3F3946AEAFBA8651", "n_operation": 1 }, { "block": 149572, "account": 747335, "ophash": "4448020047670B00010000005A3C43E1AFBE16668507937B603E2B4A93615069", "n_operation": 1 }, { "block": 107022, "account": 285630, "ophash": "0EA20100BE5B04008DD30E002EAD6E0F1E8E2A29040C5B6D4A822DDE3B044CC0", "n_operation": 971661 }, { "block": 125576, "account": 285630, "ophash": "88EA0100BE5B040089B41A00B916604BAC23D8398220A9CFE3D1C148CCFC480E", "n_operation": 1750153 }, { "block": 57287, "account": 285350, "ophash": "C7DF0000A65A0400020000000871D5830BC3676B190FCBFDF1D08E3B3697B7A0", "n_operation": 2 }, { "block": 78407, "account": 340845, "ophash": "473201006D33050061020000E9AD4996D9DE35F3941E660A118B8F02C7689463", "n_operation": 609 }, { "block": 91886, "account": 52558, "ophash": "EE6601004ECD000001000000D124806E30AB904624E18A508438A77439FF9122", "n_operation": 1 }, { "block": 111032, "account": 86646, "ophash": "B8B1010076520100A40400006DC0EF600ECD124CFA920EA76D10B09D9E200F64", "n_operation": 1188 }, { "block": 220118, "account": 1100065, "ophash": "D65B030021C91000010000000331E441FFF44349181982F380979417D6566778", "n_operation": 1 }, { "block": 241257, "account": 1205755, "ophash": "69AE0300FB65120001000000FDC4B087DDA138A7619B738BEFA6EB6E5BB33103", "n_operation": 1 }, { "block": 243548, "account": 1217195, "ophash": "5CB70300AB92120001000000D52E6FA2AD1B859AAED97C289E49801FB2029C17", "n_operation": 1 }, { "block": 132079, "account": 285630, "ophash": "EF030200BE5B0400FEE31E00EA7E8535A3927BD9455C8790E09E5C5BFFBC3D5B", "n_operation": 2024446 }, { "block": 68606, "account": 285630, "ophash": "FE0B0100BE5B0400A925020080F8CDD18CA989A37EDF91177CC048CD76BFE82F", "n_operation": 140713 }, { "block": 245375, "account": 1226315, "ophash": "7FBE03004BB61200010000000952EE10D3E0CE4D4C5B96B13CAF23FC95A5E2BC", "n_operation": 1 }, { "block": 261265, "account": 274532, "ophash": "91FC03006430040007000000D6A767CD048FE485525FD47162359FC1893BC63F", "n_operation": 7 }, { "block": 281063, "account": 34500, "ophash": "E7490400C4860000F81F0000C78E37C4C78B81F377046B90EFD6D410843B05D9", "n_operation": 8184 }, { "block": 107774, "account": 285630, "ophash": "FEA40100BE5B04006C3D0F0004191D8026D0E20662A466F54B4F672593F60C5D", "n_operation": 998764 }, { "block": 195452, "account": 976725, "ophash": "7CFB020055E70E0001000000CEC4C88BAD7837CABA50E7C9C51399B6D6599E97", "n_operation": 1 }, { "block": 270803, "account": 1353485, "ophash": "D32104000DA714000100000048BAEC9151E6158BDBF17566AE9562B4865BECF6", "n_operation": 1 }, { "block": 101306, "account": 285630, "ophash": "BA8B0100BE5B040097500C006AEA6C6F7A612DEF397477A52D5A93A76CE7679A", "n_operation": 807063 }, { "block": 62231, "account": 290760, "ophash": "17F30000C86F04002A370000A1D12B45A54977FA0BEA2B5E78E36E8DA3683A7B", "n_operation": 14122 }, { "block": 132316, "account": 285630, "ophash": "DC040200BE5B0400750B1F003DE6F1121E75ACB66030B6772CD3F168FBCE794B", "n_operation": 2034549 }, { "block": 188141, "account": 350506, "ophash": "EDDE02002A59050031050000879155E92867EE82E55AE0FD6F2817D97A3DFA60", "n_operation": 1329 }, { "block": 126371, "account": 631325, "ophash": "A3ED01001DA2090001000000414882C816683728D969F1646619BE9ECF6814F8", "n_operation": 1 }, { "block": 75435, "account": 285630, "ophash": "AB260100BE5B0400DF76040096B016D797DBDDDCBBF28C8C86C6DCD821AFD495", "n_operation": 292575 }, { "block": 94595, "account": 285630, "ophash": "83710100BE5B0400A81C0A00FA5E88A0FEDA6C4F7012DB515F1C73EC8DF441E0", "n_operation": 662696 }, { "block": 24553, "account": 121790, "ophash": "E95F0000BEDB0100010000009E0BE8CC2ED939A88A8BCFAE1C6912E29DE746E0", "n_operation": 1 }, { "block": 209490, "account": 571630, "ophash": "52320300EEB8080046290000302347C6494A3A45082969588B5311BF4CADCBD4", "n_operation": 10566 }, { "block": 110262, "account": 285630, "ophash": "B6AE0100BE5B04005A931000A33141E99FAD8EDBC589688C9C54F9433DF260CB", "n_operation": 1086298 }, { "block": 211707, "account": 1057995, "ophash": "FB3A0300CB24100001000000811E3A853A3D65171166E11D7EB3C74D07AE4A0C", "n_operation": 1 }, { "block": 105352, "account": 285630, "ophash": "889B0100BE5B040067FA0D0065E746D346A352CFE546E0DBDF2B29B78AF8EEBC", "n_operation": 916071 }, { "block": 110239, "account": 285630, "ophash": "9FAE0100BE5B0400658F10005F2B4E6073F8F8B8031DECD5CA9034E974AB113B", "n_operation": 1085285 }, { "block": 66814, "account": 290760, "ophash": "FE040100C86F0400905E00004C254E10AD3FFEC24D98665CD01DCB2F4194716D", "n_operation": 24208 }, { "block": 125099, "account": 619703, "ophash": "ABE80100B77409000B000000D019D80BD44D5245D16CEC5BDDEF5C044A16176E", "n_operation": 11 }, { "block": 60195, "account": 285630, "ophash": "23EB0000BE5B0400258000005302755C5C44C4D62A236AAC47568A55DFACB383", "n_operation": 32805 }, { "block": 62919, "account": 285630, "ophash": "C7F50000BE5B0400C00201007C5401FA64D6D1481A132C8A15B6F51BB23AEA64", "n_operation": 66240 }, { "block": 120796, "account": 86646, "ophash": "DCD70100765201000506000049B586745219336B34E2B3789AAD5F20DD8912E4", "n_operation": 1541 }, { "block": 249491, "account": 1246925, "ophash": "93CE0300CD06130001000000B8E624341F90B048B339CFAF5DF072D820283767", "n_operation": 1 }, { "block": 159888, "account": 798905, "ophash": "90700200B9300C0001000000902875E956F5F0F08586344042BD18D921478BCF", "n_operation": 1 }, { "block": 120372, "account": 571630, "ophash": "34D60100EEB80800C9000000516389EFCB95697E58914E734CC5B4DA9AF6EB42", "n_operation": 201 }, { "block": 134020, "account": 669575, "ophash": "840B020087370A00010000003CEED020A8715A3E2298F501BB403232BD4B8DC1", "n_operation": 1 }, { "block": 128483, "account": 285630, "ophash": "E3F50100BE5B0400C3AD1C00AFB2AB4FB9E75A7FB76FEE05776CC7E23A862168", "n_operation": 1879491 }, { "block": 55228, "account": 274075, "ophash": "BCD700009B2E04000100000075C854FC4C1B55CB59920BE511297065795428E8", "n_operation": 1 }, { "block": 68681, "account": 285630, "ophash": "490C0100BE5B0400322E02005D01D1D0477EBB3DF8B01807CEE82BA22D93A7D8", "n_operation": 142898 }, { "block": 208524, "account": 1042060, "ophash": "8C2E03008CE60F0001000000F3DC304CE960FB3BB106D4DCB1843D90554B1970", "n_operation": 1 }, { "block": 214374, "account": 571630, "ophash": "66450300EEB80800782A00001BA1A05A134776FBBCD673702F816B1C55472C9E", "n_operation": 10872 }, { "block": 240583, "account": 1202385, "ophash": "C7AB0300D15812000100000001E3EB52CDDCFDAA5A1D9A9DA5074C96E02E7DAB", "n_operation": 1 }, { "block": 272262, "account": 499123, "ophash": "86270400B39D070002000000E3C1069E796EE5F10743BC1E898FCAA3F0CE6762", "n_operation": 2 }, { "block": 189188, "account": 110027, "ophash": "04E30200CBAD01007002000093597F8C4CE789F00BB0DDD690B2D65C2C4AD048", "n_operation": 624 }, { "block": 55907, "account": 279005, "ophash": "63DA0000DD41040001000000D7D3D5CE0C4828155C09BA1988EC36F9462DEB82", "n_operation": 1 }, { "block": 225327, "account": 1126060, "ophash": "2F700300AC2E110001000000B6249D63D51C4ADBF98D23C8AF8DCCB5A5E46532", "n_operation": 1 }, { "block": 162807, "account": 813490, "ophash": "F77B0200B2690C00010000000E2C596E6DF63EF4123CA19C9D076BFD92802725", "n_operation": 1 }, { "block": 117108, "account": 285630, "ophash": "74C90100BE5B040086B5140086545A0AFDD880EA29D5A58EC0DF0D34BC47B09A", "n_operation": 1357190 }, { "block": 255033, "account": 1274610, "ophash": "39E40300F272130001000000C31A9382CC74A8B656B33ABD6DE63BF01C46D7C7", "n_operation": 1 }, { "block": 77597, "account": 285630, "ophash": "1D2F0100BE5B04002724050071C47BE73A7BF47B9816A45D9F19CB5F0050207F", "n_operation": 336935 }, { "block": 66643, "account": 285630, "ophash": "53040100BE5B0400549D0100E0B54073C66D13319BBE0DB4356F955D9C0E0602", "n_operation": 105812 }, { "block": 175254, "account": 285630, "ophash": "96AC0200BE5B04008D1B3700863A05EA1C2148724E0A1F0B8760856D357FD19B", "n_operation": 3611533 }, { "block": 160261, "account": 800755, "ophash": "05720200F3370C00010000008654CDE5BC7BAB05983EC459A539121FC3276BB7", "n_operation": 1 }, { "block": 231726, "account": 571630, "ophash": "2E890300EEB80800CA2D0000AF3FC5EB975D172E43CDF50375DE4A6408B74877", "n_operation": 11722 }, { "block": 99975, "account": 285630, "ophash": "87860100BE5B040060D40B00BF793A68F0BBBBE295F55F975D3FB59B0199BBAF", "n_operation": 775264 }, { "block": 260154, "account": 1299805, "ophash": "3AF803005DD513000100000050E00622DC2339EFE3AF8C3896AC80F75BD60C7A", "n_operation": 1 }, { "block": 59738, "account": 285630, "ophash": "5AE90000BE5B0400386B0000ECC49734ED0973B4C5DD3C7EDEE21E02FFD135FA", "n_operation": 27448 }, { "block": 68526, "account": 342085, "ophash": "AE0B010045380500010000007F03901A7A7A3C5C7C922E34712598D9DA83E7C8", "n_operation": 1 }, { "block": 195277, "account": 285630, "ophash": "CDFA0200BE5B0400E8464100B203C197029D8933FAAB6599160252E6D1C46ABE", "n_operation": 4277992 }, { "block": 280710, "account": 661981, "ophash": "86480400DD190A00110000000B6E978CAA4F52233C897FB392FE80050548A246", "n_operation": 17 }, { "block": 168536, "account": 126308, "ophash": "5892020064ED010007000000F104FC4708F7ECCE214F949D735737EF1D4A019C", "n_operation": 7 }, { "block": 233553, "account": 1167190, "ophash": "5190030056CF1100010000007A75F676593423A94812E3E23CF54F6E16764A67", "n_operation": 1 }, { "block": 68163, "account": 285630, "ophash": "430A0100BE5B040035FB0100024815FD4F69666A1FF90D9CC17136087313962A", "n_operation": 129845 }, { "block": 111932, "account": 285630, "ophash": "3CB50100BE5B0400D98411008347F882E45FB4E31DE1E6C79ACED29D9CCB5D74", "n_operation": 1148121 }, { "block": 169051, "account": 844695, "ophash": "5B94020097E30C0001000000978389F49EF0C8AAEAB63F2632A451014C45B4BF", "n_operation": 1 }, { "block": 236686, "account": 1182920, "ophash": "8E9C0300C80C12000100000061897271E7E64884AE7BEF47C5535A68AC2C3AFD", "n_operation": 1 }, { "block": 201120, "account": 86646, "ophash": "A0110300765201006E140000DBCE2E3463E5FD7FEB6BF72BF6D0E863EF05D9F1", "n_operation": 5230 }, { "block": 69512, "account": 290760, "ophash": "880F0100C86F04006170000085C83EBCE8DA33885A8818CAA6B1F1FCF048A7A1", "n_operation": 28769 }, { "block": 289396, "account": 1446445, "ophash": "746A04002D12160001000000E7122678E2BC9C345BE2AC64F3AC9A7019A84BBE", "n_operation": 1 }, { "block": 144650, "account": 384560, "ophash": "0A35020030DE0500020000004E6A3E95B1FAB429FF0E25DD4A1A8C36CB7A4F93", "n_operation": 2 }, { "block": 71260, "account": 355780, "ophash": "5C160100C46D05000100000002C665578F44B5980ED40DBDACB1C940E548C347", "n_operation": 1 }, { "block": 203501, "account": 1016970, "ophash": "ED1A03008A840F0001000000BA67CCC961C5D117D63D4C1F3C8B103777E23EE4", "n_operation": 1 }, { "block": 203251, "account": 1015730, "ophash": "F3190300B27F0F00010000008E23D20DEC93E8EB4724A985FE12852CEDDEE8A9", "n_operation": 1 }, { "block": 106213, "account": 285630, "ophash": "E59E0100BE5B040020710E00E63C6D7A80B0DD148BCD32140A8269EC0D3BAD05", "n_operation": 946464 }, { "block": 60985, "account": 304390, "ophash": "39EE000006A5040001000000F706DEC9E3EA773F5B2B1217E74707CA7DAD7743", "n_operation": 1 }, { "block": 216985, "account": 1084400, "ophash": "994F0300F08B1000010000008B98970042A1DCF3A5F6B04899D0C5FD9812FE1B", "n_operation": 1 }, { "block": 60900, "account": 285630, "ophash": "E4ED0000BE5B040047A30000A6BC21E517D3AF7DEE9D13F444F97EB80DC40315", "n_operation": 41799 }, { "block": 229390, "account": 571630, "ophash": "0E800300EEB808005C2D00004FDE2CB28A2C485884E7B7BC5A44D5A3B21EFB4B", "n_operation": 11612 }, { "block": 102737, "account": 285630, "ophash": "51910100BE5B0400B1CD0C00291A37EBB07CE6517F5B47A0543B0EB6DD378492", "n_operation": 839089 }, { "block": 84790, "account": 285630, "ophash": "364B0100BE5B04007E4007009938B3A091C0BB80E88E7C763FE040DE64CDFC8F", "n_operation": 475262 }, { "block": 216260, "account": 1080765, "ophash": "C44C0300BD7D1000010000006C5A83DBF3B7FB025086D9873F4E287E2FF19165", "n_operation": 1 }, { "block": 58934, "account": 285630, "ophash": "36E60000BE5B0400D9460000BACDEAE7645EE80EDC6EA8C08CEF9608C6373756", "n_operation": 18137 }, { "block": 204466, "account": 1021800, "ophash": "B21E030068970F00010000005A059C81BE705C8821F8E9BCEAD3337D0EAFF90F", "n_operation": 1 }, { "block": 101407, "account": 285630, "ophash": "1F8C0100BE5B04009E590C00D27F7ACFD113B10523D2A500CB3716F9771AF365", "n_operation": 809374 }, { "block": 169031, "account": 77, "ophash": "479402004D000000120200008E7C7927576CB27600F98F85640D5D69CB3CBBCE", "n_operation": 530 }, { "block": 165395, "account": 331889, "ophash": "138602007110050002000000A563C275A7C25E5B7C4D83CE2C803392C073194E", "n_operation": 2 }, { "block": 78398, "account": 285630, "ophash": "3E320100BE5B04003F600500D2852F8F7296BB55E74D2993AB91E0CE1549B708", "n_operation": 352319 }, { "block": 264717, "account": 6038, "ophash": "0D0A04009617000001000000E2BC62CB7BF016F8D064404488A84581138F272A", "n_operation": 1 }, { "block": 97460, "account": 285630, "ophash": "B47C0100BE5B040070060B0038E8D1BE57770C06DE486548A0018FFEA1551D45", "n_operation": 722544 }, { "block": 48933, "account": 242570, "ophash": "25BF00008AB30300010000001F567E14BCDBB928B164774E0AC362FCB9AE8EFB", "n_operation": 1 }, { "block": 186115, "account": 930040, "ophash": "03D70200F8300E00010000003F688555B86F398BECE4F2A2939DA63F6B3E7E1F", "n_operation": 1 }, { "block": 135945, "account": 679195, "ophash": "091302001B5D0A00010000000CF5E8D1E0902790E734C4DACC4591F25012EDD5", "n_operation": 1 }, { "block": 70418, "account": 285630, "ophash": "12130100BE5B040042C802007BEEBAD4E179F66C95C90CFB6478FE8EC8328E38", "n_operation": 182338 }, { "block": 181565, "account": 907290, "ophash": "3DC502001AD80D000100000033CCB3AFF81BC94520248982CDC6BFEE5340B28E", "n_operation": 1 }, { "block": 44216, "account": 218550, "ophash": "B8AC0000B65503000100000018CC7C7B947FC1701E0031AE2382ED641FD43786", "n_operation": 1 }, { "block": 115158, "account": 318320, "ophash": "D6C1010070DB040003000000FB2F7D10395860EEB8D9E473405C4134C849CA25", "n_operation": 3 }, { "block": 114567, "account": 572125, "ophash": "87BF0100DDBA08000100000085191F92C351553A52BA7771A1E7C5EBF3E7B136", "n_operation": 1 }, { "block": 69236, "account": 285630, "ophash": "740E0100BE5B0400105F02007D7D481C6FE6AEB5F27FA4A9053531052713D188", "n_operation": 155408 }, { "block": 78087, "account": 285630, "ophash": "07310100BE5B0400DD480500458EE942481D16F03A1E6650226598278E336A6D", "n_operation": 346333 }, { "block": 66277, "account": 285630, "ophash": "E5020100BE5B0400E58801002C58C245CA5B6DD5AC39215E085FE24E465B470E", "n_operation": 100581 }, { "block": 165529, "account": 827100, "ophash": "99860200DC9E0C00010000000E4708E92B1F7711D8566703D8AD3FED19AAB002", "n_operation": 1 }, { "block": 204759, "account": 938265, "ophash": "D71F030019510E005B00000080F36AFEE7FAA3E33DEDA778D2084505AF1C07D4", "n_operation": 91 }, { "block": 59919, "account": 290760, "ophash": "0FEA0000C86F04005F1D00007C8A6566E23432F0463C560AFB2D6EBB0A8C22BB", "n_operation": 7519 }, { "block": 180417, "account": 300124, "ophash": "C1C002005C940400433000004A9A5FE94F94B19DA3581D4E5F7B92A7E74FC9C2", "n_operation": 12355 }, { "block": 281558, "account": 582406, "ophash": "D64B040006E30800490000007D4828D9EE866D2C1BF0A07EC0B50AEA4175BD15", "n_operation": 73 }, { "block": 137464, "account": 285630, "ophash": "F8180200BE5B04000B462200D2B45959DF17E967BF3B8121EA6741D3C9067886", "n_operation": 2246155 }, { "block": 160505, "account": 800475, "ophash": "F9720200DB360C0005000000C8241F5C842C2C4CFED7A7F4442E800F29B1D1FD", "n_operation": 5 }, { "block": 96765, "account": 285630, "ophash": "FD790100BE5B0400CCC90A00B77431A0C685B31D42AA250804E2C427D17ADB89", "n_operation": 707020 }, { "block": 161997, "account": 809455, "ophash": "CD780200EF590C0001000000B750F65E7BDABBDC103CBDAEE19CBC8F2D9682D8", "n_operation": 1 }, { "block": 103739, "account": 285630, "ophash": "3B950100BE5B040032300D0050F71276BC6689D439B490AF9E7F94507CA4C616", "n_operation": 864306 }, { "block": 60091, "account": 285630, "ophash": "BBEA0000BE5B0400027C000084D09BACF1796444B1269C9AD98872F0F65B8063", "n_operation": 31746 }, { "block": 96747, "account": 285630, "ophash": "EB790100BE5B0400ADC70A004876B0CD8FAA81AAE9F521949A1E094E1F84D9FD", "n_operation": 706477 }, { "block": 146684, "account": 732865, "ophash": "FC3C0200C12E0B0001000000D7FE4F0D4CE6D8E80F05EE6FAA83464FBB9EA606", "n_operation": 1 }, { "block": 299377, "account": 1496365, "ophash": "719104002DD5160001000000785D54E293B2C50D7FE57A29CC29E549B3A4C21B", "n_operation": 1 }, { "block": 93863, "account": 468720, "ophash": "A76E0100F026070001000000F7706868A208A32C4E11C83CF01AE51059907CC9", "n_operation": 1 }, { "block": 266927, "account": 1320300, "ophash": "AF1204006C251400E502000031D6A1C7219D262560EAF8F5AF8607D6BBC2B232", "n_operation": 741 }, { "block": 249971, "account": 1249285, "ophash": "73D003000510130001000000BC2B8C94116FA3D4AEA1A3FD07D7AD7E852105ED", "n_operation": 1 }, { "block": 135148, "account": 332823, "ophash": "EC0F0200171405004100000020E5181760F1EA17C40FFC87BFBE771DC73F535A", "n_operation": 65 }, { "block": 286604, "account": 1432490, "ophash": "8C5F0400AADB15000100000029A02686AF586494A5D94672F9BE33399325FAF8", "n_operation": 1 }, { "block": 223749, "account": 1118200, "ophash": "056A0300F80F110001000000A179E27A0C8E20C23A98F3161168DF5AB5093222", "n_operation": 1 }, { "block": 190587, "account": 67050, "ophash": "7BE80200EA0501003B00000072A592725EBABD3DF1C34F83A93E263DC2B2F57D", "n_operation": 59 }, { "block": 136240, "account": 285630, "ophash": "30140200BE5B04000582210004F6DAFF9A8169D265230E131A76191E80F97178", "n_operation": 2195973 }, { "block": 170572, "account": 126586, "ophash": "4C9A02007AEE0100080000004E696F9BEAD389A53CFD9618EA3A23DE248553AB", "n_operation": 8 }, { "block": 240961, "account": 86646, "ophash": "41AD030076520100AA160000A88638293F8AE0F002EAF2CDA08B5DA2F0812331", "n_operation": 5802 }, { "block": 71084, "account": 168570, "ophash": "AC1501007A92020036000000E9B0EEC83BD7AB2A0537641E8DD6536083E08763", "n_operation": 54 }, { "block": 75834, "account": 285630, "ophash": "3A280100BE5B04005095040043E72AA8A9888CFF24BB5548ABF3664A3AC229AC", "n_operation": 300368 }, { "block": 212751, "account": 671468, "ophash": "0F3F0300EC3E0A0003000000493B24447042445667F08C2E65223F27AEF432CE", "n_operation": 3 }, { "block": 223241, "account": 86646, "ophash": "0968030076520100181600009D095280783348C2E51DDF30125A41C4D13FAC96", "n_operation": 5656 }, { "block": 146993, "account": 734450, "ophash": "313E0200F2340B0001000000420CEBF53F3BE40D776F3630D69E7D8A09B670C5", "n_operation": 1 }, { "block": 206527, "account": 571630, "ophash": "BF260300EEB8080005270000FFBFCB14892664E65187388A3FAF401676443453", "n_operation": 9989 }, { "block": 256960, "account": 1284260, "ophash": "C0EB0300A4981300010000007B68B39C8F505AA77296391FE4D0A09AC1BD3C1B", "n_operation": 1 }, { "block": 172905, "account": 259630, "ophash": "69A302002EF6030006000000A6CC8907BFC8DDF522B00A6881A6DD77FACB2C63", "n_operation": 6 }, { "block": 208131, "account": 1040110, "ophash": "032D0300EEDE0F0001000000A429B621D58A954CD8749DAA7E3FF32BB602289F", "n_operation": 1 }, { "block": 297225, "account": 1485620, "ophash": "0989040034AB160001000000EC725C0E4E7DE38DA0D48DDA317CF9613AC4EE47", "n_operation": 1 }, { "block": 206382, "account": 1031395, "ophash": "2E260300E3BC0F00010000006004A45FF1B9EBA46B28A26B51408B656BC8F7C6", "n_operation": 1 }, { "block": 179511, "account": 896871, "ophash": "37BD020067AF0D0001000000E130CF96B86E2E8DA390AD2147C975EF358CDA5E", "n_operation": 1 }, { "block": 105150, "account": 285630, "ophash": "BE9A0100BE5B0400DDDE0D0002CE92289F7D5D2F7012B1FBD26C29780EF16678", "n_operation": 909021 }, { "block": 285276, "account": 591320, "ophash": "5C5A0400D80509000F00000001DA436139651B8EF7A7EC0283D98D26BB26428A", "n_operation": 15 }, { "block": 139750, "account": 110027, "ophash": "E6210200CBAD01001F000000E34135897332911006913803D9B0D2F54DBB032A", "n_operation": 31 }, { "block": 120092, "account": 285630, "ophash": "1CD50100BE5B040042EE16001449BAC5979828CA234090988D0EBDA140C4A65C", "n_operation": 1502786 }, { "block": 157226, "account": 300124, "ophash": "2A6602005C94040086230000454FED24EDF145A233792AD27726DE57C373363F", "n_operation": 9094 }, { "block": 113250, "account": 285630, "ophash": "62BA0100BE5B0400873D120063BC943C5E932A53DD854D04CAB7F02EB3A2E542", "n_operation": 1195399 }, { "block": 163189, "account": 86646, "ophash": "757D020076520100820B000086F6059A89632BD66BBA71E1AFA8D90F4A0B2FE1", "n_operation": 2946 }, { "block": 102875, "account": 285630, "ophash": "DB910100BE5B04004ADB0C006AF55F9DAD8FC743DCE9E76A5BDC72DD84E5B91B", "n_operation": 842570 }, { "block": 271877, "account": 1358755, "ophash": "05260400A3BB1400010000006F7D36CC17E65CB9D55AA4FA251A304F1203812F", "n_operation": 1 }, { "block": 195483, "account": 976895, "ophash": "9BFB0200FFE70E00010000008EAD0F9D8BD2F70EE6E0713C5FF1DCDEB9AF70F0", "n_operation": 1 }, { "block": 74112, "account": 168571, "ophash": "802101007B920200690100001AD05DF2FBD70CD8EA2F2F1BDF6922EACC4E1908", "n_operation": 361 }, { "block": 273976, "account": 285630, "ophash": "382E0400BE5B0400CECD4D0074D40389572CE47DB2ABFFE9041101F2BDD0434B", "n_operation": 5098958 }, { "block": 176699, "account": 77, "ophash": "3BB202004D000000430200002042C157001FC1C7AE50573FDD25E7D06932C2FA", "n_operation": 579 }, { "block": 291028, "account": 1454620, "ophash": "D47004001C321600010000001D83CE912708A89082606F2E6761F1B83AB03676", "n_operation": 1 }, { "block": 139428, "account": 273569, "ophash": "A4200200A12C04001000000047BD0B865BEDE097AB81E218BBEDA36FB1306D8B", "n_operation": 16 }, { "block": 92276, "account": 285630, "ophash": "74680100BE5B0400304F0900C3BDA561C79E09168ABB226684EFF1699BDE3259", "n_operation": 610096 }, { "block": 65866, "account": 290760, "ophash": "4A010100C86F04004659000073CEF7C4F96CB6482A5525B2F6291EF8432CF64A", "n_operation": 22854 }, { "block": 270008, "account": 1349500, "ophash": "B81E04007C9714000100000069628DFE07124310ADB6779B5917870CB2466977", "n_operation": 1 }, { "block": 91346, "account": 456060, "ophash": "D26401007CF50600010000005675992D7EC60AB2F8E00A722D2639F76386477E", "n_operation": 1 }, { "block": 111388, "account": 192168, "ophash": "1CB30100A8EE02000300000094EFB3755715554605A8D8B24C350F32976EB7C8", "n_operation": 3 }, { "block": 185446, "account": 926720, "ophash": "66D4020000240E0001000000BC8FFD0EE461466381395DD4C1B00101F4398698", "n_operation": 1 }, { "block": 144019, "account": 285630, "ophash": "93320200BE5B0400860926001CD15BD616B6E7141E45107E0A5A2137705DC0AF", "n_operation": 2492806 }, { "block": 167179, "account": 472841, "ophash": "0B8D0200093707000A000000B3CDB2787D3098CA8A5A05AA7091E3CD43498006", "n_operation": 10 }, { "block": 198194, "account": 990445, "ophash": "32060300ED1C0F00010000006F556D9CD02BF0E2C715CA3102490FDAADE31A26", "n_operation": 1 }, { "block": 116676, "account": 571630, "ophash": "C4C70100EEB8080047000000C937111C99286440592E17D366EA7B076192079A", "n_operation": 71 }, { "block": 145282, "account": 725855, "ophash": "823702005F130B0001000000C63046CDDF7BB3B1669B18F2B97C358259167309", "n_operation": 1 }, { "block": 68302, "account": 340965, "ophash": "CE0A0100E5330500010000009EBE7CB5075B0578BBC0AE08A3816DF19FDA1F38", "n_operation": 1 }, { "block": 292762, "account": 1463260, "ophash": "9A770400DC531600010000009573DE0C18B02945D7A3055E365A18AA42655412", "n_operation": 1 }, { "block": 77600, "account": 285630, "ophash": "202F0100BE5B0400C7240500F77A89B9E5A089B8393E59D5182A568F9F77E0FE", "n_operation": 337095 }, { "block": 118042, "account": 86646, "ophash": "1ACD010076520100CE05000077EDEBB2089E78EED08FB599C9E79AD512D2A083", "n_operation": 1486 }, { "block": 298966, "account": 1494315, "ophash": "D68F04002BCD16000100000030442ECB809F1377F49E77192D0E66BA358F1FC7", "n_operation": 1 }, { "block": 58067, "account": 289805, "ophash": "D3E200000D6C04000100000095019C32A5EC2006735384B6C35A6A6CD9E8B7E0", "n_operation": 1 }, { "block": 120005, "account": 285630, "ophash": "C5D40100BE5B0400B6DC1600E051D6404FB61DE779C740D5FFF48DFCBE492B1C", "n_operation": 1498294 }, { "block": 115238, "account": 94501, "ophash": "26C201002571010005000000B738645D33DAD8FF81AA285679AEBD930EB20E5E", "n_operation": 5 }, { "block": 91946, "account": 76300, "ophash": "2A6701000C2A01000200000091B55A0BAC27F596FD1EF90EDE41A64D5D5C963F", "n_operation": 2 }, { "block": 180741, "account": 903180, "ophash": "05C202000CC80D0002000000B3AA3FE8477862DFD68C5B2434D0E47458F329E8", "n_operation": 2 }, { "block": 66419, "account": 331575, "ophash": "73030100370F050001000000CF9EAE73030817DAAD531D4BA6B87E6D230A731E", "n_operation": 1 }, { "block": 169444, "account": 207854, "ophash": "E4950200EE2B030007000000E78B2B7E4BCD3BA996B20A3570DB1579C7278ADD", "n_operation": 7 }, { "block": 283576, "account": 1417325, "ophash": "B85304006DA0150001000000F60BED19AE89964F3A38D2DF45695792368659EE", "n_operation": 1 }, { "block": 222016, "account": 86646, "ophash": "40630300765201000D1600007CAC29D3190053347BA08B124E7C30746B89D0D0", "n_operation": 5645 }, { "block": 91763, "account": 458240, "ophash": "7366010000FE06000100000028B96EAE3DE72BBED27B086FFF6A88A9E280290C", "n_operation": 1 }, { "block": 286503, "account": 819643, "ophash": "275F0400BB810C0002000000833DF0B7DCAAF5030BAA8A4C0C4537F49B02BE72", "n_operation": 2 }, { "block": 259299, "account": 1295955, "ophash": "E3F4030053C61300010000000AE0A7DF1DA7E810896469718A79BED6BD59ED70", "n_operation": 1 }, { "block": 261956, "account": 285630, "ophash": "44FF0300BE5B040021464C0056786C064E5383A85E9420DF2B2D1357B3C13252", "n_operation": 4998689 }, { "block": 152105, "account": 55095, "ophash": "2952020037D7000005000000887FCD010B3D37C4719A067D6F8BA688293A315E", "n_operation": 5 }, { "block": 76945, "account": 285630, "ophash": "912C0100BE5B040097F30400AF689F4C5591C3439B2E84EDDEA834CBF231A074", "n_operation": 324503 }, { "block": 143841, "account": 285630, "ophash": "E1310200BE5B0400A5F8250089E6D365A09A0769003FDDC9F9EAD9C6ED4AB27D", "n_operation": 2488485 }, { "block": 82416, "account": 411530, "ophash": "F04101008A47060001000000E6A873B3756D2ED10A5F7BB1350031A7AB4DC79B", "n_operation": 1 }, { "block": 189835, "account": 273710, "ophash": "8BE502002E2D0400250000009AFAFF3FED70727C022DB3C711308A6533A49117", "n_operation": 37 }, { "block": 300414, "account": 285630, "ophash": "7E950400BE5B0400676A4F00EBCE0F72FC983BF288D50D463D8E229583B410FB", "n_operation": 5204583 }, { "block": 85122, "account": 425050, "ophash": "824C01005A7C0600010000002C5166BD73151CCEBAB832715261EC8583FCBC8A", "n_operation": 1 }, { "block": 126650, "account": 285630, "ophash": "BAEE0100BE5B0400576F1B003F07B68E90D66DDA1D8C53DE1E1DF50D3C8D232C", "n_operation": 1797975 }, { "block": 117044, "account": 584700, "ophash": "34C90100FCEB080001000000DDC1909ECE9FBBA6ECD590A375B42A31F30AFEE1", "n_operation": 1 }, { "block": 237186, "account": 1185375, "ophash": "829E03005F16120001000000AC576564B2E704833BE0AC1DC64D1D17B0928F09", "n_operation": 1 }, { "block": 63660, "account": 285630, "ophash": "ACF80000BE5B04006224010015B1A21D0B260B3454B775FBF55DB109C0BEAD85", "n_operation": 74850 }, { "block": 108643, "account": 285630, "ophash": "63A80100BE5B0400CEAE0F009C4B9D7F452096D95F221F07C7FD9BE3A0BAB586", "n_operation": 1027790 }, { "block": 236689, "account": 300124, "ophash": "919C03005C940400883100008C4F8AB91710E50E1A798C6D68F3E26D542FBAB0", "n_operation": 12680 }, { "block": 299499, "account": 122202, "ophash": "EB9104005ADD0100070000009D08E63FB13698F54EAE3A0A56FE31C25FBE4C0B", "n_operation": 7 }, { "block": 218185, "account": 1090395, "ophash": "495403005BA31000010000009D7EA3A6742ED11EA662C3FEBB1437899B21FDBC", "n_operation": 1 }, { "block": 75206, "account": 290760, "ophash": "C6250100C86F04007E860000BF643C17B6A3CAA028F281D50721F4B9D6B54700", "n_operation": 34430 }, { "block": 217951, "account": 54358, "ophash": "5F53030056D400000D000000AEB66B7BDEB05199D769AB37293AFE24225CC11B", "n_operation": 13 }, { "block": 103462, "account": 285630, "ophash": "26940100BE5B04006A140D00FB862B96154EE4C2A7E65DD1DB8201BA16F06410", "n_operation": 857194 }, { "block": 203048, "account": 285630, "ophash": "28190300BE5B040041124400C50190D5D5BD25918CA8C0E04E2D4F325BFF21E1", "n_operation": 4461121 }, { "block": 103757, "account": 285630, "ophash": "4D950100BE5B040020330D00E767C90D8499073DF8982A30A6E6F38978D2E9D0", "n_operation": 865056 } ]; } } ================================================ FILE: test/common/fixtures/privatekey.dart ================================================ class PrivateKeyFixtures { List> curve714; List> curve715; List> curve716; List> curve729; PrivateKeyFixtures() { curve714 = [ { "encrypted": "53616C7465645F5FED4A37ECAD2BF13FF24A66DDA299A57632520447B28B9E642C4B2A301CACC217FBD7713F6282C20CCCFDC5FFD2AB93A8E48D8C2C81704D36", "password": "test1234", "b58_pubkey": "3GhhbopDPbi883HVV6Hxun6q6AN43CB1yUD9km64cDoZMhgM1KkLy3N41vT1H1zqw4kHdqM64NHMSpSNviVkUP7fCrisZwYzb89dDs", "enc_privkey": "CA02200046B7A086680D208272F6982F574FE226042F30D049F9A226283FC3346506411D" }, { "encrypted": "53616C7465645F5FB3431FD46EB80DE81273E0181F170552DE01506A9C91D47269312A81841C82B7F55014CEA471A7D8D3187DA2D10B4F9A8481DA7911CCC1A2", "password": "test333", "b58_pubkey": "3GhhbouATXfrARGuHA8ymY2DoPYjKY9i7xBvdpyHjNWtWwdphWwkSaKaKx3tRNV47yGFhnkLZbJkL8crjE129FHUvrFod6dZQt3H8C", "enc_privkey": "CA02200066C8426FA1DC0980DBDEE7F8656AE6FCA9EF746975E3FFB9EB425B53638C0175" }, { "encrypted": "53616C7465645F5FFA7AEBA0EB6776EA2D3BCDADBD5908F1B032B78DA75193A9D8040B94E49CA95920E130BD0CB7603DB72D2639AEA95E8D75CB31AEBBD6EA19", "password": "techworker", "b58_pubkey": "3GhhborMr2ApiQC19ah4EMkFBj46WymwABvqJ8hMwVkVA5TB58Dver6cfosn8zVKpNnJZCsn8HTdnSobopiMgVe4R8S5ap49NvNsEP", "enc_privkey": "CA02200046D101363B3330D65373A70F6E47BB7745FC8EE1F9B3F71992D6B82648158D73" } ]; curve715 = [ { "encrypted": "53616C7465645F5FE1C85F75A6E3D75EF916DD9F60E14660F9C63A8AE407060EAA6F7CF373CB830B6E50D27EC46B013602A75305BD3D6433669607846D7335C9A1849664E8A35DD8E5FDECF40CAE5325", "password": "test1234", "b58_pubkey": "gD8AW4fgASg1cneBp3csnKeF2x3F2q8LpT4VFCyjEBPG8FraaoRHRJfFtBaGSQH3Y2tqqjJbjAAFWvPgVpL5RvA1xokiN9oVeARsi5jPYdmx7FxrZ1XrtyrDPQPArCJyHsq11wqJVhMgfiAjJ", "enc_privkey": "CB02300015B2267604593566FEB5292B32F60C3399540C7EB83F3AABB7255BC73B3809E2B742BB971B6274DF452862D6D902466B" }, { "encrypted": "53616C7465645F5F28F251B908FECEA1DFA33CB727F44E43E95D67DEE66536DFB08D20DF62B69474FF8C36167B8BBB9C5B00D042EC338FFE5A701A8A36F7087FB49007110207B1F880B876467D17F195", "password": "blabla", "b58_pubkey": "gD8AW39i8QV2g57X1F19CGHPF8b6oQcDqw9Q5LAq4X6JofJsg1hJhN1i7yhhkeGZQW86bWKeWSssTZVUvmxU4yFu5ivPpU5qMQk6oHofMKKwmoY4uLjzGWnmCsmNdbtK7k6MnhHkeDJqZiRwt", "enc_privkey": "CB023000FCABEC25AAA049A524CEA1D69360711C1EB4E83D86D2CB45B0EFA1F69B218CCABE3CF0A16AE8ED170919B7B39D1C390E" }, { "encrypted": "53616C7465645F5F7C4731B8A43D75E23C7F77FB51C44DCCA59394ED2B06F1291849D248AB096A32C93A3E76F32395158E9624AFF2B6AB543CD5C0DCC3735E4879A1E7063F74C11251618A192082F9F4", "password": "zulu55", "b58_pubkey": "gD8AW3uCN1tF8FRNNvnGMcSdgNS7uzQErW4mHXpBgG4RCcLhZ2VcGAUuqiW6MrYV4Fsxfbo5n3ToRmn7WaMGCzMFcJX5SCrTY4fvEGrE2q5XTw2gasapYvRYe1c8UKmQoxE6VmKgvDkaiVC51", "enc_privkey": "CB023000225EDA615DBF3F296B295E76454444024E5F1CF17D5275135C00CDCADF966889890F5618874D95A9EEFF22B6DDCF9C4A" } ]; curve716 = [ { "encrypted": "53616C7465645F5FD5B6C81AE89043F4ECE78CD21F68BFD0FBCC3C6E5BF4BD10B199D53F1DE8C8DFC6FF8CCE3AE309734C268337B1E80A5C70256480482EC8D99D52A8B448BD016C72D3FA482DE6C2F4CBBF604B2D3A7CBB78745AA9057486A0", "password": "test1234", "b58_pubkey": "JJj2GZDjhAHPKfyJ4GyEMhFNfUZPAUFe7oqfChPY5HinqNY5NPsCydcY2XEw8iwqhxSuaoAg6hf7qFagZJQ6HTYCGdTdgBxNangcV4ZcbcKwesZ5QmX6sVvLDPvGosfjk8LnrVgVhwJnv26dzKbPceNUzPHR8Pm2zwx6kjESRa3bKajupKngZGYyGmVJNuuyJ", "enc_privkey": "CC02420001F4C77C1DC868F62A96DD74343B9C03C594F6E42DA3A0139D6988D45D75FBC167E6D98D1F101D4FE8F8D58514F7571399E4548A017645609F5F575176849A38335B" }, { "encrypted": "53616C7465645F5FA78798633EC647B4BCCC1240730C24E5852B649C3930EF7F9703018D4ED62000B254A875A021B32CFB7B8E87C2C19099CF6E7F1F9E76556F4391A00B118EB6A8EECCFB4CA19C4333B2A7393EF1A88D200D257E3A32DC4C0B", "password": "techworker", "b58_pubkey": "JJj2A5Fyw46yWMLegHqvHpUgeBgx957FK2kCRy4nA2qYGbmwMP9tNHnaVJFX6Yha8bQPbNyjLtdrjsqgTFRbFKAWhzx6s9hCjCmVwUsw1ZrE8MB1MkrqgobAr4xxZNmw4i3osKvMwE9LWafSQC5DWCRsx636u6h1Vdpt1CeGthDZfRU35DcKusZ7ZD2W2Pfns", "enc_privkey": "CC024100E9551B5AD9D9084DD878E224BE718E2929892FC03A5420DCC9A8B9AB5260B47A039D3838DB32D003141AEB1C2F53A285A78470D65AAD2222B2A4C9B279AF76CBF1" }, { "encrypted": "53616C7465645F5FD8FEC5F2D15CF8997170913FF9F1D715481431196F35BCB60B8A127440D0B7E9D67F908FF857E4EF7B9EFA6BC5A4F8B8FB8F1AD40B9642A2E20BDC5F1B08C3D6128BE2F67CCBAA2CD2C91E287CB4FA298317EBCC872919EF", "password": "zulu123", "b58_pubkey": "JJj2GZDo19rm39iih31cyJns2PBUqkTqFvpLZ6tucYNrjf8e9yWr4hckXP9C85x4zBRNJk1YqA6HSkX9AaTUearmYs9qFVzRzwX7X2gEt1gFhHq1dJaeLmDyRXVbd7cmiuxD6tjjXS7V4Jt3A5esgHTpZRQ6PXReDkWmx4fmcURcHFiWwtwWdQrz5ZTEQrQ3r", "enc_privkey": "CC024100231B27482A4ED27F9CECBFA1D6E610146852D720A5F7623E24D348499B46F31E02B46572D55A7C97CD303D4361242B11A5789C2FEB9A793B0C285B5DF4EBC08984" } ]; curve729 = [ { "encrypted": "53616C7465645F5F096AF812ED1B1275E123BAF0F351A50554CC62C50C6181AD6313B5E6A319F6ABAF9A1CC29FFA47162355E5A731F379420B7FB6ACC92F9F82", "password": "test1234", "b58_pubkey": "2jR5AN61cbT1MmPmhdBPCcYCUidSf4hSzKHycfw6Kk8DFqcxD4MS1y7YezmsoC19S4QJmQvwnDnVDjnHgRaeX8PyE1pWvFzBWVnDBNuawD1wQ9iaa", "enc_privkey": "D9022300252A9478A7C732CE80E1BE5E2F529050F8521D8F7FEC3C0C8EA2936307244AF9F38E55" }, { "encrypted": "53616C7465645F5FE327D1F4C88764F6D6F5CD48BC563D5E53D31E641AF18BAE52DDCE43AE7252D2BA294BB1D1F7D6C66195D2021C77B72E95C36D659ABC0BBF", "password": "techworker", "b58_pubkey": "2jR5AN5umYFQ2Xgh2p4Scva1NTbipBXtDe4NgTZPd7SSoQkRkPBp6b7AizVZ5nXLboHH6h4fDhAxmUCVkfZ6NGv7GNuoRnqQ1WcwnEEEf2NyiBmVc", "enc_privkey": "D9022300BD97C143CB26947DE0DF445972F929C1C76DA86B2B33C5B486C8AE926872D77DB4042D" }, { "encrypted": "53616C7465645F5F36404950AA92E7632F88BE903B9DF3DBFA5CDDE67E776655C95773F80ED982DB55917A685B50F514F813D091C6768280E98C66F996CB7127", "password": "zandura", "b58_pubkey": "PkWwgfjhzQWM3sBjXFWRdXAq6ge2Wrr4vXGMp3vMTzj3yVbdqhWxfTwo6ySRAiVP39LZRoH6T9sZq7F5bCHMTD7b92fEynFwSV6xGqTDikNSnoc", "enc_privkey": "D9022400011FC78E156FFB72C1C830740D34BD711DBFF03AD0F2A272D160D41A28FCB0C1000CBFED" } ]; } } ================================================ FILE: test/common/fixtures/publickey.dart ================================================ class PublicKeyFixtures { List> curve714; List> curve715; List> curve716; List> curve729; PublicKeyFixtures() { curve714 = [ { "ec_nid": 714, "x": "6E75266E1865288874BFE1C3A9686BCF4D22CADCC250B134EA2C470A9E3F912B", "y": "F8362CB5EE97113D93880303E99B63208CEBDB62D49874D739E33DFFFD3C4162", "enc_pubkey": "CA0220006E75266E1865288874BFE1C3A9686BCF4D22CADCC250B134EA2C470A9E3F912B2000F8362CB5EE97113D93880303E99B63208CEBDB62D49874D739E33DFFFD3C4162", "b58_pubkey": "3GhhbopVb9wfo4HzecYwKYMWRvLCssTeFWjocfnWv12Yt3GtaW3seeatH9GqhVmnYrF586RKLwjFFMYn7Txq8X2D4qT7CbqrZgbdRm" }, { "ec_nid": 714, "x": "1FD6019F7FBFCD9A34491643287402FB0CCB77F2A4F99482ADC11137CDF1FBD6", "y": "46924461A9069850A64E48E8EDB9C88764D3A0DC74AF929E335719F8A65B809B", "enc_pubkey": "CA0220001FD6019F7FBFCD9A34491643287402FB0CCB77F2A4F99482ADC11137CDF1FBD6200046924461A9069850A64E48E8EDB9C88764D3A0DC74AF929E335719F8A65B809B", "b58_pubkey": "3Ghhbom3CjrhKPrcumjaucV19hDkNWnzqxvW2pgqdmSqgKQCQmdLFe3CPSGeYE99mk9gBhKfuKXhy8nTcRz93oDLz41DFSkJ1xHQPm" }, { "ec_nid": 714, "x": "209E3EF49EBED0BE1D9583589CD4B8F365966592E3644FFA243A1941480C676A", "y": "61DEA0745D3892C2A056CA4C071A8C1F38AB1CAA99EAC43636B800D71B0553C2", "enc_pubkey": "CA022000209E3EF49EBED0BE1D9583589CD4B8F365966592E3644FFA243A1941480C676A200061DEA0745D3892C2A056CA4C071A8C1F38AB1CAA99EAC43636B800D71B0553C2", "b58_pubkey": "3Ghhbom5CNJKVeJn3ujKLha4sek4QWWv5MvZNQuGgXDkMiXYxHnWVUkzvkE9hAxTL85tgighg4Zham5VvesMckfAfi6qyrquwKgATC" } ]; curve715 = [ { "ec_nid": 715, "x": "BB536B465C54D36644BBA2F42DD664EB4AF7E441D5F80892E4FB4DC289E3D8E826A736114D7BC9F5E89FB4C498611520", "y": "A062FC5AD7995A21478D57B403B11D615C028D215B7B5BB37C8207DCA3932F0D5860D7F3D885D13E714671F816F91FC7", "enc_pubkey": "CB023000BB536B465C54D36644BBA2F42DD664EB4AF7E441D5F80892E4FB4DC289E3D8E826A736114D7BC9F5E89FB4C4986115203000A062FC5AD7995A21478D57B403B11D615C028D215B7B5BB37C8207DCA3932F0D5860D7F3D885D13E714671F816F91FC7", "b58_pubkey": "gD8AW4sifppye1utSGPZn2TpTkTXd1EAxGiGTfEEZe87JXCx7oCKFw3ew6hz8E5RB66NuMjSzp59qWu4YrwSEjBh4P9RSsb2Xhc5z7rA4feG5eCAz9mQYqCTQRYsDQHwtftCxMRB625c5X7b4" }, { "ec_nid": 715, "x": "EF4EAAF2F5AEA04C8B7E846C7E97D1BF591D66C4751C0C306C9B583637C106604D5A4FF08B87AF6DD44710E7C7B4C766", "y": "FEFDBF5649030F19D4F1725B29C1B9A3C677EEFEA56CB8D084479DCC5A307A04153C6297971ED72E39FAEA31E9000E34", "enc_pubkey": "CB023000EF4EAAF2F5AEA04C8B7E846C7E97D1BF591D66C4751C0C306C9B583637C106604D5A4FF08B87AF6DD44710E7C7B4C7663000FEFDBF5649030F19D4F1725B29C1B9A3C677EEFEA56CB8D084479DCC5A307A04153C6297971ED72E39FAEA31E9000E34", "b58_pubkey": "gD8AW5Z5NgdhARmyGeBcEA8NcECXNQCUgqCtZh7MDoxr9vDkyTjBYQVjybeH5uULg5oC2FwBqpzdGvC9o543uzSrC5DpTdujQGbt9tsCG1ZJ9wun7LgNXqNgkC3gNdp1MHwGRZXbCfXY2BRc2" }, { "ec_nid": 715, "x": "FEAB4CE80AA556DF0A487B67F5EE043D5AA977B1DF9A9B5379ADB4D8E981FDFE89FFBDA6D07380FAE7F811A51B5B0640", "y": "DB340DDDA596C9A0563C339B17B40F431F3AED1F5A9D79B9F3A72E0C08C74987813A5D929161F35E1E1D9B9DDE946C38", "enc_pubkey": "CB023000FEAB4CE80AA556DF0A487B67F5EE043D5AA977B1DF9A9B5379ADB4D8E981FDFE89FFBDA6D07380FAE7F811A51B5B06403000DB340DDDA596C9A0563C339B17B40F431F3AED1F5A9D79B9F3A72E0C08C74987813A5D929161F35E1E1D9B9DDE946C38", "b58_pubkey": "gD8AW5khyLy3UjwNcqfLFQGowZTtGPGH412aNwDYEHNZdBUbbFCwcYAzjE2bVucStxzBMW1r8JHDXYX5AsjrHs1PCKLa9G7iuv28oBzSqsN7uzLLsQ5FQvhpm2HgbT2TdAbjLhwyPAD2kkpg1" } ]; curve716 = [ { "ec_nid": 716, "x": "0182EF9FEFDC476E4CDD19874FB40B42CCD192BA507C964861E393B9AD7FD68F2516141AD3A9264D8C8D4C695086F45EAF6E878FF5B9FC11B317E5871A4F71CBB81D", "y": "450B01A829CD036553859C48DDE2E7DD832FB4021B5CD07FEE3C46F5B238ECA935C046854C6DA89DB3BF9212F2FAB53A93B20C09BE54CEAA176AC493982328FBA4", "enc_pubkey": "CC0242000182EF9FEFDC476E4CDD19874FB40B42CCD192BA507C964861E393B9AD7FD68F2516141AD3A9264D8C8D4C695086F45EAF6E878FF5B9FC11B317E5871A4F71CBB81D4100450B01A829CD036553859C48DDE2E7DD832FB4021B5CD07FEE3C46F5B238ECA935C046854C6DA89DB3BF9212F2FAB53A93B20C09BE54CEAA176AC493982328FBA4", "b58_pubkey": "JJj2GZDmBCwbQuFLumucGMqWALmqpNxuYDEpPYi6aq4y4gvdFv1JLJYGgQyt5oUa1sgyUoAK8m7adARBa9JfGuKRtJQsiegEWQ41WcsyQAZ9iUcnFRVgKe89mqmbuU8hPF7ewisP49HdJCpfHxiwiz95JGWoBP3XdSf4TvBQShjFeVuMEB6pXCMQCfcnG1uBi" }, { "ec_nid": 716, "x": "2A33CFF7E6F75FA4F0EA32A5DED773ADA3C18D67A334DD89820B658BD5E38EE101094B00A1558136F8F2F53BF9778AE92ADA48E2F77F03A3A4F0B2A50D3A3798B5", "y": "BE20E8BCC886C01ABBA58FEBCDABE0EE17E6D0A4695A70D753EB82D6074EE8868BF0562E0A615B394FBB5537B90A1C15CE98F9D21DABC45A8F1CBBF9CD0A740D71", "enc_pubkey": "CC0241002A33CFF7E6F75FA4F0EA32A5DED773ADA3C18D67A334DD89820B658BD5E38EE101094B00A1558136F8F2F53BF9778AE92ADA48E2F77F03A3A4F0B2A50D3A3798B54100BE20E8BCC886C01ABBA58FEBCDABE0EE17E6D0A4695A70D753EB82D6074EE8868BF0562E0A615B394FBB5537B90A1C15CE98F9D21DABC45A8F1CBBF9CD0A740D71", "b58_pubkey": "4vQZtSkqEV2ukA7CCji2BZTg2DkPj3d32wwzr2nWmSNrmeKxTgK9mqLzonGwKxDfK2uAmrkjosp1C6KnhLBT8GWpUyx1G5cixM1UL5BnYckNDdut4bVNabDvkEMaAt4nZwLLDWNnWwTAD3BPoBnzoiGiBa4h4RmhDnB1Eh8ZacP6ZCiHNkTEroZwdxzjo1b9" }, { "ec_nid": 716, "x": "010992690C56A6AFFDF7A4D3757B4F08667085DFC4805C06BF3104EE285696A746469CFC3178DFC9C16023036E40364DD5DA02FFD3F26694F34F8820D82EF9541833", "y": "010A75148A885BDE17D930057A9F868FB92A7A3B8643FDF971830649324D4B59606D0856A7A917B4B06B200C426B5A27F3A96E7DEE0227D3216FEDF19163A30AC4F5", "enc_pubkey": "CC024200010992690C56A6AFFDF7A4D3757B4F08667085DFC4805C06BF3104EE285696A746469CFC3178DFC9C16023036E40364DD5DA02FFD3F26694F34F8820D82EF95418334200010A75148A885BDE17D930057A9F868FB92A7A3B8643FDF971830649324D4B59606D0856A7A917B4B06B200C426B5A27F3A96E7DEE0227D3216FEDF19163A30AC4F5", "b58_pubkey": "2KPEUbfCdWs9TcCCzUwzDSMeaBQKxcb1yfGJ2egofZJTqoZf1knfmKWumhMvQ944goU3jFosEq2odUvNqJGzizL9ySLJ9oQVopGYuXHtFNZu3kZz44bErDAhMBr92VwKPB7rqPTiio6KbwWA9sLjscKxmZ8DGQ7mTdLoh1sVRFMdk4N6U1HeQBFjHkZqApQeNUi" } ]; curve729 = [ { "ec_nid": 729, "x": "0101F596D1D86DED3882C21A6E9912BAC0ABDA8C717D8729F11923DABAD313950C9D6AFF", "y": "0494BF2B4C630036477F4904BDD7DD2D293F590C572F59B523215FB03915B49A62F3A94F", "enc_pubkey": "D90224000101F596D1D86DED3882C21A6E9912BAC0ABDA8C717D8729F11923DABAD313950C9D6AFF24000494BF2B4C630036477F4904BDD7DD2D293F590C572F59B523215FB03915B49A62F3A94F", "b58_pubkey": "2jR5AN5ogKAq5jid8fqQJCUNU27irJs5ayrXDZ2ZiC4ywhuWPyUf7z2LPSVLuWdt7CvHsZPGoQFA8eZXyqoY4dvtQnuG8AsKHK4E4veB1PVc2tptw" }, { "ec_nid": 729, "x": "07C6FD9F07F4F13B5FFBEC8D85D1F20D87C001235FEDC4730AC019DC687669A04D1108A5", "y": "03D1746173F9F27AA751A583FD588861C7544C1E870EB4BFF2707E0262F90F6B7D0324B5", "enc_pubkey": "D902240007C6FD9F07F4F13B5FFBEC8D85D1F20D87C001235FEDC4730AC019DC687669A04D1108A5240003D1746173F9F27AA751A583FD588861C7544C1E870EB4BFF2707E0262F90F6B7D0324B5", "b58_pubkey": "2jR5AN62R87v3Z6AANSMZfx7Kmp41cVaB4XaupbtX6unJmTSLLxHESjamiPuS7gEuekDFEBkeSiqxKcDWzQGoHxTp6tthbM8oJTw47qk6bay6eHEj" }, { "ec_nid": 729, "x": "06D5F0F67401DCB8B923B6202D4D8FB0F0F6F76CAE235DAEC163ACCDEF7F4DF57731F545", "y": "035C917A8D63AE33D1FFF193BE5555DD7C52E5D36338A31B77B9009D9669668625B0AD3C", "enc_pubkey": "D902240006D5F0F67401DCB8B923B6202D4D8FB0F0F6F76CAE235DAEC163ACCDEF7F4DF57731F5452400035C917A8D63AE33D1FFF193BE5555DD7C52E5D36338A31B77B9009D9669668625B0AD3C", "b58_pubkey": "2jR5AN5zeMvr1ZFLcBHXnC8fJYLvfjPSK8YVub3Y7qBDGEGwdhKn9WxHfXGgV2MG6UafxJBcjYNHeBVjkGga58Fu7cN2KVhZnACHeSFeY2ywEwSXH" } ]; } } ================================================ FILE: test/common/model/AccountName_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('common.model.AccountName', () { test('can be initialized with a valid start pascal64 string', () { String startStr = r'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-+{}[]_:"|<>,.?/~'; AccountName name = AccountName(startStr); expect(name.accountName, startStr); }); test('cannot be initialized with an invalid char', () { for (int i = 0; i < 32; i++) { expect(() => AccountName(String.fromCharCode(i)), throwsException); } }); test('will skip empty validation', () { AccountName a1 = AccountName(''); expect(a1.toString(), ''); }); test('will throw an error if the encoding starts with a number', () { for (int i = 0; i < 10; i++) { expect(() => AccountName(i.toString() + 'techworker'), throwsException); } }); test('will throw an error if the name is not at least 3 characters', () { expect(() => AccountName('t'), throwsException); expect(() => AccountName('te'), throwsException); }); test('will throw an error if a wrong char is used beyond 3 chars', () { expect(() => AccountName('techä'), throwsException); }); test('will escape certain characters', () { expect(AccountName('(abc').toStringEscaped(), '\\(abc'); expect(AccountName(')abc').toStringEscaped(), '\\)abc'); expect(AccountName('{abc').toStringEscaped(), '\\{abc'); expect(AccountName('}abc').toStringEscaped(), '\\}abc'); expect(AccountName('[abc').toStringEscaped(), '\\[abc'); expect(AccountName(']abc').toStringEscaped(), '\\]abc'); expect(AccountName(':abc').toStringEscaped(), '\\:abc'); expect(AccountName('"abc').toStringEscaped(), '\\"abc'); expect(AccountName('abc').toStringEscaped(), '\\>abc'); }); test('will get a value identifying an escape sequence', () { expect(AccountName.isEscape('\\', '('), true); expect(AccountName.isEscape('\\', ')'), true); expect(AccountName.isEscape('\\', '{'), true); expect(AccountName.isEscape('\\', '}'), true); expect(AccountName.isEscape('\\', '['), true); expect(AccountName.isEscape('\\', ']'), true); expect(AccountName.isEscape('\\', ':'), true); expect(AccountName.isEscape('\\', '"'), true); expect(AccountName.isEscape('\\', '<'), true); expect(AccountName.isEscape('\\', '>'), true); expect(AccountName.isEscape('\\', 'A'), false); expect(AccountName.isEscape('\\', 'B'), false); expect(AccountName.isEscape('\\', 'C'), false); expect(AccountName.isEscape('\\', '1'), false); expect(AccountName.isEscape('\\', '2'), false); expect(AccountName.isEscape('\\', '3'), false); }); }); } ================================================ FILE: test/common/model/AccountNumber_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('common.model.AccountNumber', () { test('can be created from a non-checksum pasa number', () { AccountNumber an = AccountNumber.fromInt(77); expect(an.toString(), '77-44'); }); test('can be created from a non-checksum pasa string', () { AccountNumber an = AccountNumber('77'); expect(an.toString(), '77-44'); }); test('can be created from a checksummed pasa', () { AccountNumber an = AccountNumber('77-44'); expect(an.toString(), '77-44'); }); test('will fail with an invalid account number', () { expect(() => AccountNumber('-77'), throwsException); }); test('will fail with an invalid account checksum', () { expect(() => AccountNumber('77-33'), throwsException); }); test('will deliver the correct block info', () { AccountNumber an = AccountNumber.fromInt(77); expect(an.createdInBlock, 15); an = AccountNumber.fromInt(75); expect(an.createdInBlock, 15); an = AccountNumber.fromInt(80); expect(an.createdInBlock, 16); an = AccountNumber.fromInt(0); expect(an.createdInBlock, 0); }); test('will return the correct account number', () { AccountNumber an = AccountNumber.fromInt(77); expect(an.account, 77); }); test('will return the correct checksum', () { AccountNumber an = AccountNumber.fromInt(77); expect(an.checksum, 44); }); test('will detect if it is a developer reward account', () { AccountNumber an = AccountNumber.fromInt(77); expect(an.isFoundationReward, false); an = AccountNumber.fromInt(1050000); expect(an.isFoundationReward, false); // first dev reward account an = AccountNumber.fromInt(1050004); expect(an.isFoundationReward, true); }); test('can compare account number objects', () { AccountNumber an = AccountNumber.fromInt(77); expect(an == AccountNumber.fromInt(77), true); an = AccountNumber.fromInt(77); expect(an == AccountNumber.fromInt(99), false); }); }); } ================================================ FILE: test/common/model/Currency_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('common.model.Currency', () { test('can be created from various types', () { Currency c = Currency('1'); expect(c.toStringOpt(), '1'); c = Currency('1.1'); expect(c.toStringOpt(), '1.1'); c = Currency('1.1110'); expect(c.toStringOpt(), '1.111'); }); test('can add', () { Currency c1 = Currency('1.1'); Currency c2 = Currency('1.2'); expect((c1 + c2).toStringOpt(), '2.3'); }); test('can sub', () { Currency c1 = Currency('1.2'); Currency c2 = Currency('1.1'); expect((c1 - c2).toStringOpt(), '0.1'); }); test('can output a fixed decimal', () { Currency c1 = Currency('1.2'); expect(c1.toString(), '1.2000'); }); test('can output a optimized decimal', () { Currency c1 = Currency('1.2000'); expect(c1.toStringOpt(), '1.2'); }); test('can output molina', () { Currency c1 = Currency('1.2000'); expect(c1.toMolina(), "12000"); }); test('can handle negatives', () { Currency c1 = Currency('-1.2000'); Currency c2 = Currency((-0.0001).toString()); expect(c1.toMolina(), "-12000"); expect(c1.toStringOpt(), "-1.2"); expect(c2.toStringOpt(), '-0.0001'); }); test('can make a negative value positive', () { Currency c1 = Currency('-1.2000'); expect(c1.toPositive().toMolina(), "12000"); }); test('skips when a positive value should be converted to positive', () { Currency c1 = Currency('1.2000'); expect(c1.toPositive().toMolina(), "12000"); }); test('can compare values if they are equal', () { Currency c1 = Currency('1.2000'); Currency c2 = Currency('1.2000'); Currency c3 = Currency('1.3000'); Currency c4 = Currency('-1.2000'); expect(c1 == c2, true); expect(c1 == c3, false); expect(c1 == c4, false); }); test('can compare values if they are lower', () { Currency c1 = Currency('1.2000'); Currency c2 = Currency('1.2000'); Currency c3 = Currency('1.3000'); Currency c4 = Currency('-1.2000'); expect(c1 < c2, false); expect(c1 < c3, true); expect(c1 < c4, false); }); test('can compare values if they are greater', () { Currency c1 = Currency('1.2000'); Currency c2 = Currency('1.2000'); Currency c3 = Currency('1.3000'); Currency c4 = Currency('-1.2000'); expect(c1 > c2, false); expect(c1 > c3, false); expect(c1 > c4, true); }); test('can compare values if they are greater or equal', () { Currency c1 = Currency('1.2000'); Currency c2 = Currency('1.2000'); Currency c3 = Currency('1.3000'); Currency c4 = Currency('-1.2000'); expect(c1 >= c2, true); expect(c1 >= c3, false); expect(c1 >= c4, true); }); test('can compare values if they are lower or equal', () { Currency c1 = Currency('1.2000'); Currency c2 = Currency('1.2000'); Currency c3 = Currency('1.3000'); Currency c4 = Currency('-1.2000'); expect(c1 <= c2, true); expect(c1 <= c3, true); expect(c1 <= c4, false); }); }); } ================================================ FILE: test/common/model/OperationHash_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('common.model.OperationHash', () { test('can be created manually and returns correct initialization values', () { OperationHash oph = OperationHash( 1, 2, 3, PDUtil.hexToBytes(List.filled(20, 'AA').join())); expect(oph.block, 1); expect(oph.account.account, 2); expect(oph.nOperation, 3); expect(PDUtil.byteToHex(oph.md160), List.filled(20, 'AA').join()); }); test('checks a valid md160', () { expect(() => OperationHash(1, 2, 3, PDUtil.hexToBytes('AAAAAAAAAAA')), throwsException); }); }); } ================================================ FILE: test/common/model/keys/Curves_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('common.model.keys.Curves', () { test('can be created from a number', () { Curve c = Curve(714); // secp256k1 expect(c.id, 714); expect(c.name, 'secp256k1'); c = Curve(715); // secp384r1 expect(c.id, 715); expect(c.name, 'secp384r1'); c = Curve(716); // secp521r1 expect(c.id, 716); expect(c.name, 'secp521r1'); c = Curve(729); // sect283k1 expect(c.id, 729); expect(c.name, 'sect283k1'); }); test('can be created from a name', () { Curve c = Curve.fromString('secp256k1'); expect(c.id, 714); expect(c.name, 'secp256k1'); c = Curve.fromString('secp384r1'); expect(c.id, 715); expect(c.name, 'secp384r1'); c = Curve.fromString('secp521r1'); expect(c.id, 716); expect(c.name, 'secp521r1'); c = Curve.fromString('sect283k1'); expect(c.id, 729); expect(c.name, 'sect283k1'); }); test('will return false for sect283k1 supported', () { expect(Curve(Curve.CI_SECT283K1).supported, false); expect(Curve(Curve.CI_SECP256K1).supported, true); expect(Curve(Curve.CI_SECP384R1).supported, true); expect(Curve(Curve.CI_SECP521R1).supported, true); }); test('will throw an error with unknown curve', () { expect(() => Curve.fromString('abc'), throwsException); expect(() => Curve(100101), throwsException); }); test('will return the name on toString()', () { Curve c = Curve.fromString('secp521r1'); expect(c.toString(), 'secp521r1'); }); test('will return secp256k1 as default curve()', () { Curve c = Curve.getDefaultCurve(); expect(c.id, 714); expect(c.name, 'secp256k1'); }); test('provides constants to access the name in a controlled manner', () { expect(Curve.CN_SECP256K1, 'secp256k1'); expect(Curve.CN_SECP384R1, 'secp384r1'); expect(Curve.CN_SECT283K1, 'sect283k1'); expect(Curve.CN_SECP521R1, 'secp521r1'); }); }); } ================================================ FILE: test/common/model/keys/KeyPair_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../fixtures/privatekey.dart'; void main() { PrivateKeyCoder privCoder; PublicKeyCoder pubCoder; PrivateKeyFixtures fixtures; setUp(() { privCoder = PrivateKeyCoder(); pubCoder = PublicKeyCoder(); fixtures = PrivateKeyFixtures(); }); group('common.model.keys.KeyPair', () { test('can be created from a public and private key', () { fixtures.curve714.forEach((c) { PrivateKey priv = privCoder.decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); PublicKey pub = pubCoder.decodeFromBase58(c['b58_pubkey']); KeyPair kp = KeyPair(priv, pub); expect(pubCoder.encodeToBase58(kp.publicKey), c['b58_pubkey']); expect(PDUtil.byteToHex(privCoder.encodeToBytes(kp.privateKey)), c['enc_privkey']); expect(kp.curve.id, 714); }); }); test('does not allow different curves', () { fixtures.curve714.forEach((c) { PrivateKey priv = privCoder.decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); String b58wrong = 'gD8AW4sifppye1utSGPZn2TpTkTXd1EAxGiGTfEEZe87JXCx7oCKFw3ew6hz8E5RB66NuMjSzp59qWu4YrwSEjBh4P9RSsb2Xhc5z7rA4feG5eCAz9mQYqCTQRYsDQHwtftCxMRB625c5X7b4'; PublicKey pub = pubCoder.decodeFromBase58(b58wrong); expect(() => KeyPair(priv, pub), throwsException); }); fixtures.curve715.forEach((c) { PrivateKey priv = privCoder.decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); String b58wrong = '3GhhbopVb9wfo4HzecYwKYMWRvLCssTeFWjocfnWv12Yt3GtaW3seeatH9GqhVmnYrF586RKLwjFFMYn7Txq8X2D4qT7CbqrZgbdRm'; PublicKey pub = pubCoder.decodeFromBase58(b58wrong); expect(() => KeyPair(priv, pub), throwsException); }); fixtures.curve716.forEach((c) { PrivateKey priv = privCoder.decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); String b58wrong = '3GhhbopVb9wfo4HzecYwKYMWRvLCssTeFWjocfnWv12Yt3GtaW3seeatH9GqhVmnYrF586RKLwjFFMYn7Txq8X2D4qT7CbqrZgbdRm'; PublicKey pub = pubCoder.decodeFromBase58(b58wrong); expect(() => KeyPair(priv, pub), throwsException); }); fixtures.curve729.forEach((c) { PrivateKey priv = privCoder.decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); String b58wrong = '3GhhbopVb9wfo4HzecYwKYMWRvLCssTeFWjocfnWv12Yt3GtaW3seeatH9GqhVmnYrF586RKLwjFFMYn7Txq8X2D4qT7CbqrZgbdRm'; PublicKey pub = pubCoder.decodeFromBase58(b58wrong); expect(() => KeyPair(priv, pub), throwsException); }); }); }); } ================================================ FILE: test/common/model/keys/PrivateKey_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../fixtures/privatekey.dart'; void main() { group('common.model.keys.PrivateKey', () { PrivateKeyFixtures fixtures; setUp(() { fixtures = PrivateKeyFixtures(); }); test('cannot be created with wrong length values managed by the curve', () { expect( () => PrivateKey( PDUtil.hexToBytes(List.filled(35, '00').join()), Curve(714)), throwsException); expect( () => PrivateKey( PDUtil.hexToBytes(List.filled(51, '00').join()), Curve(715)), throwsException); expect( () => PrivateKey( PDUtil.hexToBytes(List.filled(69, '00').join()), Curve(716)), throwsException); expect( () => PrivateKey( PDUtil.hexToBytes(List.filled(39, '00').join()), Curve(729)), throwsException); }); test('returns key as ec', () { fixtures.curve714.forEach((c) { PrivateKey pkey = PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); expect(PDUtil.byteToHex(pkey.key), PDUtil.byteToHex(pkey.ec())); }); fixtures.curve715.forEach((c) { PrivateKey pkey = PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); expect(PDUtil.byteToHex(pkey.key), PDUtil.byteToHex(pkey.ec())); }); fixtures.curve716.forEach((c) { PrivateKey pkey = PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); expect(PDUtil.byteToHex(pkey.key), PDUtil.byteToHex(pkey.ec())); }); fixtures.curve729.forEach((c) { PrivateKey pkey = PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); expect(PDUtil.byteToHex(pkey.key), PDUtil.byteToHex(pkey.ec())); }); }); }); } ================================================ FILE: test/common/model/keys/PublicKey_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../fixtures/publickey.dart'; void main() { group('common.model.keys.PublicKey', () { const List curves = [ Curve.CI_SECP256K1, Curve.CI_SECT283K1, Curve.CI_SECP521R1, Curve.CI_SECP384R1 ]; PublicKeyFixtures fixtures; PublicKeyCoder coder; setUp(() { coder = PublicKeyCoder(); fixtures = PublicKeyFixtures(); }); test('can be created as an empty key (used by pasc v2)', () { PublicKey pubkey = PublicKey.empty(); expect(pubkey.curve.id, 0); expect(pubkey.x.length, 0); expect(pubkey.y.length, 0); }); test('cannot be created with wronfues managed by the curve', () { curves.forEach((c) { Curve curve = Curve(c); expect( () => { PublicKey( PDUtil.hexToBytes( List.filled(curve.xylPublicKey() + 1, '00').join()), PDUtil.hexToBytes( List.filled(curve.xylPublicKey(), '00').join()), curve) }, throwsException); expect( () => { PublicKey( PDUtil.hexToBytes( List.filled(curve.xylPublicKey(), '00').join()), PDUtil.hexToBytes( List.filled(curve.xylPublicKey() + 1, '00').join()), curve) }, throwsException); }); }); test('can return a value only containing x and y', () { fixtures.curve714.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(PDUtil.byteToHex(key.ec()), c['x'] + c['y']); }); fixtures.curve715.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(PDUtil.byteToHex(key.ec()), c['x'] + c['y']); }); fixtures.curve716.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(PDUtil.byteToHex(key.ec()), c['x'] + c['y']); }); fixtures.curve729.forEach((c) { PublicKey key = coder.decodeFromBytes(PDUtil.hexToBytes(c['enc_pubkey'])); expect(PDUtil.byteToHex(key.ec()), c['x'] + c['y']); }); }); }); } ================================================ FILE: test/common/sha_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('Common.Sha', () { List> sha256data; List> sha512data; setUp(() { sha256data = [ { "input": "", "expected": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855" }, { "input": "HashLib4Pascal", "expected": "BCF45544CB98DDAB731927F8760F81821489ED04C0792A4D254134887BEA9E38" }, { "input": "123456789", "expected": "15E2B0D3C33891EBB0F1EF609EC419420C20E320CE94C65FBC8C3312448EB225" }, { "input": "abcde", "expected": "36BBE50ED96841D10443BCB670D6554F0A34B761BE67EC9C4A8AD2C0C44CA42C" } ]; sha512data = [ { "input": "", "expected": "CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E" }, { "input": "HashLib4Pascal", "expected": "0A5DA12B113EBD3DEA4C51FD10AFECF1E2A8EE6C3848A0DD4407141ADDA04375068D85A1EEF980FAFF68DC3BF5B1B3FBA31344178042197B5180BD95530D61AC" }, { "input": "123456789", "expected": "D9E6762DD1C8EAF6D61B3C6192FC408D4D6D5F1176D0C29169BC24E71C3F274AD27FCD5811B313D681F7E55EC02D73D499C95455B6B5BB503ACF574FBA8FFE85" }, { "input": "abcde", "expected": "878AE65A92E86CAC011A570D4C30A7EAEC442B85CE8ECA0C2952B5E3CC0628C2E79D889AD4D5C7C626986D452DD86374B6FFAA7CD8B67665BEF2289A5C70B0A1" } ]; }); test('SHA256 Test', () { String result; sha256data.forEach((hash) { result = PDUtil.byteToHex( Sha.sha256([PDUtil.stringToBytesUtf8(hash['input'])])); expect(result, hash['expected']); }); }); test('SHA512 Test', () { String result; sha512data.forEach((hash) { result = PDUtil.byteToHex( Sha.sha512([PDUtil.stringToBytesUtf8(hash['input'])])); expect(result, hash['expected']); }); }); }); } ================================================ FILE: test/common/util_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('Common.Util', () { test('test hex to byte array and back', () { String hex = '67EDBC8F904091738DF33B4B6917261DB91DD9002D3985A7BA090345264A46C6'; Uint8List byteArray = PDUtil.hexToBytes( '67EDBC8F904091738DF33B4B6917261DB91DD9002D3985A7BA090345264A46C6'); expect(PDUtil.byteToHex(byteArray), hex); }); test('test hex to binary and back', () { String hex = '67EDBC8F904091738DF33B4B6917261DB91DD9002D3985A7BA090345264A46C6'; String binary = PDUtil.hexToBinary( '67EDBC8F904091738DF33B4B6917261DB91DD9002D3985A7BA090345264A46C6'); expect(PDUtil.binaryToHex(binary), hex); }); test('can concat one or more byte arrays', () { final List hexas = ['ABCD', '0020', 'FFFFFFDD'] .map((hex) => PDUtil.hexToBytes(hex)) .toList(); expect(PDUtil.byteToHex(PDUtil.concat(hexas)), 'ABCD0020FFFFFFDD'); }); test('can convert int to byte array and back', () { final Uint8List h = PDUtil.intToBytes(714); expect(PDUtil.bytesToInt(h), 714); }); test('can decode and encode a bigint to little-endian byte array', () { BigInt a = BigInt.from(1000); Uint8List encoded = PDUtil.encodeBigInt(a); String hex = 'E803'; expect(PDUtil.byteToHex(encoded), hex); expect(PDUtil.decodeBigInt(encoded), a); }); test('can decode and encode a bigint to big-endian byte array', () { BigInt a = BigInt.from(1000); Uint8List encoded = PDUtil.encodeBigInt(a, endian: Endian.big); String hex = '03E8'; expect(PDUtil.byteToHex(encoded), hex); expect(PDUtil.decodeBigInt(encoded, endian: Endian.big), a); }); }); } ================================================ FILE: test/crypto/encrypt/aes/cbcpkcs7_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:pascaldart/crypto.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('crypto.encrypt.aes.cbcpkcs7', () { List> aesData; setUp(() { aesData = [ { "mode": "aes-128", "key": "2b7e151628aed2a6abf7158809cf4f3c", "iv": "000102030405060708090A0B0C0D0E0F", "input": "6bc1bee22e409f96e93d7e117393172a", "output": "7649abac8119b246cee98e9b12e9197d" }, { "mode": "aes-128", "key": "2b7e151628aed2a6abf7158809cf4f3c", "iv": "7649ABAC8119B246CEE98E9B12E9197D", "input": "ae2d8a571e03ac9c9eb76fac45af8e51", "output": "5086cb9b507219ee95db113a917678b2" }, { "mode": "aes-128", "key": "2b7e151628aed2a6abf7158809cf4f3c", "iv": "5086CB9B507219EE95DB113A917678B2", "input": "30c81c46a35ce411e5fbc1191a0a52ef", "output": "73bed6b8e3c1743b7116e69e22229516" }, { "mode": "aes-128", "key": "2b7e151628aed2a6abf7158809cf4f3c", "iv": "73BED6B8E3C1743B7116E69E22229516", "input": "f69f2445df4f9b17ad2b417be66c3710", "output": "3ff1caa1681fac09120eca307586e1a7" }, { "mode": "aes-192", "key": "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "iv": "000102030405060708090A0B0C0D0E0F", "input": "6bc1bee22e409f96e93d7e117393172a", "output": "4f021db243bc633d7178183a9fa071e8" }, { "mode": "aes-192", "key": "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "iv": "4F021DB243BC633D7178183A9FA071E8", "input": "ae2d8a571e03ac9c9eb76fac45af8e51", "output": "b4d9ada9ad7dedf4e5e738763f69145a" }, { "mode": "aes-192", "key": "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "iv": "B4D9ADA9AD7DEDF4E5E738763F69145A", "input": "30c81c46a35ce411e5fbc1191a0a52ef", "output": "571b242012fb7ae07fa9baac3df102e0" }, { "mode": "aes-192", "key": "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "iv": "571B242012FB7AE07FA9BAAC3DF102E0", "input": "f69f2445df4f9b17ad2b417be66c3710", "output": "08b0e27988598881d920a9e64f5615cd" }, { "mode": "aes-256", "key": "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "iv": "000102030405060708090A0B0C0D0E0F", "input": "6bc1bee22e409f96e93d7e117393172a", "output": "f58c4c04d6e5f1ba779eabfb5f7bfbd6" }, { "mode": "aes-256", "key": "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "iv": "F58C4C04D6E5F1BA779EABFB5F7BFBD6", "input": "ae2d8a571e03ac9c9eb76fac45af8e51", "output": "9cfc4e967edb808d679f777bc6702c7d" }, { "mode": "aes-256", "key": "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "iv": "9CFC4E967EDB808D679F777BC6702C7D", "input": "30c81c46a35ce411e5fbc1191a0a52ef", "output": "39f23369a9d9bacfa530e26304231461" }, { "mode": "aes-256", "key": "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "iv": "39F23369A9D9BACFA530E26304231461", "input": "f69f2445df4f9b17ad2b417be66c3710", "output": "b2eb05e2c39be9fcda6c19078c6a9d1b" } ]; }); test('passes aes-cbc vector tests', () { aesData.forEach((aes) { Uint8List input = PDUtil.hexToBytes(aes['input']); Uint8List output = PDUtil.hexToBytes(aes['output']); Uint8List iv = PDUtil.hexToBytes(aes['iv']); Uint8List key = PDUtil.hexToBytes(aes['key']); Uint8List encrypted = AesCbcPkcs7.encrypt(input, key: key, iv: iv); expect(PDUtil.byteToHex(output), PDUtil.byteToHex(encrypted.sublist(0, 16))); }); }); }); } ================================================ FILE: test/crypto/encrypt/pascal/ecies_crypt_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:pascaldart/crypto.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../fixtures/ecies.dart'; void main() { group('crypto.encrypt.pascal.EciesCrypt', () { test('can decrypt an PascalCoin payload', () { KeyPair keyPair = Keys.fromPrivateKey(PrivateKeyCrypt.decrypt( PDUtil.hexToBytes( '53616C7465645F5F1CD34699BAFAD73EAE8A574154F08760BF8E8B9A554CA9691819BA06962D4A3774B9ACADA4B75471A85A10B2C418A56B1AFFF8F560AC6F66'), 'test123')); eciesTestData.forEach((d) { expect( PDUtil.bytesToUtf8String(EciesCrypt.decrypt( PDUtil.hexToBytes(d['encrypted']), keyPair.privateKey)), d['decrypted']); }); }); test('can encrypt and decrypt a value', () { KeyPair keyPair = Keys.fromPrivateKey(PrivateKeyCrypt.decrypt( PDUtil.hexToBytes( '53616C7465645F5F1CD34699BAFAD73EAE8A574154F08760BF8E8B9A554CA9691819BA06962D4A3774B9ACADA4B75471A85A10B2C418A56B1AFFF8F560AC6F66'), 'test123')); Uint8List encrypted = EciesCrypt.encrypt( PDUtil.stringToBytesUtf8('test123'), keyPair.publicKey); expect( PDUtil.bytesToUtf8String( EciesCrypt.decrypt(encrypted, keyPair.privateKey)), 'test123'); }); test('can will throw an error if decryption fails', () { KeyPair keyPair = Keys.fromPrivateKey(PrivateKeyCrypt.decrypt( PDUtil.hexToBytes( '53616C7465645F5F1CD34699BAFAD73EAE8A574154F08760BF8E8B9A554CA9691819BA06962D4A3774B9ACADA4B75471A85A10B2C418A56B1AFFF8F560AC6F66'), 'test123')); expect( () => EciesCrypt.decrypt( PDUtil.stringToBytesUtf8('Hello'), keyPair.privateKey), throwsRangeError); }); }); } ================================================ FILE: test/crypto/encrypt/pascal/kdf_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:pascaldart/crypto.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('crypto.encrypt.pascal.KDF', () { test('generates key and iv without salt', () { KeyIV data = KDF.pascalCoin('test'); expect(data.key is Uint8List, true); expect(data.iv is Uint8List, true); expect(data.key.length == 32, true); expect(data.iv.length == 16, true); }); test('generates key and iv with salt', () { KeyIV data = KDF.pascalCoin('test', salt: PDUtil.stringToBytesUtf8('123456798')); expect(data.key is Uint8List, true); expect(data.iv is Uint8List, true); expect(data.key.length == 32, true); expect(data.iv.length == 16, true); }); }); } ================================================ FILE: test/crypto/encrypt/pascal/privatekey_crypt_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:pascaldart/crypto.dart'; import 'package:pascaldart/pascaldart.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../fixtures/privatekey.dart'; void main() { group('crypto.encrypt.pascal.PrivateKeyCryptor', () { PrivateKeyFixtures fixtures; setUp(() { fixtures = PrivateKeyFixtures(); }); test('can encrypt and decrypt a private key', () { fixtures.curve714.forEach((c) { PrivateKey pkDecrypted = PrivateKeyCrypt.decrypt( PDUtil.hexToBytes(c['encrypted']), c['password']); // encrypt and get private key encrpted Uint8List pkEncrypted = PrivateKeyCrypt.encrypt(pkDecrypted, 'test123'); // decrypt and get keypair PrivateKey pkDecrypted2 = PrivateKeyCrypt.decrypt(pkEncrypted, 'test123'); expect(PDUtil.byteToHex(PrivateKeyCoder().encodeToBytes(pkDecrypted2)), c['enc_privkey']); }); }); }); } ================================================ FILE: test/crypto/fixtures/ecies.dart ================================================ List> eciesTestData = [ { "encrypted": "21109600A00002F5441AB2530130FF8CA8CC9C2737E34C35EAFAED908747E93EEA0D890E774AF9B02198396FE01755965ED87B0A00D1AD37A52E77C735CE0EB8FA1B92FCCB92AFA37DD88854069D57707E5FCF6459CA3B98FA6EB2B52C52C53A6B6768862E58C9082DC7B59E709D8F347184C771A4BAF2709FD84B89868991E21FAFB04AC3E0C3B0E0FB630671E2B7CB2F280032ABF241883626932C60E324802C57F7D4733D47FF415F916CCE69B7C0A416FEFA048FA536BECF3E9EECA0F1768A35C3FF08F6FC24CAF4272457AF23E2028A3050519E94", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "21109700A0000328EE5EBA3FC11EB9ED4FF81492EFE85F4FACCF9007CD55189446906F5609261064D41434524940988926A87DA6EE5A187AB59F4FB81CB2E4ECB8F848CB10B1FE3FE7DDF5B48AD52117DA31CAEB547BAB7A2FBD0C2EB395206F0B7D3FB10823E7BC78B997B2939824571EED01CFD69C8912754A20EC12897D69C7FDE513AEDF2F91AEB78972BBE51D2535E592BD852927C5517616CDFCE220F85C78B549FA14C3AEF9C2D85D7FF0F05F9854339C03065222F79A625FC20197072B168DE8024368C48EBFF1091AFC210325C635AEABC685", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "21109800A0000361D2638E2F96A0107506B7DC231F9EA12617B67CFF4218395F315001727F4C2B5F90BB73F43FBD83FBF36938A61930D342080AE3052B43FC9C38098CD394B07CDBAC33FE9FEA55CBEA4D4EF3909802DCCE7F0EEE75846D2FF092C56079696FAA143E6678969FF2924B52BEACA3FFF319CA186B884598D67678FA7598AA9BC9F8FC6910B38609E4AFEC08208D5E992FBAB0E7FF92BFAAA4AEEB5FC2341BE6F5862C4656656896FAC01A41E690CEE89DB05E51C818B49DF2E0025D44FFD7EDEECEB7ACCB471626C035E833799216AB5EBB", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "21109900A000038B69A3E98F70B03DB7B0A6CCC9F22D67D727A728A50E035E79B505D43621A8045EC09802BF614324A208C45F075E0F0A7EA05EC924A9B7575D40EAAACEF71158454B64FFA9256FA232668652F0469873C3F92F9FCC59B908D6DD6BE9807A69751322750D9ECC67B762751BA9EF53AAD4DBE1A1A433727FA420E3BC1F1B41C97CDC6EAAF7CD11B71727420295E4FF6507BCB7BA255942B7183BFCE80B5ED2A6CD12B8D06C284B8B700AA2CC3BD0BFF0EF1A0D0D83E034BF882B94AFCFE79683A70486D01DD2EA8AA6D4DB5A8F8AB0C4DD", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "21109A00A00003D51575543F97BBDF5EBBF1923D83DE66D5FA4C643618C11D2CFCA1B9E3AF873BDE3EB2EBAABC735A64FDD21626FDF95F5C17E12EF70C1E1CFCFFE14180F87C1FEFFDBB304E84037B01700B0C5937D6F8CB3475CF1ECEA474E52141C89D28B42C88E672F8F6D84851623CF441FB7043C5569F7A90D171381EA563D85A6E5C7E08722B9ECA17FBBA128E95FD46E77F4F39BF711D1A9DE129A0F6CF43D07FF95A2F3CF6632BEB87B60E8ABB5FD3CCF551F64241D7C3163A2412E310DCE6048D577C83337D33F5145824C48FFBC513981C95", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "21109B00A00003C80AE7EAE5C736B7200609B98191B93C55DF8CBD9C181234BAA58428650798F405147A3E3FCB9F65830D42E1142823976888A6F3D292ED7915B53297429F8ED08B84D9368AB366B5BFFDD09330162E016FD3F5DCD76434E698E26C92E84D511011ADF8B3E07CA64800291FF671119FF0B074B7755644CD730ED5F0E2364F6393AC1C04D466EB686DF1A2D5F1D4E8CF9F4A306EFCD459FB0D97CB5F4CDD67B790D85D66FAFC52549A1B0F863AAA83D64409300D63CC0C1A85423712F715FA13F456DE82DBB15E99CF48DD331C7323A86C", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "21109C00A000025CBEC0F4505AD429CB0E2BB349D7BD80221E961B950980969F42F6C054519D4717916754CCFE1E41FE41769A620122D021102886FDD54B9A902D16E435FFB779F5798AF6E6D7ED27C992B22576A92F73C71BAE8DDF9EB39F7C3E7020F2CA5177DDDF3C6E6F673069D67967D694DC4F661DB68B440D0EAA87541996CAB2C10F0DD47DBD54E24E45EE19C0467C4684CA9C7A668D20B4516755F18ECF63B7BCF2E1540754FF2BE116108DE71615E26C4507C4C7027983FFB45E8871C52EC251E63A645D25033D8C611BBC00BB91E1EA89F5", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "21109D00A000035F3794C9940A3AA167C2EF173EA93E0CCA9C2DA18ACFE1DBA53A11439D181D8C338699BEEDD5373A649814CF2145033A57D3A5955A48F6510B38E606CB6C763FBE983717144CED18A935143B171135A32649913847EF25DE48816581D7B88B300F0AC7D7E1B5EC3C1B81E58DA8E10105B957A5D81461756499B30DF39A0EDA33B3D3E16330C93BD6DA9B695783C7A852B7212DD94CD5596EC96853A8AAD10EB3A4169C5964C0A4322A4437D344025A3DCB701DA483FB387E97BBBA9EB55A7AA98DE6C20449B7EA5BEEB99510272B7EAC", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "21109E00A000035D3407234CDE57B8AA6D760381FC30AABDFA15960923B82A911BCC845B064D8EE69D7C96E90FB8357E06302EA2BF69409F6F986CCA4537251A21551C2AE3F619FB860D9A8D19E51F37FB12F73A00C9AE9B6D2C24326EC2A2336DEE04BB5384D3048D942664E7D6D8B0796C1C16D84C0BEAFFF1EC4FFD5A437F93845905D8CEE1B053E92DF277E9AD63648DC31BD70A9769168A8A037299CF38D5A7BE2ADB50704634E955B0ACDDC6A1AA2F701B32587A356A1B96CFB917DC4141EA1EA45EE24FBBCC246F4F4A07DAFA00B6621B557625", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "21109F00A00003B6E5B25AFC3E13A0698E41E0EFDF66D5824A1A37D5C6F809A2A96E75E943906C4D4AEA11B5A66313607DB383B532358810CF3413178C0D0FCBAF003759517DF1AE567DA4063BDB4777E1FFAA0C8CDD6BFE806FD9A7A8B5F3DE1D4B3BE51A80591F60F77407081E7E0529A50603619B31919CF9ED6439CC09B61B0829A1F85D9A97D1C7D861CE62996B6E9E6DF314C538285438914B718BF7537559061AAB838AF6286912ADF2583E0D86084C05B086A78E70E6B3AD5E71F1C9246219FE0516F52D3A0802FFFC0AE3B2F37DD576EE7175", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110A000A000031A95E74485AFF91F635DBDEBEBF0AEF70C3CBC1B8D7C872881B715E3DC34A5CD5ED3DD56F5E4AAD035225E0B0A0CA0897DCFEB00139140F62B798EBB352279E461A0D35A8868476A8C10DEAA7F83E7ACA5F0C30E0E25E5055462D0D411B4D20B974A4D0D82F8EFD566E526954FE15B34FDD48A5D1FF7B2279855156A69D20D2691DF735538F5CEB77A3FB7B67296BB53A4DD199895DF43C7A992A28C1E5400478722F1A61FADD220C1632428945D6C803A2D5766DEE19D38765852522A38B6E75D561318FE86B877BB52B3C58E4DB9C1", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110A100B000025693FF78AE08E71F5C6AD98483279B3FEAC64B842F40B41C094B11328DD73E994810F21F496A13FBB1DC45428BBC4DE17D7BFA9B38DADDD68290A6D7F7CBD3DE81BDE51F565425C77EA18B079B8454D64E1217C064CCC8DE29F5BEA064380CF098044534CEE3DBFF131FDF79DB4C6D02E16F4012E244C263B8DDD77066A698392FC301E3FE93EFE0E2B9884E98EC02C63B11B7AE3D11E0F1F1F53BEE73E1F9FB16381F29D2BEE2E74165AEC46B88DAFBF97413284153E93D2C2FC0C868004FE7456047E698F05CFCD3B410735E9AB23643D689DDD234C91089965D1687C98ABF", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110A200B00002C8CB4BAC2A0D5A44BA198202F0B807A2140F278FD339C26897400ED3E24FA1BFFF6283B29E1A6CA750489D85E8BDC6F2AD7BA211A1219C35A17B5A30632E4198E89C4095CCEF7DC24B75373D14A6ED653928C393FE9AFD462924F9B3E8D2BA9CA4CEFB552B3B5A92961E5541CB9E901DD5F2C964C5621E3A0BF3038D0AF34B49DCD84B9C350622826BCC8FF265B8694FEB4F8440669C9F534BB75BAFC60AF680B1C334E289E8276D57E9CC1AB26EC9DB7F609FC7E81D58FB58E9A835050415EF5C55FE97F5F2D42B3F8095DF22A22CBF4BC0372FED680E039ECFA5D74B133860", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110A300B0000285E7BBA7F35B7225E01B92E4F9D29255979A199CDAF8BE1368CEFCA651687B399BAA18946E2B62D5455CC1AA18E2C0F40D94068EB026ADFF318F632E8F40316C0135A780B55EB8183A9B67A9FB03524F86A37550FBF38DFDF8C93FCD734330E1B69106AE2F5C84EB342B3B80B258C69A39D5165B68EF3CEF31CC03449620B8F39DAC9C49B72AD8DA004F8FE58A9C3106A073483121F951E8A6BF7449FB818AA49E90C75956420A47FF8E77191202D497231837FD5D717AEBB1202C0BC2A59E115056624B46A9FFAD5EE310E7AC0AEE94406EFF526DDC367C8BB8715DDA3D2D15", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110A400B000031B1A06085B30730C2AE5D78AD43A45DF38F7006908A98C0D02C62789D2640E4DCDD0A4B172A74E61DC18D9E851E02E1E589AE52559C22F5941602BF5EB7B0F4337AB3858E0FE614A5917FE7CE0FE222932F863629924DC6904552C1FF580D115239075A391A190303F00A02EA90170722E490015A2FF1A8D7842994DF145666FD702279750ED093ACC03E09B8C423C821FFC81AF556398EBD5362AEBFD56A06D2590357C1629FCB48E14E85E9BBA73747BD057A2CDC07C5F526C6BD52CBF027EDA986D061384029A863C22A8B5C3BD53B653F422829CA3E8013D0434E0D389A5", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110A500B00002F9212FC6DDD56511AEBE4D2419D81DAD3845137610AEFBD888A740AC8337BA746766A17B98D6AC2D65801DAE23821836A0F46F5FF19813FFCC5B40710677208CEDE835EE8EC2751DBD4BE18DBF86B5C389ABB261BFA4FA9F0E11D30542281FCAB55B4DB9B5D288A1B10C2CBC87434FDCD405D94698633F3DB86440736DC81633DBC3846D57A99F9BA17D7D3C85993A190576E4EA338F99C9338A066EE7A6798FDBBDB7AC606A65A708B6DD47CC30583988F56E92F7E9FAC2E152707B774379063B4A1F803AC071822C5EC9A97D8E339ADA3E3C35AB430E42CA201719C3DFD2C9", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110A600B000036411A55FBCA66AB20CF23E61768F42BAF053F1D29B631770A7F14F34C27C7C11D7C3D1AC6341B5905EA192C1BDB722EAA400B74CCA921BA27DD865B111D53A7ACC4CD2A1EFCB0CA8D7DFB682E9DEF9238A2EC46CF0FB44BA04890D7FB0434CD7CED2009E7CFBF054119DD84F3951EA5C97F699CA21A0D257A7C14DE38AC269DE497C645DFD9E6C73D7B2B1C0E67C34EB390039464B0ADBC2E04FE62A0EAD7B58ABA977D4DD2432E6751E2287BCCC5B445D4C7D1F2DDBE3B63EFC1D3984EE60ACB88458127391684BE661649E02BAFD42E8396EDADA7813AE76938B964448845D", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110A700B0000298A12D4E3928D902B142C3602F827BD84A12ED9C5FDBB03EC76154094C84375E36D3A7CC52BB9ED5C6A45030C9A0D5FB42A14A07DC188676C8806BFF6A8C80679A7375781BBA7E963A670261C2DC9242C33B014BB59F2F85235646E10C4B613ACF772895D57C7E82BC67A1041135D9551F36C84FC298A9320396B68DA68D6466AA36CF05C61D3583346C1CAAC0F758823A650168DD7B60004EB72F048F5FAA42F97E2406E39CADDA75B327D9EFDE8AD56EC801EAC05BDFA127E8E8A0915E5FE76A2A6393338492106FC17560170149121E90B85F8CB3D4B26B02D651CFBCAF83", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110A800B000022F276540E5F963A5BC91E0DDFD2D9634D3F2C9B5D3513DFFA4760EDBD97D95413A5475ED68456CF9B2CF72E1433873EB963AB540ED59247AC03C9861228F34FD832CF9F7AB4557C9C048A59E1CB50545E1B2A3E84C46A0C59B76A85F57991174B39BD3811FEE00461FA042B983AD2543BA3903AC35EE9BB7E5ECA577138A055233C6A0CCBDA020A6B5D1FD0F0B2AC5CB74EA0180BF6AD53494E6733F65065734C46BDBC586C142FD1391C908C104F926E848AAD328DB093CEAC0245AC924A9E418529A1925EC166E29CCB2BBC90DE5C89A2A4EAD9E79D09B6A9F5AB8098DD7D0", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110A900B00002A365139F5C29957ECD6481A1BFBDB0AAF814596FABB961043D230C86A951E71B5E58C2041BC66BAC58B76AFC40265E01518FB1AD9EE3423FA57E12BCE867CDFC03564E696ADC2355A9AE0235DFEB06CD0FA14FEBB25BB2432DF1BABFF95ECADDA3543A5C27F1D431016AD7C3F9E6D40AB0693D0D442779245EEBA91DE04866E5AB473FED5E2084800499ACB8A21C5BC10A5EA010C3DF393B85A240BFE144E3013797BA2A7A592C6999F32DF19EF6A1C64110689B385A25EF46B793D58355BAC9D489387BBCFA5726E2F45692B9EFC1F66E814CCE6EEEF530F70DFA55852C6F19", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110AA00B00003A96F3F2E08EC5BB9FB209C347DB786D0E00CA57A7B589E848FC1E3D601D8C9846C244DA1E64B081710CF51F78B8C508D6698487B390FAA98BBF416D084A5B10DBBCBA02880445FD10D03DADCD2AC80442AAC0FA88475FAF53F5C9D1EC0C557A0E57272FCD0C361D544653E821A2A77EE229BE131E8889AEF2073452F7E1F8D7DC389A50B482F9A75DA42810EA59F2ABFD3378F490158F5B3DFC34C765C06B7A662D2AD2B5D673CC1AF4D78EA7102625DE6EA97589BD9A85CE06276913120F59DA8917FF31D01C550D933D6CF24EA61349BE70FCB3F0FB0F8DA20FA558F201D44", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110AB00B00002EE3FEB28A7276896FD8C50E3C73E2F3E48BBB92D2833DCF6564FEA7F635715E7EF6934F26D5FF228C4DC845403BB370B196DED49D5D64E1F30D0E0E79D2A92F4AE1D3986AD3BC831ED6456E0044293C90563786F04C04D82B0B124FB879B37B388E6211CEFAF626655860BBE78EFA21158B7EDF41D6B673ADB97B2D88CD109D4722A4CA700EAD277094456753060421112F07B68F5490B6C76588D7D7807F821FA9BE66673B1B39284E38C3D7A52F73A393785E6B0B252C64202291D53ED6E25BD8ECAC8C22B16D13597F3B612E222CB5343DC6B409D82E73B11AC13A6F7DD5F", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110AC00B000023909AE7DAC7F9A978ACF4190E4EC8EB03B953C87C0AEC2E21F7895A63EEA5EE8B32B8D692849F591FDED5B1CA0FA1DBE4E365568DD3759D7A8869D0C12C3412E0540902EC2A2C82D11CDA3A27F4AE9709D1D8F08FC02A00F27BC7072F4E8139E9DD777FE76BDD291043C9EDF3F64EE53241384262D00518FB78264D18050B6A83A2E2B89B8D56ECD2939FB5A8E6546B5958C32249830EC91C71E160A78F368E0242D23506D8A0809DA230A672F8E2BE9FE18B22BBF92522A8DAF157A0EFC8A913AE67E1F8C499363744B89ADE51C047304B36DB02E3DD0414240619AB7CB2979", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110AD00B00002E3012FDB1D2E468A6F02DC6C67995427A6E7937AB74BA988C73A8F8C1C0A545BF738CB86FADA287D29EE57EE0E805CEDBA1FC0299A0EB068C6CD252A87FF8A6E0B01A1F300496903314FD1EEDE0C0466F563B245BD53DCEC57EF21C21465F5AEDEF6B0F57269DE97D020503E2CADE1C8BC428F2A7AE1943754591B4BCCF0C9973530E8938CF83753E0B2227363DAD9E4E5C0BEFE92D0D5018118A48538295780B1D9F67486E7E5B3D401B4C2271B038132B3D0E6079D45FBF175FE5AA29E7030ED1274994BC0ABB2D1D7CE063B5BC6224F00C4A610B39CCE093764DAC700E206", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110AE00B00003C43AA8C428A814FC2B36B2874DC38AF5BA562D7CAD256E7E01BEB1C7701F343916E98D59019A8D082B1070FF44175CCCC6EB2A96774B178641EEE5CECC23C86360054DC76BFDA858DFEA3AE121C1FAE58514CFC6E328499F989245A76143263DD5B9F1AB3A9AACCBF8F4721F4D962DDCC916CACB0EADB9419BD88B798E7344219AF4322BC91FFCE9DE3C1E940A25F738D1ADEF42EFC150936B6CB7974AC29B43AEF9C0133EFBC465FFFFFA9B48DA6669AD7C42A8172CFE3D2D1CA2EC4B5021DE9D4F68C7177ABCFEDB77B2959B0E625CF25A04556B643D0163411D762603EA39", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110AF00B000031D566D01B1A5A33C8CF06810EBC2F2AEC0AFDF147C49365BB5E66EE1CFAEDB6B53F4E3CDB7F8777705CECAEA2A4DE114E68CDBF9ECB18FD072A0FE425DBD0208A323D4BF3E4D0EE96BFFC7945F89DF1F62DAA8D4991E705BB1476291BD731D0F40C64D47EA5439CC1AA23EEFEBB3B887361750EBDC9C9F1503956CE9BC803BA09ABEBAE5D03220CAA27B87FE921928AF21080DD070B65A3C8C862312823AFB6D66466DE6E00144AC008EBF40564DEAECF63288E1F5938455B304085C594A18ECC56AE334780EBC041E2DA69AE3E8C97EE69AD430D65646A1FECEB52F8746ABD2", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110B000B000026C36931463B13DBE9FE5B9F8771DAF99AEE55C23D6391752E13F79BBAE32BD604977AE7CE4D97BBDEB236A1A54AEC045CDE4A5D3E945435AED28B814CB2EAA5C628C1DF6C8BC165E0608C9B916C6381134AF02562C6521AB94C01A1D6D4709E5C4447ECE9144D322093D240618D7C5F31A86BFBD783E9C5A073900B2979DDC16A8CB626357013C18C8CDD0F4A2BE55488AF90F2D11E0599D2D03A5817EFE4093A8580AD49365DA75C7DF17D4AEB47C4F4E7FE9A3FC8D0D324EC3891B0F4A90F379BB9E5407524D8C7496E329541A1BC11723D9AD4266E42862FA4D98321BA8B0", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110B100C000025F1C0E37FAB6FDD573841A1C1A4377B039F7C2DE61D796AFC15FE0467B7F15F441282348A4A17789F2EC903E029E2B93C07D1BB2C8DE6433DB3ECD5C93844DAF8F7B758DCE485145E3BAAD5E0AA1F9284B274475765F952D0BB00BE9B1E4A5C684474F4DE7DE402B7D0D2BA11667876C6EB99C0D9DB3AF551EB5879A81DDD4D6C75A107E71E87609552233C2AC4282BBF0EC5470F1E40F2E53EC1DA2B1F0562C077513F1FEBB34F843EE844FDBAC307938EC3A79B8B268F97F4791D30249740B93C8EBAC02D4D2828CBDFAE29FFC6D769252D2A1BD5942313BA7945F7A7E6B49D20CB54DABB7C94DC774EA622678820D", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110B200C00002C1719E153B7073803E84FAAEAEB0A04A37212EA4481F94EC5D6732A8ED37E46B2C18C4C3449FFC4D2598874C40FDC55CED750A37388B45684F90EA098419F6EE2A296F930A2D7531C4B95702779E0FA4CC0BC8FBDA086E8A4C149280A3F5B3041BFB63C9C31E0F4D1F812C7858D61A92E8172016889C623F1C9B7DEF39F35DAAC1648F4228AD8D1A93C72BA6928E116F0A463A23683723648C84A765BB56535FA518BB77DF823E45A42409888BE1A935EB5AEB447EC056686EE2DD1A665601E0E0FF892A16B9EC5B478DDEFC9BC68C4B26C5995A999179506C5CD0CD363FC992DDB05F9D605606F6E7D9565B27D484F8", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110B300C000037FCB508B7D707FBADD9E4EAE88EBCAF3FF53FD2F129EA8528C1AE4F79B62BD0BF952A3C29308EBD64F0EAFE4B63551F06D74C9684B23D8710578DE54CF9314C3A851B54D32F55B04E55B8BACD6BB0FC7EBD5B5B1E5FB8B90E71E45AE4B8ECD1DB749BBA477E2A4D6CDD6600AC637853A7E1ACA1C78421D5DCA49C3BC98334F7CD0A2CCCF6E7AAB22CD9F23297F81610395D2EF52719E432DFA3930D2B2C74D749E880CBA7CB316FC2530A9B969659265FBE0A3B48ED381DE1C5F28BBC741D58D1EEBE502FDD25EF6093C85E187A507E25DA066D99EB8BC0070CE6D4CC65D4D3A675B9206A04A0B6233CE542389DC22E5", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110B400C0000233E8E4E8285A62FFC35945437F34FEAC4B438053ED7154424451C5DD8A409EA6B56081CF032B2EE7798589E690580E13164942933DAA5C2AF4903311427243C33989037552056147C0D05E7CDBB1C310344E34F74A24909D319FB58CA6D409B003ECBC2F0C73BE01E98B6D898DFF7249BF0A19DA5708E8CC058B213A69EFD70CB498239C90FAC0EAC0B0B9607FFDD44B82599FCC168DDC6164D95631F2AE6BF49DFB52ABB8E2CB98148DF967F00CE264972DE0755D01971F9602DCC4881D433BCBE421D3B64AFE0D2C58F9291F9812C05E9D49B133547E12FB45D7C1780B015B239260C584E1BEBA77DB2563076A85D5", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110B500C000031DF17BE88ABD5F52D04B58C6FB103D9BED9CE5137C4425E7A010092D38D1995AB3DDD855BD785047B653F7732F30A42E6684FA6BD8907EBDAF78DA41DEE46545E6C6C6A36E7DBDBD6071B2F03E82AD2D7FCE75A9DE719FA067B87CE026F4126E1656876925CC2E2EBEDE406EE35A47C53D9423415D79E41065975F13FA89F32C893E2EC7283376BCD0BE0E81924ECEDA064FA67E4C751E87D0748ABE1CB913416D48B71153DE5EDE2ED7CD55C82786896582DF76FC06BE60D4B2534FFECAE12801DDB682DE4772E77D751F3257D2056481DA3A5BC3ED6D563F0DC165F4FA54BE3DD424B74B72A882445CB9C5DF2A23F0", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110B600C0000329BB2EA43B2CCA8A518B46663B560248673A297B1FF886C1128A0F193F7465F4497B9393A365238E381198EF71B226BD41F400F76960E9FD8E7530B1DC2AAF1064934890F0A9382A38473BAEDEBB403B9BFE114493ED9C9F1E2936CB4E5AD2124BBA4B34EF9D779B33620F9F79CAE73DC0543D98759D8CFF55889F4A959B7B764C0918C0174C878455815F820E2F4520101855F81C884F4A1B3551D0C83A22D50971725AF5E44947036043088C8085A95DC86D8D36F46D549B278CA4ECF9F1AF2600B28506FEC21E842772779CE68CE94104D8684C7811C69639FF04CF2C1EC087F75862AE5551C4895D278746A1F514", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110B700C0000247F673CBBF000A37419B7BC1F274A9B8741C74E6C482027CB6700D63B0273F0A87DF3F2C40D0BDA643A68B05B8FCD6DC6EEA99977DF52004E05E5C762BA7AFA173BEF0D32357EBD1B9C0110210CE1D00C5F28D6A631F6C5EA4E999D860DCA413D894E0447FC67EEC7203A505E7AD1BD3416E17E2AEE4CDA60FF2CDC0314B2C8B26B2F51E0C33B43057DE5AA6C05485356436B7D9B65417E090235D4AC63233A5FD024385CE8B8460E2F5BAAE7D0D1A0D5A56F595E1E1E87A952D167529265D359EAC14174F37F611E5A3E1DBCEBEE606B1770AF29C6D816F5CF3B250B9E2091FB43185DFD25BAD075667158F17173C62", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110B800C0000222E43CDDC775E2AAE4C706CF75938341925F69188F158F73F57D9FBC6CC7714787A5D63B003980BC085138AE982E0AC7F1B9DBE2A362E0730282511EE1A6945639A4C24929FEA686F6D5BB13073D339E7214AB39D72DC8B1382B0F467A403FAF87ABD8EB912D966123A7513DCDB5DD4A39FDC366DA83ABA547676CFCAE3AF0C422B4AEE8703EE31B0B15E6318066BCD36A4B7BC92DDB922113EBE21B690C5C0E9ED3DF9109C8E29B114C48F856E7895DE12DC8CE29B26A236491CEAEDDEC2310DEFB5BF205D60311F5084785B956FC6944793A0D0A887B1795779383EB74DA4F8E0BE053B28255C35ABAF5F3F90A61A4", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110B900C00003B0D6C26A9595CA3690A26057227CD3B18D38957ADFCC878912DD6A7D19523F9A4807746CD904D6E1C988DB2CD24B6A1F6A8FF98E55EB32EF5043F076F1A0426C30FDF70393CED8CF60AA278529740E73A9BB41FFAD39103E7B897BEACC5E96334A4E099E507F79DC6AFBFED51D16CCC3DD18F55ACEB8D9781C447073265008CBF4FF880F84E65587B10390195CC97FFCA8A2EB36E3CDFD841C0992DFD2223B80EF5563DA3D3F7112ED645005CF4E8619D122936EC55DA4F1C82760273789F44B89281BD37E544D62C9136D6076868CAC3A59A8BEBAB45762371F18F19372BA8DE4763CC58335FD43D403A6830E0EEF2B", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110BA00C00003C00B795287AF8D9F47144197E1367BA8A36DC387153516419C32C61A03A2B14D18317999DA96331B09C7FEEAB496AC16E3A665715FA180EE7BFD266BEFA0D061635F66F2087E74D2101AC1232680774F10710CA705757F2D9C9AEBA4B598DA29C3B390064FA38BF829C9ADC91F51031AD0B0B302804213BE526109146F5A7C2E02CFF9A2670A96B5950944B4DB315A8292EB0472468EAAA37A02D4DE134F629884A01A9AC0FAB5269CA29545C36236141677BF69329997634C57C9DD821F26294E27E1D53202B869F8453484B014D49FA848DC0A5F2C2B5D93649690C40B3C68A089B1FCE1569ADBB7283CB615CEABDE", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110BB00C0000256D1F43BF111FCA31FF8CEC8D478108913ABE1512E65AE542740876770A976B6B3E6959796553752A3B4D86939DEF0E0BACC27B4EEFA310A73FCD468C48BB47CDFEDEEF3E8EEAB8AED713F60B82B38CA519289FAF7FE67DCEA980A09CED7E901915C810B842A8402432A47C2AC32420E28A8B8F855BF3A6E659839A79638F14C2DEC5832139AEF185B4010B023B2A14F186EFA33D25B3E41D472A41EBF27E1DA97648F14A6CBE93836A95723D3CF2F5BF465FD8AD723CFA564A88D37C568B1EFDC22C663D1A7019BD3F86C2AD05C68B56CC57D7DB1C9D25E0D4B893BA387E093CDC4BDBC617080F430D0652F1A8D2866", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110BC00C000039266D02E577FB8B0EF58865C59007F4428E28F215DEEBD7FA545BE474D6BA8E080FC59530A3D2EBCA71C10B7CDF8917110B2A3BD22425E4E8CB82C847E175530A1BFF7E7CDE54F2632B554D6471847BDC4B3A7F3ED1C8A95D66642DF9FE5894453F222B9C0EE62BF9E57D4899050B28FF33E3CC04EC3AE904927297A67B33D916A15150653AE278ED7B6C778BE0F0441A08F638DF6221C8F2FD18372E84F6BBE23FA1E77D08651C211B6D354196342BB8571F09753897481C55A2DD2055A99A9068A0D1ED9AAE2A2FE5087A98A6798E8264362370A52F34690041E2F69CD38C011668E4EC6BD270C98CD8F711AD7E5C3", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110BD00C00003A9BE116480308809BBD0E5D4B07EDBC54F4C9D8F10999288571A56A1F8DAA1386B704DA270AA27A1B6A5D0D4F39E7ABF056934FB677F89D46F0231E126DCB01BCFC27471DCEE3365C6E4EB0D1A7AB91D5A5D001E88F8DF022863322E797FF0DE85A51051BA14670CC48E7E64801649BE21E0CCBDD3B08222AB1F3D1B66A3EE0FD13C9567BDBE9C3318B8DB256985B0B2CB7CBFDDBA8BCE7A401405CBB9CA8449D30CA6DE056CFCE3BF7D16144FA50D0F12858FD894D2CFF8A501C046CECEA6740315D1A938F53265B8B1B91AD1F33BF6A50272AFB571FE649B47B13DC4DE72BF6F87256000F294988187D86817ABB307", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110BE00C00002207F7834DE34868B9EE12AC3681125A0F0041E774BB726195A47875098D8E28DA389FDCA50D7CF65C02D1D9D630A0428A70548AAED48218D0DB70A9FED6F43E4201785C9CF12A374BA759AF02951F51BD60B629CA5E97DB9F282D831FE847165C11AB4311EE68B15B5A8399D93146E9D54DA6A815FC9A0194FD2C86A93CDFB93ADB7176DCB4A3E5A8C24F949F95E8D2621E9E1480F4FABCCCB6528C0D4CB1DF34385DEC659435E187454296A637D3F71AAEC73A5763AADA25B02348BF08060F12BB00217625D028CE33C68CD863F3048E31F325498900266AE46738F3E7A26B327F2EACB22384963E20CA256E3D2B889", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110BF00C00003529441066EE82EB28EFCEBEA845CF4DBF11E668FBF3454E653FEAD8496D2E91CEAC406DB2ED68B9533774682399AFB25382BF99682BF076FDBED5C96FBDC33F850998579302381E15FB626ACADA877C558FB7835564755DC4F046181F588A0D7503F36F5416116608A1ABA51D8CBEB5BD1DE8964E5F43EDAC232C6D8AF1AA81A350AC01E39D924D6CCF96B9C0A085C0272DD70F78E8227577BB336275D23917E9A9E3F20D862379508DCE6DA2D9329EC5CE9BA5B72988AB5C95EB73FC13CEA83F833F6C778254ECEB3039CB9A97BEDB2C146D7A4CB33ABBF44CF63EDBF88E4C03320E90784984011EAAB9AF816CD1D86", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110C000C00003EB72D2D4A8B3D2859692FDB66EAACC5E77C54C3D2D5BC516DE5A7C4A3401695304F55C4A32BA68964B2CA9A1F1F1F8537620EF728BFAD04ECE09D20518DD27DBEE077D16C778B87026E5CA48E7B78FE18C151069CFA20507C72BDC56FE3B1A5CB154E5D2C28429AB3B8FA35464B839712E1C4149F57DBBCF9A17ECB96255875210277AE9C4CB2E81992BF129A3B4D4209B905AC878043C0F6F1DCC47A2CA86003FA74BE46258691FC2503A3DE6BA4024988A8010891BDE994F5518642EB9F7286B3C1BAA76286C7351D6DF9998A69AC5038B55BE7908411D8C4903E6437904D6727BF3DA2043E17B47130BF0D13818A4", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110C100D0000209D4DCB3B7E7116ED6847BDBACE0A7F23A1B4AAEBE40B6C85BA67B52D22EE36FD200A2CD2FE153C2D8D6DD5E91FD14303DC76C4F91202CFA37821040F78B4C8052F7A85317EECE9FD05A8AB20283C644BC9D8D2B61A4E7EA5A052CDC1E57C877E1568C41CD0ED69E59A7962BA050733EF01A368FBC7ED2EC234882A288253247E67CD0F903C821F224BEF78B4BA4BB1847FBAC5162B1111EC8523B486FEFF8332681275EF95B1F93B3D90ECF357AA92F89B6D679CE93BF3C6E31AC250E5E0B26191E47246EF1EBE19ABA5F01C793FEDB6D8AAAA244602D4097BFB5A5E4CFC0C0506BD6E9C1911092BFC12FC19179BA156E5D859C349A3C864226E2E80EC83B87", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110C200D000020730DA494E2254141764BAF26FFE09DED42298A9FE5B4184C3FB0E5DABD0617B8300ADA39D2F7DD57B37140A2561AA50DA44135E10110C5EF996D27FE8CD37FBD5A01AE703D7FDE0F7823E088F8787B39C361101D4A1BCF4F032D078EFCB7076A31B6D2B1B17B89326B29FDCE1DDC842592744D555FFB256DEE0B28947B1FC13AB3D106A3F0076B6BC9A153B6926236BE9156D3DB9CFD7B1A4AEDBD5F472592C44E47163F10B9B71DA57DAE42B2339A2811E3B4BF9EA229B81AAF951325A6A9C49690D81EA6C916F810163E28D06334204077A0AEA6471679BD7884E48EAED23699B2A7108E29D2D8673631BC699CF25E0751B252061B72A355D1657B81129F6", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110C300D000022899048D2829D3568D4CEE3BFCA948AF9D74B12ED057E85FA3C3FB780E802249B20E4B1E3DD6088DAB93DDA0390D382028597ED08D792F7DC034F700DC1D010880964F2C60947E639B64B852E902EB7DBE3F8F426D7A1A9C7A4C8BEDC0F784A235FA574489BD54260858CB49D3F6215B6181B076CE955A22FA1E73CE9F73E15502E24DAEA676322D05E0B6DDB990EFFA7614CFACE8DF4268F013A45F693D5830E91E2883C3A70B9487E55FA20BBBA0C6DFC47CBC939D8701E283EDD40D4B47941F04875158713DC404924003811A1D16A237C819D9112B72470F8F76FDF8F4BFE22A901491173B4829284AEBE77F84E281FA88D4A3935D1A2AE16EF010454651", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110C400D00002AF79B20C7AFBC634C90F93FF01D55CDF5CDD523FA74C2AD14718DFDC22925CB5156C37E0B23DC4B5C1569D4694B960A443BA0A3C11B97C1FE3EE3140862569342B21E161BA46E69502CFC2EB1861058112C870158B40E700668620D6EBDA96CD01B43C883DB152DFA16A75879678EFB0876242DCB99284250E2410EEFFC7D6E39F8332B202E70F4F718FA3B24E6FF012CB39F1DA502DEFF58A92B3BF744D149B2F96796D200F8EDC244CA7E6155EDE4356AF78232212CE52E3677C42D8ECC30FAC5AE1E1BD92D5B81340D1B6B025417B055CB0C5F1449ECC944D45B96B11C448F830AE1ADD85E4F5ED003D9D11CED3B823A7967D45CA742293045DD61F3670E1", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110C500D000039DB7D3FE54E9ACE83AE68C02E2D3DB879F41DA0963B7A4A25E6592014FDCC58AEE8E70F063FC5766C9045F54A28107E3F8A4EF203A7C2CCD41BE4543EAE0AF73FF35248AF455792456477E4FCAAFCAE903C755E2CFF500C7DD8200C4049990B86C401A9920126D811A67E256F7DC635E72F619352BA2D047D2BAA46E4A2E93FE4DFCAE3FB1C30E80C9EE292C53EAE64F79019948E890CCCB408EABD4A0AF7541935B1B337635E4AFF49A5643B5028EDA480662F316484C5A6E60EC8EEBC5FA6E24FEB071AF2486975E9239CD26C59F700C8D55281071F32BBEB5A776A0ECCF69684B6DCBC5E51E18127264A3AF425B53FCDCE6F191D123ACA9BF4D019FDBC602", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110C600D00002C924A1CA68F027FA6FDCDCA1027E0E746A9ED145AF4BA62E965BC0CC35A4FCCB82CA3577998BAF3CB46D0F122AF61F036D5DD4C276568D5623E213D13281DC2B16375EBBB5F267BA0FBDEFD709F2F373AF8370664CB4249FA02DD673189B249B89181C2430998FEBDB909D50E8E7979DFEB6EC842F506C16A13979EB5D7BFB9A545E643D28C5AFCFC3496F6810E2C3E76209DDDEB3F91BE8C429E6D88EE6F842475341EFD8B45C7D8DF93D4C3757C3FAE95229A0A0E37698A3BE689171FAB6B847FD6F4DEED90C2F5C60CE6ECE2663ED4C88B165DA423D15F4376E5F0C7AA55F2ED22896CB2352F9B05FBE47741A69535161F832B41FE7AF2CED1AEBF02C8AD9", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110C700D000035B477E066A252D1D14779CB676572582BA748133EC943843AD2E98CD8DB6A9B0AA5F289355DE3A6C93A4E18878CA509E495A966E2732DEF757A448FAAEBA7C2B875CD77C3618A73095777FEABF420779F3949FAFB1DCBAE7DEE86DAD31195F2032734D543441BFEEDE4C8419EEA398CF1C2C26BA65AC8B7F68705F9A79E51C69BE7607D90D7F4540C3A6AF8411081CDA6ACE9616358647092CD37E8ECC490F3A1D0B1D99F67F1B1D82A39079F6E0B4699AF15C8B5D35552FE8723B1A23C13225735CBDE09DE9DFA7640456D4E25A20B2F9F6F3902E3982156D734E931918B613756278F88073DC5905E480801E6E811A7FF68EE6FF85C7BEF2F6E455287BBDBE", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110C800D000039BF0C49403D0211F0C4933AC1991B638D58112C298CFC66D77BD3C16D7D6180E193BCE74779B147C5E1D5A16D4E5EF309A1AB9E3D71AE72139F53A31A3AAB5A59B99016D2CCFB7D6A444FBB86F416F0F76533A07A32EFB078CA01180521D018893C2F7427783B3E174175BE80325B0CEB2CB9DDAFCDB59319FA5A3520A5EF856B4034639D73632ACD97BCEA3A4ABFE6CD1A0B0BBFB2537DACEF7FAA551776951A1F8197B8A51F47D8653D44A27D68B0B0C0021F39C6870F18116ACD7CB6C0531E2D320B36DCE0EFB6808208A575AA76529EBDB02980E7942E62C91D454DCC85B9FDD1F57AD52DCF10F74D97FDEC8F362AFA630DA0F7500E766A3AF78DD7626F6", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110C900D000028309598DF33EFDA54EE05A076B5187EFD0C1DDE0B6FC2F6CD9575763DB47DCA3BC2CB0400F736025C704C7C30B848B5940AA0B998028DF827BDBB7C74565B036D410283F4699B3C48015D44DA92EB918228267A510F438C090F0F6F0FC188B535563FFAD644289035F6A04091F31F5D55F147234FF69F9ADCB0BECC3EDD2EABFBE4935DF9B133DEEF83247ECE259A1E9D4F1D1D44672B6F49CD065FF233CAC858F085959E0AA590DA00D35714BCE868A406A562F35F1632E3C9299A66EA064A67D489EE930F1B9DF447FADB633461F00E464E475F5E0EBF61CAB3E57F6E2B1B8D7BF184418432B0C24C1A8AA9FCE0C4D8E629CD5BB93EDF47C63428C38AD05EC", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110CA00D00003A50FB366DA44EC96A0D228439FEDA2C1FD15D8F8365BB34607021497B874469FE44B1494A9CB6BAF3825D0BFFB829C2E0CFF9C55C9423C1BF64255B0C07A644FF1FFB324BAFDB7266AFA82869A2AED2B2C5E1D9623203014F356A9BB1ED73ED3494195DFA0A76564A269CC5D36C8B69DB4F3FD8C77C1F1839C3275441C847E1D1BB8D45511B2C3E0C4500AC7AF6F66E986993F24DF8F03480417053464F71B66A18C4CC97AF67A1C7E7E68D62A4085DA84A0A689D3A2F348EFF14DDB73E7DC8BED66000115EF43F748F28D6F989AAFB720E4D341DCDE1D14CFA1CC7566EF039D2E3ECDF42A926EED5A10B2CED686B59CF7D7094E1277B52A1EA33BA6BC8E6750", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110CB00D000020C8C4BE7BECF69A8BB863BA236204991366B446D27C3702494287D836D4E98E4DEF45A375CAF37CB87058BDA87DEA49B6AA65F0F0D523D2D163609E2299F2100717C5083EAE30519B3C5EA2C525DF5EDDAA2B5F89E0AD4CE7D5D0353793FCFF559A70419C701DB12A0D6833FEDE16280BC26B0217CA7D64B40C3A0B376E28605632AD49855608EB8AA4505BBBC615F034CCE66C20A75D1A791179264EE0B0AB373CECE469401B9A2A74E39D31D3C59A68C5124FE500DF5E9C26308543702EAB3B62B7F54870493CD1EBD5818391FA4239564C942E2F9725FCC78E96E5DF6C16C8E9B3995F2C23FB980F75F4C05E30B66A0273501BA4D0CA51AF68D4D05C52560", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110CC00D000023D395D4F7A00CBE4FD3A6063A0933687EC57652472DBCF94EC9ACF8B32A4869A84977EBF1105C915CD499C0245151CEAF89DEFA61657E3182DE583944B3937B32E67CCCCBBD5F55B968446F74B3F2F86ECD6685EB901ED9B3AFD28D7BF1F1AEF43B670D1B01C68C2CE1D55ED85213AC8504695F0CE7EF9BF3457BF87CBDF9259116C7548A2F3D79AE513E4104F436052372D457D8CA3EBFD30DE14833A4110988F40537535B34583367621C3F44A3FA719062AB68008920251E65F291E9ACD92A3BF79A65F77EBF2048FB65735E0894BD8BE579E4C0850B2941C0B718971CFDD630753FFF0D731B6B0FCAAF9AC42A33D9EC19A5210055E49C58318448982C9D9", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110CD00D0000221E2C71947D072E18D7565AACB96CBA78CA6EACE807A70EA11F312CB5175D2F826CE7CB499B009549887C6DF26B7A865895C28274ABF5783CB07BF7EAAD97AECC229F67892B5A6084EC790742C0DB48782BBE8286F635E9ED3AF2F86FEB4B67B3BC0DAEA2904E11F813EE21140F468E2AFE14D149B437C0439F0CE3CE7E7D5291171DCC38A17B221FB97B57DE0FB6C3D2B1EC9F82E4175CA95514CC4D0495A5D13F4276F0064EF0CCD16D86890107A541B61978E5E9D0390EA33A373E030F45A4D355ADAE6A212CCA8766594FEF72B9746460B7FF5604069F9FB21AF57C457B40393FDC8037DD1778A6F77A0C8F91A4519E02D7D3753C850F576C54733DC9523", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110CE00D00003DC0F340D733BC6B4C02A574A18563D2DCDC3FE92C1EAEBD03454C2E8A7A3DEDEAEC136B480C177C4855397A8B6D081C5C5A498D315E6D3097E4FB6B33797A07C5005B0AB05C3EC75F62A9FA54180AB5F8A03A0825145BA4A9F79B1664FF657D6A4E1F3E6138226289A7A8D805A198BD960E766B40568394BA57CDA2930870915C109A7564914CAEE2118AFCC56828E7D16F38E42AD0257668448ECE1EDBB0285FD04FFAC7F8A6A9EED7723C7038A8438B16D85499CA9374ED873E10BB3AC82647BB8BECC99DF1C1B8ED8392166014FC1ECEC4F9289AC44F36F32940F45393795C0AAD28CD2216AC2CA309184FDC12E739A44D6B16157AF47B785471496E89C78", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110CF00D000036E6F403B918088B95395FBC66FD7814BFC69E2070638C78C42767D6EFDD8BABC59BC9F3B82A84F610E31E16381905B82641933B5F9C60A59606818E86569EF21D7BC25B0FA1AEE3ED8EB9C773E7496AF9A74F1C104AB02657D9BB24E40535879F1BD04EDAE471FFDA838E60EF9E97DDD1D3710C3CBF13F685E098AA36E90EA5436B0141FCE2E8BB8B11E3A3A1731300FB0336BC286AD0D7E8D807BCE8B32FA10D245887421C543057E9F6DC22E0D612516CC75CF2603E44C2BF14B9A5CDE3D5BA9BA2E1354D21C80A093B95AC9B29E6E1806C96BEF44AF9AEA6B133BE3A7548D7F7ADA7117BD0EBD5636BEBE0383D762811F2D2CC9F723D147A8814C1DF9BF27", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110D000D0000237A751BFEB58EB850D7F8B24755CF7951264F48C3F0422B577CE0E72E600894A55189D360ED6FF4786E903E1A303464AC744CA8EC3E8974A0D370D8B6B4B36DF799F8658A14903FCB65D8F9BD08532C3F1ECF9E1A98962E144FAFB114378C1CFF758243E06BF99987FD40D29B5DA715523D1F7830BFE70CDE008990B8AD943CD2BCEAC0F79A7940B3D270FCABA83842B8B37C365A2CCA23F553727D4DFE117172A5170CE335AE1FFFABBAD677AF6CD787F47269C195471A3C413EB305BF47FA047526C8E9EB28D4B825F9B1087123C44598D41416FC557B54C671401B191893B676B9B088438100C1E961A7DC0D310807D5B3A34CB12D93D5AD1C62DB376FCF3", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110D100E00002874789C9ABC57955BB9AD3D2E34AF4AD79281710F0E114EE95E3C222C57BD3E8CFF0CE91C04803708DF2DEA2792301D2F2F1E538C60A27CDCE63BBC3B049F62F1169822382534DAB2A33872BC14F5A5DDDCC9787D3163EF9FBBF67B0CD693FE3A8A54C44A7B9ED6189F889202101BDB366E41BB7C57AF13F95CA52FB6C26A67D14F7A7E72942957581B4B9621FA8DB9358D062642D560365EC803EBEC0D666E6A334765547C1F342BFEC5C461E66FD98AADD0B1D366430D46A9093F97C48700D32ED15E3BFB02D5CE4FB2E18F431FC3118A37135BE77A375926CF136DD5EF150D6D52F49C318C4D6B53F89F15392AFDF081D763F8F43E1EC96CF35FA5B91D48084268B903315DE303FD6F3A4C65FDF52", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110D200E0000299414C7433A0C93EB22A972C636E7EAE55C8293A86E1F286C3D8C8449EA8C1D534380597A8BE5C8305F2E7A1F512403C505A7B55FB45AA00142C9745309D9C2B7B6C9C2CD91955F538F7580D2CA823C44592E2E9CD7B000B218A51D1EAD5AC2E14E78F2730B928589181987A43073C2B7A09FFEDCB61C8B1B7BA33A437433BAB8A71535BC62D786EA286526305A913090ACF1DB9A8B66751B963674AA02D5F679C4664968A818485C213CCD6F68612F00A787F5EB066D308D82C4AACA6AE838C9FFD8244F14048EC336B60941BC0909A555DA400069D0FF8AC35B2914D21A1E7D5499DA91F4938077845985251205D9CAC442C47B09DD0DFE73DCB554C20AE53758C14953556BECE0C0DB80F3ADD5C55", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110D300E00002E288C2EC9B0709B391868522D22B3589DD6E7C6255812F4250D10FEDAE47F7F6EF21FD16A86CA455AC281C6086170A08459FCA32FEAF82872E724A0D14AC586F30B882AFE7E767F130465ABC2648CE20FD18EA21A26A9889ADA6F49C99E4478FD7E6CE88A7D07AC737231F4510D8E6550C92FD43C558BD2F96DCE3B6DC7C12938CAD0AB1EEA96E83F75E6611190846029438875AEDCA664CCFF01F08E2FF0223A04696147E8825DA71472487F0650EBC55D3CBD0D5AD84425F367E18D3A5E94853CF8BF3C4E035685F2D92BA2D020198BA39ECC7948877401AEA2F1E1F2CA13D95EDA2F01B8D5928335729A9831446B9CE976E80D6088A1488A15BED14E65D8BA55B4C8497B070A29206B4A863999C54", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110D400E000032E8B134A68256EFAF38BE9736B2147C1481B86191B57DFF28678C06F0176F467C8BFA5D3985D341D136F8E36D604951982D4E7C99E336ED7F1672FBE76E4BE27A4BFB2108C8BB08E6600451B2540FFDF8A4679AAAEF9BC8BB213B104BF0B4BD0CE0A975AB7E2A3AA82AC4EEAC0C5479AAF0B8F343BDBB0F259D5BF8C5931802A2F589258B55AC9152DF238A4EC3EE54A5E1BFF9BDF77354AE22B65750BA89A1BCF22FDEEB5840B2C691361B1C5A9F6E131E1F9D5C1B5B5FE277B2DF8F86F6EF036BEC93A19657210B837D81A6553DBD72060574F453556E1B60B06A8C534A4F01DDE2567F8D3B76817D5035F4E7E3181BE5131A15F08E63D1E8E4C04256470A594B3F064798D30EF80E916F63C281874", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110D500E00002C91FF4CF58F7FB7149591D72BC9E8F4656DBBA4A83CA6ABE967213F88F9C9223C0F22DD684CD2854621279B44BA903DB2D6157D1EF9642E1A6718D7376EBA39E8A63D45EFB767CCC05B4F0A69B26888379E0ECBD692234DC83D0FAC38DB38785AEE8EDBD3488D3C67C4F9A2675A2108630CB3948AF2EA46CB5851D16529FEE3F6202B5480367A76EFEF69F99EE6BD413922306307A20DA3A187DE97F12996905AECBD416AB0AD4E4BAECE25A786374CE1356A686D6EEA13EF91A4181F35C00759060A237B2B108986A9A98162C756E28B9ECFB3F44FA0FB51EDB0324B338C36CBAFF5A797DDCF84CF2E4462656C81E3375D56D0964987F1F0841D05DCC846F532FFBB79F134B5AA20389F6D71E00FB8A", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110D600E000035CF7C5437E304CE5B969EED9185075D8E29ED25FD490207BF364DEF5CE68DC197E513FB8B2A911F56EFAE101B8DA838E808E53CB7AC13EE2DBF518BC2104FE795CA7F6799858E9C1CE35F7AD458D0197088D389C4B3E00A4C6020DFB4D1C0E35C0A5826CDCB43B73877CC939202E5E6CA277DEF70E6C9C7BB3C375B61138EF2E873FBB14AE364C15D7EFF641B06644EA4979A9A2B8EDE9E5047E49EC9426CE9303B5372650180D75333AF7E5E875E2A45236FBFDB6FD9531FD5118F1AC2F8AEA78F749CC79D957FAFB640DB8211809EF9CB2CEA01B286EEBCE0C48CDDF2C9F15384D95C1679B06278A8AB366625F353439029A27337A0CC54C520123209A4FE4150502CC647394DDB451CD25DDA2869F", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110D700E00002430D230A9CAD71E9F9D3A5F8EAF138319F667623249AC2052FCBEA5F73941917D148CA7ACE8DA8239CCDFD1B794CA098DEE4632B93D1A9DFEFAC9FDA9C265D4EBD0086901A846032304DCC90AA8C31C53871794D3F7D30CF1E16E004C76927266D807D019ADECBB7794243717E5AF91BCE62F77E0940ED3D6B7A1C24F572BF3C784B1D6E2DEC0F037D9210857CA6FEA00DAAE2485CBE0F80F81807DFABA84DEBE624CA0D97F37CE4FE3B151A45CD26FAFC93DC2AA4263B2CAA6F9D83CA9F0BB3F4BF3C81041ACC941F06295AC8DE7F347A897A18FC3D169DCD88FDEE5977F79AD1EE6E78BD68A8AF0E9C30E2ED654E56C9AB94F12EB1685C542AAB112C312726EA986A9BB6832B12E1539DAA26DE1537", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110D800E00003861218FB13EA4CA78529DBCB7A581070F021D0A844DA2FDF13D24283B0685A509D1797F50A12BD03E9B5FF789FB13B91EE3FEE500D669C52D9693679A498504804B71FB29B985A37151E8300235FDEC3954E35613F8890C4052C0DCB7B1E6BD352C5FAC1D7DD000B1679242FE0C73B1120144FDBECD433E4E873EF0A836C0A2FD7071700633CDB9AE31F0A76BA97A93481C5A7EFC072148665AA5CA23D3FC364A110B4873DAA29B728E8B8F87A75A462F2103BBF92CF6DEA43E601D55641EB5990ACC3AEF50DE0FC2755BB6E2620963544BADB0735C5A0BEFCABD3072BFE90FAB37E26833D461CE6FE1A1B6905E9324DD96313BA14CF81439A6FAF18FED630DE13DE396A44A2081DEECB3052F0746CCE", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110D900E000028DAD0655EB1BC9185E2AF3BB6CF3678F0B3A5FD2F618C902CE75BF775141354FD5BD633BC20941B94EAF245BA3C7D70E114B9CC08DFFB5F1BF58770BBC10DF2C4A2E0161D84DE3B6FD8E1E171D50581A5A9FE9EDFB28ED19A4892C71B543FB954F4E6DD6625E535BBC15EBB422C705800E17E8A96EAE188DB6246431F33E40A98500C92EF5B00845B769BF0BB26E34DCA37FB4E261D42650AA6EE851BC38C0C434F87D3A89DA5E94FDBAC9E80BF254CE4EA8B0F4B9BD373FACCE14B0FE9A13FAB1FA37A651514E36DC9EEDB9861AA60D58C718BA93F4C0F454448578F76B32EDC1CFB85B56AAFD7CA0EE5777D48E0F09286C15EE0932D509C1FA8BEB8D4A186CAF50B25117F95BECF83778E1A091154D", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110DA00E00002A7F9FDA45FAD8E39A51FDCA87C4E153B1DAC41C5AC20BD041D175F7F07E1656011302540E6672464FF5CAA3BE51A8C789C9AF0B6A32757B9AB5C90DD201F45A09C748759F304ADDF927F6A277BDEA466E03F91AA97C2DB4402C12E53848EFBD445E34786070A293ACD07569A110E6CFE7A321E49C897CC5E58B9CBEC256D4E8D054D702982DEE79E5BFBC0AE51A7EE3512AA23BE2DEE1202B6DEE6BF8ACE202C182D9E70DD82AD37C5CB78090EC39039DE37810F11737132133BC2480A7E57968171A44A1F743FA6C278C6D5B40BA19365E0C4CE6B0DF0C6695B1BC4689D6AC9BCB370B4A496574C01C637657165E49E4299E9B1168F0BBEAB7452ED16FE84E9B71D33791F9A45322A3529F5417D6058", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110DB00E000030A05C6C820B781902A81E9D0FF1C27EE02499DC0B2BE14A36C5B3EBF912889F2C67A04A9B23955A86FECDB7915A913F4F4AA5C04DF9E80BB7FE7E553704BE9E1E2BB383A0810315C2C837E2CC2F150D5BB7D3A7C6D3F140BFAC0EDA927194627F6ABE61A2825DCFAEE8FC550945FA0CADCA62BAD893BC275ED7F945B769ED535BCD1BF8B8A61AB046B66917BBF319C90752D7651873B6634A7210B73D94D1F5286FA72E02F16914832C471D1B2FDB48664DFC9F6F029FD039E1239D4172AEFD1DA4C653BFEF5EEBD7D52B67854700F59A3AF251147C23F733B12A19047E590A0BA439AE97C2571CA9B226781B9CE110757F233978D3512BD6886B1D3738D43E9FDC9E7F01676406A1A0A58461B3BD212", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110DC00E000025565A4E3FD0680C860A17A56023BA9D59FBF6FF6FA63BEE7FD3EC8DD182D872BC36AD38766340E3D8C43C4D6A2B18C6364104F77C652EA4111E9EBED3272E78459FD98FB8C6B766FA6625AA79E5C8932D595C06FB981363E876DDF725C728E05000EF33E04F90C007CBADB0997B43CBA9F991AC49AC64320FCF35B1060EE64635DF35A1BC0984FE526A9838E1534B418A223A53AA10F6FBCF4E676078402812379A6CDD1F974D2EA31F2C4AA2DFABDC27DFD4AC7DDC25EDB5B35E919B263EE3E37E91D40AF80F90E1383F5A7447E991540554E8C35EF34D17FB9CE3C66B444D8464A4866766235454B9A6847C19017668914BA09405328B1AF92557A81BB203BC3D7D23748C3E599A16DE1D6A6144211", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110DD00E000026EFCC3F982C57E8B485B7A3D3BB26B21F1441C8B811FCC122C1DD3CEAE1D8534C951BF9E1197A4BDC5B769427DD1F026768318ECC94ECB412EB6BE019186DDCD8D2C04ECA480A1E03DA41320C438D3C11C6A2F9936441EEAF1D761F21BA4742D804A08E053EFB3CCF7BD4E414C94FF19357A16B66CD8D5AA68F38C482FA735445494D5331E546E928079A235D3DE8AB386554FF2DD4C6815B0A177CC52AE0042D357995C23AA755483CDA6493EBFBDAEA461BCD7CD5286C43030172A79A183867943B123524882D1A5AC7D0C4932B15EAA1B0766FBBD607031ABAA228DC7EC21D3BA6EE7BEF95D1321745C51CBDEEEFD664D8A3AF9D27386A2E9EE7B8BD9A8710658B00E8AB1D7771C08BB12263FA1BF", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110DE00E0000343AF6865B5FBDE52876D933E55F77F1296A47A5E1A8799A47C5AF986E59E77B4560EF7E992D1CE3FC6EE7BF5391E454BAAD06B7172678B0449BEA11A0179B2E7BA8270ADAF3B66CBF910AA0D7856BF1F8E93A4041974EFCBA0DF88AA7D6F43C9DBA6BC8682AEADEEE4408EC7FB3A4EA55A3C583A6DE521B7369E1B3464CEF26F6D539570A6589ADD5C26A7387DAC0E2EB474AAD46352B9C7F538FC2358E634DC0AECACF888C447F122883D807D16370AF104E9D5505A23996C1D7860F950105894D069CF461F3D222CD860AD65CA387F95999E2CD6CE895941647C47FD2C8766D11DFF32EB0C9B4B4C7783AC993C9A919F0692D05B642A77B5BA8250647A2BFFFB6D3CC9DCA2328000F765B7563E6D7F", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110DF00E000034D75FFCE1A8F6E625F40129DB189F3A059910598FC32F825BA13D47152A185023BA1CC995552B60E4892109C6A000357228394076EE55472A86E5FA9325408B7C68184D9B44C64F5C05C17F48FFBAA98F561BF7A1E8097B265B436D5FE34993FA89E927ADC12AF64334661F140FDE2C97AA79924A18A22568A83091707BB95B4C37609CED25C2AD1D18B86069427628CF68787284E83FFC2AD8F21F7704B8E55B6E5BC9E46871E619377BFE87F48B9D14B9192AE92B227CFFE7E5D3E29F454CAFC054D1783902176245565A6D7453CDD4D37B5F27EDDE95FA0C8C780F589BE05570DAECA8452C572D062FAEFAAF9367F785F69EAA847C020E45EEF61460B56A76A41E2C9994F03BC22F2F641EDB7C96D", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110E000E0000268E51D240BA4BA92DCDAFF7507742AF60E1A66F35D12F2CCD0FBA923F03E15CAD94EDB679189FE6FECCE4D60EE63253F0C29407F3108A64BF23545F533D772CD4041ED8BC3D485580DBFCD3C8D9B61EE54404C036313E230373003C2DCB7647D543C7E88C0F473282212D6A09E569432D7A57B9B208936E8BF4C9F1712EA1BE7F178F44F19CD6A805A4CC2562EE1BA06F0C6237B49222DA11C06D55662927CEF739D59835117C6B3AF8376E4E91B884C056FBE530E517732D25A91EB0B3611A1F5BDA0C84BC89A1CDE9EA54AB1D9866ED9C2B39C3511ED638BA7338DDC879A61F8408EDEB8BB413CE0DA753EA02F515D69809294D22672ECABD787003BFE0434EE400E01B59CC67D133E74F310B7858C", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110E100F0000317A071B1F93DBD9DF65D23AEA1B0507E6C810CB402A9215B28FCA19DB066211D421F417BB76B4DBE84C91C4190616E017107F6F0955052D2195C92B006012378F77655614EA75A24232455DB11BC59A5A2E737C027A78AE54C3C15C7321EF93FE88BF5F9BA11562334598F2B09434E95733851E00838D69AD23E8EEFC0DFFC96513BAE3190F33F7EC4728A9E7922C210E3CEFE38B10743154EF120E857D77F101EBFAD55FCE739FD853698AA17125552077E2F32D31637AAA783F4DE7DA6870159CF25B53B71E13D0CE85752C35127702EA4D06667A27A0697844921FEB709206B96C624886E653A15CD5146D84735C4D37F6DFECF1656556BE691A988F5E17CD9D1741047DC0094FC3C15DB9765596CF7FDEBE9A3F4CD60023EBA2B31E3D8FC", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110E200F00003B259955A689FD18F88A2566727B96808BB4D7F23D78A67D1DBF1D02A022B65E026517ABF43F907AB44D5DB89769B2835117F2E528EDD3C0ADC63911FBF667F70294F98C3FAFD6796838AA8FE6F719850E17ADC34533FD8ABD4F91F44197FB2AC9F3F6CE44A9F73CD31AAF9C185D2A640B9FB21108EFF19E9F4999275EB4F93E2A8ACD19F032547422F161B0C3CB39542D017C613E202A5EEB200C94CFA2DD664E406A7100B5FCD00B9DF5EF2524649705A847310F5DDB7BC1A6C0E14B8803B1BD1E837028065F6E34EB9A0AD108B640D6738227B87F0DE6ED5F697D64BD93E69D301CD0C0B760087E92CD5AA3DDD5D4336A504994958AB099A62DD137405BD541D2377A18D21D0D6EBE4D264FEF8CE0DCC4466321D9A000DFF4AB91E99574D95", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110E300F00002604763A9D37B87F55EF8E20D856F9F760247D76F8875A46725F696BD234FB142BCBA858BA97EDC19A4978B288B0B19665C3AB29506DEECADA11F079633491C4E8C7FBD97E8140ED6737F9F2B6E9E4EC084EFAC161BDF0B1CF63BC31E7BBC1912585CA4EA147C3134858370FE15641E7A7666EE1B48DBE0143C89BAD0B257BCACE0C96646AADEAA42A2C517607E275128068FE8D443504D3E15EBCED18C1346F98122FCACB4392AFA3CF0DE4D8794D2CEFEB0943D975F3DC23B853CBEDCEA628C2C38159B51B7020712E79AFB1C18C52292A611A9108F94C30A9BE97EDB347E57C42FE98CA1A10138F51D33E08E6455121222EB7C29B0D7AC9D5CD652E794BA841A846B2B410301B0CA4EFB6D995F9C46CBBDAFB5CC17B505ECDB8CB9727EDAC9", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110E400F000026E8A4C7D8ABF28D6687026145F0E04F09EA2C3A5C1558ADBBE52514EB67105EA4719AF8C8C3D0C25C00FEC185C7012C212E00A7267FC2E3EF173B67D7F5BB821A62F6DEE64412964B67606FEB940FA1BB67796835C108B6F95C41604DA2DD790595CF8237E5520F47BCEAB69F4A596C161E2C149981981922004738640B42A0C3F5747D798353BF0EF6FFFD76C8C31F58EBE7B39596384571D25A7387249822C0D5443362E9AA84091BE3035F659789351753C29CC52D350E24A8DC4E757494574C7BFFE72A055C24878A738E5FBC55B330D673C76AFF32E88980BB8D8E9CBB2470175FC7DEF58C6E0573BC72F2060B2759A3647BC0C22167B71166B986C579E50922695334022555FF91863EE9E62404C7F3ADA04E4D0494FB4F407D6A18930", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110E500F000026D9996DD6BF80F67BE4A9F29A3FB7DF674E07ED051FFAE7F30DF09C904863F11A62053C84DD09948E6E55E058FA04A05EFA19E00F647ECE68E436FC0296A25371E27407811CDB5E6D05B222CDA158DC99CEB4F45F33CD885EEF739AA292B38044FBF0655DD590AEE54360678EED9BB613B36C381042C5FF21B52AABF1149AD213B173D1EB007CC6648D76A2E8FD38767512F26E9A207DCEFD218D0697E8493F6B49CE82D1735B4E1452A45BE73A7718D90D5DAEE0E040BF359DE137DCF6B7F3426BCECE7651C65262783703F5DF55BEAD40706E79436BF66A89B37E7F1167D62E66144A7D5A6A1E21BDFD4AC2472A2A28A5279C05C39C6DEFFCF289B3EF6199A83FDF11271E7B2B43F3C7EC7459471E9DA3B59179A25461D420ACB4111DAC958", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110E600F000026084ECA8A495C3C13AD64E6625171590AF316F9B2C67FB0779AE5C03655A1BA0D26A6B75DCA01679ABF393FC2C74A01CEDFF5479F17B2360D1CC1852DF9633727DD971B2702812BE23EA8E7CB4E3E1125EA3D56238A46E04B821C6FC372A5BD959496AB0962A4F583FDA90A24B98494DBEF30D4C50B6D9BF78546354D67B59411F4A01136CAF76153F6161EBE366A3F7F7282E21A025D05B09F9FE713E3075AD947D1784FF58EAF4E2B056951519BC3D4955D1327024C1AEA6A17E559EB3A2DFE9F69EFB6D1939CE52039FF18E586820A9770E5916D096AFEA2EC02166DAD49F5308494221F6E5BB076ED886F101EDA5A573B41AEF863E21F103D7CAD97F06C5DFFD127ABCFBC47A4BD20FF3D02E4909E224844A32591F3DA3675951C5DE0FDE", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110E700F000022D77CCE169295AB205AB58A79E16E8D7201C1CA95B427030F51F0804135C1FF8AB5B7843C2B1B245056348E3A8E27DF22053EAE3A2CB4A58B868F9E0CA7AE20B3EB5076F3A44B4907B24196B896D85B095DBE7D01A4D2C793294B06B40BDB718A46AFB3C94F84F967B512913FA7294BF987F8AF43B837A8DB288D51756312B1F7FF4D5546C8FF2F8AE2F321FD03601DE9B484BAB2270B354624228509133BA0DFE40F75EF541F1A52D017EC0D1EC5870292B7CAF812F03B500160E0F9FBCB5D1A1372A843558775513D6A72CD60FF071A781A48737E8EA5415740522ADE9CBB7C269420A9B5D3449A2B6612C900C86CB89EE6E315F30A2849F115846E9437B1B4BFA68BE2E8D71C0BEC0B9FDD85ED59B69DFCE4A3834229688009F6A09457FF6", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110E800F0000321FD479177C2B8FEEBA99883FA6AFFB6C2CD4A9047CC11907AB284A94FC00D74ABDA607F96A9CBAF287878F4AFBAA3C003D1EFE178F226E0918C020F5C0EBD008B75CEB69FEA05BBB046476028E8E728AD4DF8F49A5729002FFD3E5CAEA8F42201B1A915F456B62A13BFFACD447C70070D45DEA3E5F4FC9EB6FEC13DC581A6E3CEDE0FE2C4F787C89FA84DC489E5826C445A5FE569053C9483FB9E1DCE7FBFBD1E1C40E50EC8175FF5C3D66016FA9CF31A92FA065E50664E608D18CB3ECAA1086E07762FCB9E82BEE6A79ACB4961A2C0FC0D9663F31D08E87B265286CE4883828F0DC596BA091041234B93C96CF11CC44319B80D13D725FE0ED2E740951FCB4F6B765EACCC124BB4C9B44CEDC67596649193AF9DB9C0B7E965F89F43F356C876", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110E900F000025AFEDEEC295EE8B50CD8509C578AE9101F26AA2FA59E9E6F10E39669755F74A672DB6FB3C4A5DDC9FC2A8BA0A182ACB3515338CB3B6C065FF66A8D92154E12B3F33E2DAD4D23485FC560B7799F01F8B7B33317C08E8CFEAC1B029AD63988A2ABF53B125FC15EAD6A2572D33CEA494140144106FE42985564F47615C5207B0FB49351A9C4875A62EDFB11862D4EBD9E6CD78DD8B2C66989E2FF5F2A6FF0F14B68EEAD9E03405A1161C5576DBF3371F362E548123F761E47BBED6A7D8232128FB8E8835D18CA5875018D9F7CEE74E47FC6C034AB4C58E29D14FDEDC45C2988D5DAA003F5181C6EC35B8D67E8B2FC1A445FDD0E7ABEA5F2CAAA3579F52DE0E2E11C5D20F54017730FBAFBB49487A388B1EDC0C31BEAEEC022CB339513CFC53AC34B", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110EA00F00003B011D9DC1F3F93AAB21A34AEAD3063CFF1D7370211553E0FEA8A3DA3C2D68450D28C97C35B29EA7E47F60EA9430902A2BA8413C1C575F27A6622A90E16EC10F0E389BE0B7CBF6DCC4CA13F7353C11A637578063AEC020AFE4CA295EA32D9FCC897D977DB3535A00BA697018A8888DD0917DF07A118411C8939122553CE90F1FF7A13B48195110F781494C24FA25356196C556FE1DA230A7534DF8666D843A0AE1A65329475A4A675FB9FE24D9F3F82CC634B99B43557BDCF9CA7FCE561DEAF2D2819B6FA874E4F1E089DD575F48F9DE9B9BFCF475360C6BC7F2365E9B6E513AFCEF4ADB10A6AA9418A5DB74ADE690A3672B8082D0636273E827959B7C086BD87581D7D5EF3C9D4E3F7AAEB89427453C76F684F40FED50B9117697C4AAAB81452", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110EB00F000032CBF46964DE02250CC0C14B49E68718BA93FFF94B8A3BB3AE90BD89F3B887E973FC8FA730C9C041587FEBBBCBE56D98DAE5BD30CD0B13D2B9FD8D011A667F6D7570B9D4CACDFDD80224C0EB274D77A772E0BAD93630A852E8C2B4A22B2CF26AB865F86AA8C981F7A57F629718104A1DBC89D658489114CF0DB43B4F241D51D11B43EFB6F1C205314477DAF1391983DFE89B07971038F83136FCEAD9606F58BBCD98D2C798E47E44A8B395E8226CE874490265D6CEE6AC09C004A0FCB38672158D695A7A6CCE9C0032285A8E024B619C0F5FD01CF8623A307E47EEB19328B5E606263CB3485E08A932BFD7BBF28E57EA6E46FAEA7859E6372B93E0F27E5EFC32C0B2859B9C196965E3B8799D7FA892AD5EE2162D50C91894E51F9DA49FA554660", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110EC00F00002461FAFCB7CC64B2DC20437E318A1364C68253EAC828968EC4BCD918300D1B875BEAF5C7810B071C009F055E9C4C9C22EC2B9E3BB82CC4E66E85E812C479F1E5CCFE43938EC8ABE5FA798728AA2BE062DFFBFC037C6F2D4FE6762E6A85DB8D0F8AC295FE31DA743B36DF7FA26E7D1B1EE5A2779BEB6579152A90522469313E7049CE2C114D8B9E66483081109C3BBD87667E8A23B3061735C2DDD9A268956E4B275C6DF7384124B81560998A4F4EE2E93F3F244E7E935CBDAEC6D87F72735C01A8B7EC8F4463EAAB4432D0BD2A1A3978A762CCEED317E2B7822EAC5925FF282F7CDA6750F52E6DCD39A1804A63C70B250C982F33F0157CDCEBF8227D03205EA1AE4206A678ACA5E5FBF013A757D86FEB471A2650D58DA54475F36BD72C3D5DC9C", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110ED00F00002BE57167D05A8619169CA45BE382145B5E2DFC2E132EDD80776260A79228B688E0416FC8D11AA72B48C0C73D7FED8F10775C8457474F987EC050697767F45EE4D7BBA529713657273F73F6791BA221BCF2A51AE89319FD82ADE5C3BA95152580147D8470CE43521B1E6C5667FBA4C43F074F8F3178B7346BEDAB3A359AC30F39506BA687F74F0379909E19F373285DBD04E55F1FF3E67773673356370941001DE88BD7726F5DDBB95178E40067F95D5DA85153FDFB0DD85DD50764EF42E9622D985F9AF4D66B06B5340DF0F9BC0B56E4F6741A2E58B5152F2AAD70414EEA3B49AC7560898FA8F425C54DAD4608BFF2070D8DF31D3DE8584FF2C14A9F1B6342EC5C8F6783092C46FA63205D82730DD3FCF92152295B55E624C786AE5A2B3C37F93", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110EE00F00003DD81E90B3FCCE5A640FDA8CE916ACFF9C2EFE4CFCEAD77C06FDAD1A620D40D9FE98FD172811185097DB67D95A8F50558703C5BB08467C38B5A364227DB56CA813EFBF10F319A0377965DA99D7A74778444C072FF2F557891ED54213AED0A239450019EEE8A1C0A20FF5CE17B198DCD12190A32C76C279AD03A73AAE743BAED77F96D60A08AA9D7ADD90E33BDAB2E1DF9CF40B2972E4FDC857D51BBA84252C75EC31687FB56CB262E9BD037BB4194EE7AD8C613C67F3399CF6EFA8944A4C32B223197321568158985EFA5618AF3144C56BBC47E77A0AB6C38A8E6BA9BD8B26E8300C4D668E725330BB453935659723D4498F3B6A2F968BCA175A565BBB3ABD25D20874B7379E80BB58254B228D25C6995032ACDF39A6807526CEB51960FB947FA", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110EF00F00003B6BA6346A89A68498FC66F560F4C5E271C74BBD972FA04D653595649F2ED4A58451A1B8D0ABFDD06707F6A3CA249CDDEF82953033B3404D08CD47AE0EF1885D81B6C11623B22753B8A73F7946CECCAE95487BCA5B844E5CC39F4C2312844CA064D1588B5D179DDFC96D1E9CA93FF740C6063F4DFDCFF9294EA98C58BFA0E08C265BFEC08AB5DFBAC0C768FF3BA8AA22D66FC653B64550460A3E2150F9893BC03095887ABD8B2BF3A041BD26CF799EC5AC1E9FA61440F74DBB2EFDF8D22B96A38CB881EDD8851BF85A6A75F560AEF277C4A00740574C38C26D6316AA7BAD25F8FB58C10781299B062C483B6EE39F7146FC4D93F397C5C39DA2D054750618B3DB8A113DC7DED45C49EF70142A10EC4F5DCF9054BF28C35C688E02F68142B147673", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110F000F000032C1E37BD043BD3D6F0FCD3F79C1E2034B69615AA9199F3470A78E4352F8A6EA566D6FD6BC6B902E1364A723B2D1011707CCF3FB9E7F46E7349407D32F61BD6050A8E2B2696F28F2F59227B900EA0BFFB9040609C6A70AD8583B2C6908EF17D64434CDBB7D943B5F921C43CB4F2B5CE807DF087A6C9BC7CC1187C68694518DA5AE4769CFD347ABB0C14F5F3E01D3D823464008E4A561B137E4A6D425BA39C36EE5E43708B4BD0B4E859940DBCEBFCF49593F4F9F6D1E351C8FCE932812D9E4F9933BE204429D749765583BBAFA0F7DA844259AB567165A74DAB24A8F1C3D8BE796B03E50237D7747180EB140F127106CE33C3C30300D166C443C81FEB45D0A8AFD9F002CB5B83F75C6A72E8CAFEF1DBAB8112DD18C44C1DC7160648DDAC4CDA6A", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110F100000103DEAA08E0C61DE694DF6BEB2F051169C20F1BA2507258DA51D5C053852846E19C589657634CD0393813A8827D7902EBEBADEF32AF67C594B2190F04FAB66C4938934DDBFEC24593131071D7645EC8E0E94FD92502B5263E1CF511718B96D0A9DFA2DEEE683BA99952817848111246CE2A839E293CD619F6351BCD330591EFE33EF02DB3EE1A058BE3FE1944695223AB2C2888E61EB86EC788A6ED4C0565B5AD2621C7D37672E1B2B54AFE02CF8F2DB62B7ACC271D34E974EEE3EB433760F2BBE574191287FFC34BFC35F07B6A4B91FB4E024B6B1D92C92070D7E710A67D119ABD2828D7BD691F788626D97FA67CAD6D9E6BB0A0F3DDEADDEE1EE5F70C3A324BBFEA013E91807E15BBDDBEBB3B5B30998DEF61C59D34B4F042018BE7B8987A7D625D3FBDD40FEBEE9AB166A6C4C8620C99", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110F200000102AEE108D2728235A54E78A27F210D046F770F3587E6A2A2D37C71A0E8B081C5DDBD2219AD3DCE79830CCA79E5D8F74408D4B781278CC3D5E03F0B55A99DE0A4B7D0F278FDF2AEE107B2BE159F8E338BD9E55565DC06E5A3461BCD2DCC3F848F4D59D02491CF19DCEFB54C3A6710903B4DDE6CD037BA71ED8E39B2499D6447699CCD8D759904184949AA7281CC8591CA17E0971405A5B32449C875DF7C2E2C09BD0DB3226BDFE8FA51F2AE62E5204FC0B2F73A469545EC9154273AC05FCC2E7C37FEBF097C7CEDB840E9B153463F6ED5E5C7393E6269E6F33E0E23F33331F3A792BE2B12409ABCFAB4A907DB6EDF7901058A93FCBBA254A7B82DBEF7C6C012C11A6070224848EB58CDB99F7FD693A7FDE33EA7A96DC82D45AD236FDE3B53A19D5116CE1CC38F3942D2F01CE72154D864C4", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110F300000102D69F71390DD9A5CA0135F08466435F2B7ACFE6C1AB958189A8030C08F40FB694D7870BD6C2757DD895D200014008D45D970BF345A691E8ACDA8FD2989C01A6BA4663861B685E4CA752A619FD41ABE010159436D1F6F3520E2591EBD1367A288676CA9C0E6CEF68816BB4238816CBDECA853FC290CCE25A7DB462460E38B6C537CAD2B0069C74B89B72FED2E2296F6183A1F0864EF0B1DAD5B5D88189C429DFDF3F632090DB7DE4CEE48B44E6981D2D140E4E84F4F786B5265099EB30F5AFA006ACB733407BC9CCD2332467D48159DFC82CE73E19B182234A112FC5680F3709B39F0BFA259AAA86F003D34E41241B4CBF4446A54061AB0AA51ED39EBD3B71302D16384A1567FBDD447BC6CE0278DF913E18096446EF8445F29D4C53A1A2C0F735AD98A1AB733B5EF8596E8B9299990682", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110F400000102365F73AD30A8E109955C85C9460404DA32FD594BA36633D210C8836408F7AA5FCF4C23F144C23A962486E26E7CB5B98F4726B597121A0297D5A8399D4E971514F914322DD8D7A447A4A5D7940143B0ECDBC9A3CA47DFC7A955111ECE56DA8BE6ACE62F5A4EC50F61EF550655A3D29F6D158E1FD0AE208A10A2D7ABAF0BC48B258509EFAE1AB771FB73272AB7559B23B3D15F5681BD10A4A1E0F11B5A29A23010066A5E74A15DC169C787F2F03FFF0AE0DCDAB3F0771D88BCB5F976733A2FABCB16429D04DC6FD165C975ADE7CA131914C0422703F3206FA119A70E898A45F1135F98276C2A979AA23E70E3FDE65906DF0D192A4EB5EAA15326435BD7C1DDF698FEB4DA19DDFE6E6FA9D2C5C098C9497DE0F8999169010123E780CFCE20893A2CE55DE31DB329A070D2DD14D40981E646", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110F5000001030A549C6B2853D742F94F9B449B593F7860656F9236A32329B107FC64EDEEAC9D3DFF7690F0BE1DAAB0EC5C55C3F5E1A202494FB2C68D5DC724FC035C18CF7E4AC807E8AD8BDA4C8B2BE032D5344488EFE16EBB62E96E79906A1EA09318CE31086080E7C236764D47E6FE1F9257A0FEB023037C1DA15298A7A6752BBB06358760A1F69B1C7175D76102B557DB31A4BC6609A8F602DF1A447A5E44F58E37EA5DF30C61D41670D1E4BC27882AB453CCEA2CEF238A6D45868A5E3E519961A4A0EA0141EB63ED91562B0888CFABBC58A9EB1FB9646AE09BA72E9144C623B6A142826B5AD13F83C0409E74FA9E408C121D70AA82AF5C82A19FD40CE16439923FCB4DDC5A65D5AC5AF0BBC0D81DB7E7CC1D896E9FDB6B68F820CAF44544E2A9910BE8173AB2340683CEE52F3101F12EAF293CBF", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110F600000103AFEE9B8DA0F15F0B22173818D5FCAC7D35945BD361390B3C1E6D8DD441A1050F4FDE6AD2D210D0546AB2942AC4D1E84792C9B576E9971B15793B03E15E17633BD68086768233171B75F58D173B4C9049598727D4B5A6D41F77B307F3513987C16A71B0FEDCBB7381BF1A7BA37F074A15D3A4645FCC6BD649F56799AB4F06CD57058296417F2872C7812D215E6CFDA048141A927D78D13A6F23183A4EFA25AEF044C278587CD7FE2E79B641E1CA8DC0F7CAE40CD2BDEF6C17D46081DD183477D9CB378BF724F0A65B3511E8612CC2838BDA043DBD6C74B5678828D5CFA7A4CA24CDDB9CAF78E66C9CBEC9A1A5BD5B31E52D25787FDB19C73E72CBA18E665B9EFE4D3536F030F03B683CC35C3769D801B119F124E636FA949A91986ADFC92EBABEEFE1EACBD92B27236B5D00AEF81C746A", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110F700000103876EE7FFB85231A23DF4659F2B05B9EA90D7C75DB59BF16F6CA958D83999F4891FF071E1A8C5D8648B9E24F9C56923E6D77A7D758DAF1B5DC58AC60E6339128034768FA84293C82DDF75CF0AE24DD8960BC553CF09E6C705A6ED235DE1504B229FEE97B18ECE2C31973F97EC1D232A149F10128A2EF7EC2BBE5B01A4598F150343F107938BCAE369031F77587AC88491B7146221F9B11180E85D5CD30C6EB7CACAF89EBDB1E2EF768E2D976945F6446B1C506CBE7E759719E4F9887F68F326A230019B47C8C21F020A432A5080B18D00029660CA08C75D90870776ABE3ED5F89502D08E057B9F5ED33D8ACEC098A1DC4835A1CAFD763081BE40AF91E13BB7F2E5922F51E94564DA865B059846B0AC6B77A71D8DC3FCF695801E43667B5C4F9379C7FCEE128AF93CC53DAE5FDE7580D64", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110F80000010358BF41F25F9A59BAED2368FDC9A313E5EDC82F0AE21647E9EB96C97F9DB8469ACECAE24C89D0D3E2D1C34E4AE5131DC2D97ADD5F7059C3F05DE3431AD302E5A011C63269DD01E40BE9999391927F2B8788152D469FF253099BDFD15F7D4D4415D39A8EB041A9A9665524AC91EAC995643FD11A930CD043E14043826277EF2B461E9B9981496933C37CFDA41902CBEFA4E48E9291ABF862B7C784B3E01E84C475117FF6495A14F076A975903C77E4F55DAD116CF5E09B95045606F91E38041354DD9BA97E2363C1D79336423120A0DCAEE23A04BCCD4F8CA5A516809919874795F3A5872C337DF0AE8743BD09A7E0E72429A110B22FB383CB18D7AD5073F34F2248422E92D993B8C549C8A097B9363EE148772E3785E17C6890A18683D10FD06FCBB305EAB00EB4E2BF016C90664714B5", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110F900000103175E64AB75BF41DDE34E71E224583943D12E595A64B73D533E4551F8B30985DF6346D87E729E74DEE581C50BD57C977EA0A214C45BC5969D2B889F79487F3E5E58819DCDB5CEFF12D94861B05664B7024F8F7433687B7F9A95A945201706E11B6AB8D6BCF8533A28F8893270102EC55D54C8D83787D17959FF786F1C1FADC7DA88829F9E4CCDC848C251A16CAEAD22D1053EE7955CEB51AE791A8400F63161BE7E324B3759A2EC9637AA1927771424A3713AA8FEB323545CE700CB81D35D84E71C4D78B61A2F6BC423116F7222724484EC37556FC21753211F3FD1758E7C26D6B72F62F7C5B0D9A54AA5275096BEC25A9A7733B9F64C696AD870B0B1B708114874A07985CAF8C01EAA52B3091CF0BB6EE1AA380EC6997467971820F2FA6CE9A615E6C7E6310A36A7590AA93252D52065", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110FA00000103F4E7C169753CECC06034414D44B2AADDD8C3800D2E636F759BD2284839558E0E2E732E81116351C76394F32B3F4D6D8FE60B1A51B5F04A3FE5C9420BF80B9B31E7DEC51EF8385475A8A4DD0405B7C1F9A420142AD57A3F53A1F1CAB592E73B35880BB1E859C157126BFDA67BBA2AF58C19662895532ECA9D21A34565D442D3349DB57AD3171144E876D2CE694FE243957A4593EAB3997AE8BCC8E395738B259CA2055C422D6BB93E6747A9FCD62BAB0047D1EF52D62969C691E9D5E2AD4D0E9590B1D3EDDA113831A2FD6D6E55EE2C80FFBA9D9CD382C4E24E50FF2D98298C590D94B90300040B1EE4B08EF40B0450C383328E68878A9B8DF9DE6D4A9A8561AF0AFD6D69073ABF8E21ED71B762FCEAC2DCAF1C944C3658692BF6BC359755C3491901A3FBF89633F3AEF144FE0A619373", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110FB0000010221B645750833278CF8A42CD27D11B3AB72C9F38C23720CF6A61A2D6C4E321941A98C2EA9BB1C0C980B5DB67580074E3FC4FEF2D046C711B5B1352B2F434B660BC70D67CFC2AB19DDEB4F4380B602DC47E0B0BCB69E906DC71354A39D9EB4065FE0623ABFF3EA2FB2BE9057E662DD5EEFC03B1A8328D1CAF1C5780AE8965B33179BD2AF3C2C6B47750BE4B79952F1E66E8D965BB1930F94162625C23E0C1A981D0235ECF9DAEC37E04246071FA6EF6157B4C0F4FAA1C33D96B5E52986419AE894FFD991092D79CB6B7157C7D90E4B4510A90469FCE3D2B7B72F2EE64A165FE0036633D9A03DB8A677620159D3F48CE46897E664A8F63A56A30B74EE5E6CC8D11BF6C227FE3604BF80734FAC0F90A0CB8EE890D9524B1713556D2B0EBFABD39FBE4577C2409C3CA1746CA3958F4E28B4E5", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110FC00000103FD3C66F4D9D321BA8B93D821EC14217241DC54F1D6A995A7971379743C248964CB7B5E2FE0F2861B6CB4037C60CF61FC9DC934371EBB733F3561BFFDD167A0CA4DA42C1811EC9FA0099280A12303F6ECA0B5DB3D30199EADC4E2388212DB98FFE3C5E7ADC37C8D4FC37561F4A714259816D1243B26FEB51978B25DCCB3AE4F50EBCC356D1D310487D30E9337D75AAD880D8D71D36C0D8BF7125FF737A2A0986BE2B21B1BBE18DCA058A7D07F733A8E1308E49B7FD6228A77008C21567C04972F165B74D30D581FB6E0FCD842B7D6B1B642BF9643E793064CE437DF8736F5C910566BF6DB6C0F5914C027B1629E2561DC652D5979A5E64D72E750BBB81FC9E0AE082F1E3832F56FA23ED1F018BDB742028043E1A396C6AA8901104A8E01C921EF994892EFBC3B689197FA7277D4707227", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110FD00000103A65E888FE0D8FF4FF58CB1F1AA2EE1B2F17754FA8DA66DD15BF93CE41FF6788B3EC742223CF8D652CDAF9B7123641AA6773D1CEE333E4C7419F0FC451CC90D97AB83561C64DDA61D055691C03E14E0B55728117FE77D1546858D6C2C371BBCC36BFE477DD231F77ABE338900AA25C7391AE7CF41F2CF8ACCE9A9928FCD0C913AF979F7AB2687A6F07A25ACB81AA338BFEB9DE21B686C8086B47D5F638C3B035B55D51AC3709A29EEC1E72B6EB5B534C5223169BC3C2B068C37F52AF18F4FBA6255E482571BFD1132271B74755082AD2D1688B4A5CC7960338F7981007A81E7135E66E3218F11B5D5BE5BE755425A1C163067F0778BDB9BCCC42D1A972A62AAEFC28283D44B86DBE1733D1FF0692DC0AE937973A05C6395747575AA8E7AE66D3B904747D5A706EA14F244E31538086591", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" }, { "encrypted": "2110FE00000102D6162207A15505964559DA6D012B5C2825AEAC0E2B8004BCD5BD9947E912DA202FA6BDE50FCDD42A322F2C88B1B4D96C09E4A0FD08B18A461300F8B39A594B0236517FE6D443F2339C2EF83351B6D42088D5981DD3D59A48622C7C1FB3D112ABBA3BA808C72CC37469A20D4AC9736F832943432E5EF015AEC5E1AD48310D6279C284D263077F3F1F78CE98AA69E3CDF02C6D16D8F4DBAB395FDDC95AECFA6BA0B57E5D0483DB4A2D09FD97F399EA2693583E2F1072DD5C1CB33A1AAE4DD850CC4A5A7B7ECAE8A34F8C717F9B0304D799E6AE7FF56E7AEC1F9271722D2ED1594C1C7D165B38B176495CF6F2B68DC83295C4A7F2AFBCED77401A932231B1135496BC9C3F9DB0D7B90DDB66755468051E13F6AEFE72E4B20A7025CBB96A63731F2EA50C5B2543A3ECD969379B6C0FDDF872", "decrypted": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" } ]; ================================================ FILE: test/crypto/fixtures/privatekey.dart ================================================ class PrivateKeyFixtures { List> curve714; List> curve715; List> curve716; List> curve729; PrivateKeyFixtures() { curve714 = [ { "encrypted": "53616C7465645F5FED4A37ECAD2BF13FF24A66DDA299A57632520447B28B9E642C4B2A301CACC217FBD7713F6282C20CCCFDC5FFD2AB93A8E48D8C2C81704D36", "password": "test1234", "b58_pubkey": "3GhhbopDPbi883HVV6Hxun6q6AN43CB1yUD9km64cDoZMhgM1KkLy3N41vT1H1zqw4kHdqM64NHMSpSNviVkUP7fCrisZwYzb89dDs", "enc_privkey": "CA02200046B7A086680D208272F6982F574FE226042F30D049F9A226283FC3346506411D" }, { "encrypted": "53616C7465645F5FB3431FD46EB80DE81273E0181F170552DE01506A9C91D47269312A81841C82B7F55014CEA471A7D8D3187DA2D10B4F9A8481DA7911CCC1A2", "password": "test333", "b58_pubkey": "3GhhbouATXfrARGuHA8ymY2DoPYjKY9i7xBvdpyHjNWtWwdphWwkSaKaKx3tRNV47yGFhnkLZbJkL8crjE129FHUvrFod6dZQt3H8C", "enc_privkey": "CA02200066C8426FA1DC0980DBDEE7F8656AE6FCA9EF746975E3FFB9EB425B53638C0175" }, { "encrypted": "53616C7465645F5FFA7AEBA0EB6776EA2D3BCDADBD5908F1B032B78DA75193A9D8040B94E49CA95920E130BD0CB7603DB72D2639AEA95E8D75CB31AEBBD6EA19", "password": "techworker", "b58_pubkey": "3GhhborMr2ApiQC19ah4EMkFBj46WymwABvqJ8hMwVkVA5TB58Dver6cfosn8zVKpNnJZCsn8HTdnSobopiMgVe4R8S5ap49NvNsEP", "enc_privkey": "CA02200046D101363B3330D65373A70F6E47BB7745FC8EE1F9B3F71992D6B82648158D73" } ]; curve715 = [ { "encrypted": "53616C7465645F5FE1C85F75A6E3D75EF916DD9F60E14660F9C63A8AE407060EAA6F7CF373CB830B6E50D27EC46B013602A75305BD3D6433669607846D7335C9A1849664E8A35DD8E5FDECF40CAE5325", "password": "test1234", "b58_pubkey": "gD8AW4fgASg1cneBp3csnKeF2x3F2q8LpT4VFCyjEBPG8FraaoRHRJfFtBaGSQH3Y2tqqjJbjAAFWvPgVpL5RvA1xokiN9oVeARsi5jPYdmx7FxrZ1XrtyrDPQPArCJyHsq11wqJVhMgfiAjJ", "enc_privkey": "CB02300015B2267604593566FEB5292B32F60C3399540C7EB83F3AABB7255BC73B3809E2B742BB971B6274DF452862D6D902466B" }, { "encrypted": "53616C7465645F5F28F251B908FECEA1DFA33CB727F44E43E95D67DEE66536DFB08D20DF62B69474FF8C36167B8BBB9C5B00D042EC338FFE5A701A8A36F7087FB49007110207B1F880B876467D17F195", "password": "blabla", "b58_pubkey": "gD8AW39i8QV2g57X1F19CGHPF8b6oQcDqw9Q5LAq4X6JofJsg1hJhN1i7yhhkeGZQW86bWKeWSssTZVUvmxU4yFu5ivPpU5qMQk6oHofMKKwmoY4uLjzGWnmCsmNdbtK7k6MnhHkeDJqZiRwt", "enc_privkey": "CB023000FCABEC25AAA049A524CEA1D69360711C1EB4E83D86D2CB45B0EFA1F69B218CCABE3CF0A16AE8ED170919B7B39D1C390E" }, { "encrypted": "53616C7465645F5F7C4731B8A43D75E23C7F77FB51C44DCCA59394ED2B06F1291849D248AB096A32C93A3E76F32395158E9624AFF2B6AB543CD5C0DCC3735E4879A1E7063F74C11251618A192082F9F4", "password": "zulu55", "b58_pubkey": "gD8AW3uCN1tF8FRNNvnGMcSdgNS7uzQErW4mHXpBgG4RCcLhZ2VcGAUuqiW6MrYV4Fsxfbo5n3ToRmn7WaMGCzMFcJX5SCrTY4fvEGrE2q5XTw2gasapYvRYe1c8UKmQoxE6VmKgvDkaiVC51", "enc_privkey": "CB023000225EDA615DBF3F296B295E76454444024E5F1CF17D5275135C00CDCADF966889890F5618874D95A9EEFF22B6DDCF9C4A" } ]; curve716 = [ { "encrypted": "53616C7465645F5FD5B6C81AE89043F4ECE78CD21F68BFD0FBCC3C6E5BF4BD10B199D53F1DE8C8DFC6FF8CCE3AE309734C268337B1E80A5C70256480482EC8D99D52A8B448BD016C72D3FA482DE6C2F4CBBF604B2D3A7CBB78745AA9057486A0", "password": "test1234", "b58_pubkey": "JJj2GZDjhAHPKfyJ4GyEMhFNfUZPAUFe7oqfChPY5HinqNY5NPsCydcY2XEw8iwqhxSuaoAg6hf7qFagZJQ6HTYCGdTdgBxNangcV4ZcbcKwesZ5QmX6sVvLDPvGosfjk8LnrVgVhwJnv26dzKbPceNUzPHR8Pm2zwx6kjESRa3bKajupKngZGYyGmVJNuuyJ", "enc_privkey": "CC02420001F4C77C1DC868F62A96DD74343B9C03C594F6E42DA3A0139D6988D45D75FBC167E6D98D1F101D4FE8F8D58514F7571399E4548A017645609F5F575176849A38335B" }, { "encrypted": "53616C7465645F5FA78798633EC647B4BCCC1240730C24E5852B649C3930EF7F9703018D4ED62000B254A875A021B32CFB7B8E87C2C19099CF6E7F1F9E76556F4391A00B118EB6A8EECCFB4CA19C4333B2A7393EF1A88D200D257E3A32DC4C0B", "password": "techworker", "b58_pubkey": "JJj2A5Fyw46yWMLegHqvHpUgeBgx957FK2kCRy4nA2qYGbmwMP9tNHnaVJFX6Yha8bQPbNyjLtdrjsqgTFRbFKAWhzx6s9hCjCmVwUsw1ZrE8MB1MkrqgobAr4xxZNmw4i3osKvMwE9LWafSQC5DWCRsx636u6h1Vdpt1CeGthDZfRU35DcKusZ7ZD2W2Pfns", "enc_privkey": "CC024100E9551B5AD9D9084DD878E224BE718E2929892FC03A5420DCC9A8B9AB5260B47A039D3838DB32D003141AEB1C2F53A285A78470D65AAD2222B2A4C9B279AF76CBF1" }, { "encrypted": "53616C7465645F5FD8FEC5F2D15CF8997170913FF9F1D715481431196F35BCB60B8A127440D0B7E9D67F908FF857E4EF7B9EFA6BC5A4F8B8FB8F1AD40B9642A2E20BDC5F1B08C3D6128BE2F67CCBAA2CD2C91E287CB4FA298317EBCC872919EF", "password": "zulu123", "b58_pubkey": "JJj2GZDo19rm39iih31cyJns2PBUqkTqFvpLZ6tucYNrjf8e9yWr4hckXP9C85x4zBRNJk1YqA6HSkX9AaTUearmYs9qFVzRzwX7X2gEt1gFhHq1dJaeLmDyRXVbd7cmiuxD6tjjXS7V4Jt3A5esgHTpZRQ6PXReDkWmx4fmcURcHFiWwtwWdQrz5ZTEQrQ3r", "enc_privkey": "CC024100231B27482A4ED27F9CECBFA1D6E610146852D720A5F7623E24D348499B46F31E02B46572D55A7C97CD303D4361242B11A5789C2FEB9A793B0C285B5DF4EBC08984" } ]; curve729 = [ { "encrypted": "53616C7465645F5F096AF812ED1B1275E123BAF0F351A50554CC62C50C6181AD6313B5E6A319F6ABAF9A1CC29FFA47162355E5A731F379420B7FB6ACC92F9F82", "password": "test1234", "b58_pubkey": "2jR5AN61cbT1MmPmhdBPCcYCUidSf4hSzKHycfw6Kk8DFqcxD4MS1y7YezmsoC19S4QJmQvwnDnVDjnHgRaeX8PyE1pWvFzBWVnDBNuawD1wQ9iaa", "enc_privkey": "D9022300252A9478A7C732CE80E1BE5E2F529050F8521D8F7FEC3C0C8EA2936307244AF9F38E55" }, { "encrypted": "53616C7465645F5FE327D1F4C88764F6D6F5CD48BC563D5E53D31E641AF18BAE52DDCE43AE7252D2BA294BB1D1F7D6C66195D2021C77B72E95C36D659ABC0BBF", "password": "techworker", "b58_pubkey": "2jR5AN5umYFQ2Xgh2p4Scva1NTbipBXtDe4NgTZPd7SSoQkRkPBp6b7AizVZ5nXLboHH6h4fDhAxmUCVkfZ6NGv7GNuoRnqQ1WcwnEEEf2NyiBmVc", "enc_privkey": "D9022300BD97C143CB26947DE0DF445972F929C1C76DA86B2B33C5B486C8AE926872D77DB4042D" }, { "encrypted": "53616C7465645F5F36404950AA92E7632F88BE903B9DF3DBFA5CDDE67E776655C95773F80ED982DB55917A685B50F514F813D091C6768280E98C66F996CB7127", "password": "zandura", "b58_pubkey": "PkWwgfjhzQWM3sBjXFWRdXAq6ge2Wrr4vXGMp3vMTzj3yVbdqhWxfTwo6ySRAiVP39LZRoH6T9sZq7F5bCHMTD7b92fEynFwSV6xGqTDikNSnoc", "enc_privkey": "D9022400011FC78E156FFB72C1C830740D34BD711DBFF03AD0F2A272D160D41A28FCB0C1000CBFED" } ]; } } ================================================ FILE: test/crypto/keys_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:pascaldart/crypto.dart'; import 'package:flutter_test/flutter_test.dart'; import './fixtures/privatekey.dart'; void main() { group('crypto.keys', () { final List CURVE_INSTANCES = [ Curve.fromString(Curve.CN_SECP256K1), Curve.fromString(Curve.CN_SECP384R1), Curve.fromString(Curve.CN_SECP521R1), Curve(Curve.CI_SECT283K1), Curve(0) ]; PrivateKeyFixtures fixtures; setUp(() { fixtures = PrivateKeyFixtures(); }); test('can generate keypairs', () { CURVE_INSTANCES.forEach((curve) { if (curve.supported) { for (int i = 0; i < 100; i++) { KeyPair kp = Keys.generate(curve: curve); expect(kp is KeyPair, true); expect(kp.curve.id, curve.id); expect( PDUtil.byteToHex( Keys.fromPrivateKey(kp.privateKey).publicKey.ec()), PDUtil.byteToHex(kp.publicKey.ec())); } } }); }); test('cannot generate unsupported curves', () { CURVE_INSTANCES.forEach((curve) { if (!curve.supported) { expect(() => Keys.generate(curve: curve), throwsUnsupportedError); } }); }); test('can retrieve a keypair from a private key', () { fixtures.curve714.forEach((c) { if (Curve(714).supported) { KeyPair kp = Keys.fromPrivateKey(PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey']))); expect(kp.curve.id, 714); expect( PDUtil.byteToHex(PrivateKeyCoder().encodeToBytes(kp.privateKey)), c['enc_privkey']); expect( PublicKeyCoder().encodeToBase58(kp.publicKey), c['b58_pubkey']); } else { expect( () => Keys.fromPrivateKey(PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey']))), throwsException); } }); fixtures.curve715.forEach((c) { if (Curve(715).supported) { KeyPair kp = Keys.fromPrivateKey(PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey']))); expect(kp.curve.id, 715); expect( PDUtil.byteToHex(PrivateKeyCoder().encodeToBytes(kp.privateKey)), c['enc_privkey']); expect( PublicKeyCoder().encodeToBase58(kp.publicKey), c['b58_pubkey']); } else { expect( () => Keys.fromPrivateKey(PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey']))), throwsException); } }); fixtures.curve716.forEach((c) { if (Curve(716).supported) { KeyPair kp = Keys.fromPrivateKey(PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey']))); expect(kp.curve.id, 716); expect( PDUtil.byteToHex(PrivateKeyCoder().encodeToBytes(kp.privateKey)), c['enc_privkey']); expect( PublicKeyCoder().encodeToBase58(kp.publicKey), c['b58_pubkey']); } else { expect( () => Keys.fromPrivateKey(PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey']))), throwsException); } }); fixtures.curve729.forEach((c) { if (Curve(729).supported) { KeyPair kp = Keys.fromPrivateKey(PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey']))); expect(kp.curve.id, 729); expect( PDUtil.byteToHex(PrivateKeyCoder().encodeToBytes(kp.privateKey)), c['enc_privkey']); expect( PublicKeyCoder().encodeToBase58(kp.publicKey), c['b58_pubkey']); } else { expect( () => Keys.fromPrivateKey(PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey']))), throwsException); } }); }); test('can sign a value', () { fixtures.curve714.forEach((c) { if (Curve(714).supported && fixtures.curve714.indexOf(c) == 0) { PrivateKey pk = PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); Signature sig = Keys.sign(pk, PDUtil.stringToBytesUtf8('test123'), hashMessage: false); expect( PDUtil.byteToHex(PDUtil.encodeBigInt(sig.r)), '4C3492DC3FB565D9D4C132575BCEAA55571491A983A82A5460E341198E38C250'); expect( PDUtil.byteToHex(PDUtil.encodeBigInt(sig.s)), '7FA6FF43CD3B13F6E91F810FEF9BE6359CA355C53272C841D2BF934217EE53DE'); } }); fixtures.curve715.forEach((c) { if (Curve(715).supported && fixtures.curve715.indexOf(c) == 0) { PrivateKey pk = PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); Signature sig = Keys.sign(pk, PDUtil.stringToBytesUtf8('test123')); expect( PDUtil.byteToHex(PDUtil.encodeBigInt(sig.r)).length == 96, true); expect( PDUtil.byteToHex(PDUtil.encodeBigInt(sig.s)).length == 96, true); } }); fixtures.curve716.forEach((c) { if (Curve(716).supported && fixtures.curve716.indexOf(c) == 0) { PrivateKey pk = PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); Signature sig = Keys.sign(pk, PDUtil.stringToBytesUtf8('test123')); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(sig.r)).isNotEmpty, true); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(sig.s)).isNotEmpty, true); } }); fixtures.curve729.forEach((c) { if (Curve(729).supported) { PrivateKey pk = PrivateKeyCoder() .decodeFromBytes(PDUtil.hexToBytes(c['enc_privkey'])); Signature sig = Keys.sign(pk, PDUtil.stringToBytesUtf8('test123')); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(sig.r)).isNotEmpty, true); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(sig.s)).isNotEmpty, true); } }); }); test('can retrieve a keypair from an encrypted private key', () { fixtures.curve714.forEach((c) { if (Curve(714).supported) { PrivateKey pKey = PrivateKeyCrypt.decrypt( PDUtil.hexToBytes(c['encrypted']), c['password']); KeyPair kp = Keys.fromPrivateKey(pKey); expect(kp.curve.id, 714); expect( PDUtil.byteToHex(PrivateKeyCoder().encodeToBytes(kp.privateKey)), c['enc_privkey']); expect( PublicKeyCoder().encodeToBase58(kp.publicKey), c['b58_pubkey']); } else { expect(() { PrivateKey pKey = PrivateKeyCrypt.decrypt( PDUtil.hexToBytes(c['encrypted']), c['password']); Keys.fromPrivateKey(pKey); }, throwsException); } }); fixtures.curve715.forEach((c) { if (Curve(715).supported) { PrivateKey pKey = PrivateKeyCrypt.decrypt( PDUtil.hexToBytes(c['encrypted']), c['password']); KeyPair kp = Keys.fromPrivateKey(pKey); expect(kp.curve.id, 715); expect( PDUtil.byteToHex(PrivateKeyCoder().encodeToBytes(kp.privateKey)), c['enc_privkey']); expect( PublicKeyCoder().encodeToBase58(kp.publicKey), c['b58_pubkey']); } else { expect(() { PrivateKey pKey = PrivateKeyCrypt.decrypt( PDUtil.hexToBytes(c['encrypted']), c['password']); Keys.fromPrivateKey(pKey); }, throwsException); } }); fixtures.curve716.forEach((c) { if (Curve(716).supported) { PrivateKey pKey = PrivateKeyCrypt.decrypt( PDUtil.hexToBytes(c['encrypted']), c['password']); KeyPair kp = Keys.fromPrivateKey(pKey); expect(kp.curve.id, 716); expect( PDUtil.byteToHex(PrivateKeyCoder().encodeToBytes(kp.privateKey)), c['enc_privkey']); expect( PublicKeyCoder().encodeToBase58(kp.publicKey), c['b58_pubkey']); } else { expect(() { PrivateKey pKey = PrivateKeyCrypt.decrypt( PDUtil.hexToBytes(c['encrypted']), c['password']); Keys.fromPrivateKey(pKey); }, throwsException); } }); fixtures.curve729.forEach((c) { if (Curve(729).supported) { PrivateKey pKey = PrivateKeyCrypt.decrypt( PDUtil.hexToBytes(c['encrypted']), c['password']); KeyPair kp = Keys.fromPrivateKey(pKey); expect(kp.curve.id, 729); expect( PDUtil.byteToHex(PrivateKeyCoder().encodeToBytes(kp.privateKey)), c['enc_privkey']); expect( PublicKeyCoder().encodeToBase58(kp.publicKey), c['b58_pubkey']); } else { expect(() { PrivateKey pKey = PrivateKeyCrypt.decrypt( PDUtil.hexToBytes(c['encrypted']), c['password']); Keys.fromPrivateKey(pKey); }, throwsException); } }); }); }); } ================================================ FILE: test/json_rpc/model/pascal_account_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.pascal_account', () { test('can serialize getaccount response', () { Map rawResponse = { "jsonrpc": "2.0", "id": 123, "result": { "account": 1920, "enc_pubkey": "CA0220009D92DFA1D6F8B2CAE31194EE5433EE4AD457AE145C1C67E49A9196EE58A45B9F200046EAF20C0A26A80A7693E71C0222313A0187AFCA838209FF86FB740A4FFF7F0B", "balance": 29595.952, "n_operation": 0, "updated_b": 11973 } }; BaseResponse baseResp = BaseResponse.fromJson(rawResponse); expect(baseResp.jsonrpc, "2.0"); expect(baseResp.id, 123); PascalAccount getAcctResp = PascalAccount.fromJson(baseResp.result); expect(getAcctResp.account.account, 1920); expect( PDUtil.byteToHex( PublicKeyCoder().encodeToBytes(getAcctResp.encPubkey)), 'CA0220009D92DFA1D6F8B2CAE31194EE5433EE4AD457AE145C1C67E49A9196EE58A45B9F200046EAF20C0A26A80A7693E71C0222313A0187AFCA838209FF86FB740A4FFF7F0B'); expect(getAcctResp.balance.toStringOpt(), '29595.952'); expect(getAcctResp.nOperation, 0); expect(getAcctResp.updatedBlock, 11973); }); }); } ================================================ FILE: test/json_rpc/model/pascal_block_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.pascal_block', () { test('can serialize block response', () { Map rawResponse = { "jsonrpc": "2.0", "id": 123, "result": { "block": 8888, "enc_pubkey": "CA0220000E60B6F76778CFE8678E30369BA7B2C38D0EC93FC3F39E61468E29FEC39F13BF2000572EDE3C44CF00FF86AFF651474D53CCBDF86B953F1ECE5FB8FC7BB6FA16F114", "reward": 100, "fee": 0, "ver": 1, "ver_a": 0, "timestamp": 1473161258, "target": 559519020, "nonce": 131965022, "payload": "New Node 9/4/2016 10:10:13 PM - Pascal Coin Miner & Explorer Build:1.0.2.0", "sbh": "5B75D33D9EFBF560EF5DA9B4A603528808626FE6C1FCEC44F83AF2330C6607EF", "oph": "81BE87831F03A2FE272C89BC6D2406DD57614846D9CEF30096BF574AB4AB3EE9", "pow": "00000000213A39EBBAB6D1FAEAA1EE528E398A587848F81FF66F7DA6113FC754", "operations": 1 } }; BaseResponse baseResp = BaseResponse.fromJson(rawResponse); expect(baseResp.jsonrpc, "2.0"); expect(baseResp.id, 123); PascalBlock block = PascalBlock.fromJson(baseResp.result); expect(block.block, 8888); expect(PDUtil.byteToHex(PublicKeyCoder().encodeToBytes(block.encPubkey)), 'CA0220000E60B6F76778CFE8678E30369BA7B2C38D0EC93FC3F39E61468E29FEC39F13BF2000572EDE3C44CF00FF86AFF651474D53CCBDF86B953F1ECE5FB8FC7BB6FA16F114'); expect(block.reward.toStringOpt(), '100'); expect(block.fee.toStringOpt(), '0'); expect(block.ver, 1); expect(block.ver_a, 0); expect(block.timestamp.millisecondsSinceEpoch ~/ 1000, 1473161258); expect(block.target, 559519020); expect(block.nonce, 131965022); expect(block.payload, "New Node 9/4/2016 10:10:13 PM - Pascal Coin Miner & Explorer Build:1.0.2.0"); expect(block.sbh, "5B75D33D9EFBF560EF5DA9B4A603528808626FE6C1FCEC44F83AF2330C6607EF"); expect(block.oph, "81BE87831F03A2FE272C89BC6D2406DD57614846D9CEF30096BF574AB4AB3EE9"); expect(block.pow, "00000000213A39EBBAB6D1FAEAA1EE528E398A587848F81FF66F7DA6113FC754"); expect(block.operations, 1); }); }); } ================================================ FILE: test/json_rpc/model/pascal_operation_test.dart ================================================ import 'package:pascaldart/common.dart'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.pascal_operation', () { test('can serialize operation response', () { Map rawResponse = { "jsonrpc": "2.0", "id": 123, "result": { "block": 21555, "opblock": 0, "optype": 2, "time": 1561919606, "account": 101740, "optxt": "Change Key to secp256k1", "amount": 0, "fee": 0, "balance": 0, "payload": "", "enc_pubkey": "CA02200078D867C93D58C2C46C66667A139543DCF8420D9119B7A0E06197D22A5BBCE5542000EA2E492FD8B90E48AF3D9EF438C6FBEA57C8A8E75889807DE588B490B1D57187", "ophash": "335400006C8D0100020000003330433034464446453130354434444445424141" } }; BaseResponse baseResp = BaseResponse.fromJson(rawResponse); expect(baseResp.jsonrpc, "2.0"); expect(baseResp.id, 123); PascalOperation op = PascalOperation.fromJson(baseResp.result); expect(op.block, 21555); expect(op.opblock, 0); expect(op.optype, 2); expect(op.time.millisecondsSinceEpoch ~/ 1000, 1561919606); expect(op.time.year, 2019); expect(op.account.account, 101740); expect(op.optxt, "Change Key to secp256k1"); expect(op.amount.pasc, BigInt.zero); expect(op.fee.pasc, BigInt.zero); expect(op.balance.pasc, BigInt.zero); expect(op.payload, ""); expect(PDUtil.byteToHex(PublicKeyCoder().encodeToBytes(op.encPubkey)), 'CA02200078D867C93D58C2C46C66667A139543DCF8420D9119B7A0E06197D22A5BBCE5542000EA2E492FD8B90E48AF3D9EF438C6FBEA57C8A8E75889807DE588B490B1D57187'); expect(op.ophash, '335400006C8D0100020000003330433034464446453130354434444445424141'); }); }); } ================================================ FILE: test/json_rpc/model/request/executeoperations_request_test.dart ================================================ import 'dart:convert'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.request.executeoperations.test', () { test('can serialize executeoperations', () { ExecuteOperationsRequest opsRequest = ExecuteOperationsRequest(rawOperations: 'hexastring'); expect(json.encode(opsRequest.toJson()), '{"jsonrpc":"2.0","method":"executeoperations","id":0,"params":{"rawoperations":"hexastring"}}'); }); }); } ================================================ FILE: test/json_rpc/model/request/findaccounts_request_test.dart ================================================ import 'dart:convert'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.request.findaccounts_request', () { test('can serialize findaccounts', () { FindAccountsRequest request = FindAccountsRequest(name: 'bbedward', exact: true); expect(json.encode(request.toJson()), '{"jsonrpc":"2.0","method":"findaccounts","id":0,"params":{"name":"bbedward","exact":true}}'); }); }); } ================================================ FILE: test/json_rpc/model/request/findoperation_request_test.dart ================================================ import 'dart:convert'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.request.findoperation_request', () { test('can serialize findoperation', () { FindOperationRequest request = FindOperationRequest(ophash: '1234'); expect(json.encode(request.toJson()), '{"jsonrpc":"2.0","method":"findoperation","id":0,"params":{"ophash":"1234"}}'); }); }); } ================================================ FILE: test/json_rpc/model/request/getaccount_request_test.dart ================================================ import 'dart:convert'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.request.GetAccountRequest', () { test('can serialize GetAccountRequest', () { GetAccountRequest acctRequest = GetAccountRequest(account: 1234); expect(json.encode(acctRequest.toJson()), '{"jsonrpc":"2.0","method":"getaccount","id":0,"params":{"account":1234}}'); }); }); } ================================================ FILE: test/json_rpc/model/request/getaccountoperations_test.dart ================================================ import 'dart:convert'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.request.getaccountoperations.test', () { test('can serialize getaccountoperations', () { GetAccountOperationsRequest opsRequest = GetAccountOperationsRequest(account: 1234); expect(json.encode(opsRequest.toJson()), '{"jsonrpc":"2.0","method":"getaccountoperations","id":0,"params":{"account":1234}}'); }); }); } ================================================ FILE: test/json_rpc/model/request/getblock_request_test.dart ================================================ import 'dart:convert'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.request.GetBlockRequest', () { test('can serialize GetBlockRequest', () { GetBlockRequest blockRequest = GetBlockRequest(block: 1234); expect(json.encode(blockRequest.toJson()), '{"jsonrpc":"2.0","method":"getblock","id":0,"params":{"block":1234}}'); }); }); } ================================================ FILE: test/json_rpc/model/request/getblockoperation_request_test.dart ================================================ import 'dart:convert'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.request.getblockoperation.test', () { test('can serialize getblockoperation', () { GetBlockOperationRequest opsRequest = GetBlockOperationRequest(block: 1234); expect(json.encode(opsRequest.toJson()), '{"jsonrpc":"2.0","method":"getblockoperation","id":0,"params":{"block":1234,"opblock":0}}'); }); }); } ================================================ FILE: test/json_rpc/model/request/getblockoperations_test.dart ================================================ import 'dart:convert'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.request.getblockoperations.test', () { test('can serialize getblockoperations', () { GetBlockOperationsRequest opsRequest = GetBlockOperationsRequest(block: 1234); expect(json.encode(opsRequest.toJson()), '{"jsonrpc":"2.0","method":"getblockoperations","id":0,"params":{"block":1234}}'); }); }); } ================================================ FILE: test/json_rpc/model/request/getblocks_request_test.dart ================================================ import 'dart:convert'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.request.GetBlocksRequest', () { test('can serialize GetBlocksRequest with last', () { GetBlocksRequest blockRequest = GetBlocksRequest(last: 1234); expect(json.encode(blockRequest.toJson()), '{"jsonrpc":"2.0","method":"getblocks","id":0,"params":{"last":1234}}'); }); test('can serialize GetBlocksRequest with start and end', () { GetBlocksRequest blockRequest = GetBlocksRequest(start: 1, end: 100); expect(json.encode(blockRequest.toJson()), '{"jsonrpc":"2.0","method":"getblocks","id":0,"params":{"start":1,"end":100}}'); }); test('throws exceptions with bad arguments', () { expect(() => GetBlocksRequest(start: 1), throwsArgumentError); }); }); } ================================================ FILE: test/json_rpc/model/request/getpendings_request_test.dart ================================================ import 'dart:convert'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.request.getpendings_request', () { test('can serialize getpendings', () { GetPendingsRequest request = GetPendingsRequest(max: 0); expect(json.encode(request.toJson()), '{"jsonrpc":"2.0","method":"getpendings","id":0,"params":{"max":0}}'); }); }); } ================================================ FILE: test/json_rpc/model/request/getwalletaccounts_request_test.dart ================================================ import 'dart:convert'; import 'package:pascaldart/json_rpc.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('json_rpc.model.request.getwalletaccounts_request', () { test('can serialize getwalletaccounts', () { GetWalletAccountsRequest acctRequest = GetWalletAccountsRequest( encPubkey: 'CA0220009D92DFA1D6F8B2CAE31194EE5433EE4AD457AE145C1C67E49A9196EE58A45B9F200046EAF20C0A26A80A7693E71C0222313A0187AFCA838209FF86FB740A4FFF7F0B'); expect(json.encode(acctRequest.toJson()), '{"jsonrpc":"2.0","method":"getwalletaccounts","id":0,"params":{"enc_pubkey":"CA0220009D92DFA1D6F8B2CAE31194EE5433EE4AD457AE145C1C67E49A9196EE58A45B9F200046EAF20C0A26A80A7693E71C0222313A0187AFCA838209FF86FB740A4FFF7F0B"}}'); }); }); } ================================================ FILE: test/signing/operations/buyaccount_operation_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:pascaldart/crypto.dart'; import 'package:pascaldart/signing.dart'; import 'package:pascaldart/src/common/model/accountnumber.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('signing.operations.BuyAccountOperation', () { Map fixture; setUp(() { fixture = { 'signerPublicKey': '3Ghhboo1Q8CFLc9BTdcweNX75Nctifx8aW1ovF58F1VyjHRxuDQRx2xUcSSm6ragsTRUZHGPSvdwM1HnReE4Je8aYeVeZHFJf23H2z', 'buyerAccount': 1430880, 'accountToBuy': 1440500, 'price': 0.0001, 'fee': 0.0002, 'payload': 'techworker', 'seller': 1440500, 'n_operation': 4004, 'r': '3B3FF1BA82E8923F6FA8B92B10FEA291CC73E563D4CB92E58039F3D25DB98FA1', 's': '7449069D5E034AD0B3419532B11530B52E3EBDBBBB1E754CDEADB6E709291B6B', 'raw': '010000000600000060D51500A40F0000F4FA1500000000000000000002000000000000000A0074656368776F726B6572000000000000020100000000000000F4FA150000000000000020003B3FF1BA82E8923F6FA8B92B10FEA291CC73E563D4CB92E58039F3D25DB98FA120007449069D5E034AD0B3419532B11530B52E3EBDBBBB1E754CDEADB6E709291B6B' }; }); test('can be decode a signed operation', () { BuyAccountOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.r)), fixture['r']); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.s)), fixture['s']); expect(decoded.sender.account, fixture['buyerAccount']); expect(decoded.target.account, fixture['accountToBuy']); expect(decoded.seller.account, fixture['seller']); expect(decoded.price.toStringOpt(), fixture['price'].toString()); expect(decoded.amount.toStringOpt(), '0'); expect(decoded.fee.toStringOpt(), fixture['fee'].toString()); expect(decoded.nOperation, fixture['n_operation']); expect(PDUtil.bytesToUtf8String(decoded.payload), fixture['payload']); }); test('can be decode signed operation and encode it again', () { BuyAccountOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(RawOperationCoder.encodeToBytes(decoded)), fixture['raw']); }); test('can be built by hand', () { BuyAccountOperation op = BuyAccountOperation( sender: AccountNumber.fromInt(fixture['buyerAccount']), target: AccountNumber.fromInt(fixture['accountToBuy']), seller: AccountNumber.fromInt(fixture['seller']), price: Currency(fixture['price'].toString())) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..withSignature(Signature( r: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['r'])), s: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['s'])))); Uint8List encoded = RawOperationCoder.encodeToBytes(op); expect(PDUtil.byteToHex(encoded), fixture['raw']); }); test('can be built by hand and signed', () { PrivateKey pk = PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes( 'CA02200046D101363B3330D65373A70F6E47BB7745FC8EE1F9B3F71992D6B82648158D73')); BuyAccountOperation op = BuyAccountOperation( sender: AccountNumber.fromInt(fixture['buyerAccount']), target: AccountNumber.fromInt(fixture['accountToBuy']), seller: AccountNumber.fromInt(fixture['seller']), price: Currency(fixture['price'].toString())) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..sign(pk); expect(op.signature.r.toString().length > 30, true); expect(op.signature.s.toString().length > 30, true); }); }); } ================================================ FILE: test/signing/operations/changeaccountinfo_operation_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:pascaldart/crypto.dart'; import 'package:pascaldart/signing.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('signing.operations.ChangeAccountInfoOperation', () { Map fixture; setUp(() { fixture = { 'signerPublicKey': '3GhhbojvCFtyYSPxXfuk86uvJhURBEzF2fxV7x6kuW3Gi7ApSDd1scztAXGyeqphytxi6XibueQCoAG3yBCkXvg1BfGnosd6xKnKPT', 'signer': 1440500, 'target': 1440503, 'newPublicKey': '3Ghhbommc5imqoAUAzXoEJpYo1qLiyVZip3Pwsj7WUXq7aFXwC4tw1MFsRrhPnA51CMAoeoyGJcMGV1dSU9FAqXRsnV2LLT7tBKDeY', 'newName': 'techworker123', 'newType': '345', 'n_operation': 4003, 'fee': 0.0002, 'payload': 'techworker', 'r': '28D54FB4B87C8DEA5884876FA6DDD30115B1EAECEA50869181CC05D788C74D6A', 's': '61E719DCEE48E93B94BC0938D1CADDBCB7E59F259097C622C98E5D0C758BB5AC', 'raw': '0100000008000000F4FA1500F7FA1500A30F000002000000000000000A0074656368776F726B657200000000000007CA0220003078FA6B1419BD6F4D4F6A779358EBFA6BC12F3CE32E47A8D9F7CD5FA8956FF0200053A101778813DD24B90A5198BAE5EB9F8AA5EDFD30E4C130A69FDD62CAE774F90D0074656368776F726B65723132335901200028D54FB4B87C8DEA5884876FA6DDD30115B1EAECEA50869181CC05D788C74D6A200061E719DCEE48E93B94BC0938D1CADDBCB7E59F259097C622C98E5D0C758BB5AC' }; }); test('can be decode a signed operation', () { ChangeAccountInfoOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.r)), fixture['r']); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.s)), fixture['s']); expect(decoded.accountSigner.account, fixture['signer']); expect(decoded.targetSigner.account, fixture['target']); expect(decoded.nOperation, fixture['n_operation']); expect(decoded.fee.toStringOpt(), fixture['fee'].toString()); expect(decoded.changeType & 1, 1); expect(decoded.changeType & 2, 2); expect(decoded.changeType & 4, 4); expect(PDUtil.bytesToUtf8String(decoded.payload), fixture['payload']); expect(decoded.newName.toString(), fixture['newName']); expect(decoded.newType.toString(), fixture['newType']); expect(PublicKeyCoder().encodeToBase58(decoded.newPublicKey), fixture['newPublicKey']); }); test('can be decode signed operation and encode it again', () { ChangeAccountInfoOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(RawOperationCoder.encodeToBytes(decoded)), fixture['raw']); }); test('can be build by hand', () { ChangeAccountInfoOperation op = ChangeAccountInfoOperation( accountSigner: AccountNumber.fromInt(fixture['signer']), targetSigner: AccountNumber.fromInt(fixture['target'])) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..setNewName(AccountName(fixture['newName'])) ..setNewType(int.parse(fixture['newType'])) ..setNewPublickey( PublicKeyCoder().decodeFromBase58(fixture['newPublicKey'])) ..withSignature(Signature( r: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['r'])), s: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['s'])))); Uint8List encoded = RawOperationCoder.encodeToBytes(op); expect(PDUtil.byteToHex(encoded), fixture['raw']); }); test('can be built by hand and signed', () { PrivateKey pk = PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes( 'CA02200046D101363B3330D65373A70F6E47BB7745FC8EE1F9B3F71992D6B82648158D73')); ChangeAccountInfoOperation op = ChangeAccountInfoOperation( accountSigner: AccountNumber.fromInt(fixture['signer']), targetSigner: AccountNumber.fromInt(fixture['target'])) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..setNewName(AccountName(fixture['newName'])) ..setNewType(int.parse(fixture['newType'])) ..setNewPublickey( PublicKeyCoder().decodeFromBase58(fixture['newPublicKey'])) ..sign(pk); expect(op.signature.r.toString().length > 30, true); expect(op.signature.s.toString().length > 30, true); }); test('can compute digest correctly', () { ChangeAccountInfoOperation op = ChangeAccountInfoOperation( accountSigner: AccountNumber.fromInt(1440500), targetSigner: AccountNumber.fromInt(1440500), newName: AccountName('bbedward'), withNewName: true ) ..withNOperation(20) ..withPayload(PDUtil.stringToBytesUtf8("")) ..withFee(Currency("0")) ..withSignature(Signature( r: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['r'])), s: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['s'])))); expect(PDUtil.byteToHex(op.digest()), 'F4FA1500F4FA150014000000000000000000000000000000000000000200000000000008006262656477617264000008'); }); }); } ================================================ FILE: test/signing/operations/changekey_operation_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:pascaldart/crypto.dart'; import 'package:pascaldart/signing.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('signing.operations.ChangeKeyOperation', () { Map fixture; setUp(() { fixture = { 'signer': 1440500, 'oldPublicKey': '3GhhbojvCFtyYSPxXfuk86uvJhURBEzF2fxV7x6kuW3Gi7ApSDd1scztAXGyeqphytxi6XibueQCoAG3yBCkXvg1BfGnosd6xKnKPT', 'newPublicKey': '3Ghhbommc5imqoAUAzXoEJpYo1qLiyVZip3Pwsj7WUXq7aFXwC4tw1MFsRrhPnA51CMAoeoyGJcMGV1dSU9FAqXRsnV2LLT7tBKDeY', 'fee': 0.0002, 'n_operation': 4003, 'payload': 'techworker', 's': '40041D0D33034504B20C51277F76F2784BAE43C46F60526B50F37D90C8310919', 'r': '0981C22EDEE3E56DEBFABB8DF6229C10971E283D9B01B2D914176EFDAEF891F5', 'raw': '0100000002000000F4FA1500A30F000002000000000000000A0074656368776F726B65720000000000004600CA0220003078FA6B1419BD6F4D4F6A779358EBFA6BC12F3CE32E47A8D9F7CD5FA8956FF0200053A101778813DD24B90A5198BAE5EB9F8AA5EDFD30E4C130A69FDD62CAE774F920000981C22EDEE3E56DEBFABB8DF6229C10971E283D9B01B2D914176EFDAEF891F5200040041D0D33034504B20C51277F76F2784BAE43C46F60526B50F37D90C8310919' }; }); test('can be decode a signed operation', () { ChangeKeyOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.r)), fixture['r']); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.s)), fixture['s']); expect(decoded.signer.account, fixture['signer']); expect(decoded.nOperation, fixture['n_operation']); expect(decoded.fee.toStringOpt(), fixture['fee'].toString()); expect(PDUtil.bytesToUtf8String(decoded.payload), fixture['payload']); expect(PublicKeyCoder().encodeToBase58(decoded.newPublicKey), fixture['newPublicKey']); }); test('can be decode signed operation and encode it again', () { ChangeKeyOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(RawOperationCoder.encodeToBytes(decoded)), fixture['raw']); }); test('can be build by hand', () { ChangeKeyOperation op = ChangeKeyOperation( signer: AccountNumber.fromInt(fixture['signer']), newPublicKey: PublicKeyCoder().decodeFromBase58(fixture['newPublicKey'])) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..withSignature(Signature( r: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['r'])), s: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['s'])))); Uint8List encoded = RawOperationCoder.encodeToBytes(op); expect(PDUtil.byteToHex(encoded), fixture['raw']); }); test('can be built by hand and signed', () { PrivateKey pk = PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes( 'CA02200046D101363B3330D65373A70F6E47BB7745FC8EE1F9B3F71992D6B82648158D73')); ChangeKeyOperation op = ChangeKeyOperation( signer: AccountNumber.fromInt(fixture['signer']), newPublicKey: PublicKeyCoder().decodeFromBase58(fixture['newPublicKey'])) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..sign(pk); expect(op.signature.r.toString().length > 30, true); expect(op.signature.s.toString().length > 30, true); }); }); } ================================================ FILE: test/signing/operations/changekeysigned_operation_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:pascaldart/crypto.dart'; import 'package:pascaldart/signing.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('signing.operations.ChangeKeySignedOperation', () { Map fixture; setUp(() { fixture = { 'sender': 1440500, 'target': 1440503, 'oldPublicKey': '3GhhbojvCFtyYSPxXfuk86uvJhURBEzF2fxV7x6kuW3Gi7ApSDd1scztAXGyeqphytxi6XibueQCoAG3yBCkXvg1BfGnosd6xKnKPT', 'newPublicKey': '3Ghhbommc5imqoAUAzXoEJpYo1qLiyVZip3Pwsj7WUXq7aFXwC4tw1MFsRrhPnA51CMAoeoyGJcMGV1dSU9FAqXRsnV2LLT7tBKDeY', 'fee': 0.0002, 'payload': 'techworker', 'n_operation': 4003, 's': 'AEB41A5FB2D7A1B89C17897E87959AE63B9864236397FBDCFF118E964B1211C8', 'r': '5CAD340D46F1A926DCD3FA00962A0EFC856733E46285BF945B1F19A90DE2BEF8', 'raw': '0100000007000000F4FA1500F7FA1500A30F000002000000000000000A0074656368776F726B65720000000000004600CA0220003078FA6B1419BD6F4D4F6A779358EBFA6BC12F3CE32E47A8D9F7CD5FA8956FF0200053A101778813DD24B90A5198BAE5EB9F8AA5EDFD30E4C130A69FDD62CAE774F920005CAD340D46F1A926DCD3FA00962A0EFC856733E46285BF945B1F19A90DE2BEF82000AEB41A5FB2D7A1B89C17897E87959AE63B9864236397FBDCFF118E964B1211C8' }; }); test('can be decode a signed operation', () { ChangeKeySignedOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.r)), fixture['r']); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.s)), fixture['s']); expect(decoded.signer.account, fixture['sender']); expect(decoded.target.account, fixture['target']); expect(decoded.nOperation, fixture['n_operation']); expect(decoded.fee.toStringOpt(), fixture['fee'].toString()); expect(PDUtil.bytesToUtf8String(decoded.payload), fixture['payload']); expect(PublicKeyCoder().encodeToBase58(decoded.newPublicKey), fixture['newPublicKey']); }); test('can be decode signed operation and encode it again', () { ChangeKeySignedOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(RawOperationCoder.encodeToBytes(decoded)), fixture['raw']); }); test('can be build by hand', () { ChangeKeySignedOperation op = ChangeKeySignedOperation( signer: AccountNumber.fromInt(fixture['sender']), target: AccountNumber.fromInt(fixture['target']), newPublicKey: PublicKeyCoder().decodeFromBase58(fixture['newPublicKey'])) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..withSignature(Signature( r: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['r'])), s: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['s'])))); Uint8List encoded = RawOperationCoder.encodeToBytes(op); expect(PDUtil.byteToHex(encoded), fixture['raw']); }); test('can be built by hand and signed', () { PrivateKey pk = PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes( 'CA02200046D101363B3330D65373A70F6E47BB7745FC8EE1F9B3F71992D6B82648158D73')); ChangeKeySignedOperation op = ChangeKeySignedOperation( signer: AccountNumber.fromInt(fixture['sender']), target: AccountNumber.fromInt(fixture['target']), newPublicKey: PublicKeyCoder().decodeFromBase58(fixture['newPublicKey'])) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..sign(pk); expect(op.signature.r.toString().length > 30, true); expect(op.signature.s.toString().length > 30, true); }); }); } ================================================ FILE: test/signing/operations/delist_forsale_operation_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:pascaldart/crypto.dart'; import 'package:pascaldart/signing.dart'; import 'package:pascaldart/src/common/model/accountnumber.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('signing.operations.DeListForSaleOperation', () { Map fixture; setUp(() { fixture = { 'signer': 1554325, 'target': 1554326, 'seller': 0, 'price': 0, 'fee': 0.0001, 'payload': 'test', 'n_operation': 1, 'lockedUntilBlock': 0, 'r': '270B996CE47B3C725223A87799BF74E32E1800102B3442F271ED81396708C4EB', 's': '932D3542356861815F5A794E80D8945042B5A3852F5D99D1F30EE5C718522C0E', 'digest': '95B7170096B717000100000000000000000000000000000001000000000000007465737400000000000000000000000005', 'raw': '010000000500000095B7170096B7170005000100000001000000000000000400746573742000270B996CE47B3C725223A87799BF74E32E1800102B3442F271ED81396708C4EB2000932D3542356861815F5A794E80D8945042B5A3852F5D99D1F30EE5C718522C0E' }; }); test('can be decode a signed operation', () { DeListForSaleOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.r)), fixture['r']); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.s)), fixture['s']); expect(decoded.accountSigner.account, fixture['signer']); expect(decoded.targetSigner.account, fixture['target']); expect(decoded.accountToPay.account, fixture['seller']); expect(decoded.price.toStringOpt(), fixture['price'].toString()); expect(decoded.fee.toStringOpt(), fixture['fee'].toString()); expect(decoded.nOperation, fixture['n_operation']); expect(decoded.lockedUntilBlock, fixture['lockedUntilBlock']); expect(PDUtil.bytesToUtf8String(decoded.payload), fixture['payload']); }); test('can be decode signed operation and encode it again', () { DeListForSaleOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(RawOperationCoder.encodeToBytes(decoded)), fixture['raw']); }); test('can be built by hand', () { DeListForSaleOperation op = DeListForSaleOperation( accountSigner: AccountNumber.fromInt(fixture['signer']), targetSigner: AccountNumber.fromInt(fixture['target'])) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..withSignature(Signature( r: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['r'])), s: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['s'])))); Uint8List encoded = RawOperationCoder.encodeToBytes(op); expect(PDUtil.byteToHex(encoded), fixture['raw']); }); test('can be built by hand and signed', () { PrivateKey pk = PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes( 'CA02200046D101363B3330D65373A70F6E47BB7745FC8EE1F9B3F71992D6B82648158D73')); DeListForSaleOperation op = DeListForSaleOperation( accountSigner: AccountNumber.fromInt(fixture['signer']), targetSigner: AccountNumber.fromInt(fixture['target'])) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..sign(pk); expect(op.signature.r.toString().length > 30, true); expect(op.signature.s.toString().length > 30, true); }); test('can generate correct digest', () { DeListForSaleOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(decoded.digest()), fixture['digest']); }); }); } ================================================ FILE: test/signing/operations/list_forsale_operation_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:pascaldart/crypto.dart'; import 'package:pascaldart/signing.dart'; import 'package:pascaldart/src/common/model/accountnumber.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('signing.operations.ListForSaleOperation', () { Map fixture; setUp(() { fixture = { 'signerPublicKey': '3GhhbojvCFtyYSPxXfuk86uvJhURBEzF2fxV7x6kuW3Gi7ApSDd1scztAXGyeqphytxi6XibueQCoAG3yBCkXvg1BfGnosd6xKnKPT', 'signer': 1440500, 'target': 1440503, 'seller': 1440500, 'lockedUntilBlock': 350000, 'price': 0.0017, 'newPublicKey': '3Ghhbommc5imqoAUAzXoEJpYo1qLiyVZip3Pwsj7WUXq7aFXwC4tw1MFsRrhPnA51CMAoeoyGJcMGV1dSU9FAqXRsnV2LLT7tBKDeY', 'fee': 0.0002, 'payload': 'techworker', 'n_operation': 4003, 's': '32D4CAF3A0FCF1854C00E18FA0838A2EF926C63FAEEF7C862E1750CB6D20DFD0', 'r': 'A8A08439B6A54BF7E42EFB971B5709874E7F12F6B265684CAA9937BD28BDC793', 'raw': '0100000004000000F4FA1500F7FA15000400A30F00001100000000000000F4FA15000000000000004600CA0220003078FA6B1419BD6F4D4F6A779358EBFA6BC12F3CE32E47A8D9F7CD5FA8956FF0200053A101778813DD24B90A5198BAE5EB9F8AA5EDFD30E4C130A69FDD62CAE774F93057050002000000000000000A0074656368776F726B65722000A8A08439B6A54BF7E42EFB971B5709874E7F12F6B265684CAA9937BD28BDC793200032D4CAF3A0FCF1854C00E18FA0838A2EF926C63FAEEF7C862E1750CB6D20DFD0' }; }); test('can be decode a signed operation', () { ListForSaleOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.r)), fixture['r']); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.s)), fixture['s']); expect(decoded.accountSigner.account, fixture['signer']); expect(decoded.targetSigner.account, fixture['target']); expect(decoded.accountToPay.account, fixture['seller']); expect(decoded.price.toStringOpt(), fixture['price'].toString()); expect(decoded.fee.toStringOpt(), fixture['fee'].toString()); expect(decoded.nOperation, fixture['n_operation']); expect(decoded.lockedUntilBlock, fixture['lockedUntilBlock']); expect(PDUtil.bytesToUtf8String(decoded.payload), fixture['payload']); expect(PublicKeyCoder().encodeToBase58(decoded.newPublicKey), fixture['newPublicKey']); }); test('can be decode signed operation and encode it again', () { ListForSaleOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(RawOperationCoder.encodeToBytes(decoded)), fixture['raw']); }); test('can be built by hand', () { ListForSaleOperation op = ListForSaleOperation( accountSigner: AccountNumber.fromInt(fixture['signer']), targetSigner: AccountNumber.fromInt(fixture['target']), price: Currency(fixture['price'].toString()), accountToPay: AccountNumber.fromInt(fixture['seller'])) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..asPrivateSale( PublicKeyCoder().decodeFromBase58(fixture['newPublicKey']), fixture['lockedUntilBlock']) ..withSignature(Signature( r: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['r'])), s: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['s'])))); Uint8List encoded = RawOperationCoder.encodeToBytes(op); expect(PDUtil.byteToHex(encoded), fixture['raw']); }); test('can be built by hand and signed', () { PrivateKey pk = PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes( 'CA02200046D101363B3330D65373A70F6E47BB7745FC8EE1F9B3F71992D6B82648158D73')); ListForSaleOperation op = ListForSaleOperation( accountSigner: AccountNumber.fromInt(fixture['signer']), targetSigner: AccountNumber.fromInt(fixture['target']), price: Currency(fixture['price'].toString()), accountToPay: AccountNumber.fromInt(fixture['seller'])) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..asPrivateSale( PublicKeyCoder().decodeFromBase58(fixture['newPublicKey']), fixture['lockedUntilBlock']) ..sign(pk); expect(op.signature.r.toString().length > 30, true); expect(op.signature.s.toString().length > 30, true); }); }); } ================================================ FILE: test/signing/operations/transaction_operation_test.dart ================================================ import 'dart:typed_data'; import 'package:pascaldart/common.dart'; import 'package:pascaldart/crypto.dart'; import 'package:pascaldart/signing.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('signing.operations.TransactionOperation', () { Map fixture; setUp(() { fixture = { 'sender': 1440500, 'target': 1440503, 'senderPubkey': '3GhhbojvCFtyYSPxXfuk86uvJhURBEzF2fxV7x6kuW3Gi7ApSDd1scztAXGyeqphytxi6XibueQCoAG3yBCkXvg1BfGnosd6xKnKPT', 'targetPubkey': '3GhhbojvCFtyYSPxXfuk86uvJhURBEzF2fxV7x6kuW3Gi7ApSDd1scztAXGyeqphytxi6XibueQCoAG3yBCkXvg1BfGnosd6xKnKPT', 'amount': 0.0015, 'fee': 0.0002, 'payload': 'techworker', 'n_operation': 4003, 'r': '1201D10D537B0F6D7CB2032A768A851E0EF2426AA37756BCADB994AF189D124A', 's': '9DACA47597ADFB3E529B82EB231396F464EE1C4E76DC6E85CD10CA3711C2D6C2', 'raw': '0100000001000000F4FA1500A30F0000F7FA15000F0000000000000002000000000000000A0074656368776F726B657200000000000020001201D10D537B0F6D7CB2032A768A851E0EF2426AA37756BCADB994AF189D124A20009DACA47597ADFB3E529B82EB231396F464EE1C4E76DC6E85CD10CA3711C2D6C2' }; }); test('can be decode a signed operation', () { TransactionOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.r)), fixture['r']); expect(PDUtil.byteToHex(PDUtil.encodeBigInt(decoded.signature.s)), fixture['s']); expect(decoded.sender.account, fixture['sender']); expect(decoded.target.account, fixture['target']); expect(decoded.amount.toStringOpt(), fixture['amount'].toString()); expect(decoded.fee.toStringOpt(), fixture['fee'].toString()); expect(decoded.nOperation, fixture['n_operation']); expect(PDUtil.bytesToUtf8String(decoded.payload), fixture['payload']); }); test('can be decode signed operation and encode it again', () { TransactionOperation decoded = RawOperationCoder.decodeFromBytes(PDUtil.hexToBytes(fixture['raw'])); expect(PDUtil.byteToHex(RawOperationCoder.encodeToBytes(decoded)), fixture['raw']); }); test('can be built by hand', () { TransactionOperation op = TransactionOperation( sender: AccountNumber.fromInt(fixture['sender']), target: AccountNumber.fromInt(fixture['target']), amount: Currency(fixture['amount'].toString())) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..withSignature(Signature( r: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['r'])), s: PDUtil.decodeBigInt(PDUtil.hexToBytes(fixture['s'])))); Uint8List encoded = RawOperationCoder.encodeToBytes(op); expect(PDUtil.byteToHex(encoded), fixture['raw']); }); test('can be built by hand and signed', () { PrivateKey pk = PrivateKeyCoder().decodeFromBytes(PDUtil.hexToBytes( 'CA02200046D101363B3330D65373A70F6E47BB7745FC8EE1F9B3F71992D6B82648158D73')); TransactionOperation op = TransactionOperation( sender: AccountNumber.fromInt(fixture['sender']), target: AccountNumber.fromInt(fixture['target']), amount: Currency(fixture['amount'].toString())) ..withNOperation(fixture['n_operation']) ..withPayload(PDUtil.stringToBytesUtf8(fixture['payload'])) ..withFee(Currency(fixture['fee'].toString())) ..sign(pk); expect(op.signature.r.toString().length > 30, true); expect(op.signature.s.toString().length > 30, true); }); test('can calculate digest correctly', () { TransactionOperation op = TransactionOperation( sender: AccountNumber.fromInt(1440500), target: AccountNumber.fromInt(1440503), amount: Currency('0.0015') ) ..withFee(Currency('0')) ..withPayload(PDUtil.stringToBytesUtf8('')) ..withNOperation(4003); expect(PDUtil.byteToHex(op.digest()), 'F4FA1500A30F0000F7FA15000F000000000000000000000000000000000001'); TransactionOperation op2 = TransactionOperation( sender: AccountNumber.fromInt(1440500), target: AccountNumber.fromInt(1440503), amount: Currency('0.0015') ) ..withFee(Currency('0.0002')) ..withPayload(PDUtil.stringToBytesUtf8('techworker')) ..withNOperation(4003); expect(PDUtil.byteToHex(op2.digest()), 'F4FA1500A30F0000F7FA15000F00000000000000020000000000000074656368776F726B6572000001'); }); }); }