├── .gitignore ├── src ├── build │ ├── last_build_run.json │ └── app │ │ └── intermediates │ │ └── flutter │ │ └── debug │ │ └── .last_build_id ├── ios │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Info.plist │ │ └── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── .gitignore │ └── Podfile ├── web │ ├── favicon.ico │ ├── favicon.png │ ├── kira_logo.png │ ├── favicon-16x16.png │ ├── manifest.json │ └── index.html ├── assets │ ├── images │ │ ├── key.png │ │ ├── cover.jpeg │ │ ├── background.png │ │ ├── kira_logo.png │ │ ├── kira_qr_logo.png │ │ └── kira_logo_gray.png │ └── config.json ├── lib │ ├── models │ │ ├── transactions │ │ │ ├── messages │ │ │ │ ├── export.dart │ │ │ │ ├── msg_vote.g.dart │ │ │ │ ├── msg_send.g.dart │ │ │ │ ├── msg_vote.dart │ │ │ │ └── msg_send.dart │ │ │ ├── std_signature.dart │ │ │ ├── ext_option.dart │ │ │ ├── export.dart │ │ │ ├── std_public_key.dart │ │ │ ├── std_coin.g.dart │ │ │ ├── std_coin.dart │ │ │ ├── std_encode_message.dart │ │ │ ├── auth_info.dart │ │ │ ├── std_encode_message.g.dart │ │ │ ├── std_tx.dart │ │ │ ├── vote_tx.dart │ │ │ ├── std_encode_tx.dart │ │ │ ├── std_fee.g.dart │ │ │ ├── std_signature_message.dart │ │ │ ├── vote_signature_message.dart │ │ │ ├── std_fee.dart │ │ │ ├── std_signature_message.g.dart │ │ │ ├── vote_signature_message.g.dart │ │ │ ├── vote_msg.dart │ │ │ ├── std_msg.dart │ │ │ └── signer_info.dart │ │ ├── export.dart │ │ ├── method.dart │ │ ├── protocol_version.dart │ │ ├── protocol_version.g.dart │ │ ├── method.g.dart │ │ ├── transaction.dart │ │ ├── network_info.dart │ │ ├── cosmos_account.g.dart │ │ ├── validator_info.dart │ │ ├── validator_info.g.dart │ │ ├── transaction.g.dart │ │ ├── sync_info.dart │ │ ├── account.g.dart │ │ ├── token.dart │ │ ├── sync_info.g.dart │ │ ├── token.g.dart │ │ ├── node_info.dart │ │ ├── node_info.g.dart │ │ ├── cosmos_account.dart │ │ ├── validator.dart │ │ ├── block.dart │ │ ├── block_transaction.dart │ │ └── tx_types.dart │ ├── blocs │ │ ├── export.dart │ │ ├── network_event.dart │ │ ├── token_event.dart │ │ ├── validator_event.dart │ │ ├── network_bloc.dart │ │ ├── account_event.dart │ │ ├── validator_state.dart │ │ ├── network_state.dart │ │ ├── token_state.dart │ │ ├── token_bloc.dart │ │ ├── validator_bloc.dart │ │ ├── account_bloc.dart │ │ └── account_state.dart │ ├── helpers │ │ ├── export.dart │ │ ├── encode_tx_builder.dart │ │ ├── encode_tx_sender.dart │ │ ├── validation_checker.dart │ │ ├── tx_builder.dart │ │ ├── tx_sender.dart │ │ └── tx_offline_signer.dart │ ├── utils │ │ ├── tokens.dart │ │ ├── export.dart │ │ ├── utils.dart │ │ ├── pc_utils.dart │ │ ├── responsive.dart │ │ ├── map_sorter.dart │ │ ├── bech32_encoder.dart │ │ ├── styles.dart │ │ ├── colors.dart │ │ ├── encrypt.dart │ │ └── msg_signer.dart │ ├── services │ │ ├── export.dart │ │ ├── gravatar_service.dart │ │ ├── query_service.dart │ │ ├── rpc_methods_service.dart │ │ └── status_service.dart │ ├── widgets │ │ ├── custom_card.dart │ │ ├── export.dart │ │ ├── custom_slider_thumb_circle.dart │ │ ├── appbar_button.dart │ │ ├── custom_dialog.dart │ │ ├── mnemonic_display.dart │ │ ├── custom_button.dart │ │ └── dotted_border.dart │ ├── webcam │ │ ├── qr_code_scanner_web_impl_no_web.dart │ │ ├── qr_code_scanner_web.dart │ │ └── qr_code_scanner_web_impl.dart │ ├── data │ │ ├── token_repository.dart │ │ ├── validator_repository.dart │ │ └── account_repository.dart │ ├── screens │ │ └── global_screen.dart │ ├── config.dart │ └── main.dart ├── android │ ├── gradle.properties │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ └── values │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ └── src │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── settings.gradle │ ├── build.gradle │ └── .project ├── .metadata ├── .gitignore ├── test │ └── widget_test.dart ├── README.md ├── pubspec.yaml.native └── pubspec.yaml ├── .DS_Store ├── google-chrome-safe.sh ├── .vscode └── launch.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | front_signed.json 2 | response.json 3 | -------------------------------------------------------------------------------- /src/build/last_build_run.json: -------------------------------------------------------------------------------- 1 | ["--track-widget-creation"] -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/.DS_Store -------------------------------------------------------------------------------- /src/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /src/build/app/intermediates/flutter/debug/.last_build_id: -------------------------------------------------------------------------------- 1 | ec53254d692bda8028b69ae44ebcc312 -------------------------------------------------------------------------------- /src/web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/web/favicon.ico -------------------------------------------------------------------------------- /src/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/web/favicon.png -------------------------------------------------------------------------------- /src/web/kira_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/web/kira_logo.png -------------------------------------------------------------------------------- /src/assets/images/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/assets/images/key.png -------------------------------------------------------------------------------- /src/lib/models/transactions/messages/export.dart: -------------------------------------------------------------------------------- 1 | export 'msg_send.dart'; 2 | export 'msg_vote.dart'; 3 | -------------------------------------------------------------------------------- /src/web/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/web/favicon-16x16.png -------------------------------------------------------------------------------- /src/assets/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "api_url": ["0.0.0.0:11000", "127.0.0.1:11000"], 3 | "autoconnect": false 4 | } 5 | -------------------------------------------------------------------------------- /src/assets/images/cover.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/assets/images/cover.jpeg -------------------------------------------------------------------------------- /src/assets/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/assets/images/background.png -------------------------------------------------------------------------------- /src/assets/images/kira_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/assets/images/kira_logo.png -------------------------------------------------------------------------------- /src/assets/images/kira_qr_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/assets/images/kira_qr_logo.png -------------------------------------------------------------------------------- /src/assets/images/kira_logo_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/assets/images/kira_logo_gray.png -------------------------------------------------------------------------------- /src/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /google-chrome-safe.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | /usr/bin/google-chrome-stable --disable-web-security --user-data-dir="/Volumes/Dev/Blockchain/KiraCore/kira-frontend/tmp" $* -------------------------------------------------------------------------------- /src/lib/blocs/export.dart: -------------------------------------------------------------------------------- 1 | export './account_bloc.dart'; 2 | export './token_bloc.dart'; 3 | export './validator_bloc.dart'; 4 | export './network_bloc.dart'; 5 | -------------------------------------------------------------------------------- /src/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /src/android/app/src/main/kotlin/com/example/src/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.src 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamsuiux/kira-frontend/HEAD/src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /src/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 3 | #include "Generated.xcconfig" 4 | -------------------------------------------------------------------------------- /src/lib/helpers/export.dart: -------------------------------------------------------------------------------- 1 | export 'validation_checker.dart'; 2 | export 'encode_tx_builder.dart'; 3 | export 'encode_tx_sender.dart'; 4 | export 'tx_builder.dart'; 5 | export 'tx_sender.dart'; 6 | export 'tx_signer.dart'; 7 | -------------------------------------------------------------------------------- /src/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 3 | #include "Generated.xcconfig" 4 | -------------------------------------------------------------------------------- /src/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/lib/utils/tokens.dart: -------------------------------------------------------------------------------- 1 | class Tokens { 2 | static String getTokenFromDenom(String denom) { 3 | switch (denom) { 4 | case 'ukex': 5 | case 'mkex': 6 | return "KEX"; 7 | } 8 | return ''; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /src/lib/services/export.dart: -------------------------------------------------------------------------------- 1 | export 'query_service.dart'; 2 | export 'rpc_methods_service.dart'; 3 | export 'status_service.dart'; 4 | export 'token_service.dart'; 5 | export 'gravatar_service.dart'; 6 | export 'transaction_service.dart'; 7 | export 'network_service.dart'; 8 | export 'proposal_service.dart'; 9 | -------------------------------------------------------------------------------- /src/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /src/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/lib/models/transactions/std_signature.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | class StdSignature { 4 | final String signature; 5 | 6 | const StdSignature({ 7 | @required this.signature, 8 | }) : assert(signature != null); 9 | 10 | Map toJson() => { 11 | 'secp256k1': signature, 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /src/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 916c3ac648aa0498a70f32b5fc4f6c51447628e3 8 | channel: beta 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /src/lib/utils/export.dart: -------------------------------------------------------------------------------- 1 | export 'bech32_encoder.dart'; 2 | export 'cache.dart'; 3 | export 'colors.dart'; 4 | export 'encrypt.dart'; 5 | export 'pc_utils.dart'; 6 | export 'responsive.dart'; 7 | export 'strings.dart'; 8 | export 'styles.dart'; 9 | export 'tokens.dart'; 10 | export 'map_sorter.dart'; 11 | export 'utils.dart'; 12 | export 'saifu_fast_qr.dart'; 13 | -------------------------------------------------------------------------------- /src/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | 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. -------------------------------------------------------------------------------- /src/lib/blocs/network_event.dart: -------------------------------------------------------------------------------- 1 | part of 'network_bloc.dart'; 2 | 3 | @immutable 4 | abstract class NetworkEvent extends Equatable { 5 | const NetworkEvent(); 6 | } 7 | 8 | class SetNetworkInfo extends NetworkEvent { 9 | final String networkId; 10 | final String nodeAddress; 11 | 12 | const SetNetworkInfo(this.networkId, this.nodeAddress); 13 | 14 | @override 15 | List get props => []; 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "src", 9 | "cwd": "src", 10 | "request": "launch", 11 | "type": "dart" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /src/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/models/export.dart: -------------------------------------------------------------------------------- 1 | export 'account.dart'; 2 | export 'cosmos_account.dart'; 3 | export 'method.dart'; 4 | export 'network_info.dart'; 5 | export 'protocol_version.dart'; 6 | export 'token.dart'; 7 | export 'node_info.dart'; 8 | export 'sync_info.dart'; 9 | export 'validator_info.dart'; 10 | export 'transaction.dart'; 11 | export 'transactions/export.dart'; 12 | export 'validator.dart'; 13 | export 'block.dart'; 14 | export 'block_transaction.dart'; 15 | export 'proposal.dart'; 16 | export 'tx_types.dart'; 17 | -------------------------------------------------------------------------------- /src/lib/services/gravatar_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:jdenticon_dart/jdenticon_dart.dart'; 2 | 3 | class GravatarService { 4 | String getIdenticon(address) { 5 | return Jdenticon.toSvg( 6 | address, 7 | colorSaturation: 0.41, 8 | grayscaleSaturation: 0.67, 9 | colorLightnessMinValue: 0.15, 10 | colorLightnessMaxValue: 0.80, 11 | grayscaleLightnessMinValue: 0.40, 12 | grayscaleLightnessMaxValue: 0.71, 13 | backColor: '#cecb9f28', 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/models/transactions/ext_option.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | // ignore: todo 4 | // TODO: Define ExtOption 5 | class ExtOption { 6 | final String option; 7 | 8 | const ExtOption({ 9 | @required this.option, 10 | }) : assert(option != null); 11 | 12 | factory ExtOption.fromJson(Map json) => ExtOption( 13 | option: json['option'] as String, 14 | ); 15 | 16 | Map toJson() => { 17 | 'option': this.option, 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /src/lib/blocs/token_event.dart: -------------------------------------------------------------------------------- 1 | part of 'token_bloc.dart'; 2 | 3 | @immutable 4 | abstract class TokenEvent extends Equatable { 5 | const TokenEvent(); 6 | } 7 | 8 | class QueryTokens extends TokenEvent { 9 | final List tokens; 10 | const QueryTokens(this.tokens); 11 | 12 | @override 13 | List get props => []; 14 | } 15 | 16 | class SetFeeToken extends TokenEvent { 17 | final Token feeToken; 18 | const SetFeeToken(this.feeToken); 19 | 20 | @override 21 | List get props => []; 22 | } 23 | -------------------------------------------------------------------------------- /src/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Kira Frontend", 3 | "short_name": "Kira Frontend", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "Kira Auth", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/kira_logo.PNG", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /src/lib/models/transactions/export.dart: -------------------------------------------------------------------------------- 1 | export 'messages/export.dart'; 2 | export 'ext_option.dart'; 3 | export 'auth_info.dart'; 4 | export 'signer_info.dart'; 5 | export 'std_coin.dart'; 6 | export 'std_fee.dart'; 7 | export 'std_msg.dart'; 8 | export 'std_public_key.dart'; 9 | export 'std_signature.dart'; 10 | export 'std_signature_message.dart'; 11 | export 'std_encode_message.dart'; 12 | export 'std_tx.dart'; 13 | export 'std_encode_tx.dart'; 14 | export 'vote_tx.dart'; 15 | export 'vote_msg.dart'; 16 | export 'vote_signature_message.dart'; 17 | -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/models/method.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'method.g.dart'; 4 | 5 | @JsonSerializable(fieldRename: FieldRename.snake) 6 | class Method { 7 | final String description; 8 | final bool enabled; 9 | final double rateLimit; 10 | final double authRateLimit; 11 | 12 | Method({this.description, this.enabled, this.rateLimit, this.authRateLimit}); 13 | 14 | factory Method.fromJson(Map json) => _$MethodFromJson(json); 15 | 16 | Map toJson() => _$MethodToJson(this); 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/models/transactions/std_public_key.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | import 'dart:convert'; 3 | 4 | class StdPublicKey { 5 | final String type; 6 | final String key; 7 | 8 | const StdPublicKey({@required this.type, @required this.key}) 9 | : assert(type != null), 10 | assert(key != null); 11 | 12 | factory StdPublicKey.fromJson(Map json) => StdPublicKey(type: json['@type'], key: json['key']); 13 | 14 | Map toJson() => {'@type': this.type, 'key': this.key}; 15 | 16 | String toString() => jsonEncode(toJson()); 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/models/protocol_version.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'protocol_version.g.dart'; 4 | 5 | @JsonSerializable() 6 | class ProtocolVersion { 7 | final String p2p; 8 | final String block; 9 | final String app; 10 | 11 | ProtocolVersion({ 12 | this.p2p, 13 | this.block, 14 | this.app, 15 | }) { 16 | assert(this.p2p != null || this.block != null || this.app != null); 17 | } 18 | 19 | factory ProtocolVersion.fromJson(Map json) => _$ProtocolVersionFromJson(json); 20 | 21 | Map toJson() => _$ProtocolVersionToJson(this); 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/blocs/validator_event.dart: -------------------------------------------------------------------------------- 1 | part of 'validator_bloc.dart'; 2 | 3 | @immutable 4 | abstract class ValidatorEvent extends Equatable { 5 | const ValidatorEvent(); 6 | } 7 | 8 | class GetCachedValidators extends ValidatorEvent { 9 | final String userAddress; 10 | 11 | const GetCachedValidators(this.userAddress); 12 | 13 | @override 14 | List get props => []; 15 | } 16 | 17 | class ToggleFavoriteAddress extends ValidatorEvent { 18 | final String address; 19 | final String userAddress; 20 | 21 | const ToggleFavoriteAddress(this.address, this.userAddress); 22 | 23 | @override 24 | List get props => []; 25 | } 26 | -------------------------------------------------------------------------------- /src/lib/models/transactions/std_coin.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'std_coin.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | StdCoin _$StdCoinFromJson(Map json) { 10 | return StdCoin( 11 | denom: json['denom'] as String, 12 | amount: json['amount'] as String, 13 | ); 14 | } 15 | 16 | Map _$StdCoinToJson(StdCoin instance) => { 17 | 'denom': instance.denom, 18 | 'amount': instance.amount, 19 | }; 20 | -------------------------------------------------------------------------------- /src/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /src/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /src/lib/widgets/custom_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class CustomCard extends StatelessWidget { 5 | final Widget child; 6 | final Color color; 7 | final Function() onTap; 8 | 9 | const CustomCard({Key key, this.child, this.color, this.onTap}) 10 | : super(key: key); 11 | @override 12 | Widget build(BuildContext context) { 13 | return Material( 14 | color: color ?? Colors.white, 15 | child: InkWell( 16 | child: Padding(child: child, padding: EdgeInsets.all(20)), 17 | onTap: onTap, 18 | borderRadius: BorderRadius.circular(30)), 19 | shadowColor: Colors.grey, 20 | elevation: 12, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/models/protocol_version.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'protocol_version.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | ProtocolVersion _$ProtocolVersionFromJson(Map json) { 10 | return ProtocolVersion( 11 | p2p: json['p2p'] as String, 12 | block: json['block'] as String, 13 | app: json['app'] as String, 14 | ); 15 | } 16 | 17 | Map _$ProtocolVersionToJson(ProtocolVersion instance) => { 18 | 'p2p': instance.p2p, 19 | 'block': instance.block, 20 | 'app': instance.app, 21 | }; 22 | -------------------------------------------------------------------------------- /src/lib/models/transactions/messages/msg_vote.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'msg_vote.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | MsgVote _$MsgVoteFromJson(Map json) { 10 | return MsgVote( 11 | proposalId: json['proposal_id'] as String, 12 | voter: json['voter'] as String, 13 | option: json['option'], 14 | ); 15 | } 16 | 17 | Map _$MsgVoteToJson(MsgVote instance) => { 18 | 'proposal_id': instance.proposalId, 19 | 'voter': instance.voter, 20 | 'option': instance.option, 21 | }; 22 | -------------------------------------------------------------------------------- /src/lib/widgets/export.dart: -------------------------------------------------------------------------------- 1 | export 'app_text_field.dart'; 2 | export 'appbar_button.dart'; 3 | export 'custom_button.dart'; 4 | export 'custom_card.dart'; 5 | export 'custom_slider.dart'; 6 | export 'custom_slider_thumb_circle.dart'; 7 | export 'floating_quick_access_bar.dart'; 8 | export 'hamburger_drawer.dart'; 9 | export 'header_wrapper.dart'; 10 | export 'mnemonic_display.dart'; 11 | export 'token_balances_table.dart'; 12 | export 'top_bar_contents.dart'; 13 | export 'web_scrollbar.dart'; 14 | export 'deposit_transactions_table.dart'; 15 | export 'withdrawal_transactions_table.dart'; 16 | export 'validators_table.dart'; 17 | export 'blocks_table.dart'; 18 | export 'dropzone.dart'; 19 | export 'custom_dialog.dart'; 20 | export 'proposals_table.dart'; 21 | export 'token_table.dart'; 22 | -------------------------------------------------------------------------------- /src/lib/models/transactions/std_coin.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | import 'package:meta/meta.dart'; 4 | 5 | part 'std_coin.g.dart'; 6 | 7 | /// Contains the data of a specific coin amount. 8 | @JsonSerializable(explicitToJson: true) 9 | class StdCoin extends Equatable { 10 | @JsonKey(name: 'denom') 11 | final String denom; 12 | 13 | @JsonKey(name: 'amount') 14 | final String amount; 15 | 16 | const StdCoin({ 17 | @required this.denom, 18 | @required this.amount, 19 | }); 20 | 21 | @override 22 | List get props => [denom, amount]; 23 | 24 | factory StdCoin.fromJson(Map json) => 25 | _$StdCoinFromJson(json); 26 | 27 | Map toJson() => _$StdCoinToJson(this); 28 | } 29 | -------------------------------------------------------------------------------- /src/lib/webcam/qr_code_scanner_web_impl_no_web.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class QrCodeCameraWebImpl extends StatefulWidget { 4 | final void Function(String qrValue) qrCodeCallback; 5 | final Widget child; 6 | final BoxFit fit; 7 | final Widget Function(BuildContext context, Object error) onError; 8 | 9 | QrCodeCameraWebImpl({ 10 | Key key, 11 | @required this.qrCodeCallback, 12 | this.child, 13 | this.fit = BoxFit.cover, 14 | this.onError, 15 | }) : assert(qrCodeCallback != null), 16 | super(key: key); 17 | 18 | @override 19 | State createState() { 20 | throw FittedBox( 21 | fit: this.fit, 22 | child: Container( 23 | child: Text('it is not in web environment'), 24 | ), 25 | ); 26 | } 27 | 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android 4 | Project android created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | 19 | 1608756501778 20 | 21 | 30 22 | 23 | org.eclipse.core.resources.regexFilterMatcher 24 | node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/lib/data/token_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | import 'package:kira_auth/models/export.dart'; 3 | import 'package:kira_auth/services/export.dart'; 4 | 5 | abstract class TokenRepository { 6 | Future getFeeTokenFromCache(); 7 | Future> getTokens(address); 8 | } 9 | 10 | class ITokenRepository implements TokenRepository { 11 | @override 12 | Future getFeeTokenFromCache() async { 13 | SharedPreferences prefs = await SharedPreferences.getInstance(); 14 | String feeTokenString = prefs.getString('feeToken'); 15 | 16 | return Token.fromString(feeTokenString); 17 | } 18 | 19 | @override 20 | Future> getTokens(address) async { 21 | TokenService tokenService = new TokenService(); 22 | await tokenService.getTokens(address); 23 | return tokenService.tokens; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/lib/models/method.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'method.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Method _$MethodFromJson(Map json) { 10 | return Method( 11 | description: json['description'] as String, 12 | enabled: json['enabled'] as bool, 13 | rateLimit: (json['rate_limit'] as num)?.toDouble(), 14 | authRateLimit: (json['auth_rate_limit'] as num)?.toDouble(), 15 | ); 16 | } 17 | 18 | Map _$MethodToJson(Method instance) => { 19 | 'description': instance.description, 20 | 'enabled': instance.enabled, 21 | 'rate_limit': instance.rateLimit, 22 | 'auth_rate_limit': instance.authRateLimit, 23 | }; 24 | -------------------------------------------------------------------------------- /src/lib/models/transactions/std_encode_message.dart: -------------------------------------------------------------------------------- 1 | import 'package:kira_auth/models/transactions/std_encode_tx.dart'; 2 | import 'package:meta/meta.dart'; 3 | 4 | part 'std_encode_message.g.dart'; 5 | 6 | class StdEncodeMessage { 7 | final String chainId; 8 | final String accountNumber; 9 | final String sequence; 10 | final StdEncodeTx tx; 11 | 12 | const StdEncodeMessage({ 13 | @required this.chainId, 14 | @required this.accountNumber, 15 | @required this.sequence, 16 | @required this.tx, 17 | }) : assert(chainId != null), 18 | assert(accountNumber != null), 19 | assert(sequence != null), 20 | assert(tx != null); 21 | 22 | factory StdEncodeMessage.fromJson(Map json) { 23 | return _$StdEncodeMessageFromJson(json); 24 | } 25 | 26 | Map toJson() { 27 | return _$StdEncodeMessageToJson(this); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/lib/models/transactions/auth_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:kira_auth/models/export.dart'; 2 | import 'package:meta/meta.dart'; 3 | 4 | class AuthInfo { 5 | List signerInfos; 6 | final StdFee stdFee; 7 | 8 | AuthInfo({ 9 | @required this.signerInfos, 10 | @required this.stdFee, 11 | }) : assert(stdFee != null); 12 | 13 | factory AuthInfo.fromJson(Map json) => AuthInfo( 14 | signerInfos: (json['signer_infos'] as List) 15 | ?.map((e) => e == null 16 | ? null 17 | : SignerInfo.fromJson(e as Map)) 18 | ?.toList(), 19 | stdFee: StdFee.fromJson(json['std_fee']), 20 | ); 21 | 22 | Map toJson() => { 23 | 'signer_infos': 24 | this.signerInfos.map((signerInfo) => signerInfo.toJson()).toList(), 25 | 'fee': this.stdFee.toJson(), 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/models/transaction.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'dart:convert'; 3 | 4 | part 'transaction.g.dart'; 5 | 6 | @JsonSerializable(fieldRename: FieldRename.snake) 7 | class Transaction { 8 | String hash; 9 | String action; 10 | String sender; 11 | String recipient; 12 | String token; 13 | String amount; 14 | String gas; 15 | String status; 16 | String timestamp; 17 | bool isNew; 18 | 19 | Transaction({ 20 | this.hash, 21 | this.action, 22 | this.sender, 23 | this.recipient, 24 | this.token, 25 | this.amount, 26 | this.isNew, 27 | this.gas, 28 | this.status, 29 | this.timestamp, 30 | }); 31 | 32 | factory Transaction.fromJson(Map json) => _$TransactionFromJson(json); 33 | 34 | Map toJson() => _$TransactionToJson(this); 35 | 36 | String toString() => jsonEncode(toJson()); 37 | } 38 | -------------------------------------------------------------------------------- /src/lib/webcam/qr_code_scanner_web.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'qr_code_scanner_web_impl_no_web.dart' 3 | if (dart.library.html) 'qr_code_scanner_web_impl.dart'; 4 | 5 | class QrCodeCameraWeb extends StatelessWidget { 6 | final void Function(String qrValue) qrCodeCallback; 7 | final Widget child; 8 | final BoxFit fit; 9 | final Widget Function(BuildContext context, Object error) onError; 10 | 11 | QrCodeCameraWeb({ 12 | Key key, 13 | @required this.qrCodeCallback, 14 | this.child, 15 | this.fit = BoxFit.cover, 16 | this.onError, 17 | }) : assert(qrCodeCallback != null), 18 | super(key: key); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return QrCodeCameraWebImpl( 23 | key: key, 24 | qrCodeCallback: qrCodeCallback, 25 | child: child, 26 | fit: fit, 27 | onError: onError, 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/lib/models/transactions/std_encode_message.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'std_encode_message.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | StdEncodeMessage _$StdEncodeMessageFromJson(Map json) { 10 | return StdEncodeMessage( 11 | chainId: json['chain_id'] as String, 12 | accountNumber: json['account_number'] as String, 13 | sequence: json['sequence'] as String, 14 | tx: json['tx'] as StdEncodeTx, 15 | ); 16 | } 17 | 18 | Map _$StdEncodeMessageToJson(StdEncodeMessage instance) => 19 | { 20 | 'chain_id': instance.chainId, 21 | 'account_number': instance.accountNumber, 22 | 'sequence': instance.sequence, 23 | 'tx': instance.tx.toJson(), 24 | }; 25 | -------------------------------------------------------------------------------- /src/lib/models/transactions/messages/msg_send.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'msg_send.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | MsgSend _$MsgSendFromJson(Map json) { 10 | return MsgSend( 11 | fromAddress: json['from_address'] as String, 12 | toAddress: json['to_address'] as String, 13 | amount: (json['amount'] as List) 14 | ?.map((e) => 15 | e == null ? null : StdCoin.fromJson(e as Map)) 16 | ?.toList(), 17 | ); 18 | } 19 | 20 | Map _$MsgSendToJson(MsgSend instance) => { 21 | 'from_address': instance.fromAddress, 22 | 'to_address': instance.toAddress, 23 | 'amount': instance.amount?.map((e) => e?.toJson())?.toList(), 24 | }; 25 | -------------------------------------------------------------------------------- /src/lib/models/transactions/std_tx.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:meta/meta.dart'; 4 | import 'package:kira_auth/models/transactions/export.dart'; 5 | 6 | class StdTx { 7 | final StdMsg stdMsg; 8 | final AuthInfo authInfo; 9 | final List signatures; 10 | String accountNumber; 11 | String sequence; 12 | 13 | StdTx({ 14 | @required this.stdMsg, 15 | @required this.authInfo, 16 | @required this.signatures, 17 | this.accountNumber, 18 | this.sequence, 19 | }) : assert(stdMsg != null), 20 | assert(authInfo != null), 21 | assert(signatures == null || signatures.isNotEmpty); 22 | 23 | Map toJson() => { 24 | 'body': this.stdMsg.toJson(), 25 | 'auth_info': this.authInfo.toJson(), 26 | 'signatures': this.signatures != null ? this.signatures : [], 27 | }; 28 | 29 | @override 30 | String toString() { 31 | return jsonEncode(toJson()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/lib/models/transactions/vote_tx.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:meta/meta.dart'; 4 | import 'package:kira_auth/models/transactions/export.dart'; 5 | 6 | class VoteTx { 7 | final VoteMsg voteMsg; 8 | final AuthInfo authInfo; 9 | final List signatures; 10 | String accountNumber; 11 | String sequence; 12 | 13 | VoteTx({ 14 | @required this.voteMsg, 15 | @required this.authInfo, 16 | @required this.signatures, 17 | this.accountNumber, 18 | this.sequence, 19 | }) : assert(voteMsg != null), 20 | assert(authInfo != null), 21 | assert(signatures == null || signatures.isNotEmpty); 22 | 23 | Map toJson() => { 24 | 'body': this.voteMsg.toJson(), 25 | 'auth_info': this.authInfo.toJson(), 26 | 'signatures': this.signatures != null ? this.signatures : [], 27 | }; 28 | 29 | @override 30 | String toString() { 31 | return jsonEncode(toJson()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/lib/blocs/network_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:meta/meta.dart'; 6 | import 'package:kira_auth/utils/export.dart'; 7 | 8 | import 'package:equatable/equatable.dart'; 9 | 10 | part 'network_event.dart'; 11 | part 'network_state.dart'; 12 | 13 | class NetworkBloc extends Bloc { 14 | NetworkBloc() : super(NetworkInitial()); 15 | 16 | @override 17 | Stream mapEventToState( 18 | NetworkEvent event, 19 | ) async* { 20 | if (event is SetNetworkInfo) { 21 | yield* _mapNetworkInfoToState(event); 22 | } 23 | } 24 | 25 | Stream _mapNetworkInfoToState(event) async* { 26 | final String networkId = event.networkId == Strings.customNetwork ? Strings.noAvailableNetworks : event.networkId; 27 | yield NetworkInfoUpdated(networkId: networkId, nodeAddress: event.nodeAddress); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/lib/models/transactions/std_encode_tx.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:meta/meta.dart'; 4 | import 'package:kira_auth/models/transactions/export.dart'; 5 | 6 | class StdEncodeTx { 7 | final List msg; 8 | final StdFee fee; 9 | final List signatures; 10 | final String memo; 11 | 12 | StdEncodeTx({ 13 | @required this.msg, 14 | @required this.fee, 15 | @required this.signatures, 16 | @required this.memo, 17 | }) : assert(msg != null), 18 | assert(fee != null), 19 | assert(signatures == null || signatures.isNotEmpty); 20 | 21 | Map toJson() => { 22 | 'msg': this.msg.map((e) => e?.toEncodeJson())?.toList(), 23 | 'fee': this.fee.toEncodeJson(), 24 | 'signatures': this.signatures != null ? this.signatures : [], 25 | 'memo': this.memo, 26 | }; 27 | 28 | @override 29 | String toString() { 30 | return jsonEncode(toJson()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/lib/models/network_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:meta/meta.dart'; 3 | 4 | /// Contains the information of a generic Cosmos-based network. 5 | class NetworkInfo extends Equatable { 6 | final String bech32Hrp; // Bech32 human readable part 7 | final String lcdUrl; // Url to call when accessing the LCD 8 | 9 | NetworkInfo({ 10 | @required this.bech32Hrp, 11 | @required this.lcdUrl, 12 | }) : assert(bech32Hrp != null), 13 | assert(lcdUrl != null); 14 | 15 | @override 16 | List get props { 17 | return [bech32Hrp, lcdUrl]; 18 | } 19 | 20 | factory NetworkInfo.fromJson(Map json) { 21 | return NetworkInfo( 22 | bech32Hrp: json['bech32_hrp'] as String, 23 | lcdUrl: json['lcd_url'] as String, 24 | ); 25 | } 26 | 27 | Map toJson() => { 28 | 'bech32_hrp': this.bech32Hrp, 29 | 'lcd_url': this.lcdUrl, 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /src/lib/blocs/account_event.dart: -------------------------------------------------------------------------------- 1 | part of 'account_bloc.dart'; 2 | 3 | @immutable 4 | abstract class AccountEvent extends Equatable { 5 | const AccountEvent(); 6 | } 7 | 8 | class CreateNewAccount extends AccountEvent { 9 | final String password; 10 | final String accountName; 11 | 12 | const CreateNewAccount(this.password, this.accountName); 13 | 14 | @override 15 | List get props => []; 16 | } 17 | 18 | class GetCachedAccounts extends AccountEvent { 19 | const GetCachedAccounts(); 20 | 21 | @override 22 | List get props => []; 23 | } 24 | 25 | class SetCurrentAccount extends AccountEvent { 26 | final Account currentAccount; 27 | 28 | const SetCurrentAccount(this.currentAccount); 29 | 30 | @override 31 | List get props => []; 32 | } 33 | 34 | class SetInterxPubKey extends AccountEvent { 35 | final String interxPubKey; 36 | 37 | const SetInterxPubKey(this.interxPubKey); 38 | 39 | @override 40 | List get props => []; 41 | } 42 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | .env* 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/flutter_assets 34 | /build/ios 35 | /build/*/*.stamp 36 | /build/*.dill 37 | 38 | # Web related 39 | lib/generated_plugin_registrant.dart 40 | 41 | # Symbolication related 42 | app.*.symbols 43 | 44 | # Obfuscation related 45 | app.*.map.json 46 | 47 | # Exceptions to above rules. 48 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 49 | -------------------------------------------------------------------------------- /src/lib/models/cosmos_account.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'cosmos_account.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | CosmosAccount _$CosmosAccountFromJson(Map json) { 10 | return CosmosAccount( 11 | type: json['@type'] as String ?? '', 12 | address: json['address'] as String ?? '', 13 | accountNumber: json['account_number'] as String ?? '', 14 | sequence: json['sequence'] as String ?? '0', 15 | pubKey: json['pubKey'] as String ?? '', 16 | ); 17 | } 18 | 19 | Map _$CosmosAccountToJson(CosmosAccount instance) => { 20 | '@type': instance.type, 21 | 'address': instance.address, 22 | 'account_number': instance.accountNumber, 23 | 'sequence': instance.sequence, 24 | 'pubKey': instance.pubKey, 25 | }; 26 | -------------------------------------------------------------------------------- /src/lib/models/transactions/std_fee.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'std_fee.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | StdFee _$StdFeeFromJson(Map json) { 10 | return StdFee( 11 | amount: (json['amount'] as List) 12 | ?.map((e) => 13 | e == null ? null : StdCoin.fromJson(e as Map)) 14 | ?.toList(), 15 | gas: json['gas'] as String, 16 | ); 17 | } 18 | 19 | Map _$StdFeeToJson(StdFee instance) => { 20 | 'amount': instance.amount?.map((e) => e?.toJson())?.toList(), 21 | 'gas_limit': instance.gas, 22 | }; 23 | 24 | Map _$StdFeeToEncodeJson(StdFee instance) => { 25 | 'amount': instance.amount?.map((e) => e?.toJson())?.toList(), 26 | 'gas': instance.gas, 27 | }; 28 | -------------------------------------------------------------------------------- /src/lib/blocs/validator_state.dart: -------------------------------------------------------------------------------- 1 | part of 'validator_bloc.dart'; 2 | 3 | @immutable 4 | abstract class ValidatorState extends Equatable { 5 | final List favoriteValidators; 6 | 7 | ValidatorState({this.favoriteValidators}); 8 | 9 | @override 10 | List get props => [favoriteValidators]; 11 | } 12 | 13 | // Events for getting cached accounts 14 | class ValidatorInitial extends ValidatorState { 15 | ValidatorInitial() : super(favoriteValidators: []); 16 | 17 | @override 18 | List get props => []; 19 | 20 | @override 21 | String toString() => 'Validator Initial State'; 22 | } 23 | 24 | class FavoriteValidatorUpdated extends ValidatorState { 25 | final List favoriteValidators; 26 | 27 | FavoriteValidatorUpdated({this.favoriteValidators}) : super(favoriteValidators: favoriteValidators); 28 | 29 | @override 30 | List get props => [favoriteValidators]; 31 | 32 | @override 33 | String toString() => 'Favorite validators updated - ${favoriteValidators.length}'; 34 | } 35 | -------------------------------------------------------------------------------- /src/lib/models/transactions/std_signature_message.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | import 'package:kira_auth/models/transactions/export.dart'; 3 | 4 | part 'std_signature_message.g.dart'; 5 | 6 | class StdSignatureMessage { 7 | final String accountNumber; 8 | final String chainId; 9 | final String sequence; 10 | final String memo; 11 | final StdFee fee; 12 | final List msgs; 13 | 14 | const StdSignatureMessage({ 15 | @required this.chainId, 16 | @required this.accountNumber, 17 | @required this.sequence, 18 | @required this.memo, 19 | @required this.fee, 20 | @required this.msgs, 21 | }) : assert(chainId != null), 22 | assert(accountNumber != null), 23 | assert(sequence != null), 24 | assert(msgs != null); 25 | 26 | factory StdSignatureMessage.fromJson(Map json) { 27 | return _$StdSignatureMessageFromJson(json); 28 | } 29 | 30 | Map toJson() { 31 | return _$StdSignatureMessageToJson(this); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/lib/models/transactions/vote_signature_message.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | import 'package:kira_auth/models/transactions/export.dart'; 3 | 4 | part 'vote_signature_message.g.dart'; 5 | 6 | class VoteSignatureMessage { 7 | final String accountNumber; 8 | final String chainId; 9 | final String sequence; 10 | final String memo; 11 | final StdFee fee; 12 | final List msgs; 13 | 14 | const VoteSignatureMessage({ 15 | @required this.chainId, 16 | @required this.accountNumber, 17 | @required this.sequence, 18 | @required this.memo, 19 | @required this.fee, 20 | @required this.msgs, 21 | }) : assert(chainId != null), 22 | assert(accountNumber != null), 23 | assert(sequence != null), 24 | assert(msgs != null); 25 | 26 | factory VoteSignatureMessage.fromJson(Map json) { 27 | return _$VoteSignatureMessageFromJson(json); 28 | } 29 | 30 | Map toJson() { 31 | return _$VoteSignatureMessageToJson(this); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/lib/blocs/network_state.dart: -------------------------------------------------------------------------------- 1 | part of 'network_bloc.dart'; 2 | 3 | @immutable 4 | abstract class NetworkState extends Equatable { 5 | final String networkId; 6 | final String nodeAddress; 7 | 8 | NetworkState({this.networkId, this.nodeAddress}); 9 | 10 | @override 11 | List get props => [networkId, nodeAddress]; 12 | } 13 | 14 | // Events for getting cached accounts 15 | class NetworkInitial extends NetworkState { 16 | NetworkInitial() : super(); 17 | 18 | @override 19 | List get props => []; 20 | 21 | @override 22 | String toString() => 'Network Initial State'; 23 | } 24 | 25 | class NetworkInfoUpdated extends NetworkState { 26 | final String networkId; 27 | final String nodeAddress; 28 | 29 | NetworkInfoUpdated({this.networkId, this.nodeAddress}) : super(networkId: networkId, nodeAddress: nodeAddress); 30 | 31 | @override 32 | List get props => [networkId, nodeAddress]; 33 | 34 | @override 35 | String toString() => 'Network Info updated - ' + networkId + ", " + nodeAddress; 36 | } 37 | -------------------------------------------------------------------------------- /src/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /src/lib/utils/utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:fluttertoast/fluttertoast.dart'; 3 | import 'package:kira_auth/utils/colors.dart'; 4 | 5 | void showToast(String message) { 6 | Fluttertoast.showToast( 7 | msg: message, 8 | toastLength: Toast.LENGTH_LONG, 9 | gravity: ToastGravity.BOTTOM, 10 | timeInSecForIosWeb: 2, 11 | backgroundColor: KiraColors.purple1.withOpacity(0.8), 12 | webBgColor: "#fff", 13 | textColor: KiraColors.purple1, 14 | webPosition: "center", 15 | fontSize: 16, 16 | ); 17 | } 18 | 19 | void copyText(String message) { 20 | Clipboard.setData(ClipboardData(text: message)); 21 | } 22 | 23 | String getIPOnly(String address) { 24 | String rpcUrl = address; 25 | 26 | rpcUrl = rpcUrl.replaceAll('https://cors-anywhere.kira.network/', ''); 27 | rpcUrl = rpcUrl.replaceAll('http://', ''); 28 | rpcUrl = rpcUrl.replaceAll('https://', ''); 29 | rpcUrl = rpcUrl.replaceAll('/api', ''); 30 | 31 | List urlArray = rpcUrl.split(':'); 32 | return urlArray[0]; 33 | } 34 | -------------------------------------------------------------------------------- /src/lib/models/transactions/std_fee.dart: -------------------------------------------------------------------------------- 1 | import 'package:kira_auth/models/transactions/export.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import 'package:json_annotation/json_annotation.dart'; 4 | import 'package:meta/meta.dart'; 5 | 6 | part 'std_fee.g.dart'; 7 | 8 | /// Contains the data that needs to be specified about the fee to pay 9 | /// when performing a chain transaction. 10 | /// More details on https://docs.cosmos.network/master/basics/gas-fees.html#introduction-to-gas-and-fees 11 | @JsonSerializable(explicitToJson: true) 12 | class StdFee extends Equatable { 13 | @JsonKey(name: 'amount') 14 | final List amount; 15 | final String gas; 16 | 17 | const StdFee({ 18 | @required this.amount, 19 | @required this.gas, 20 | }); 21 | 22 | @override 23 | List get props => [amount, gas]; 24 | 25 | factory StdFee.fromJson(Map json) => _$StdFeeFromJson(json); 26 | 27 | Map toJson() => _$StdFeeToJson(this); 28 | Map toEncodeJson() => _$StdFeeToEncodeJson(this); 29 | } 30 | -------------------------------------------------------------------------------- /src/lib/utils/pc_utils.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-present, the authors of the Pointy Castle project 2 | // This library is dually licensed under LGPL 3 and MPL 2.0. 3 | // See file LICENSE for more information. 4 | 5 | library pointycastle.src.utils; 6 | 7 | import "dart:typed_data"; 8 | 9 | /// Decode a BigInt from bytes in big-endian encoding. 10 | BigInt decodeBigInt(List bytes) { 11 | BigInt result = new BigInt.from(0); 12 | for (int i = 0; i < bytes.length; i++) { 13 | result += new BigInt.from(bytes[bytes.length - i - 1]) << (8 * i); 14 | } 15 | return result; 16 | } 17 | 18 | var _byteMask = new BigInt.from(0xff); 19 | 20 | /// Encode a BigInt into bytes using big-endian encoding. 21 | Uint8List encodeBigInt(BigInt number) { 22 | // Not handling negative numbers. Decide how you want to do that. 23 | int size = (number.bitLength + 7) >> 3; 24 | var result = new Uint8List(size); 25 | for (int i = 0; i < size; i++) { 26 | result[size - i - 1] = (number & _byteMask).toInt(); 27 | number = number >> 8; 28 | } 29 | return result; 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/helpers/encode_tx_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:kira_auth/models/export.dart'; 2 | import 'package:kira_auth/services/export.dart'; 3 | 4 | class EncodeTransactionBuilder { 5 | static Future buildEncodeTx( 6 | Account account, 7 | List messages, { 8 | String memo = '', 9 | StdFee stdFee, 10 | }) async { 11 | // Validate the messages 12 | messages.forEach((msg) { 13 | final error = msg.validate(); 14 | if (error != null) { 15 | throw error; 16 | } 17 | }); 18 | 19 | final CosmosAccount cosmosAccount = 20 | await QueryService.getAccountData(account); 21 | 22 | StatusService service = StatusService(); 23 | await service.getNodeStatus(); 24 | 25 | final stdEncodeTx = 26 | StdEncodeTx(msg: messages, fee: stdFee, signatures: null, memo: memo); 27 | 28 | return StdEncodeMessage( 29 | chainId: service.nodeInfo.network, 30 | accountNumber: cosmosAccount.accountNumber, 31 | sequence: cosmosAccount.sequence, 32 | tx: stdEncodeTx); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/lib/models/validator_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'validator_info.g.dart'; 4 | 5 | @JsonSerializable(fieldRename: FieldRename.snake) 6 | class PubKey { 7 | final String type; 8 | final String value; 9 | 10 | PubKey({this.type, this.value}) { 11 | assert(this.type != null || this.value != null); 12 | } 13 | 14 | factory PubKey.fromJson(Map json) => _$PubKeyFromJson(json); 15 | 16 | Map toJson() => _$PubKeyToJson(this); 17 | } 18 | 19 | @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) 20 | class ValidatorInfo { 21 | final String address; 22 | final PubKey pubKey; 23 | final String votingPower; 24 | 25 | ValidatorInfo({ 26 | this.address, 27 | this.pubKey, 28 | this.votingPower, 29 | }) { 30 | assert(this.address != null || this.pubKey != null || this.votingPower != null); 31 | } 32 | 33 | factory ValidatorInfo.fromJson(Map json) => _$ValidatorInfoFromJson(json); 34 | 35 | Map toJson() => _$ValidatorInfoToJson(this); 36 | } 37 | -------------------------------------------------------------------------------- /src/lib/services/query_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:http/http.dart' as http; 4 | import 'package:kira_auth/models/account.dart'; 5 | import 'package:kira_auth/models/cosmos_account.dart'; 6 | import 'package:kira_auth/config.dart'; 7 | 8 | class QueryService { 9 | static Future getAccountData(Account account) async { 10 | var apiUrl = await loadInterxURL(); 11 | 12 | print(account.bech32Address); 13 | 14 | final endpoint = apiUrl[0] + "/cosmos/auth/accounts/${account.bech32Address}"; 15 | var response = await http.get(endpoint, headers: {'Access-Control-Allow-Origin': apiUrl[1]}); 16 | 17 | if (response.statusCode != 200) { 18 | throw Exception( 19 | "Expected status code 200 but got ${response.statusCode} - ${response.body}", 20 | ); 21 | } 22 | 23 | var data = jsonDecode(response.body) as Map; 24 | if (data.containsKey("account")) { 25 | data = data["account"]; 26 | } 27 | 28 | // var headerData = response.headers; 29 | // var signature = headerData['interx_signature']; 30 | return CosmosAccount.fromJson(data); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:kira_auth/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/utils/responsive.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ResponsiveWidget extends StatelessWidget { 4 | final Widget largeScreen; 5 | final Widget mediumScreen; 6 | final Widget smallScreen; 7 | 8 | const ResponsiveWidget({Key key, @required this.largeScreen, this.mediumScreen, this.smallScreen}) : super(key: key); 9 | 10 | static bool isSmallScreen(BuildContext context) { 11 | return MediaQuery.of(context).size.width < 800; 12 | } 13 | 14 | static bool isLargeScreen(BuildContext context) { 15 | return MediaQuery.of(context).size.width > 1200; 16 | } 17 | 18 | static bool isMediumScreen(BuildContext context) { 19 | return MediaQuery.of(context).size.width <= 1000; 20 | } 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return LayoutBuilder( 25 | builder: (context, constraints) { 26 | if (constraints.maxWidth > 1200) { 27 | return largeScreen; 28 | } else if (constraints.maxWidth <= 1200 && constraints.maxWidth >= 800) { 29 | return mediumScreen ?? largeScreen; 30 | } else { 31 | return smallScreen ?? largeScreen; 32 | } 33 | }, 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/lib/models/validator_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'validator_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | PubKey _$PubKeyFromJson(Map json) { 10 | return PubKey( 11 | type: json['type'] as String, 12 | value: json['value'] as String, 13 | ); 14 | } 15 | 16 | Map _$PubKeyToJson(PubKey instance) => { 17 | 'type': instance.type, 18 | 'value': instance.value, 19 | }; 20 | 21 | ValidatorInfo _$ValidatorInfoFromJson(Map json) { 22 | return ValidatorInfo( 23 | address: json['address'] as String, 24 | pubKey: json['pub_key'] == null ? null : PubKey.fromJson(json['pub_key'] as Map), 25 | votingPower: json['voting_power'] as String, 26 | ); 27 | } 28 | 29 | Map _$ValidatorInfoToJson(ValidatorInfo instance) => { 30 | 'address': instance.address, 31 | 'pub_key': instance.pubKey?.toJson(), 32 | 'voting_power': instance.votingPower, 33 | }; 34 | -------------------------------------------------------------------------------- /src/lib/data/validator_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | abstract class ValidatorRepository { 4 | Future> getFavoriteValidatorsFromCache(userAddress); 5 | Future toggleFavoriteValidator(address, userAddress); 6 | } 7 | 8 | class IValidatorRepository implements ValidatorRepository { 9 | @override 10 | Future> getFavoriteValidatorsFromCache(userAddress) async { 11 | SharedPreferences prefs = await SharedPreferences.getInstance(); 12 | String favoriteValidatorsString = prefs.getString('favoriteValidators-$userAddress') ?? ""; 13 | return favoriteValidatorsString.split(","); 14 | } 15 | 16 | @override 17 | Future toggleFavoriteValidator(address, userAddress) async { 18 | var favoriteValidators = await getFavoriteValidatorsFromCache(userAddress); 19 | if (favoriteValidators.contains(address)) 20 | favoriteValidators.remove(address); 21 | else 22 | favoriteValidators.add(address); 23 | var favoriteValidatorsString = favoriteValidators.join(","); 24 | SharedPreferences prefs = await SharedPreferences.getInstance(); 25 | prefs.setString('favoriteValidators-$userAddress', favoriteValidatorsString); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/blocs/token_state.dart: -------------------------------------------------------------------------------- 1 | part of 'token_bloc.dart'; 2 | 3 | @immutable 4 | abstract class TokenState extends Equatable { 5 | final String message; 6 | final Token feeToken; 7 | final List tokens; 8 | 9 | TokenState({this.feeToken, this.tokens, this.message}); 10 | 11 | @override 12 | List get props => [feeToken, tokens, message]; 13 | } 14 | 15 | // Events for getting cached accounts 16 | class TokenInitial extends TokenState { 17 | TokenInitial() : super(); 18 | 19 | @override 20 | List get props => []; 21 | 22 | @override 23 | String toString() => 'Token Initial State'; 24 | } 25 | 26 | class QueryTokensFinished extends TokenState { 27 | final List tokens; 28 | 29 | QueryTokensFinished({this.tokens}) : super(tokens: tokens); 30 | 31 | @override 32 | List get props => [tokens]; 33 | 34 | @override 35 | String toString() => 'Tokens are quried - '; 36 | } 37 | 38 | class FeeTokenUpdated extends TokenState { 39 | final Token feeToken; 40 | 41 | FeeTokenUpdated({this.feeToken}) : super(feeToken: feeToken); 42 | 43 | @override 44 | List get props => [feeToken]; 45 | 46 | @override 47 | String toString() => 'Fee token updated - ' + feeToken.ticker; 48 | } 49 | -------------------------------------------------------------------------------- /src/lib/models/transaction.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'transaction.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Transaction _$TransactionFromJson(Map json) { 10 | return Transaction( 11 | hash: json['hash'] as String, 12 | action: json['action'] as String, 13 | sender: json['sender'] as String, 14 | recipient: json['recipient'] as String, 15 | token: json['token'] as String, 16 | amount: json['amount'] as String, 17 | isNew: false, 18 | gas: json['gas'] as String, 19 | status: json['status'] as String, 20 | timestamp: json['timestamp'] as String, 21 | ); 22 | } 23 | 24 | Map _$TransactionToJson(Transaction instance) => { 25 | 'hash': instance.hash, 26 | 'action': instance.action, 27 | 'sender': instance.sender, 28 | 'recipient': instance.recipient, 29 | 'token': instance.token, 30 | 'amount': instance.amount, 31 | 'gas': instance.gas, 32 | 'status': instance.status, 33 | 'timestamp': instance.timestamp, 34 | }; 35 | -------------------------------------------------------------------------------- /src/lib/blocs/token_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:kira_auth/data/token_repository.dart'; 6 | import 'package:meta/meta.dart'; 7 | 8 | import 'package:equatable/equatable.dart'; 9 | import 'package:kira_auth/models/export.dart'; 10 | 11 | part 'token_event.dart'; 12 | part 'token_state.dart'; 13 | 14 | class TokenBloc extends Bloc { 15 | final TokenRepository tokenRepository; 16 | 17 | TokenBloc(this.tokenRepository) : super(TokenInitial()); 18 | 19 | @override 20 | Stream mapEventToState( 21 | TokenEvent event, 22 | ) async* { 23 | if (event is QueryTokens) { 24 | yield* _mapQueriedTokensToState(event); 25 | } else if (event is SetFeeToken) { 26 | yield* _mapFeeTokenToState(event); 27 | } 28 | } 29 | 30 | Stream _mapQueriedTokensToState(event) async* { 31 | final List quriedTokens = 32 | await tokenRepository.getTokens(event.address); 33 | 34 | yield QueryTokensFinished(tokens: quriedTokens); 35 | } 36 | 37 | Stream _mapFeeTokenToState(event) async* { 38 | final Token feeToken = await tokenRepository.getFeeTokenFromCache(); 39 | 40 | yield FeeTokenUpdated(feeToken: feeToken); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/lib/models/transactions/std_signature_message.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'std_signature_message.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | StdSignatureMessage _$StdSignatureMessageFromJson(Map json) { 10 | return StdSignatureMessage( 11 | chainId: json['chain_id'] as String, 12 | accountNumber: json['account_number'] as String, 13 | sequence: json['sequence'] as String, 14 | memo: json['memo'] as String, 15 | fee: json['fee'] == null 16 | ? null 17 | : StdFee.fromJson(json['fee'] as Map), 18 | msgs: (json['msgs'] as List) 19 | ?.map((e) => 20 | e == null ? null : MsgSend.fromJson(e as Map)) 21 | ?.toList(), 22 | ); 23 | } 24 | 25 | Map _$StdSignatureMessageToJson( 26 | StdSignatureMessage instance) => 27 | { 28 | 'chain_id': instance.chainId, 29 | 'account_number': instance.accountNumber, 30 | 'sequence': instance.sequence, 31 | 'memo': instance.memo, 32 | 'fee': instance.fee?.toEncodeJson(), 33 | 'msgs': instance.msgs?.map((e) => e?.toEncodeJson())?.toList(), 34 | }; 35 | -------------------------------------------------------------------------------- /src/lib/helpers/encode_tx_sender.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:http/http.dart' as http; 4 | import 'package:meta/meta.dart'; 5 | import 'package:kira_auth/models/account.dart'; 6 | import 'package:kira_auth/models/transactions/export.dart'; 7 | import 'package:kira_auth/config.dart'; 8 | 9 | class EncodeTransactionSender { 10 | static Future broadcastStdEncodeTx({ 11 | @required Account account, 12 | @required StdEncodeMessage stdEncodeMsg, 13 | }) async { 14 | // Get the endpoint 15 | var apiUrl = await loadInterxURL(); 16 | 17 | // Build the request body 18 | final requestBodyJson = jsonEncode(stdEncodeMsg.toJson()); 19 | 20 | // Get the response 21 | http.post(apiUrl[0] + '/cosmos/txs/encode', 22 | body: requestBodyJson, headers: {'Access-Control-Allow-Origin': apiUrl[1]}).then((response) { 23 | if (response.statusCode != 200) { 24 | final responseBody = json.decode(response.body); 25 | if (responseBody['message'].contains("decoding bech32 failed")) return "Invalid withdrawal address"; 26 | 27 | return "Something went wrong!"; 28 | } 29 | 30 | // Convert the response 31 | final jsonResponse = jsonDecode(response.body); 32 | final decoded = base64Decode(jsonResponse['tx']); 33 | 34 | return json.decode(utf8.decode(decoded)); 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/lib/models/transactions/vote_signature_message.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'vote_signature_message.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | VoteSignatureMessage _$VoteSignatureMessageFromJson(Map json) { 10 | return VoteSignatureMessage( 11 | chainId: json['chain_id'] as String, 12 | accountNumber: json['account_number'] as String, 13 | sequence: json['sequence'] as String, 14 | memo: json['memo'] as String, 15 | fee: json['fee'] == null 16 | ? null 17 | : StdFee.fromJson(json['fee'] as Map), 18 | msgs: (json['msgs'] as List) 19 | ?.map((e) => 20 | e == null ? null : MsgVote.fromJson(e as Map)) 21 | ?.toList(), 22 | ); 23 | } 24 | 25 | Map _$VoteSignatureMessageToJson( 26 | VoteSignatureMessage instance) => 27 | { 28 | 'chain_id': instance.chainId, 29 | 'account_number': instance.accountNumber, 30 | 'sequence': instance.sequence, 31 | 'memo': instance.memo, 32 | 'fee': instance.fee?.toEncodeJson(), 33 | 'msgs': instance.msgs?.map((e) => e?.toEncodeJson())?.toList(), 34 | }; 35 | -------------------------------------------------------------------------------- /src/lib/helpers/validation_checker.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:meta/meta.dart'; 4 | import 'package:kira_auth/models/export.dart'; 5 | 6 | // Check if the encoded data is same as the original request 7 | class ValidationChecker { 8 | static bool checkDecodedMsgValidation({ 9 | @required Map decoded, 10 | @required StdEncodeMessage stdEncodeMsg, 11 | }) { 12 | if (decoded['account_number'] != stdEncodeMsg.accountNumber) { 13 | print("account number mismatch"); 14 | return false; 15 | } 16 | 17 | if (decoded['chain_id'] != stdEncodeMsg.chainId) { 18 | print("chain id mismatch"); 19 | return false; 20 | } 21 | 22 | if (decoded['sequence'] != stdEncodeMsg.sequence) { 23 | print("sequence mismatch"); 24 | return false; 25 | } 26 | 27 | if (decoded['memo'] != stdEncodeMsg.tx.memo) { 28 | print("memo mismatch"); 29 | return false; 30 | } 31 | 32 | if (jsonEncode(decoded['fee']) != 33 | jsonEncode(stdEncodeMsg.tx.fee.toEncodeJson())) { 34 | print("fee mismatch"); 35 | return false; 36 | } 37 | 38 | if (jsonEncode(decoded['msgs']) 39 | .contains(stdEncodeMsg.tx.msg[0].amount[0].amount) != 40 | true) { 41 | print("messages mismatch"); 42 | return false; 43 | } 44 | 45 | return true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/lib/models/sync_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'sync_info.g.dart'; 4 | 5 | @JsonSerializable(fieldRename: FieldRename.snake) 6 | class SyncInfo { 7 | final String latestBlockHash; 8 | final String latestAppHash; 9 | final String latestBlockHeight; 10 | final String latestBlockTime; 11 | final String earlistBlockHash; 12 | final String earlistAppHash; 13 | final String earlistBlockHeight; 14 | final String earlistBlockTime; 15 | final bool catchingUp; 16 | 17 | SyncInfo( 18 | {this.latestBlockHash, 19 | this.latestAppHash, 20 | this.latestBlockHeight, 21 | this.latestBlockTime, 22 | this.earlistBlockHash, 23 | this.earlistAppHash, 24 | this.earlistBlockHeight, 25 | this.earlistBlockTime, 26 | this.catchingUp}) { 27 | assert(this.latestBlockHash != null || 28 | this.latestAppHash != null || 29 | this.latestBlockHeight != null || 30 | this.latestBlockTime != null || 31 | this.earlistBlockHash != null || 32 | this.earlistAppHash != null || 33 | this.earlistBlockHeight != null || 34 | this.earlistBlockTime != null || 35 | this.catchingUp != null); 36 | } 37 | 38 | factory SyncInfo.fromJson(Map json) => _$SyncInfoFromJson(json); 39 | 40 | Map toJson() => _$SyncInfoToJson(this); 41 | } 42 | -------------------------------------------------------------------------------- /src/lib/blocs/validator_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:kira_auth/data/validator_repository.dart'; 6 | import 'package:meta/meta.dart'; 7 | 8 | import 'package:equatable/equatable.dart'; 9 | 10 | part 'validator_event.dart'; 11 | part 'validator_state.dart'; 12 | 13 | class ValidatorBloc extends Bloc { 14 | final ValidatorRepository validatorRepository; 15 | 16 | ValidatorBloc(this.validatorRepository) : super(ValidatorInitial()); 17 | 18 | @override 19 | Stream mapEventToState( 20 | ValidatorEvent event, 21 | ) async* { 22 | if (event is GetCachedValidators) { 23 | yield* _mapFavoriteValidatorsToState(event); 24 | } else if (event is ToggleFavoriteAddress) { 25 | yield* _mapToggleFavoriteAddressToState(event); 26 | } 27 | } 28 | 29 | Stream _mapFavoriteValidatorsToState(event) async* { 30 | final List favoriteValidators = await validatorRepository.getFavoriteValidatorsFromCache(event.userAddress); 31 | yield FavoriteValidatorUpdated(favoriteValidators: favoriteValidators); 32 | } 33 | 34 | Stream _mapToggleFavoriteAddressToState(event) async* { 35 | await validatorRepository.toggleFavoriteValidator(event.address, event.userAddress); 36 | yield* _mapFavoriteValidatorsToState(event); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/lib/utils/map_sorter.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | /// Allows to easily sort a [Map] by its keys. 4 | class MapSorter { 5 | /// Takes the given [value] and orders each one of the contained 6 | /// items that are present inside it by calling [_encodeValue]. 7 | static List _encodeList(dynamic value) { 8 | final result = []; 9 | for (var item in value.cast()) { 10 | result.add(_encodeValue(item)); 11 | } 12 | return result; 13 | } 14 | 15 | /// Takes a generic [value] and returns its sorted representation. 16 | /// * If it is a map, [sort] is called. 17 | /// * If it is a list, [_encodeList] is called. 18 | /// * Otherwise, the same value is returned. 19 | static dynamic _encodeValue(dynamic value) { 20 | if (value is Map) { 21 | return sort(value); 22 | } else if (value is List) { 23 | return _encodeList(value); 24 | } 25 | return value; 26 | } 27 | 28 | /// Takes the given [map] and orders its values based on their keys. 29 | /// Returns the sorted map. 30 | static Map sort(Map map) { 31 | // Get the sorted keys 32 | final sortedKeys = map.keys.toList(); 33 | sortedKeys.sort(); 34 | 35 | // Sort each value 36 | final result = SplayTreeMap(); 37 | sortedKeys.forEach((key) { 38 | result[key] = _encodeValue(map[key]); 39 | }); 40 | 41 | return result; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/lib/services/rpc_methods_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'package:http/http.dart' as http; 3 | import 'package:kira_auth/models/method.dart'; 4 | import 'package:kira_auth/config.dart'; 5 | 6 | class RPCMethodsService { 7 | Map getMethods = new Map(); 8 | Map postMethods = new Map(); 9 | 10 | Future getRPCMethods() async { 11 | var apiUrl = await loadInterxURL(); 12 | 13 | var data = await http.get(apiUrl[0] + "/rpc_methods", headers: {'Access-Control-Allow-Origin': apiUrl[1]}); 14 | var bodyData = json.decode(data.body); 15 | 16 | // Parse Get Methods 17 | getMethods.addAll({ 18 | // 'GetAccounts': Method.fromJson(bodyData['GET']['/api/cosmos/auth/accounts']), 19 | 'GetBalances': Method.fromJson(bodyData['GET']['/api/cosmos/bank/balances']), 20 | 'GetTotalSupply': Method.fromJson(bodyData['GET']['/api/cosmos/bank/supply']), 21 | 'GetNetworkStatus': Method.fromJson(bodyData['GET']['/api/status']), 22 | 'GetTransactionHash': Method.fromJson(bodyData['GET']['/api/cosmos/txs']), 23 | 'GetFaucet': Method.fromJson(bodyData['GET']['/api/faucet']), 24 | }); 25 | 26 | // Parse Post Methods 27 | postMethods.addAll({ 28 | 'PostTransaction': Method.fromJson(bodyData['POST']['/api/cosmos/txs']), 29 | 'PostTransactionEncode': Method.fromJson(bodyData['POST']['/api/cosmos/txs/encode']) 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/lib/models/account.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'account.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Account _$AccountFromJson(Map json) { 10 | return Account( 11 | name: json['name'] as String, 12 | version: json['version'] as String, 13 | algorithm: json['algorithm'] as String, 14 | secretKey: json['secret_key'] as String, 15 | encryptedMnemonic: json['encrypted_mnemonic'] as String, 16 | checksum: json['checksum'] as String, 17 | networkInfo: 18 | NetworkInfo.fromJson(json['network_info'] as Map), 19 | hexAddress: json['hex_address'] as String, 20 | privateKey: json['private_key'] as String, 21 | publicKey: json['public_key'] as String, 22 | ); 23 | } 24 | 25 | Map _$AccountToJson(Account instance) => { 26 | 'name': instance.name, 27 | 'version': instance.version, 28 | 'algorithm': instance.algorithm, 29 | 'secret_key': instance.secretKey, 30 | 'encrypted_mnemonic': instance.encryptedMnemonic, 31 | 'checksum': instance.checksum, 32 | 'hex_address': instance.hexAddress, 33 | 'private_key': instance.privateKey, 34 | 'public_key': instance.publicKey, 35 | 'network_info': instance.networkInfo.toJson(), 36 | }; 37 | -------------------------------------------------------------------------------- /src/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /src/lib/models/token.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'dart:convert'; 3 | 4 | part 'token.g.dart'; 5 | 6 | @JsonSerializable() 7 | class Pagination { 8 | String nextKey; 9 | String total; 10 | 11 | Pagination({this.nextKey, this.total}) : super(); 12 | factory Pagination.fromJson(Map json) => _$PaginationFromJson(json); 13 | 14 | Map toJson() => _$PaginationToJson(this); 15 | } 16 | 17 | @JsonSerializable(fieldRename: FieldRename.snake) 18 | class Token { 19 | String graphicalSymbol; 20 | String assetName; 21 | String ticker; 22 | double balance; 23 | String denomination; 24 | int decimals; 25 | Pagination pagination; 26 | 27 | Token( 28 | {this.graphicalSymbol, 29 | this.assetName, 30 | this.ticker, 31 | this.balance, 32 | this.denomination, 33 | this.decimals, 34 | this.pagination}) { 35 | assert(this.graphicalSymbol != null || 36 | this.assetName != null || 37 | this.ticker != null || 38 | this.balance != null || 39 | this.denomination != null || 40 | this.decimals != null || 41 | this.pagination != null); 42 | } 43 | 44 | factory Token.fromJson(Map json) => _$TokenFromJson(json); 45 | 46 | factory Token.fromString(String data) { 47 | Map accMap = json.decode(data); 48 | return Token.fromJson(accMap); 49 | } 50 | Map toJson() => _$TokenToJson(this); 51 | 52 | String toString() => jsonEncode(toJson()); 53 | } 54 | -------------------------------------------------------------------------------- /src/lib/models/sync_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'sync_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | SyncInfo _$SyncInfoFromJson(Map json) { 10 | return SyncInfo( 11 | latestBlockHash: json['latest_block_hash'] as String, 12 | latestAppHash: json['latest_app_hash'] as String, 13 | latestBlockHeight: json['latest_block_height'] as String, 14 | latestBlockTime: json['latest_block_time'] as String, 15 | earlistBlockHash: json['earlist_block_hash'] as String, 16 | earlistAppHash: json['earlist_app_hash'] as String, 17 | earlistBlockHeight: json['earlist_block_height'] as String, 18 | earlistBlockTime: json['earlist_block_time'] as String, 19 | catchingUp: json['catching_up'] as bool, 20 | ); 21 | } 22 | 23 | Map _$SyncInfoToJson(SyncInfo instance) => { 24 | 'latest_block_hash': instance.latestBlockHash, 25 | 'latest_app_hash': instance.latestAppHash, 26 | 'latest_block_height': instance.latestBlockHeight, 27 | 'latest_block_time': instance.latestBlockTime, 28 | 'earlist_block_hash': instance.earlistBlockHash, 29 | 'earlist_app_hash': instance.earlistAppHash, 30 | 'earlist_block_height': instance.earlistBlockHeight, 31 | 'earlist_block_time': instance.earlistBlockTime, 32 | 'catching_up': instance.catchingUp, 33 | }; 34 | -------------------------------------------------------------------------------- /src/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 18 | 19 | 20 | 21 | 22 | src 23 | 24 | 25 | 26 | 29 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/lib/models/token.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'token.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Pagination _$PaginationFromJson(Map json) { 10 | return Pagination( 11 | nextKey: json['nextKey'] as String, 12 | total: json['total'] as String, 13 | ); 14 | } 15 | 16 | Map _$PaginationToJson(Pagination instance) => { 17 | 'nextKey': instance.nextKey, 18 | 'total': instance.total, 19 | }; 20 | 21 | Token _$TokenFromJson(Map json) { 22 | return Token( 23 | graphicalSymbol: json['graphical_symbol'] as String, 24 | assetName: json['asset_name'] as String, 25 | ticker: json['ticker'] as String, 26 | balance: (json['balance'] as num)?.toDouble(), 27 | denomination: json['denomination'] as String, 28 | decimals: json['decimals'] as int, 29 | pagination: json['pagination'] == null ? null : Pagination.fromJson(json['pagination'] as Map), 30 | ); 31 | } 32 | 33 | Map _$TokenToJson(Token instance) => { 34 | 'graphical_symbol': instance.graphicalSymbol, 35 | 'asset_name': instance.assetName, 36 | 'ticker': instance.ticker, 37 | 'balance': instance.balance, 38 | 'denomination': instance.denomination, 39 | 'decimals': instance.decimals, 40 | 'pagination': instance.pagination, 41 | }; 42 | -------------------------------------------------------------------------------- /src/lib/models/transactions/vote_msg.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:meta/meta.dart'; 4 | import 'package:kira_auth/models/transactions/export.dart'; 5 | 6 | class VoteMsg { 7 | final List messages; 8 | final String memo; 9 | final String timeoutHeight; 10 | final List extensionOptions; 11 | final List nonCriticalExtensionOptions; 12 | 13 | VoteMsg({ 14 | @required this.messages, 15 | @required this.memo, 16 | @required this.timeoutHeight, 17 | @required this.extensionOptions, 18 | @required this.nonCriticalExtensionOptions, 19 | }) : assert(messages.isNotEmpty), 20 | assert(memo != null), 21 | assert(timeoutHeight != null), 22 | assert(extensionOptions != null), 23 | assert(nonCriticalExtensionOptions != null); 24 | 25 | Map toJson() => { 26 | 'messages': this.messages.map((message) => message.toJson()).toList(), 27 | 'memo': this.memo, 28 | 'timeout_height': this.timeoutHeight, 29 | 'extension_options': 30 | this.extensionOptions?.map((option) => option.toJson())?.toList(), 31 | 'non_critical_extension_options': this 32 | .nonCriticalExtensionOptions 33 | ?.map((option) => option.toJson()) 34 | ?.toList(), 35 | }; 36 | 37 | Exception validate() { 38 | messages.forEach((msg) { 39 | final error = msg.validate(); 40 | if (error != null) { 41 | throw error; 42 | } 43 | }); 44 | return null; 45 | } 46 | 47 | @override 48 | String toString() { 49 | return jsonEncode(toJson()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/lib/models/transactions/std_msg.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:meta/meta.dart'; 4 | import 'package:kira_auth/models/transactions/export.dart'; 5 | 6 | class StdMsg { 7 | final List messages; 8 | final String memo; 9 | final String timeoutHeight; 10 | final List extensionOptions; 11 | final List nonCriticalExtensionOptions; 12 | 13 | StdMsg({ 14 | @required this.messages, 15 | @required this.memo, 16 | @required this.timeoutHeight, 17 | @required this.extensionOptions, 18 | @required this.nonCriticalExtensionOptions, 19 | }) : assert(messages != null), 20 | assert(memo != null), 21 | assert(timeoutHeight != null), 22 | assert(extensionOptions != null), 23 | assert(nonCriticalExtensionOptions != null); 24 | 25 | Map toJson() => { 26 | 'messages': this.messages.map((message) => message.toJson()).toList(), 27 | 'memo': this.memo, 28 | 'timeout_height': this.timeoutHeight, 29 | 'extension_options': 30 | this.extensionOptions?.map((option) => option.toJson())?.toList(), 31 | 'non_critical_extension_options': this 32 | .nonCriticalExtensionOptions 33 | ?.map((option) => option.toJson()) 34 | ?.toList(), 35 | }; 36 | 37 | Exception validate() { 38 | messages.forEach((msg) { 39 | final error = msg.validate(); 40 | if (error != null) { 41 | throw error; 42 | } 43 | }); 44 | return null; 45 | } 46 | 47 | @override 48 | String toString() { 49 | return jsonEncode(toJson()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/lib/models/node_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:kira_auth/models/protocol_version.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | part 'node_info.g.dart'; 5 | 6 | @JsonSerializable(fieldRename: FieldRename.snake) 7 | class Other { 8 | final String txIndex; 9 | final String rpcAddress; 10 | 11 | Other({this.txIndex, this.rpcAddress}) { 12 | assert(this.txIndex != null || this.rpcAddress != null); 13 | } 14 | 15 | factory Other.fromJson(Map json) => _$OtherFromJson(json); 16 | 17 | Map toJson() => _$OtherToJson(this); 18 | } 19 | 20 | @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) 21 | class NodeInfo { 22 | ProtocolVersion protocolVersion; 23 | final String id; 24 | @JsonKey(name: 'listen_addr') 25 | final String listenAddress; 26 | final String network; 27 | final String version; 28 | final String channels; 29 | final String moniker; 30 | Other other; 31 | 32 | NodeInfo( 33 | {this.protocolVersion, 34 | this.id, 35 | this.listenAddress, 36 | this.network, 37 | this.version, 38 | this.channels, 39 | this.moniker, 40 | this.other}) { 41 | assert(this.protocolVersion != null || 42 | this.id != null || 43 | this.listenAddress != null || 44 | this.network != null || 45 | this.version != null || 46 | this.channels != null || 47 | this.moniker != null || 48 | this.other != null); 49 | } 50 | 51 | factory NodeInfo.fromJson(Map json) => _$NodeInfoFromJson(json); 52 | 53 | Map toJson() => _$NodeInfoToJson(this); 54 | } 55 | -------------------------------------------------------------------------------- /src/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | src 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/lib/models/node_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'node_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Other _$OtherFromJson(Map json) { 10 | return Other( 11 | txIndex: json['tx_index'] as String, 12 | rpcAddress: json['rpc_address'] as String, 13 | ); 14 | } 15 | 16 | Map _$OtherToJson(Other instance) => { 17 | 'tx_index': instance.txIndex, 18 | 'rpc_address': instance.rpcAddress, 19 | }; 20 | 21 | NodeInfo _$NodeInfoFromJson(Map json) { 22 | return NodeInfo( 23 | protocolVersion: json['protocol_version'] == null 24 | ? null 25 | : ProtocolVersion.fromJson(json['protocol_version'] as Map), 26 | id: json['id'] as String, 27 | listenAddress: json['listen_addr'] as String, 28 | network: json['network'] as String, 29 | version: json['version'] as String, 30 | channels: json['channels'] as String, 31 | moniker: json['moniker'] as String, 32 | other: json['other'] == null ? null : Other.fromJson(json['other'] as Map), 33 | ); 34 | } 35 | 36 | Map _$NodeInfoToJson(NodeInfo instance) => { 37 | 'protocol_version': instance.protocolVersion?.toJson(), 38 | 'id': instance.id, 39 | 'listen_addr': instance.listenAddress, 40 | 'network': instance.network, 41 | 'version': instance.version, 42 | 'channels': instance.channels, 43 | 'moniker': instance.moniker, 44 | 'other': instance.other?.toJson(), 45 | }; 46 | -------------------------------------------------------------------------------- /src/lib/utils/bech32_encoder.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | import 'package:bip_bech32/bip_bech32.dart'; 3 | 4 | /// Allows to easily encode into Bech32 some data using a 5 | /// given human readable part. 6 | class Bech32Encoder { 7 | /// Encodes the given data using the Bech32 encoding with the 8 | /// given human readable part 9 | static String encode(String humanReadablePart, Uint8List data) { 10 | final List converted = _convertBits(data, 8, 5); 11 | final bech32Codec = Bech32Codec(); 12 | final bech32Data = Bech32(humanReadablePart, converted); 13 | return bech32Codec.encode(bech32Data); 14 | } 15 | 16 | /// for bech32 coding 17 | static Uint8List _convertBits( 18 | List data, 19 | int from, 20 | int to, { 21 | bool pad = true, 22 | }) { 23 | var acc = 0; 24 | var bits = 0; 25 | final result = []; 26 | final maxv = (1 << to) - 1; 27 | 28 | for (var v in data) { 29 | if (v < 0 || (v >> from) != 0) { 30 | throw Exception(); 31 | } 32 | acc = (acc << from) | v; 33 | bits += from; 34 | while (bits >= to) { 35 | bits -= to; 36 | result.add((acc >> bits) & maxv); 37 | } 38 | } 39 | 40 | if (pad) { 41 | if (bits > 0) { 42 | result.add((acc << (to - bits)) & maxv); 43 | } 44 | } else if (bits >= from) { 45 | throw Exception('illegal zero padding'); 46 | } else if (((acc << (to - bits)) & maxv) != 0) { 47 | throw Exception('non zero'); 48 | } 49 | 50 | return Uint8List.fromList(result); 51 | } 52 | 53 | static Uint8List decode(String data) { 54 | final bech32Codec = Bech32Codec(); 55 | final bech32Data = bech32Codec.decode(data); 56 | return bech32Data.data; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/lib/models/transactions/messages/msg_vote.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:meta/meta.dart'; 3 | 4 | part 'msg_vote.g.dart'; 5 | 6 | /// [MsgVote] represents the message that should be 7 | /// used when sending tokens from one user to another one. 8 | /// It requires to specify the address from which to send the tokens, 9 | /// the one that should receive the tokens and the amount of tokens 10 | /// to send. 11 | @JsonSerializable(explicitToJson: true) 12 | class MsgVote { 13 | /// Bech32 address of the voter. 14 | @JsonKey(name: 'voter') 15 | final String voter; 16 | 17 | /// Proposal ID to vote on. 18 | @JsonKey(name: 'proposal_id') 19 | final String proposalId; 20 | 21 | /// Vote option. 22 | @JsonKey(name: 'option') 23 | final int option; 24 | 25 | /// Public constructor. 26 | MsgVote({ 27 | @required this.voter, 28 | @required this.proposalId, 29 | @required this.option, 30 | }); 31 | 32 | factory MsgVote.fromJson(Map json) { 33 | return _$MsgVoteFromJson(json); 34 | } 35 | 36 | Map toJson() { 37 | Map response = {'@type': "/kira.gov.MsgVoteProposal"}; 38 | response.addAll(_$MsgVoteToJson(this)); 39 | return response; 40 | } 41 | 42 | Map toEncodeJson() { 43 | Map response = { 44 | 'type': "kiraHub/MsgVoteProposal", 45 | 'value': _$MsgVoteToJson(this) 46 | }; 47 | return response; 48 | } 49 | 50 | Exception validate() { 51 | if (proposalId.isEmpty) { 52 | return Exception('proposal id must be set'); 53 | } 54 | 55 | if (voter.isEmpty) { 56 | return Exception('user session is invalid'); 57 | } 58 | 59 | return null; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/lib/models/transactions/messages/msg_send.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:meta/meta.dart'; 3 | import 'package:kira_auth/models/transactions/export.dart'; 4 | 5 | part 'msg_send.g.dart'; 6 | 7 | /// [MsgSend] represents the message that should be 8 | /// used when sending tokens from one user to another one. 9 | /// It requires to specify the address from which to send the tokens, 10 | /// the one that should receive the tokens and the amount of tokens 11 | /// to send. 12 | @JsonSerializable(explicitToJson: true) 13 | class MsgSend { 14 | /// Bech32 address of the sender. 15 | @JsonKey(name: 'from_address') 16 | final String fromAddress; 17 | 18 | /// Bech32 address of the recipient. 19 | @JsonKey(name: 'to_address') 20 | final String toAddress; 21 | 22 | /// Coins that will be sent. 23 | @JsonKey(name: 'amount') 24 | final List amount; 25 | 26 | /// Public constructor. 27 | MsgSend({ 28 | @required this.fromAddress, 29 | @required this.toAddress, 30 | @required this.amount, 31 | }); 32 | 33 | factory MsgSend.fromJson(Map json) { 34 | return _$MsgSendFromJson(json); 35 | } 36 | 37 | Map toJson() { 38 | Map response = {'@type': "/cosmos.bank.v1beta1.MsgSend"}; 39 | response.addAll(_$MsgSendToJson(this)); 40 | return response; 41 | } 42 | 43 | Map toEncodeJson() { 44 | Map response = { 45 | 'type': "cosmos-sdk/MsgSend", 46 | 'value': _$MsgSendToJson(this) 47 | }; 48 | return response; 49 | } 50 | 51 | Exception validate() { 52 | if (fromAddress.isEmpty || toAddress.isEmpty) { 53 | return Exception('from_address and to_address cannot be empty'); 54 | } 55 | 56 | if (amount.isEmpty) { 57 | return Exception('amount cannot be empty'); 58 | } 59 | 60 | return null; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/lib/models/transactions/signer_info.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:kira_auth/models/export.dart'; 4 | import 'package:meta/meta.dart'; 5 | 6 | enum SignMode { 7 | SIGN_MODE_UNSPECIFIED, 8 | SIGN_MODE_DIRECT, 9 | SIGN_MODE_TEXTUAL, 10 | SIGN_MODE_LEGACY_AMINO_JSON 11 | } 12 | 13 | class Single { 14 | String mode; 15 | 16 | Single({this.mode}) : assert(mode != null); 17 | 18 | Map toJson() => {'mode': mode}; 19 | 20 | factory Single.fromJson(Map json) { 21 | return Single(mode: json['mode']); 22 | } 23 | } 24 | 25 | class ModeInfo { 26 | final Single single; 27 | 28 | ModeInfo({this.single}) : assert(single != null); 29 | 30 | Map toJson() => {'single': single.toJson()}; 31 | 32 | factory ModeInfo.fromJson(Map json) => 33 | ModeInfo(single: Single.fromJson(json['single'] as Map)); 34 | 35 | String toString() => jsonEncode(toJson()); 36 | } 37 | 38 | // ignore: todo 39 | // TODO: Here modeInfo should be Mode_Info object 40 | class SignerInfo { 41 | final StdPublicKey publicKey; 42 | final ModeInfo modeInfo; // 43 | final String sequence; 44 | 45 | const SignerInfo( 46 | {@required this.publicKey, 47 | @required this.modeInfo, 48 | @required this.sequence}) 49 | : assert(publicKey != null), 50 | assert(modeInfo != null), 51 | assert(sequence != null); 52 | 53 | factory SignerInfo.fromJson(Map json) => SignerInfo( 54 | publicKey: 55 | StdPublicKey.fromJson(json['public_key'] as Map), 56 | modeInfo: ModeInfo.fromJson(json['mode_info'] as Map), 57 | sequence: json['sequence'] as String, 58 | ); 59 | 60 | Map toJson() => { 61 | 'public_key': this.publicKey, 62 | 'mode_info': this.modeInfo, 63 | 'sequence': this.sequence, 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /src/lib/widgets/custom_slider_thumb_circle.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:kira_auth/utils/colors.dart'; 3 | 4 | class CustomSliderThumbCircle extends SliderComponentShape { 5 | final double thumbRadius; 6 | final int min; 7 | final int max; 8 | 9 | const CustomSliderThumbCircle({ 10 | @required this.thumbRadius, 11 | this.min = 0, 12 | this.max = 10, 13 | }); 14 | 15 | @override 16 | Size getPreferredSize(bool isEnabled, bool isDiscrete) { 17 | return Size.fromRadius(thumbRadius); 18 | } 19 | 20 | @override 21 | void paint( 22 | PaintingContext context, 23 | Offset center, { 24 | Animation activationAnimation, 25 | Animation enableAnimation, 26 | bool isDiscrete, 27 | TextPainter labelPainter, 28 | RenderBox parentBox, 29 | SliderThemeData sliderTheme, 30 | TextDirection textDirection, 31 | double value, 32 | double textScaleFactor, 33 | Size sizeWithOverflow, 34 | }) { 35 | final Canvas canvas = context.canvas; 36 | 37 | final paint = Paint() 38 | ..color = KiraColors.kPrimaryLightColor //Thumb Background Color 39 | ..style = PaintingStyle.fill; 40 | 41 | TextSpan span = new TextSpan( 42 | style: new TextStyle( 43 | fontSize: thumbRadius * .8, 44 | fontWeight: FontWeight.w700, 45 | color: sliderTheme.thumbColor, //Text Color of Value on Thumb 46 | ), 47 | text: getValue(value), 48 | ); 49 | 50 | TextPainter tp = new TextPainter(text: span, textAlign: TextAlign.center, textDirection: TextDirection.ltr); 51 | 52 | tp.layout(); 53 | 54 | Offset textCenter = Offset(center.dx - (tp.width / 2), center.dy - (tp.height / 2)); 55 | 56 | canvas.drawCircle(center, thumbRadius * .9, paint); 57 | tp.paint(canvas, textCenter); 58 | } 59 | 60 | String getValue(double value) { 61 | return (min + (max - min) * value).round().toString(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.example.src" 42 | minSdkVersion 16 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // TODO: Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig signingConfigs.debug 53 | } 54 | } 55 | } 56 | 57 | flutter { 58 | source '../..' 59 | } 60 | 61 | dependencies { 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 63 | } 64 | -------------------------------------------------------------------------------- /src/lib/helpers/tx_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:kira_auth/models/transactions/export.dart'; 2 | 3 | /// Allows to easily build and sign a [StdTx] that can later be sent over 4 | /// the network. 5 | class TransactionBuilder { 6 | /// Builds a [StdTx] object containing the given [stdMsgs] and having the 7 | /// optional [memo] and [fee] specified. 8 | static StdTx buildStdTx( 9 | List messages, { 10 | String memo = '', 11 | String timeoutHeight = '0', 12 | StdFee stdFee, 13 | }) { 14 | // Validate the messages 15 | messages.forEach((msg) { 16 | final error = msg.validate(); 17 | if (error != null) { 18 | throw error; 19 | } 20 | }); 21 | 22 | final stdMsg = StdMsg( 23 | messages: messages, 24 | memo: memo, 25 | timeoutHeight: timeoutHeight, 26 | extensionOptions: [], 27 | nonCriticalExtensionOptions: []); 28 | 29 | final authInfo = AuthInfo(stdFee: stdFee, signerInfos: []); 30 | 31 | return StdTx( 32 | stdMsg: stdMsg, 33 | authInfo: authInfo, 34 | signatures: null, 35 | ); 36 | } 37 | 38 | /// Builds a [VoteTx] object containing the given [voteMsg] and having the 39 | /// optional [memo] and [fee] specified. 40 | static VoteTx buildVoteTx( 41 | List messages, { 42 | String memo = '', 43 | String timeoutHeight = '0', 44 | StdFee stdFee, 45 | }) { 46 | // Validate the messages 47 | messages.forEach((msg) { 48 | final error = msg.validate(); 49 | if (error != null) { 50 | throw error; 51 | } 52 | }); 53 | 54 | final voteMsg = VoteMsg( 55 | messages: messages, 56 | memo: memo, 57 | timeoutHeight: timeoutHeight, 58 | extensionOptions: [], 59 | nonCriticalExtensionOptions: []); 60 | 61 | final authInfo = AuthInfo(stdFee: stdFee, signerInfos: []); 62 | 63 | return VoteTx( 64 | voteMsg: voteMsg, 65 | authInfo: authInfo, 66 | signatures: null, 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Kira Frontend 2 | 3 | Kira Frontend is a user interface for Kira Network Users to manage their accounts, balance, transfer tokens between different wallets. 4 | 5 | ## Installation 6 | 7 | _NOTE: For development, run chrome browser without security enabled, unless the api doesn't fetch data due to the cors error._ 8 | 9 | #### - Frontend 10 | 11 | - Install required packages in pubspec.yaml 12 | 13 | ``` 14 | flutter pub get 15 | ``` 16 | 17 | - Run commands 18 | 19 | ``` 20 | flutter run -d chrome --dart-define=FLUTTER_WEB_USE_SKIA=true 21 | flutter run -d web-server --dart-define=FLUTTER_WEB_USE_SKIA=true 22 | ``` 23 | 24 | - For development, you may need to run google chrome without cors 25 | 26 | ``` 27 | open -n -a /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security 28 | ``` 29 | 30 | - Static Build 31 | 32 | ``` 33 | flutter build web 34 | ``` 35 | 36 | This command will generate static build of the web project in the `build/web` directory so that you can deploy it on hosting server. 37 | 38 | _NOTE: To render svg in flutter, we need to enable SKIA mode when running command_ 39 | 40 | User input the password which will be used for encrypting mnemonic words and kira addresses, public/private keys. 41 | 42 | After creating account, don't forget to keep the mnemonic words (seed) in a safe place and export the account as a file for restoring. 43 | 44 | #### - Backend 45 | 46 | To interact with INTERX, clone `KIP_9` branch of sekaid repository and check out INTERX readme for more information. 47 | 48 | ``` 49 | https://github.com/KiraCore/sekai/tree/KIP_9 50 | ``` 51 | 52 | - Run sekaid 53 | 54 | ``` 55 | sh sekaidtestsetup.sh 56 | ``` 57 | 58 | - Run INTERX 59 | 60 | ``` 61 | make install 62 | interx 63 | ``` 64 | 65 | or 66 | 67 | ``` 68 | make start 69 | ``` 70 | 71 | #### - Environment File 72 | 73 | Update `config.json` file for API configuration. It's in assets folder. 74 | 75 | ``` 76 | { 77 | "api_url": "http://:/api" 78 | } 79 | ``` 80 | -------------------------------------------------------------------------------- /src/lib/utils/styles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:kira_auth/utils/colors.dart'; 3 | import 'package:kira_auth/utils/responsive.dart'; 4 | 5 | class AppStyles { 6 | // Text style for numbers of mnemonic 7 | static TextStyle textStyleNumbersOfMnemonic(BuildContext context) { 8 | return TextStyle( 9 | fontSize: AppFontSizes.smallText(context), 10 | color: KiraColors.black, 11 | fontFamily: 'OverpassMono', 12 | fontWeight: FontWeight.w300, 13 | ); 14 | } 15 | 16 | // Text style for mnemonic 17 | static TextStyle textStyleMnemonic(BuildContext context) { 18 | return TextStyle( 19 | fontSize: AppFontSizes.smallText(context), 20 | color: KiraColors.black.withOpacity(0.7), 21 | fontFamily: 'OverpassMono', 22 | fontWeight: FontWeight.w300, 23 | ); 24 | } 25 | 26 | // Text style for mnemonic success 27 | static TextStyle textStyleMnemonicSuccess(BuildContext context) { 28 | return TextStyle( 29 | fontSize: AppFontSizes.smallText(context), 30 | color: KiraColors.kYellowColor2.withOpacity(0.8), 31 | fontFamily: 'OverpassMono', 32 | fontWeight: FontWeight.w300, 33 | ); 34 | } 35 | } 36 | 37 | class AppFontSizes { 38 | static const smallest = 13.0; 39 | static const small = 14.0; 40 | static const medium = 18.0; 41 | static const _large = 22.0; 42 | static const larger = 26.0; 43 | static const _largest = 30.0; 44 | static const largestc = 30.0; 45 | static const _sslarge = 20.0; 46 | static const _sslargest = 24.0; 47 | 48 | static double largest(context) { 49 | if (ResponsiveWidget.isSmallScreen(context)) { 50 | return _sslargest; 51 | } 52 | return _largest; 53 | } 54 | 55 | static double large(context) { 56 | if (ResponsiveWidget.isSmallScreen(context)) { 57 | return _sslarge; 58 | } 59 | return _large; 60 | } 61 | 62 | static double smallText(context) { 63 | if (ResponsiveWidget.isSmallScreen(context)) { 64 | return smallest; 65 | } 66 | return small; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/lib/blocs/account_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:meta/meta.dart'; 6 | 7 | import 'package:equatable/equatable.dart'; 8 | import 'package:kira_auth/models/account.dart'; 9 | import 'package:kira_auth/data/account_repository.dart'; 10 | 11 | part 'account_event.dart'; 12 | part 'account_state.dart'; 13 | 14 | class AccountBloc extends Bloc { 15 | final AccountRepository accountRepository; 16 | 17 | AccountBloc(this.accountRepository) : super(AccountInitial()); 18 | 19 | @override 20 | Stream mapEventToState( 21 | AccountEvent event, 22 | ) async* { 23 | if (event is GetCachedAccounts) { 24 | yield* _mapCachedAccountsToState(); 25 | } else if (event is CreateNewAccount) { 26 | yield* _mapCreateAccountToState(event); 27 | } else if (event is SetCurrentAccount) { 28 | yield* _mapCurrentAccountToState(event); 29 | } else if (event is SetInterxPubKey) { 30 | yield* _mapInterxPubKeyToState(event); 31 | } 32 | } 33 | 34 | Stream _mapCachedAccountsToState() async* { 35 | yield CachedAccountsLoading(); 36 | 37 | final List availableAccounts = await accountRepository.getAccountsFromCache(); 38 | 39 | yield CachedAccountsLoaded(accounts: availableAccounts); 40 | } 41 | 42 | Stream _mapCreateAccountToState(event) async* { 43 | yield AccountCreating(); 44 | 45 | final Account createdAccount = await accountRepository.createNewAccount(event.password, event.accountName); 46 | 47 | // final Account createdAccount = 48 | // await accountRepository.fakeFetchForTesting(); 49 | 50 | yield CurrentAccountUpdated(currentAccount: createdAccount); 51 | } 52 | 53 | Stream _mapCurrentAccountToState(event) async* { 54 | yield CurrentAccountUpdated(currentAccount: event.currentAccount); 55 | } 56 | 57 | Stream _mapInterxPubKeyToState(event) async* { 58 | yield InterxPubKeyUpdated(interxPubKey: event.interxPubKey); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/lib/models/cosmos_account.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import 'package:json_annotation/json_annotation.dart'; 4 | 5 | part 'cosmos_account.g.dart'; 6 | 7 | @immutable 8 | @JsonSerializable(explicitToJson: true) 9 | class CosmosAccount extends Equatable { 10 | @JsonKey(name: '@type', defaultValue: '') 11 | final String type; 12 | 13 | @JsonKey(name: 'account_number', defaultValue: '') 14 | final String accountNumber; 15 | 16 | @JsonKey(name: 'address', defaultValue: '') 17 | final String address; 18 | 19 | @JsonKey(name: 'sequence', defaultValue: '0') 20 | final String sequence; 21 | 22 | @JsonKey(name: 'pubKey', defaultValue: '') 23 | final String pubKey; 24 | 25 | const CosmosAccount({ 26 | @required this.type, 27 | @required this.address, 28 | @required this.accountNumber, 29 | @required this.sequence, 30 | @required this.pubKey, 31 | }); 32 | 33 | factory CosmosAccount.offline(String address) { 34 | return CosmosAccount(type: '', address: address, accountNumber: '', sequence: '0', pubKey: ''); 35 | } 36 | 37 | factory CosmosAccount.fromJson(Map json) { 38 | return _$CosmosAccountFromJson(json); 39 | } 40 | 41 | Map toJson() { 42 | return _$CosmosAccountToJson(this); 43 | } 44 | 45 | CosmosAccount copyWith({String type, String address, String accountNumber, String sequence, String pubKey}) { 46 | return CosmosAccount( 47 | type: type ?? this.type, 48 | address: address ?? this.address, 49 | accountNumber: accountNumber ?? this.accountNumber, 50 | sequence: sequence ?? this.sequence, 51 | pubKey: pubKey ?? this.pubKey, 52 | ); 53 | } 54 | 55 | @override 56 | List get props { 57 | return [type, address, accountNumber, sequence, pubKey]; 58 | } 59 | 60 | @override 61 | String toString() { 62 | return 'CosmosAccount { ' 63 | 'type: $type, ' 64 | 'address: $address, ' 65 | 'account_number: $accountNumber, ' 66 | 'sequence: $sequence, ' 67 | 'pubKey: $pubKey ' 68 | '}'; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/lib/widgets/appbar_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:kira_auth/utils/colors.dart'; 3 | 4 | // ignore: must_be_immutable 5 | class AppBarButton extends StatefulWidget { 6 | AppBarButton( 7 | {this.key, 8 | this.text, 9 | this.width, 10 | this.height, 11 | this.onPressed, 12 | this.backgroundColor}) 13 | : super(key: key); 14 | 15 | Key key; 16 | String text; 17 | double width; 18 | double height; 19 | VoidCallback onPressed; 20 | Color backgroundColor; 21 | 22 | @override 23 | _AppBarButtonState createState() => _AppBarButtonState(); 24 | } 25 | 26 | class _AppBarButtonState extends State { 27 | @override 28 | Widget build(BuildContext context) { 29 | return Padding( 30 | padding: EdgeInsets.all(8.0), 31 | child: InkWell( 32 | onTap: () { 33 | widget.onPressed(); 34 | }, 35 | child: Container( 36 | margin: EdgeInsets.only(left: 0), 37 | width: widget.width, 38 | height: widget.height, 39 | decoration: BoxDecoration( 40 | gradient: LinearGradient( 41 | colors: [KiraColors.purple1, KiraColors.purple2], 42 | begin: Alignment.bottomRight, 43 | end: Alignment.topLeft), 44 | borderRadius: BorderRadius.circular(10), 45 | border: Border.all(width: 2, color: Colors.white12), 46 | boxShadow: [ 47 | BoxShadow( 48 | color: KiraColors.purple3.withOpacity(0.3), 49 | offset: Offset(0, 8), //Shadow starts at x=0, y=8 50 | blurRadius: 8) 51 | ]), 52 | child: Material( 53 | color: Colors.transparent, 54 | child: Center( 55 | child: Text( 56 | widget.text, 57 | style: TextStyle( 58 | color: KiraColors.white[50], 59 | fontSize: 18, 60 | letterSpacing: 1), 61 | ), 62 | ), 63 | ), 64 | ), 65 | ), 66 | ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/lib/utils/colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class KiraColors { 4 | static const MaterialColor white = const MaterialColor( 5 | 0xFFFFFFFF, 6 | const { 7 | 50: const Color(0xFFFFFFFF), 8 | 100: const Color(0xFFFFFFFF), 9 | 200: const Color(0xFFFFFFFF), 10 | 300: const Color(0xFFFFFFFF), 11 | 400: const Color(0xFFFFFFFF), 12 | 500: const Color(0xFFFFFFFF), 13 | 600: const Color(0xFFFFFFFF), 14 | 700: const Color(0xFFFFFFFF), 15 | 800: const Color(0xFFFFFFFF), 16 | 900: const Color(0xFFFFFFFF), 17 | }, 18 | ); 19 | 20 | static const Color buttonBorder = Color.fromRGBO(255, 255, 255, 0.5); 21 | static const Color transparent = Color(0x00ffffff); 22 | static const Color black = Color(0xFF000000); 23 | 24 | static const Color blue1 = Color(0xFF00b0ff); 25 | static const Color blue2 = Color(0xFF0081cb); 26 | static const Color blue3 = Color(0xFF4fc3f7); 27 | static const Color blue4 = Color(0xFF8591B0); 28 | 29 | static Color green1 = Colors.green[900]; 30 | static Color green2 = Colors.green[600]; 31 | static const Color green3 = Colors.greenAccent; 32 | 33 | static Color purple1 = Color(0xFF6F35A5); 34 | static Color purple2 = Color(0xFF6F2595); 35 | static const Color purple3 = Colors.purpleAccent; 36 | 37 | static Color danger = Color(0xFFF03020); 38 | 39 | static const Color orange1 = Colors.deepOrange; 40 | static const Color orange2 = Colors.orange; 41 | static const Color orange3 = Colors.orangeAccent; 42 | static const Color green4 = Color(0xCCFFD180); 43 | 44 | static const kPrimaryColor = Color(0xFF6F35A5); 45 | static const kPrimaryLightColor = Color(0xFFF1E6FF); 46 | static const kBackgroundColor = Color.fromARGB(255, 27, 16, 51); 47 | static const kGrayColor = Color.fromARGB(180, 220, 220, 220); 48 | static const kPurpleColor = Color.fromARGB(255, 134, 53, 213); 49 | static const kYellowColor1 = Color(0xCCFF9100); 50 | static const kYellowColor = Color(0xFFFFD180); 51 | static const kYellowColor2 = Color(0xFFe09c12); 52 | static const kBrownColor = Color(0xFF410f42); 53 | static const kLightPurpleColor = Color(0xFF7b409d); 54 | } 55 | -------------------------------------------------------------------------------- /src/lib/helpers/tx_sender.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:http/http.dart' as http; 4 | import 'package:meta/meta.dart'; 5 | import 'package:kira_auth/models/account.dart'; 6 | import 'package:kira_auth/models/transactions/export.dart'; 7 | import 'package:kira_auth/config.dart'; 8 | 9 | class TransactionSender { 10 | static Future broadcastStdTx({ 11 | @required Account account, 12 | @required StdTx stdTx, 13 | String mode = "block", 14 | }) async { 15 | // final apiUrl = "${account.networkInfo.lcdUrl}/txs"; 16 | // Get the endpoint 17 | var apiUrl = await loadInterxURL(); 18 | 19 | // Build the request body 20 | final requestBody = {"tx": stdTx.toJson(), "mode": mode}; 21 | final requestBodyJson = jsonEncode(requestBody); 22 | 23 | // Get the response 24 | final response = await http.post(apiUrl[0] + '/cosmos/txs', 25 | headers: {'Access-Control-Allow-Origin': apiUrl[1]}, body: requestBodyJson); 26 | 27 | if (response.statusCode != 200) { 28 | // throw Exception( 29 | // "Expected status code 200 but got ${response.statusCode} - ${response.body}", 30 | // ); 31 | return false; 32 | } 33 | 34 | // Convert the response 35 | final json = jsonDecode(response.body); 36 | 37 | return json; 38 | } 39 | 40 | static Future broadcastVoteTx({ 41 | @required Account account, 42 | @required VoteTx voteTx, 43 | String mode = "block", 44 | }) async { 45 | // final apiUrl = "${account.networkInfo.lcdUrl}/txs"; 46 | // Get the endpoint 47 | var apiUrl = await loadInterxURL(); 48 | 49 | // Build the request body 50 | final requestBody = {"tx": voteTx.toJson(), "mode": mode}; 51 | final requestBodyJson = jsonEncode(requestBody); 52 | 53 | // Get the response 54 | final response = await http.post(apiUrl[0] + '/cosmos/txs', 55 | headers: {'Access-Control-Allow-Origin': apiUrl[1]}, body: requestBodyJson); 56 | 57 | if (response.statusCode != 200) { 58 | // throw Exception( 59 | // "Expected status code 200 but got ${response.statusCode} - ${response.body}", 60 | // ); 61 | return false; 62 | } 63 | 64 | // Convert the response 65 | final json = jsonDecode(response.body); 66 | 67 | return json; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/lib/widgets/custom_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:kira_auth/utils/colors.dart'; 5 | 6 | import 'package:kira_auth/utils/strings.dart'; 7 | 8 | class CustomDialog extends StatefulWidget { 9 | final List contentWidgets; 10 | 11 | const CustomDialog({Key key, this.contentWidgets}) : super(key: key); 12 | 13 | @override 14 | _CustomDialogState createState() => _CustomDialogState(); 15 | } 16 | 17 | class _CustomDialogState extends State { 18 | @override 19 | Widget build(BuildContext context) { 20 | return AlertDialog( 21 | shape: RoundedRectangleBorder( 22 | borderRadius: BorderRadius.circular(20), 23 | ), 24 | elevation: 0, 25 | backgroundColor: Colors.transparent, 26 | content: contentBox(context), 27 | ); 28 | } 29 | 30 | contentBox(context) { 31 | return Stack( 32 | children: [ 33 | Container( 34 | padding: EdgeInsets.only(left: 40, top: 65, right: 40, bottom: 20), 35 | margin: EdgeInsets.only(top: 45), 36 | decoration: BoxDecoration( 37 | shape: BoxShape.rectangle, 38 | color: Colors.white, 39 | borderRadius: BorderRadius.circular(20), 40 | border: new Border.all( 41 | color: KiraColors.kPurpleColor, 42 | width: 3, 43 | ), 44 | boxShadow: [ 45 | BoxShadow(color: KiraColors.kLightPurpleColor, offset: Offset(5, 10), blurRadius: 10), 46 | ]), 47 | child: Column( 48 | mainAxisSize: MainAxisSize.min, 49 | children: widget.contentWidgets, 50 | ), 51 | ), 52 | Positioned( 53 | left: 20, 54 | right: 20, 55 | child: Container( 56 | decoration: BoxDecoration( 57 | shape: BoxShape.circle, 58 | border: new Border.all( 59 | color: KiraColors.kPurpleColor, 60 | width: 3, 61 | ), 62 | ), 63 | child: CircleAvatar( 64 | backgroundColor: KiraColors.white, 65 | radius: 45, 66 | child: ClipRRect(borderRadius: BorderRadius.all(Radius.circular(45)), child: Image.asset(Strings.logoImage)), 67 | )), 68 | ), 69 | ], 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kira Frontend 2 | 3 | Kira Frontend is a user interface for Kira Network Users to manage their accounts, balance, transfer tokens between different wallets. 4 | 5 | ## Installation 6 | 7 | _NOTE: For development, run chrome browser without security enabled, unless the api doesn't fetch data due to the cors error. GO is required to install Sekai and Interix for this current test build_ 8 | 9 | #### - Frontend 10 | 11 | - Install required packages in pubspec.yaml 12 | 13 | ``` 14 | flutter pub get 15 | ``` 16 | 17 | - Run commands 18 | 19 | ``` 20 | flutter run -d chrome --dart-define=FLUTTER_WEB_USE_SKIA=true 21 | flutter run -d web --dart-define=FLUTTER_WEB_USE_SKIA=true 22 | ``` 23 | 24 | _NOTE: To render svg in flutter, we need to enable SKIA mode when running command_ 25 | 26 | User input the password which will be used for encrypting mnemonic words and kira addresses, public/private keys. 27 | 28 | After creating account, don't forget to keep the mnemonic words (seed) in a safe place and export the account as a file for restoring. 29 | 30 | #### - Backend 31 | 32 | At this current stage: Both INTERX and Sekai will need to be launched before launching the front-end application. Sekai can be considered a validator API service, while INTERX a proxy between the API service and the front-end. 33 | 34 | To interact with INTERX, clone `KIP_9` branch of sekaid repository and check out INTERX readme for more information. 35 | 36 | ``` 37 | https://github.com/KiraCore/sekai/tree/KIP_9 38 | ``` 39 | 40 | - Run sekaid 41 | To get sekai started. Cloned the branch, heaad to current directory "/sekai" within a command line and run the following command: 42 | 43 | ``` 44 | go install ./cmd/sekaid 45 | ``` 46 | 47 | once the dependecies are complete. run the following command: 48 | 49 | ``` 50 | sh sekaidtestsetup.sh 51 | or 52 | run sekaidtestsetup.sh 53 | ``` 54 | 55 | This will launch a local validator which will start producting blocks. 56 | ![](https://imgur.com/sLU1XvA.png) 57 | 58 | - Run INTERX 59 | Interix is a proxy between a front-end and a API provider. In this current version. Sekai acts as the local service provider and Interx interacts with it, to provide services to the front-end. 60 | Head to the active branch and clone the repository. Open the folder and navigate to "/INTERX", get this directory open within a command line and run this command: 61 | 62 | ``` 63 | go run main.go 64 | ``` 65 | 66 | this will download the required dependecies and launch the proxy. 67 | ![](https://imgur.com/GF50xBQ.png) 68 | -------------------------------------------------------------------------------- /src/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/lib/screens/global_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:shared_preferences/shared_preferences.dart'; 4 | 5 | import 'package:kira_auth/models/export.dart'; 6 | import 'package:kira_auth/utils/cache.dart'; 7 | import 'package:kira_auth/widgets/header_wrapper.dart'; 8 | import 'package:kira_auth/blocs/export.dart'; 9 | 10 | class GlobalScreen extends StatefulWidget { 11 | @override 12 | _GlobalScreenState createState() { 13 | return new _GlobalScreenState(); 14 | } 15 | } 16 | 17 | class _GlobalScreenState extends State { 18 | String accountId; 19 | @override 20 | void initState() { 21 | super.initState(); 22 | 23 | getFeeTokenFromCache(); 24 | getCurrentAccountFromCache(); 25 | 26 | checkPasswordExpired().then((success) { 27 | if (success) { 28 | Navigator.pushReplacementNamed(context, '/login'); 29 | } else { 30 | Navigator.pushReplacementNamed(context, '/account'); 31 | } 32 | }); 33 | } 34 | 35 | void getCurrentAccountFromCache() async { 36 | SharedPreferences prefs = await SharedPreferences.getInstance(); 37 | 38 | String currentAccountString = prefs.getString('currentAccount'); 39 | Account currentAccount; 40 | 41 | if (currentAccountString != null && currentAccountString != "") { 42 | currentAccount = Account.fromString(currentAccountString); 43 | } 44 | 45 | if (BlocProvider.of(context).state.currentAccount == null && currentAccount != null) { 46 | BlocProvider.of(context).add(SetCurrentAccount(currentAccount)); 47 | BlocProvider.of(context).add(GetCachedValidators(currentAccount.hexAddress)); 48 | } 49 | } 50 | 51 | void getFeeTokenFromCache() async { 52 | SharedPreferences prefs = await SharedPreferences.getInstance(); 53 | 54 | String feeTokenString = prefs.getString('feeToken'); 55 | Token feeToken; 56 | 57 | if (feeTokenString != null && feeTokenString != "") { 58 | feeToken = Token.fromString(feeTokenString); 59 | } 60 | 61 | if (BlocProvider.of(context).state.feeToken == null && feeToken != null) { 62 | BlocProvider.of(context).add(SetFeeToken(feeToken)); 63 | } 64 | } 65 | 66 | @override 67 | Widget build(BuildContext context) { 68 | return Scaffold( 69 | body: HeaderWrapper( 70 | childWidget: Padding( 71 | padding: const EdgeInsets.all(20.0), 72 | child: Column( 73 | mainAxisAlignment: MainAxisAlignment.center, 74 | children: [], 75 | ), 76 | ))); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/lib/models/validator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | import 'package:kira_auth/utils/colors.dart'; 4 | 5 | enum ValidatorStatus { UNDEFINED, ACTIVE, INACTIVE, PAUSED } 6 | 7 | @JsonSerializable(fieldRename: FieldRename.snake) 8 | class Validator { 9 | final String address; 10 | final String valkey; 11 | final String pubkey; 12 | final String moniker; 13 | final String website; 14 | final String social; 15 | final String identity; 16 | final double commission; 17 | final String status; 18 | final int rank; 19 | final int streak; 20 | final int mischance; 21 | int top; 22 | bool isFavorite; 23 | 24 | String get getReducedAddress => address.replaceRange(10, address.length - 7, '....'); 25 | 26 | Validator({ 27 | this.address = "", 28 | this.valkey = "", 29 | this.pubkey = "", 30 | this.moniker = "", 31 | this.website = "", 32 | this.social = "", 33 | this.identity = "", 34 | this.commission = 0, 35 | this.status = "", 36 | this.top = 0, 37 | this.rank = 0, 38 | this.streak = 0, 39 | this.mischance = 0, 40 | this.isFavorite = false}) { 41 | assert(this.address != null || 42 | this.valkey != null || 43 | this.pubkey != null || 44 | this.moniker != null || 45 | this.status != null); 46 | } 47 | 48 | ValidatorStatus getStatus() { 49 | switch (status) { 50 | case "ACTIVE": 51 | return ValidatorStatus.ACTIVE; 52 | case "INACTIVE": 53 | return ValidatorStatus.INACTIVE; 54 | case "PAUSED": 55 | return ValidatorStatus.PAUSED; 56 | default: 57 | return ValidatorStatus.UNDEFINED; 58 | } 59 | } 60 | 61 | Color getStatusColor() { 62 | switch (getStatus()) { 63 | case ValidatorStatus.ACTIVE: 64 | return KiraColors.green3; 65 | case ValidatorStatus.INACTIVE: 66 | return KiraColors.kGrayColor; 67 | case ValidatorStatus.PAUSED: 68 | return KiraColors.orange3; 69 | default: 70 | return KiraColors.danger; 71 | } 72 | } 73 | 74 | Color getCommissionColor() { 75 | if (commission >= 0.75) 76 | return KiraColors.green3; 77 | if (commission >= 0.5) 78 | return KiraColors.orange3; 79 | if (commission >= 0.25) 80 | return KiraColors.kGrayColor; 81 | return KiraColors.danger; 82 | } 83 | 84 | String checkUnknownWith(String field) { 85 | var value = field == "website" 86 | ? website : field == "social" 87 | ? social : field == "identity" 88 | ? identity : ""; 89 | return value.isEmpty ? "Unknown" : value; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 12 | 19 | 23 | 27 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/lib/data/account_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | import 'package:blake_hash/blake_hash.dart'; 3 | import 'package:bip39/bip39.dart' as bip39; 4 | import 'dart:convert'; 5 | 6 | import 'package:kira_auth/models/network_info.dart'; 7 | import 'package:kira_auth/models/account.dart'; 8 | import 'package:kira_auth/utils/encrypt.dart'; 9 | import 'package:kira_auth/config.dart'; 10 | 11 | abstract class AccountRepository { 12 | Future> getAccountsFromCache(); 13 | Future createNewAccount(String password, String accountName); 14 | Future fakeFetchForTesting(); 15 | } 16 | 17 | class IAccountRepository implements AccountRepository { 18 | @override 19 | Future fakeFetchForTesting() async { 20 | var apiUrl = await loadInterxURL(); 21 | 22 | return Future.delayed(Duration(seconds: 5), () { 23 | return Account( 24 | networkInfo: NetworkInfo( 25 | bech32Hrp: "kira", 26 | lcdUrl: apiUrl[0] + "/cosmos", 27 | ), 28 | hexAddress: "null", 29 | privateKey: "null", 30 | publicKey: "null"); 31 | }); 32 | } 33 | 34 | @override 35 | Future> getAccountsFromCache() async { 36 | SharedPreferences prefs = await SharedPreferences.getInstance(); 37 | String cachedAccountString = prefs.getString('accounts'); 38 | List accounts = []; 39 | 40 | var array = cachedAccountString.split('---'); 41 | 42 | for (int index = 0; index < array.length; index++) { 43 | if (array[index] != '') { 44 | accounts.add(Account.fromString(array[index])); 45 | } 46 | } 47 | 48 | return accounts; 49 | } 50 | 51 | @override 52 | Future createNewAccount(String password, String accountName) async { 53 | Account account; 54 | 55 | // Generate Mnemonic for creating a new account 56 | String mnemonic = bip39.generateMnemonic(strength: 256); 57 | List wordList = mnemonic.split(' '); 58 | List bytes = utf8.encode(password); 59 | 60 | var apiUrl = await loadInterxURL(); 61 | 62 | // Get hash value of password and use it to encrypt mnemonic 63 | var hashDigest = Blake256().update(bytes).digest(); 64 | 65 | final networkInfo = NetworkInfo( 66 | bech32Hrp: "kira", 67 | lcdUrl: apiUrl[0] + "/cosmos", 68 | ); 69 | 70 | account = Account.derive(wordList, networkInfo); 71 | 72 | account.secretKey = String.fromCharCodes(hashDigest); 73 | 74 | // Encrypt Mnemonic with AES-256 algorithm 75 | account.encryptedMnemonic = encryptAESCryptoJS(mnemonic, account.secretKey); 76 | account.checksum = encryptAESCryptoJS('kira', account.secretKey); 77 | account.name = accountName; 78 | 79 | return account; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/lib/config.dart: -------------------------------------------------------------------------------- 1 | // ignore: avoid_web_libraries_in_flutter 2 | import 'dart:html' as html; 3 | import 'dart:async' show Future; 4 | import 'package:http/http.dart' as http; 5 | import 'package:flutter/services.dart' show rootBundle; 6 | import 'package:kira_auth/utils/export.dart'; 7 | import 'dart:convert'; 8 | 9 | Future loadInterxURL() async { 10 | String rpcUrl = await getInterxRPCUrl(); 11 | 12 | String origin = html.window.location.host + html.window.location.pathname; 13 | origin = origin.replaceAll('/', ''); 14 | 15 | bool startsWithHttp = rpcUrl.startsWith('http://') || !rpcUrl.startsWith('http'); 16 | bool noHttp = rpcUrl.startsWith('http'); 17 | 18 | if (rpcUrl != null) { 19 | if (rpcUrl.startsWith('https://cors-anywhere.kira.network/')) { 20 | } else if (rpcUrl.startsWith('http://') || !rpcUrl.startsWith('http')) { 21 | rpcUrl = rpcUrl.replaceAll('http://', ''); 22 | List urlArray = rpcUrl.split(':'); 23 | 24 | if (urlArray.length == 2) { 25 | int port = int.tryParse(urlArray[1]); 26 | if (port == null || port < 1024 || port > 65535) { 27 | rpcUrl = urlArray[0] + ':11000'; 28 | } 29 | } else if (noHttp) { 30 | var response; 31 | try { 32 | response = await http.get(rpcUrl + "/api/kira/status", 33 | headers: {'Access-Control-Allow-Origin': origin}).timeout(Duration(seconds: 3)); 34 | 35 | if (response.body.contains('node_info') == false) { 36 | try { 37 | response = await http.get(rpcUrl + ":11000/api/kira/status", 38 | headers: {'Access-Control-Allow-Origin': origin}).timeout(Duration(seconds: 3)); 39 | 40 | if (response.body.contains('node_info') == true) { 41 | rpcUrl = rpcUrl + ':11000'; 42 | } 43 | } catch (e) { 44 | print(e); 45 | } 46 | } 47 | } catch (e) { 48 | print(e); 49 | } 50 | } else { 51 | rpcUrl = rpcUrl + ':11000'; 52 | } 53 | 54 | if (startsWithHttp) { 55 | rpcUrl = 'http://' + rpcUrl; 56 | rpcUrl = 'https://cors-anywhere.kira.network/' + rpcUrl; 57 | } 58 | } 59 | 60 | return [rpcUrl + '/api', origin]; 61 | } 62 | 63 | return ["", origin]; 64 | } 65 | 66 | Future loadConfig() async { 67 | String config = await rootBundle.loadString('assets/config.json'); 68 | bool autoConnect = json.decode(config)['autoconnect']; 69 | List rpcUrls = json.decode(config)['api_url'].cast(); 70 | 71 | var rpcUrl = rpcUrls[0]; 72 | if (autoConnect == true) await setInterxRPCUrl(rpcUrl); 73 | 74 | if (rpcUrl.contains('http://') == false) { 75 | return [autoConnect, "http://" + rpcUrl + '/api']; 76 | } 77 | 78 | return [autoConnect, rpcUrl + '/api']; 79 | } 80 | -------------------------------------------------------------------------------- /src/lib/helpers/tx_offline_signer.dart: -------------------------------------------------------------------------------- 1 | import 'package:kira_auth/utils/map_sorter.dart'; 2 | import 'package:kira_auth/services/export.dart'; 3 | import 'package:kira_auth/models/account.dart'; 4 | import 'package:kira_auth/models/node_info.dart'; 5 | import 'package:kira_auth/models/cosmos_account.dart'; 6 | import 'package:kira_auth/models/transactions/export.dart'; 7 | 8 | // Library used to separate Online Transcation Signer into separate components 9 | // Depending on whether they are online or offline. This is required to built an offline signer 10 | class TransactionOfflineSigner { 11 | // Retrieves online Account's information [AccountNumber, Sequence] 12 | static Future> getOnlineInformation( 13 | Account account, 14 | StdTx stdTx, 15 | ) async { 16 | // Get the account data and node info from the network 17 | final CosmosAccount cosmosAccount = await QueryService.getAccountData(account); 18 | StatusService service = StatusService(); 19 | await service.getNodeStatus(); 20 | final signature = _getSortedJson( 21 | account, 22 | cosmosAccount, 23 | service.nodeInfo, 24 | stdTx.stdMsg.messages, 25 | stdTx.authInfo.stdFee, 26 | stdTx.stdMsg.memo, 27 | ); 28 | return signature; 29 | } 30 | 31 | // This is one of the separated components from TranscationOnlineSigner 32 | // This returns the structured JSON data that needs to be signed by an Offline device 33 | static Map _getSortedJson( 34 | Account account, 35 | CosmosAccount cosmosAccount, 36 | NodeInfo nodeInfo, 37 | List messages, 38 | StdFee fee, 39 | String memo, 40 | ) { 41 | final signature = StdSignatureMessage( 42 | sequence: cosmosAccount.sequence, 43 | accountNumber: cosmosAccount.accountNumber, 44 | chainId: nodeInfo.network, 45 | fee: fee, 46 | msgs: messages, 47 | memo: memo, 48 | ); 49 | final jsonSignature = signature.toJson(); 50 | final sortedJson = MapSorter.sort(jsonSignature); 51 | 52 | return sortedJson; 53 | } 54 | 55 | // The second separated component, this retrieves the signed and structured transcation from an offline device 56 | // and processes the rest of the code required. Before it can be broadcasted 57 | static Future signOfflineStdTx(Account account, StdTx stdTx, var signature) async { 58 | final CosmosAccount cosmosAccount = await QueryService.getAccountData(account); 59 | 60 | StatusService service = StatusService(); 61 | await service.getNodeStatus(); 62 | 63 | Single single = Single(mode: "SIGN_MODE_LEGACY_AMINO_JSON"); 64 | ModeInfo modeInfo = ModeInfo(single: single); 65 | 66 | StdPublicKey person = StdPublicKey(key: signature['publicKey'].key, type: signature['publicKey'].type); 67 | SignerInfo signerInfo = SignerInfo(publicKey: person, modeInfo: modeInfo, sequence: cosmosAccount.sequence); 68 | 69 | stdTx.authInfo.signerInfos = [ 70 | signerInfo 71 | ]; 72 | return StdTx(stdMsg: stdTx.stdMsg, authInfo: stdTx.authInfo, signatures: [ 73 | signature['signature'] 74 | ]); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/lib/models/block.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | import 'package:date_time_format/date_time_format.dart'; 4 | import 'package:kira_auth/models/export.dart'; 5 | import 'package:kira_auth/services/gravatar_service.dart'; 6 | 7 | @JsonSerializable(fieldRename: FieldRename.snake) 8 | class Block { 9 | final int blockSize; 10 | final String hash; 11 | final String appHash; 12 | final String chainId; 13 | final String consensusHash; 14 | final String dataHash; 15 | final String evidenceHash; 16 | final int height; 17 | final String lastCommitHash; 18 | final String lastResultsHash; 19 | final String nextValidatorsHash; 20 | final String proposerAddress; 21 | final DateTime time; 22 | final String validatorsHash; 23 | final int txAmount; 24 | Validator validator; 25 | 26 | String get getHash => '0x$hash'; 27 | String get getReducedHash => '0x$hash'.replaceRange(7, hash.length - 3, '....'); 28 | String get getProposer => validator != null ? validator.moniker : ""; 29 | String get getProposerIcon => GravatarService().getIdenticon(validator != null ? validator.address : ""); 30 | 31 | Block( 32 | {this.blockSize = 0, 33 | this.hash = "", 34 | this.appHash = "", 35 | this.chainId = "", 36 | this.consensusHash = "", 37 | this.dataHash = "", 38 | this.evidenceHash = "", 39 | this.height = 0, 40 | this.lastCommitHash = "", 41 | this.lastResultsHash = "", 42 | this.validator, 43 | this.nextValidatorsHash = "", 44 | this.proposerAddress = "", 45 | this.time, 46 | this.validatorsHash = "", 47 | this.txAmount = 0}) { 48 | assert(this.blockSize != null || 49 | this.hash != null || 50 | this.appHash != null || 51 | this.chainId != null || 52 | this.consensusHash != null || 53 | this.dataHash != null || 54 | this.evidenceHash != null || 55 | this.height != null || 56 | this.lastCommitHash != null || 57 | this.lastResultsHash != null || 58 | this.dataHash != null || 59 | this.nextValidatorsHash != null || 60 | this.proposerAddress != null || 61 | this.time != null || 62 | this.validatorsHash != null || 63 | this.txAmount != null); 64 | } 65 | 66 | String getLongTimeString() { 67 | var formatter = DateFormat("d MMM yyyy, h:mm:ssa 'UTC'"); 68 | return formatter.format(time.toUtc()); 69 | } 70 | 71 | String getTimeString() { 72 | return time.relative(appendIfAfter: 'ago'); 73 | } 74 | 75 | String getHeightString() { 76 | if (height > -1000 && height < 1000) return height.toString(); 77 | 78 | final String digits = height.abs().toString(); 79 | final StringBuffer result = StringBuffer(height < 0 ? '-' : ''); 80 | final int maxDigitIndex = digits.length - 1; 81 | for (int i = 0; i <= maxDigitIndex; i += 1) { 82 | result.write(digits[i]); 83 | if (i < maxDigitIndex && (maxDigitIndex - i) % 3 == 0) result.write(','); 84 | } 85 | return result.toString(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/lib/widgets/mnemonic_display.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:kira_auth/utils/colors.dart'; 3 | import 'package:kira_auth/utils/styles.dart'; 4 | 5 | /// A widget for displaying a mnemonic phrase 6 | class MnemonicDisplay extends StatefulWidget { 7 | final List wordList; 8 | final bool obscureSeed; 9 | final bool isCopied; 10 | final int rowNumber; 11 | 12 | MnemonicDisplay({@required this.wordList, this.obscureSeed = false, this.isCopied = false, this.rowNumber = 6}); 13 | 14 | _MnemonicDisplayState createState() => _MnemonicDisplayState(); 15 | } 16 | 17 | class _MnemonicDisplayState extends State { 18 | // static final List _obscuredSeed = List.filled(24, '•' * 6); 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | } 24 | 25 | List _buildMnemonicRows() { 26 | int nRows = widget.rowNumber; 27 | int itemsPerRow = 24 ~/ nRows; 28 | int curWord = 0; 29 | List ret = []; 30 | for (int i = 0; i < nRows; i++) { 31 | // Build individual items 32 | List items = []; 33 | for (int j = 0; j < itemsPerRow; j++) { 34 | items.add(Expanded( 35 | child: Container( 36 | margin: EdgeInsets.symmetric(horizontal: 10), 37 | height: 32, 38 | decoration: BoxDecoration( 39 | border: Border.all( 40 | width: 1, 41 | color: widget.isCopied 42 | ? KiraColors.kYellowColor.withOpacity(0.7) 43 | : KiraColors.kGrayColor.withOpacity(0.7)), 44 | color: KiraColors.kGrayColor.withOpacity(0.0), 45 | borderRadius: BorderRadius.circular(20.0)), 46 | child: Center( 47 | child: RichText( 48 | textAlign: TextAlign.start, 49 | text: TextSpan(children: [ 50 | TextSpan( 51 | text: curWord < 9 ? " " : "", 52 | style: AppStyles.textStyleNumbersOfMnemonic(context), 53 | ), 54 | TextSpan( 55 | text: " ${curWord + 1}. ", 56 | style: AppStyles.textStyleNumbersOfMnemonic(context), 57 | ), 58 | TextSpan( 59 | text: widget.wordList != null && widget.wordList.length > 0 ? widget.wordList[curWord] : "", 60 | style: AppStyles.textStyleMnemonic(context), 61 | ) 62 | ]), 63 | )), 64 | ))); 65 | curWord++; 66 | } 67 | ret.add( 68 | Padding( 69 | padding: EdgeInsets.symmetric(vertical: 10), 70 | child: Row( 71 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 72 | crossAxisAlignment: CrossAxisAlignment.center, 73 | children: items), 74 | ), 75 | ); 76 | } 77 | return ret; 78 | } 79 | 80 | @override 81 | Widget build(BuildContext context) { 82 | return Container( 83 | margin: EdgeInsets.only(top: 10), 84 | child: Column( 85 | children: _buildMnemonicRows(), 86 | ), 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/pubspec.yaml.native: -------------------------------------------------------------------------------- 1 | name: src 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.7.0 <3.0.0" 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | 27 | 28 | # The following adds the Cupertino Icons font to your application. 29 | # Use with the CupertinoIcons class for iOS style icons. 30 | cupertino_icons: ^0.1.3 31 | 32 | dev_dependencies: 33 | flutter_test: 34 | sdk: flutter 35 | 36 | # For information on the generic Dart part of this file, see the 37 | # following page: https://dart.dev/tools/pub/pubspec 38 | 39 | # The following section is specific to Flutter. 40 | flutter: 41 | 42 | # The following line ensures that the Material Icons font is 43 | # included with your application, so that you can use the icons in 44 | # the material Icons class. 45 | uses-material-design: true 46 | 47 | # To add assets to your application, add an assets section, like this: 48 | # assets: 49 | # - images/a_dot_burr.jpeg 50 | # - images/a_dot_ham.jpeg 51 | 52 | # An image asset can refer to one or more resolution-specific "variants", see 53 | # https://flutter.dev/assets-and-images/#resolution-aware. 54 | 55 | # For details regarding adding assets from package dependencies, see 56 | # https://flutter.dev/assets-and-images/#from-packages 57 | 58 | # To add custom fonts to your application, add a fonts section here, 59 | # in this "flutter" section. Each entry in this list should have a 60 | # "family" key with the font family name, and a "fonts" key with a 61 | # list giving the asset and other descriptors for the font. For 62 | # example: 63 | # fonts: 64 | # - family: Schyler 65 | # fonts: 66 | # - asset: fonts/Schyler-Regular.ttf 67 | # - asset: fonts/Schyler-Italic.ttf 68 | # style: italic 69 | # - family: Trajan Pro 70 | # fonts: 71 | # - asset: fonts/TrajanPro.ttf 72 | # - asset: fonts/TrajanPro_Bold.ttf 73 | # weight: 700 74 | # 75 | # For details regarding fonts from package dependencies, 76 | # see https://flutter.dev/custom-fonts/#from-packages 77 | -------------------------------------------------------------------------------- /src/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:kira_auth/data/account_repository.dart'; 6 | import 'package:kira_auth/data/token_repository.dart'; 7 | import 'package:kira_auth/data/validator_repository.dart'; 8 | import 'package:kira_auth/router.dart'; 9 | import 'package:kira_auth/utils/colors.dart'; 10 | import 'package:kira_auth/blocs/export.dart'; 11 | 12 | Future main() async { 13 | runApp(MyApp()); 14 | } 15 | 16 | class MyApp extends StatelessWidget { 17 | // This widget is the root of your application. 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | FluroRouter.setupRouter(); 22 | 23 | return MultiBlocProvider( 24 | providers: [ 25 | BlocProvider(create: (context) => AccountBloc(IAccountRepository())), 26 | BlocProvider(create: (context) => NetworkBloc()), 27 | BlocProvider(create: (context) => TokenBloc(ITokenRepository())), 28 | BlocProvider(create: (context) => ValidatorBloc(IValidatorRepository())), 29 | ], 30 | child: MaterialApp( 31 | title: 'Kira Network', 32 | initialRoute: '/', 33 | onGenerateRoute: FluroRouter.router.generator, 34 | debugShowCheckedModeBanner: false, 35 | theme: ThemeData( 36 | // The background color for major parts of the app (toolbars, tab bars, etc) 37 | primaryColor: KiraColors.kPrimaryColor, 38 | // The default color of the Material that underlies the Scaffold. 39 | // scaffoldBackgroundColor: Colors.white, 40 | // // Text with a color that contrasts with the card and canvas colors. 41 | textTheme: GoogleFonts.redHatTextTextTheme(Theme.of(context).textTheme), 42 | // fontFamily: GoogleFonts.aleo().toString(), 43 | visualDensity: VisualDensity.adaptivePlatformDensity, 44 | primarySwatch: Colors.purple, 45 | // A color that contrasts with the primaryColor, e.g. used as the remaining part of a progress bar. 46 | backgroundColor: KiraColors.kBackgroundColor, 47 | canvasColor: KiraColors.kBrownColor, 48 | // cardColor: KiraColors.white, 49 | primaryTextTheme: TextTheme( 50 | button: TextStyle( 51 | fontFamily: 'Montserrat', 52 | fontSize: 60, 53 | color: KiraColors.white, 54 | decorationColor: KiraColors.kPurpleColor, 55 | ), 56 | subtitle2: TextStyle( 57 | color: KiraColors.blue2, 58 | ), 59 | //Used for the primary text in lists 60 | subtitle1: TextStyle( 61 | color: KiraColors.green1, 62 | ), 63 | headline1: TextStyle(color: Colors.purple[50]), 64 | ), 65 | // bottomAppBarColor: Colors.purple[900], 66 | // iconTheme: IconThemeData(color: Colors.purple), 67 | ))); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/lib/models/block_transaction.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:intl/intl.dart'; 4 | import 'package:json_annotation/json_annotation.dart'; 5 | import 'package:date_time_format/date_time_format.dart'; 6 | import 'package:kira_auth/models/export.dart'; 7 | import 'package:kira_auth/utils/colors.dart'; 8 | import 'package:kira_auth/utils/export.dart'; 9 | 10 | @JsonSerializable(fieldRename: FieldRename.snake) 11 | class BlockTransaction { 12 | final String hash; 13 | final String status; 14 | final int blockHeight; 15 | final int timestamp; 16 | final int confirmation; 17 | final int gasWanted; 18 | final int gasUsed; 19 | List transactions; 20 | List messages; 21 | List fees; 22 | 23 | String get getHash => '0x$hash'; 24 | String get getReducedHash => '0x$hash'.replaceRange(7, hash.length - 3, '....'); 25 | 26 | BlockTransaction( 27 | {this.hash = "", 28 | this.status = "", 29 | this.blockHeight = 0, 30 | this.confirmation = 0, 31 | this.gasWanted = 0, 32 | this.gasUsed = 0, 33 | this.timestamp = 0, 34 | this.transactions, 35 | this.messages, 36 | this.fees}) { 37 | assert(this.hash != null || 38 | this.status != null || 39 | this.timestamp != null || 40 | this.confirmation != null || 41 | this.gasWanted != null || 42 | this.gasUsed != null); 43 | } 44 | 45 | Color getStatusColor() { 46 | return status == "Success" ? KiraColors.green3 : status == "Pending" ? KiraColors.orange1 : KiraColors.danger; 47 | } 48 | 49 | List getTypes() { 50 | return messages.map((msg) => msg.getType).toList(); 51 | } 52 | 53 | String getLongTimeString() { 54 | var formatter = DateFormat("d MMM yyyy, h:mm:ssa 'UTC'"); 55 | return formatter.format(DateTime.fromMillisecondsSinceEpoch(timestamp * 1000).toUtc()); 56 | } 57 | 58 | String getTimeString() { 59 | return DateTime.fromMillisecondsSinceEpoch(timestamp * 1000).toUtc().relative(appendIfAfter: 'ago'); 60 | } 61 | 62 | String getHeightString() { 63 | if (blockHeight > -1000 && blockHeight < 1000) return blockHeight.toString(); 64 | 65 | final String digits = blockHeight.abs().toString(); 66 | final StringBuffer result = StringBuffer(blockHeight < 0 ? '-' : ''); 67 | final int maxDigitIndex = digits.length - 1; 68 | for (int i = 0; i <= maxDigitIndex; i += 1) { 69 | result.write(digits[i]); 70 | if (i < maxDigitIndex && (maxDigitIndex - i) % 3 == 0) result.write(','); 71 | } 72 | return result.toString(); 73 | } 74 | 75 | static BlockTransaction fromJson(Map data) { 76 | return BlockTransaction( 77 | hash: data['hash'], 78 | status: data['status'], 79 | blockHeight: data['block_height'], 80 | timestamp: data['block_timestamp'], 81 | confirmation: data['confirmation'], 82 | gasWanted: data['gas_wanted'], 83 | gasUsed: data['gas_used'], 84 | transactions: (data['transactions'] as List).map((e) => TxSend.fromJson(e)).toList(), 85 | messages: (data['msgs'] as List).map((e) => TxMsg.fromJson(e)).toList(), 86 | fees: (data['fees'] as List).map((e) => StdCoin.fromJson(e)).toList(), 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/lib/blocs/account_state.dart: -------------------------------------------------------------------------------- 1 | part of 'account_bloc.dart'; 2 | 3 | @immutable 4 | abstract class AccountState extends Equatable { 5 | final String message; 6 | final Account currentAccount; 7 | final List accounts; 8 | final String interxPubKey; 9 | 10 | AccountState({this.currentAccount, this.accounts, this.message, this.interxPubKey}); 11 | 12 | @override 13 | List get props => [currentAccount, accounts, message]; 14 | } 15 | 16 | // Events for getting cached accounts 17 | class AccountInitial extends AccountState { 18 | AccountInitial() : super(); 19 | 20 | @override 21 | List get props => []; 22 | 23 | @override 24 | String toString() => 'Account Initial State'; 25 | } 26 | 27 | class CachedAccountsLoading extends AccountState { 28 | CachedAccountsLoading() : super(); 29 | 30 | @override 31 | List get props => []; 32 | 33 | @override 34 | String toString() => 'Get Accounts Loading State'; 35 | } 36 | 37 | class CachedAccountsLoaded extends AccountState { 38 | final List accounts; 39 | 40 | CachedAccountsLoaded({this.accounts}) : super(accounts: accounts); 41 | 42 | @override 43 | List get props => [accounts]; 44 | 45 | @override 46 | String toString() => 'Get Accounts Loaded'; 47 | } 48 | 49 | class CachedAccountsError extends AccountState { 50 | final String message; 51 | 52 | CachedAccountsError({this.message}) : super(message: message); 53 | 54 | @override 55 | List get props => [message]; 56 | 57 | @override 58 | String toString() => 'Get Accounts Error'; 59 | } 60 | 61 | // Events for creating a new account 62 | class AccountCreating extends AccountState { 63 | AccountCreating() : super(); 64 | 65 | @override 66 | List get props => []; 67 | 68 | @override 69 | String toString() => 'Creating new account'; 70 | } 71 | 72 | class AccountCreationError extends AccountState { 73 | final String message; 74 | 75 | AccountCreationError({this.message}) : super(message: message); 76 | 77 | @override 78 | bool operator ==(Object o) { 79 | if (identical(this, o)) return true; 80 | 81 | return o is AccountCreationError && o.message == message; 82 | } 83 | 84 | @override 85 | int get hashCode => message.hashCode; 86 | 87 | @override 88 | List get props => [message]; 89 | 90 | @override 91 | String toString() => 'Error happened while creating a new account'; 92 | } 93 | 94 | // Event for updating current account 95 | class CurrentAccountUpdated extends AccountState { 96 | final Account currentAccount; 97 | 98 | CurrentAccountUpdated({this.currentAccount}) : super(currentAccount: currentAccount); 99 | 100 | @override 101 | List get props => [currentAccount]; 102 | 103 | @override 104 | String toString() => 'Current account updated - ' + currentAccount.name; 105 | } 106 | 107 | class InterxPubKeyUpdated extends AccountState { 108 | final String interxPubKey; 109 | 110 | InterxPubKeyUpdated({this.interxPubKey}) : super(interxPubKey: interxPubKey); 111 | 112 | @override 113 | List get props => [interxPubKey]; 114 | 115 | @override 116 | String toString() => 'Interx PubKey Updated - ' + interxPubKey; 117 | } 118 | -------------------------------------------------------------------------------- /src/lib/utils/encrypt.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:math'; 3 | import 'dart:typed_data'; 4 | import 'package:crypto/crypto.dart'; 5 | import 'package:tuple/tuple.dart'; 6 | import 'package:encrypt/encrypt.dart' as encrypt; 7 | 8 | String encryptAESCryptoJS(String plainText, String passphrase) { 9 | try { 10 | final salt = genRandomWithNonZero(8); 11 | var keyndIV = deriveKeyAndIV(passphrase, salt); 12 | final key = encrypt.Key(keyndIV.item1); 13 | final iv = encrypt.IV(keyndIV.item2); 14 | 15 | final encrypter = encrypt.Encrypter( 16 | encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7")); 17 | final encrypted = encrypter.encrypt(plainText, iv: iv); 18 | 19 | Uint8List encryptedBytesWithSalt = Uint8List.fromList( 20 | createUint8ListFromString("Salted__") + salt + encrypted.bytes); 21 | 22 | return base64.encode(encryptedBytesWithSalt); 23 | } catch (error) { 24 | return null; 25 | } 26 | } 27 | 28 | String decryptAESCryptoJS(String encrypted, String passphrase) { 29 | try { 30 | Uint8List encryptedBytesWithSalt = base64.decode(encrypted); 31 | Uint8List encryptedBytes = 32 | encryptedBytesWithSalt.sublist(16, encryptedBytesWithSalt.length); 33 | 34 | final salt = encryptedBytesWithSalt.sublist(8, 16); 35 | var keyndIV = deriveKeyAndIV(passphrase, salt); 36 | final key = encrypt.Key(keyndIV.item1); 37 | final iv = encrypt.IV(keyndIV.item2); 38 | 39 | final encrypter = encrypt.Encrypter( 40 | encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7")); 41 | final decrypted = 42 | encrypter.decrypt64(base64.encode(encryptedBytes), iv: iv); 43 | 44 | return decrypted; 45 | } catch (error) { 46 | return null; 47 | } 48 | } 49 | 50 | Tuple2 deriveKeyAndIV(String passphrase, Uint8List salt) { 51 | var password = createUint8ListFromString(passphrase); 52 | Uint8List concatenatedHashes = Uint8List(0); 53 | Uint8List currentHash = Uint8List(0); 54 | Uint8List preHash = Uint8List(0); 55 | bool enoughBytesForKey = false; 56 | 57 | while (!enoughBytesForKey) { 58 | // int preHashLength = currentHash.length + password.length + salt.length; 59 | if (currentHash.length > 0) 60 | preHash = Uint8List.fromList(currentHash + password + salt); 61 | else 62 | preHash = Uint8List.fromList(password + salt); 63 | 64 | currentHash = md5.convert(preHash).bytes; 65 | concatenatedHashes = Uint8List.fromList(concatenatedHashes + currentHash); 66 | if (concatenatedHashes.length >= 48) enoughBytesForKey = true; 67 | } 68 | 69 | var keyBtyes = concatenatedHashes.sublist(0, 32); 70 | var ivBtyes = concatenatedHashes.sublist(32, 48); 71 | 72 | return new Tuple2(keyBtyes, ivBtyes); 73 | } 74 | 75 | Uint8List createUint8ListFromString(String s) { 76 | var ret = new Uint8List(s.length); 77 | for (var i = 0; i < s.length; i++) { 78 | ret[i] = s.codeUnitAt(i); 79 | } 80 | return ret; 81 | } 82 | 83 | Uint8List genRandomWithNonZero(int seedLength) { 84 | final random = Random.secure(); 85 | final Uint8List uint8list = Uint8List(seedLength); 86 | const int randomMax = 245; 87 | 88 | for (int i = 0; i < seedLength; i++) { 89 | uint8list[i] = random.nextInt(randomMax) + 1; 90 | } 91 | 92 | return uint8list; 93 | } 94 | -------------------------------------------------------------------------------- /src/lib/models/tx_types.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:kira_auth/models/export.dart'; 3 | import 'package:kira_auth/utils/export.dart'; 4 | 5 | enum TxType { 6 | UNKNOWN, SEND, MULTISEND, PROPOSAL_ASSIGN_PERMISSION, PROPOSAL_SET_NETWORK_PROPERTY, 7 | PROPOSAL_UPSERT_DATA_REGISTRY, PROPOSAL_UPSERT_TOKEN_ALIAS, VOTE_PROPOSAL, 8 | WHITELIST_PERMISSIONS, BLACKLIST_PERMISSIONS, CLAIM_COUNCILOR, 9 | SET_NETWORK_PROPERTIES, SET_EXECUTION_FEE, CREATE_ROLE, ASSIGN_ROLE, REMOVE_ROLE, 10 | WHITELIST_ROLE_PERMISSION, BLACKLIST_ROLE_PERMISSION, 11 | REMOVE_WHITELIST_ROLE_PERMISSION, REMOVE_BLACKLIST_ROLE_PERMISSION, 12 | CLAIM_VALIDATOR, UPSERT_TOKEN_ALIAS, UPSERT_TOKEN_RATE 13 | } 14 | 15 | class CopyableText { 16 | String value; 17 | String toast; 18 | 19 | bool get isCopyable => toast.isNotEmpty; 20 | 21 | CopyableText({ this.value, this.toast = "" }); 22 | } 23 | 24 | @JsonSerializable(fieldRename: FieldRename.snake) 25 | class TxMsg { 26 | TxType type; 27 | Map data; 28 | 29 | String get getType => Strings.messageNames[TxType.values.indexOf(type)]; 30 | 31 | TxMsg({this.type, this.data }); 32 | 33 | static TxMsg fromJson(Map json) { 34 | return TxMsg( 35 | type: TxType.values[Strings.messageTypes.indexOf(json['type'] as String) + 1], 36 | data: json['data'] as Map 37 | ); 38 | } 39 | 40 | Map getDetails() { 41 | switch (type) { 42 | case TxType.SEND: 43 | var send = TxSend.fromJson(data); 44 | return { 45 | "From": CopyableText(value: send.from, toast: Strings.senderAddressCopied), 46 | "To": CopyableText(value: send.to, toast: Strings.recipientAddressCopied), 47 | "Amount": CopyableText(value: send.amounts[0].amount + " " + send.amounts[0].denom) 48 | }; 49 | case TxType.VOTE_PROPOSAL: 50 | var vote = TxVote.fromJson(data); 51 | return { 52 | "Voter": CopyableText(value: vote.voter, toast: Strings.voterCopied), 53 | "Vote Option": CopyableText(value: Strings.voteOptions[vote.option - 1]), 54 | "Proposal Id": CopyableText(value: vote.proposalId), 55 | }; 56 | default: 57 | return {" ": CopyableText(value: "Coming Soon")}; 58 | } 59 | } 60 | } 61 | 62 | @JsonSerializable(fieldRename: FieldRename.snake) 63 | class TxSend { 64 | String from; 65 | String to; 66 | List amounts; 67 | 68 | TxSend({this.from, this.to, this.amounts}); 69 | 70 | static TxSend fromJson(Map json) { 71 | return TxSend( 72 | from: json['from'], 73 | to: json['to'], 74 | amounts: (json['amounts'] as List).map((e) => StdCoin.fromJson(e)).toList(), 75 | ); 76 | } 77 | } 78 | 79 | @JsonSerializable(fieldRename: FieldRename.snake) 80 | class TxVote { 81 | final String voter; 82 | final String proposalId; 83 | final int option; 84 | 85 | TxVote({ this.voter = "Unknown", this.proposalId = "0", this.option = 0}) { 86 | assert(this.voter != null, this.proposalId != null); 87 | } 88 | 89 | static TxVote fromJson(Map json) { 90 | return TxVote( 91 | voter: json['voter'].toString(), 92 | proposalId: json['proposal_id'].toString(), 93 | option: json['option'] 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/lib/widgets/custom_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:kira_auth/utils/colors.dart'; 3 | 4 | // ignore: must_be_immutable 5 | class CustomButton extends StatefulWidget { 6 | Key key; 7 | double fontSize; 8 | String text; 9 | bool isKey; 10 | double width; 11 | double height; 12 | EdgeInsets margin; 13 | double style; // 0: Border only, 1: Gray background, 2: Gradient background 14 | VoidCallback onPressed; 15 | bool isActive; 16 | 17 | CustomButton( 18 | {this.key, 19 | this.text, 20 | this.width, 21 | this.height, 22 | this.isKey = false, 23 | this.onPressed, 24 | this.style = 0, 25 | this.isActive = false, 26 | this.fontSize = 15.0}) 27 | : super(key: key); 28 | 29 | @override 30 | _CustomButtonState createState() => _CustomButtonState(); 31 | } 32 | 33 | class _CustomButtonState extends State { 34 | @override 35 | Widget build(BuildContext context) { 36 | return Container( 37 | margin: widget.margin, 38 | padding: EdgeInsets.all(0.0), 39 | child: TextButton( 40 | onPressed: widget.onPressed, 41 | // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)), 42 | // minWidth: 40, 43 | child: Ink( 44 | width: widget.width, 45 | height: widget.height, 46 | padding: EdgeInsets.zero, 47 | decoration: widget.style == 2 48 | ? BoxDecoration( 49 | gradient: LinearGradient( 50 | colors: [ 51 | //ADD If you need more 52 | Color.fromRGBO(134, 53, 213, 1), 53 | Color.fromRGBO(85, 53, 214, 1), 54 | Color.fromRGBO(7, 200, 248, 1), 55 | ], 56 | begin: Alignment.centerLeft, 57 | end: Alignment.centerRight, 58 | ), 59 | borderRadius: BorderRadius.circular(10.0)) 60 | : widget.style == 1 61 | ? BoxDecoration( 62 | color: KiraColors.kGrayColor.withOpacity(0.1), 63 | borderRadius: BorderRadius.circular(10.0), 64 | border: new Border.all( 65 | color: widget.isActive 66 | ? KiraColors.kYellowColor.withOpacity(0.5) 67 | : KiraColors.kGrayColor.withOpacity(0.0), 68 | width: 1, 69 | ), 70 | ) 71 | : null, 72 | child: widget.isKey 73 | ? InkWell( 74 | child: Icon( 75 | Icons.upload_rounded, 76 | color: Colors.white, 77 | size: 30, 78 | ) 79 | /* 80 | Image( 81 | image: AssetImage(Strings.keyImage), 82 | width: 40, 83 | height: 40, 84 | ) 85 | */ 86 | ) 87 | : Center( 88 | child: Text( 89 | widget.text, 90 | textAlign: TextAlign.center, 91 | style: TextStyle(color: Colors.white, fontSize: widget.fontSize), 92 | ), 93 | ), 94 | ), 95 | ), 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/lib/widgets/dotted_border.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'dart:math' as math; 3 | 4 | class DottedBorder extends StatelessWidget { 5 | final Color color; 6 | final double strokeWidth; 7 | final double gap; 8 | final Widget child; 9 | final EdgeInsets padding; 10 | 11 | DottedBorder({ 12 | this.color = Colors.black, 13 | this.strokeWidth = 1.0, 14 | this.gap = 5.0, 15 | this.padding = const EdgeInsets.all(8.0), 16 | @required this.child, 17 | }) { 18 | assert(child != null); 19 | } 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return Container( 24 | child: Padding( 25 | padding: EdgeInsets.all(strokeWidth / 2), 26 | child: Stack( 27 | children: [ 28 | Positioned.fill( 29 | child: CustomPaint( 30 | painter: _DashRectPainter( 31 | color: color, 32 | strokeWidth: strokeWidth, 33 | gap: gap, 34 | ), 35 | ), 36 | ), 37 | Padding( 38 | padding: padding, 39 | child: child, 40 | ), 41 | ], 42 | ), 43 | ), 44 | ); 45 | } 46 | } 47 | 48 | class _DashRectPainter extends CustomPainter { 49 | double strokeWidth; 50 | Color color; 51 | double gap; 52 | 53 | _DashRectPainter({ 54 | this.strokeWidth = 5.0, 55 | this.color = Colors.black, 56 | this.gap = 5.0, 57 | }); 58 | 59 | @override 60 | void paint(Canvas canvas, Size size) { 61 | Paint dashedPaint = Paint() 62 | ..color = color 63 | ..strokeWidth = strokeWidth 64 | ..style = PaintingStyle.stroke; 65 | 66 | double x = size.width; 67 | double y = size.height; 68 | 69 | Path _topPath = getDashedPath( 70 | a: math.Point(0, 0), 71 | b: math.Point(x, 0), 72 | gap: gap, 73 | ); 74 | 75 | Path _rightPath = getDashedPath( 76 | a: math.Point(x, 0), 77 | b: math.Point(x, y), 78 | gap: gap, 79 | ); 80 | 81 | Path _bottomPath = getDashedPath( 82 | a: math.Point(0, y), 83 | b: math.Point(x, y), 84 | gap: gap, 85 | ); 86 | 87 | Path _leftPath = getDashedPath( 88 | a: math.Point(0, 0), 89 | b: math.Point(0.001, y), 90 | gap: gap, 91 | ); 92 | 93 | canvas.drawPath(_topPath, dashedPaint); 94 | canvas.drawPath(_rightPath, dashedPaint); 95 | canvas.drawPath(_bottomPath, dashedPaint); 96 | canvas.drawPath(_leftPath, dashedPaint); 97 | } 98 | 99 | Path getDashedPath({ 100 | @required math.Point a, 101 | @required math.Point b, 102 | @required gap, 103 | }) { 104 | Size size = Size(b.x - a.x, b.y - a.y); 105 | Path path = Path(); 106 | path.moveTo(a.x, a.y); 107 | bool shouldDraw = true; 108 | math.Point currentPoint = math.Point(a.x, a.y); 109 | 110 | num radians = math.atan(size.height / size.width); 111 | 112 | num dx = math.cos(radians) * gap < 0 ? math.cos(radians) * gap * -1 : math.cos(radians) * gap; 113 | 114 | num dy = math.sin(radians) * gap < 0 ? math.sin(radians) * gap * -1 : math.sin(radians) * gap; 115 | 116 | while (currentPoint.x <= b.x && currentPoint.y <= b.y) { 117 | shouldDraw ? path.lineTo(currentPoint.x, currentPoint.y) : path.moveTo(currentPoint.x, currentPoint.y); 118 | shouldDraw = !shouldDraw; 119 | currentPoint = math.Point( 120 | currentPoint.x + dx, 121 | currentPoint.y + dy, 122 | ); 123 | } 124 | return path; 125 | } 126 | 127 | @override 128 | bool shouldRepaint(CustomPainter oldDelegate) { 129 | return true; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/lib/utils/msg_signer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:pointycastle/ecc/curves/secp256k1.dart'; 4 | import 'package:pointycastle/ecc/api.dart'; 5 | import 'package:pointycastle/signers/ecdsa_signer.dart'; 6 | import 'package:pointycastle/macs/hmac.dart'; 7 | import 'package:pointycastle/digests/sha256.dart'; 8 | import 'package:pointycastle/api.dart'; 9 | 10 | /// Helper class used to sign a transaction. 11 | class MessageSigner { 12 | // Constants 13 | static final BigInt _byteMask = BigInt.from(0xff); 14 | static final BigInt _prime = BigInt.tryParse( 15 | 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', 16 | radix: 16, 17 | ); 18 | 19 | static BigInt _bytesToInt(List bytes) => _decodeBigInt(bytes); 20 | 21 | static Uint8List _intToBytes(BigInt number) => _encodeBigInt(number); 22 | 23 | static Uint8List _encodeBigInt(BigInt number) { 24 | final size = (number.bitLength + 7) >> 3; 25 | final result = Uint8List(size); 26 | var num = number; 27 | for (var i = 0; i < size; i++) { 28 | result[size - i - 1] = (num & _byteMask).toInt(); 29 | num = num >> 8; 30 | } 31 | return result; 32 | } 33 | 34 | static BigInt _decodeBigInt(Uint8List bytes) { 35 | var result = BigInt.from(0); 36 | for (var i = 0; i < bytes.length; i++) { 37 | result += BigInt.from(bytes[bytes.length - i - 1]) << (8 * i); 38 | } 39 | return result; 40 | } 41 | 42 | static BigInt _recoverFromSignature(int recId, ECSignature sig, Uint8List msg, ECDomainParameters params) { 43 | final n = params.n; 44 | final i = BigInt.from(recId ~/ 2); 45 | final x = sig.r + (i * n); 46 | 47 | if (x.compareTo(_prime) >= 0) { 48 | return null; 49 | } 50 | 51 | final R = _decompressKey(x, (recId & 1) == 1, params.curve); 52 | if (!(R * n).isInfinity) { 53 | return null; 54 | } 55 | 56 | final e = _bytesToInt(msg); 57 | 58 | final eInv = (BigInt.zero - e) % n; 59 | final rInv = sig.r.modInverse(n); 60 | final srInv = (rInv * sig.s) % n; 61 | final eInvrInv = (rInv * eInv) % n; 62 | 63 | final q = (params.G * eInvrInv) + (R * srInv); 64 | 65 | final bytes = q.getEncoded(false); 66 | return _bytesToInt(bytes.sublist(1)); 67 | } 68 | 69 | static ECPoint _decompressKey(BigInt xBN, bool yBit, ECCurve c) { 70 | List x9IntegerToBytes(BigInt s, int qLength) { 71 | final bytes = _intToBytes(s); 72 | 73 | if (qLength < bytes.length) { 74 | return bytes.sublist(0, bytes.length - qLength); 75 | } else if (qLength > bytes.length) { 76 | final tmp = List.filled(qLength, 0); 77 | 78 | final offset = qLength - bytes.length; 79 | for (var i = 0; i < bytes.length; i++) { 80 | tmp[i + offset] = bytes[i]; 81 | } 82 | 83 | return tmp; 84 | } 85 | 86 | return bytes; 87 | } 88 | 89 | final compEnc = x9IntegerToBytes(xBN, 1 + ((c.fieldSize + 7) ~/ 8)); 90 | compEnc[0] = yBit ? 0x03 : 0x02; 91 | return c.decodePoint(compEnc); 92 | } 93 | 94 | static Uint8List deriveFrom( 95 | Uint8List message, 96 | ECPrivateKey privateKey, 97 | ECPublicKey publicKey, 98 | ) { 99 | final ECDomainParameters _params = ECCurve_secp256k1(); 100 | final _halfCurveOrder = _params.n >> 1; 101 | 102 | final ecdsaSigner = ECDSASigner(null, HMac(SHA256Digest(), 64))..init(true, PrivateKeyParameter(privateKey)); 103 | 104 | ECSignature ecSignature = ecdsaSigner.generateSignature(message); 105 | 106 | if (ecSignature.s.compareTo(_halfCurveOrder) > 0) { 107 | final canonicalS = _params.n - ecSignature.s; 108 | ecSignature = ECSignature(ecSignature.r, canonicalS); 109 | } 110 | 111 | final publicKeyBytes = Uint8List.view(publicKey.Q.getEncoded(false).buffer, 1); 112 | 113 | final publicKeyBigInt = _bytesToInt(publicKeyBytes); 114 | 115 | var recoveryID = -1; 116 | for (var i = 0; i < 4; i++) { 117 | final k = _recoverFromSignature(i, ecSignature, message, _params); 118 | if (k == publicKeyBigInt) { 119 | recoveryID = i; 120 | break; 121 | } 122 | } 123 | 124 | if (recoveryID == -1) { 125 | throw Exception('Invalid recoverable key'); 126 | } 127 | 128 | return Uint8List.fromList( 129 | _intToBytes(ecSignature.r) + _intToBytes(ecSignature.s), 130 | ); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: kira_auth 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: '>=2.7.0 <3.0.0' 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | fluid_layout: ^0.1.0 27 | flutter_staggered_grid_view: ^0.3.0 28 | google_fonts: ^1.1.0 29 | fluro: 30 | git: git://github.com/theyakka/fluro.git 31 | fl_chart: ^0.10.1 32 | date_time_format: ^1.1.1+1 33 | fluttertoast: ^7.1.6 34 | flutter_swiper: 1.1.6 35 | 36 | # bip39: mnemonic code for generating deterministic keys 37 | bip39: ^1.0.3 38 | 39 | # Nano core functions 40 | flutter_nano_ffi: ^1.0.3 41 | 42 | # The following adds the Cupertino Icons font to your application. 43 | # Use with the CupertinoIcons class for iOS style icons. 44 | cupertino_icons: ^0.1.3 45 | blake_hash: ^1.0.0 46 | base32: ^1.1.1 47 | tuple: ^1.0.3 48 | encrypt: ^4.0.2 49 | path_provider: ^1.6.11 50 | html: ^0.14.0+3 51 | shared_preferences: ^0.5.10 52 | clipboard: ^0.1.2+8 53 | http: ^0.12.2 54 | basic_utils: ^2.6.2 55 | json_annotation: ^3.0.1 56 | pointycastle: ^1.0.2 57 | bip32: ^1.0.5 58 | bip_bech32: ^0.1.3 59 | bloc: ^6.0.3 60 | equatable: ^1.2.5 61 | qr_flutter: ^3.2.0 62 | sacco: ^0.1.4 63 | jdenticon_dart: ^1.2.1+1 64 | intl: ^0.16.1 65 | secp256k1: ^0.2.2 66 | crypto: ^2.1.5 67 | flutter_bloc: ^6.1.1 68 | regexpattern: ^1.0.0 69 | flutter_svg: ^0.19.3 70 | jdenticon: ^1.0.1 71 | pie_chart: ^4.0.0 72 | url_launcher: ^6.0.3 73 | flutter_countdown_timer: ^3.0.1 74 | universal_ui: ^0.0.8 75 | expandable: ^4.0.0 76 | # BLAKE2b and BLAKE2s cryptographic hashing functions 77 | 78 | dev_dependencies: 79 | flutter_test: 80 | sdk: flutter 81 | build_runner: ^1.10.2 82 | json_serializable: ^3.4.1 83 | 84 | # For information on the generic Dart part of this file, see the 85 | # following page: https://dart.dev/tools/pub/pubspec 86 | # The following section is specific to Flutter. 87 | flutter: 88 | # The following line ensures that the Material Icons font is 89 | # included with your application, so that you can use the icons in 90 | # the material Icons class. 91 | uses-material-design: true 92 | # To add assets to your application, add an assets section, like this: 93 | assets: 94 | - assets/images/ 95 | - assets/config.json 96 | # - images/a_dot_ham.jpeg 97 | # An image asset can refer to one or more resolution-specific "variants", see 98 | # https://flutter.dev/assets-and-images/#resolution-aware. 99 | # For details regarding adding assets from package dependencies, see 100 | # https://flutter.dev/assets-and-images/#from-packages 101 | # To add custom fonts to your application, add a fonts section here, 102 | # in this "flutter" section. Each entry in this list should have a 103 | # "family" key with the font family name, and a "fonts" key with a 104 | # list giving the asset and other descriptors for the font. For 105 | # example: 106 | # fonts: 107 | # - family: Schyler 108 | # fonts: 109 | # - asset: fonts/Schyler-Regular.ttf 110 | # - asset: fonts/Schyler-Italic.ttf 111 | # style: italic 112 | # - family: Trajan Pro 113 | # fonts: 114 | # - asset: fonts/TrajanPro.ttf 115 | # - asset: fonts/TrajanPro_Bold.ttf 116 | # weight: 700 117 | # 118 | # For details regarding fonts from package dependencies, 119 | # see https://flutter.dev/custom-fonts/#from-packages 120 | -------------------------------------------------------------------------------- /src/lib/services/status_service.dart: -------------------------------------------------------------------------------- 1 | // ignore: avoid_web_libraries_in_flutter 2 | import 'dart:html' as html; 3 | import 'dart:convert'; 4 | import 'package:http/http.dart' as http; 5 | import 'package:kira_auth/models/export.dart'; 6 | import 'package:kira_auth/config.dart'; 7 | import 'package:kira_auth/utils/export.dart'; 8 | 9 | class StatusService { 10 | NodeInfo nodeInfo; 11 | SyncInfo syncInfo; 12 | ValidatorInfo validatorInfo; 13 | String interxPubKey; 14 | String rpcUrl = ""; 15 | bool isNetworkHealthy = true; 16 | 17 | Future getNodeStatus() async { 18 | var apiUrl = await loadInterxURL(); 19 | var config = await loadConfig(); 20 | var response; 21 | 22 | rpcUrl = getIPOnly(apiUrl[0]); 23 | 24 | response = await http.get(apiUrl[0] + "/kira/status", 25 | headers: {'Access-Control-Allow-Origin': apiUrl[1]}).timeout(Duration(seconds: 3)); 26 | 27 | if (response.body.contains('node_info') == false && config[0] == true) { 28 | rpcUrl = getIPOnly(config[1]); 29 | 30 | response = await http.get(config[1] + "/kira/status", 31 | headers: {'Access-Control-Allow-Origin': apiUrl[1]}).timeout(Duration(seconds: 3)); 32 | 33 | if (response.body.contains('node_info') == false) { 34 | return false; 35 | } 36 | } 37 | 38 | var bodyData = json.decode(response.body); 39 | nodeInfo = NodeInfo.fromJson(bodyData['node_info']); 40 | syncInfo = SyncInfo.fromJson(bodyData['sync_info']); 41 | validatorInfo = ValidatorInfo.fromJson(bodyData['validator_info']); 42 | 43 | // DateTime latestBlockTime = DateTime.tryParse(syncInfo.latestBlockTime); 44 | // var timeDifference = (DateTime.now().millisecondsSinceEpoch - latestBlockTime.millisecondsSinceEpoch) / 1000 / 60; 45 | // print(timeDifference); 46 | // isNetworkHealthy = timeDifference > 1 ? false : true; 47 | 48 | // try { 49 | // response = await http.get(apiUrl[0] + '/consensus', 50 | // headers: {'Access-Control-Allow-Origin': apiUrl[1]}).timeout(Duration(seconds: 3)); 51 | // } catch (e) { 52 | // return false; 53 | // } 54 | 55 | // bodyData = json.decode(response.body); 56 | // if (bodyData['consensus_stopped'] == true) { 57 | // isNetworkHealthy = false; 58 | // } 59 | 60 | response = await http.get(apiUrl[0] + '/status', headers: {'Access-Control-Allow-Origin': apiUrl[1]}); 61 | 62 | if (response.body.contains('interx_info') == false && config[0] == true) { 63 | response = await http.get(config[1] + "/status", headers: {'Access-Control-Allow-Origin': apiUrl[1]}); 64 | if (response.body.contains('interx_info') == false) { 65 | return false; 66 | } 67 | } 68 | 69 | bodyData = json.decode(response.body); 70 | interxPubKey = bodyData['interx_info']['pub_key']['value']; 71 | 72 | return true; 73 | } 74 | 75 | Future checkNodeStatus() async { 76 | String apiUrl = await getInterxRPCUrl(); 77 | apiUrl = apiUrl.replaceAll('http://', ''); 78 | apiUrl = apiUrl.replaceAll('https://', ''); 79 | 80 | String origin = html.window.location.host + html.window.location.pathname; 81 | origin = origin.replaceAll('/', ''); 82 | 83 | var response = await http.get(apiUrl + "/api/kira/status", 84 | headers: {'Access-Control-Allow-Origin': origin}).timeout(Duration(seconds: 3)); 85 | 86 | if (response.body.contains('node_info') == true) { 87 | setInterxRPCUrl(apiUrl); 88 | print("1"); 89 | return true; 90 | } 91 | 92 | response = await http.get('https://' + apiUrl + "/api/kira/status", 93 | headers: {'Access-Control-Allow-Origin': origin}).timeout(Duration(seconds: 3)); 94 | 95 | if (response.body.contains('node_info') == true) { 96 | setInterxRPCUrl('https://' + apiUrl); 97 | print("2"); 98 | return true; 99 | } 100 | 101 | response = await http.get('https://cors-anywhere.kira.network/http://' + apiUrl + "/api/kira/status", 102 | headers: {'Access-Control-Allow-Origin': origin}).timeout(Duration(seconds: 3)); 103 | 104 | if (response.body.contains('node_info') == true) { 105 | setInterxRPCUrl('https://cors-anywhere.kira.network/http://' + apiUrl); 106 | print("3"); 107 | return true; 108 | } 109 | 110 | response = await http.get('https://cors-anywhere.kira.network/http://' + apiUrl + ":11000/api/kira/status", 111 | headers: {'Access-Control-Allow-Origin': origin}).timeout(Duration(seconds: 3)); 112 | 113 | if (response.body.contains('node_info') == true) { 114 | setInterxRPCUrl('https://cors-anywhere.kira.network/http://' + apiUrl + ':11000'); 115 | print("4"); 116 | return true; 117 | } 118 | 119 | return false; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/lib/webcam/qr_code_scanner_web_impl.dart: -------------------------------------------------------------------------------- 1 | // Note: only work over https or localhost 2 | // 3 | // thanks: 4 | // - https://medium.com/@mk.pyts/how-to-access-webcam-video-stream-in-flutter-for-web-1bdc74f2e9c7 5 | // - https://kevinwilliams.dev/blog/taking-photos-with-flutter-web 6 | // - https://github.com/cozmo/jsQR 7 | import 'dart:async'; 8 | // ignore: avoid_web_libraries_in_flutter 9 | import 'dart:html' as html; 10 | // ignore: avoid_web_libraries_in_flutter 11 | import 'dart:js' as js; 12 | // import 'dart:ui' as ui; 13 | import 'package:universal_ui/universal_ui.dart'; 14 | 15 | import 'package:flutter/widgets.dart'; 16 | 17 | /// 18 | ///call global function jsQR 19 | /// import https://github.com/cozmo/jsQR/blob/master/dist/jsQR.js on your index.html at web folder 20 | /// 21 | dynamic _jsQR(d, w, h, o) { 22 | return js.context.callMethod('jsQR', [d, w, h, o]); 23 | } 24 | 25 | class QrCodeCameraWebImpl extends StatefulWidget { 26 | final void Function(String qrValue) qrCodeCallback; 27 | final Widget child; 28 | final BoxFit fit; 29 | final Widget Function(BuildContext context, Object error) onError; 30 | 31 | QrCodeCameraWebImpl({ 32 | Key key, 33 | @required this.qrCodeCallback, 34 | this.child, 35 | this.fit = BoxFit.cover, 36 | this.onError, 37 | }) : assert(qrCodeCallback != null), 38 | super(key: key); 39 | 40 | @override 41 | _QrCodeCameraWebImplState createState() => _QrCodeCameraWebImplState(); 42 | } 43 | 44 | class _QrCodeCameraWebImplState extends State { 45 | // final double _width = 1000; 46 | // final double _height = _width / 4 * 3; 47 | final String _uniqueKey = UniqueKey().toString(); 48 | 49 | //see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState 50 | final _HAVE_ENOUGH_DATA = 4; 51 | 52 | // Webcam widget to insert into the tree 53 | Widget _videoWidget; 54 | 55 | // VideoElement 56 | html.VideoElement _video; 57 | Timer _timer; 58 | html.CanvasElement _canvasElement; 59 | html.CanvasRenderingContext2D _canvas; 60 | html.MediaStream _stream; 61 | 62 | @override 63 | void initState() { 64 | super.initState(); 65 | 66 | // Create a video element which will be provided with stream source 67 | _video = html.VideoElement(); 68 | // Register an webcam 69 | ui.platformViewRegistry.registerViewFactory('webcamVideoElement$_uniqueKey', (int viewId) => _video); 70 | // Create video widget 71 | _videoWidget = HtmlElementView(key: UniqueKey(), viewType: 'webcamVideoElement$_uniqueKey'); 72 | 73 | // Access the webcam stream 74 | html.window.navigator.getUserMedia(video: {'facingMode': 'environment'}) 75 | // .mediaDevices //don't work rear camera 76 | // .getUserMedia({ 77 | // 'video': { 78 | // 'facingMode': 'environment', 79 | // } 80 | // }) 81 | .then((html.MediaStream stream) { 82 | _stream = stream; 83 | _video.srcObject = stream; 84 | _video.setAttribute('playsinline', 'true'); // required to tell iOS safari we don't want fullscreen 85 | _video.play(); 86 | }); 87 | _canvasElement = html.CanvasElement(); 88 | _canvas = _canvasElement.getContext("2d"); 89 | _timer = Timer.periodic(Duration(milliseconds: 20), (timer) { 90 | tick(); 91 | }); 92 | } 93 | 94 | var waiting = false; 95 | tick() { 96 | if (waiting) { 97 | return; 98 | } 99 | 100 | if (_video.readyState == _HAVE_ENOUGH_DATA) { 101 | waiting = true; 102 | _canvasElement.width = 1024; 103 | _canvasElement.height = 1024; 104 | _canvas.drawImage(_video, 0, 0); 105 | var imageData = _canvas.getImageData(0, 0, _canvasElement.width, _canvasElement.height); 106 | js.JsObject code = _jsQR(imageData.data, imageData.width, imageData.height, { 107 | 'inversionAttempts': 'dontInvert', 108 | }); 109 | waiting = false; 110 | if (code != null) { 111 | String value = code['data']; 112 | this.widget.qrCodeCallback(value); 113 | } 114 | } 115 | } 116 | 117 | @override 118 | Widget build(BuildContext context) { 119 | return Container( 120 | height: double.infinity, 121 | width: double.infinity, 122 | child: FittedBox( 123 | fit: widget.fit, 124 | child: SizedBox( 125 | width: 400, 126 | height: 300, 127 | child: _videoWidget, 128 | ), 129 | ), 130 | ); 131 | } 132 | 133 | @override 134 | void dispose() { 135 | _timer?.cancel(); 136 | _video.pause(); 137 | 138 | Future.delayed(Duration(seconds: 2), () { 139 | try { 140 | _stream?.getTracks()?.forEach((mt) { 141 | mt.stop(); 142 | }); 143 | } catch (e) { 144 | print('error on dispose qrcode: $e'); 145 | } 146 | }); 147 | super.dispose(); 148 | } 149 | } 150 | --------------------------------------------------------------------------------