├── example ├── android │ ├── settings_aar.gradle │ ├── gradle.properties │ ├── .gitignore │ ├── 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 │ │ │ │ │ │ └── example │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ └── build.gradle ├── 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 │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── 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 │ │ └── project.pbxproj │ ├── .gitignore │ └── Podfile ├── .metadata ├── README.md ├── .gitignore ├── pubspec.yaml ├── pubspec.lock └── lib │ └── main.dart ├── lib ├── types │ └── tezos │ │ ├── tezos_chain_types.dart │ │ └── query_types.dart ├── tezster_dart.dart ├── chain │ └── tezos │ │ ├── tezos_language_util.dart │ │ ├── tezos_node_reader.dart │ │ ├── tezos_message_codec.dart │ │ ├── tezos_message_utils.dart │ │ └── tezos_node_writer.dart ├── models │ ├── key_store_model.dart │ └── operation_model.dart ├── reporting │ ├── conseil_data_client.dart │ ├── conseil_query_builder.dart │ └── tezos │ │ └── tezos_conseil_client.dart ├── helper │ ├── constants.dart │ ├── password_generater.dart │ ├── http_helper.dart │ └── generateKeys.dart ├── utils │ ├── crypto_utils.dart │ ├── sodium_utils.dart │ └── gas_fee_calculator.dart ├── src │ ├── soft-signer │ │ └── soft_signer.dart │ └── tezster_dart.dart └── michelson_parser │ ├── michelson_parser.dart │ ├── grammar │ ├── michelin_grammar_tokenizer.dart │ └── michelson_grammar_tokenizer.dart │ └── parser │ └── nearley.dart ├── .metadata ├── .vscode └── launch.json ├── pubspec.yaml ├── CHANGELOG.md ├── .gitignore ├── test └── tezos_test.dart ├── pubspec.lock └── README.md /example/android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/types/tezos/tezos_chain_types.dart: -------------------------------------------------------------------------------- 1 | enum TezosParameterFormat { 2 | Michelson, 3 | Micheline, 4 | MichelsonLambda, 5 | } 6 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /lib/tezster_dart.dart: -------------------------------------------------------------------------------- 1 | library tezster_dart; 2 | 3 | export 'src/tezster_dart.dart'; 4 | 5 | export 'models/key_store_model.dart'; 6 | 7 | export 'types/tezos/tezos_chain_types.dart'; 8 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tezsure/Tezster_dart/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.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: 5f21edf8b66e31a39133177319414395cc5b5f48 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /example/.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: 69019664fe0ebe1e7e3028cdf7282d5887a5cb34 8 | channel: master 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/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. -------------------------------------------------------------------------------- /.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": "Flutter", 9 | "request": "launch", 10 | "type": "dart" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /lib/chain/tezos/tezos_language_util.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:tezster_dart/michelson_parser/michelson_parser.dart'; 3 | 4 | class TezosLanguageUtil { 5 | static String translateMichelsonToMicheline(String code) { 6 | var result = MichelsonParser.parseMichelson(code); 7 | return result; 8 | } 9 | 10 | static String translateMichelineToHex(p) { 11 | return MichelsonParser.translateMichelineToHex(p); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/types/tezos/query_types.dart: -------------------------------------------------------------------------------- 1 | class QueryTypes { 2 | static Map conseilSortDirection = {'ASC': "asc", 'DESC': "desc"}; 3 | static Map conseilOperator = { 4 | 'BETWEEN': "between", 5 | 'EQ': "eq", 6 | 'IN': "in", 7 | 'LIKE': "like", 8 | 'LT': "lt", 9 | 'BEFORE': "before", 10 | 'GT': "gt", 11 | 'AFTER': "after", 12 | 'STARTSWITH': "startsWith", 13 | 'ENDSWITH': "endsWith", 14 | 'ISNULL': "isnull" 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # tezster_dart example 2 | 3 | [![Tezster banner](https://tezster.s3-ap-southeast-1.amazonaws.com/TEZSTER_CLI/1_jDB5enULQVo2UfeiwD32qA.png)](https://github.com/Tezsure) 4 | An example to demonstrate the tezster_dart package 5 | 6 | ## Getting Started 7 | 8 | * Clone the **example** directroy. 9 | * Change the directory to **example** in terminal/cmd. 10 | * Run the flutter project using `flutter run` command. 11 | * Check **debug console** for the output. 12 | -------------------------------------------------------------------------------- /lib/models/key_store_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | class KeyStoreModel { 4 | String publicKey; 5 | String secretKey; 6 | String publicKeyHash; 7 | var seed; 8 | String email; 9 | String password; 10 | String secret; 11 | 12 | KeyStoreModel({ 13 | this.publicKey, 14 | this.secretKey, 15 | @required this.publicKeyHash, 16 | this.seed, 17 | this.secret, 18 | this.email, 19 | this.password, 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /lib/reporting/conseil_data_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:tezster_dart/helper/http_helper.dart'; 2 | 3 | class ConseilDataClient { 4 | static executeEntityQuery( 5 | serverInfo, platform, network, entity, query) async { 6 | var url = '${serverInfo['url']}/v2/data/$platform/$network/$entity'; 7 | 8 | var res = await HttpHelper.performPostRequest(url, '', query, headers: { 9 | 'apiKey': serverInfo['apiKey'], 10 | 'cache': 'no-store', 11 | }); 12 | return res; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /lib/helper/constants.dart: -------------------------------------------------------------------------------- 1 | class TezosConstants { 2 | static const OperationGroupWatermark = "03"; 3 | 4 | static const DefaultTransactionStorageLimit = 496; 5 | static const DefaultTransactionGasLimit = 10600; 6 | 7 | static const DefaultDelegationFee = 1258; 8 | static const DefaultDelegationStorageLimit = 0; 9 | static const DefaultDelegationGasLimit = 1101; 10 | 11 | static const DefaultKeyRevealFee = 1270; 12 | static const DefaultKeyRevealStorageLimit = 0; 13 | static const DefaultKeyRevealGasLimit = 1100; 14 | 15 | static const HeadBranchOffset = 54; 16 | } 17 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Flutter Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | include ':app' 6 | 7 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 8 | def properties = new Properties() 9 | 10 | assert localPropertiesFile.exists() 11 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 12 | 13 | def flutterSdkPath = properties.getProperty("flutter.sdk") 14 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 15 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 16 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: tezster_dart 2 | description: A flutter package which provides the functionalities to play around 3 | with tezos dApps 4 | version: 2.1.2-beta.2 5 | homepage: https://github.com/Tezsure/tezster_dart 6 | repository: https://github.com/Tezsure/tezster_dart 7 | issue_tracker: https://github.com/Tezsure/tezster_dart/issues 8 | 9 | environment: 10 | sdk: ">=2.3.0 <3.0.0" 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | bip39: ^1.0.3 16 | blake2b: ^0.1.2 17 | bs58check: ^1.0.1 18 | convert: ^2.1.1 19 | crypto: ^2.1.5 20 | flutter_sodium: ^0.1.9 21 | password_hash: ^2.0.0 22 | unorm_dart: ^0.1.2 23 | ed25519_hd_key: ^1.1.0 24 | 25 | dev_dependencies: 26 | flutter_test: 27 | sdk: flutter 28 | -------------------------------------------------------------------------------- /lib/utils/crypto_utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | import 'package:tezster_dart/utils/sodium_utils.dart'; 3 | 4 | class CryptoUtils { 5 | static Uint8List generateSaltForPwHash() { 6 | return SodiumUtils.salt(); 7 | } 8 | 9 | static Uint8List encryptMessage( 10 | Uint8List message, String passphrase, Uint8List salt) { 11 | var keyBytes = SodiumUtils.pwhash(passphrase, salt); 12 | var nonce = SodiumUtils.nonce(); 13 | var s = SodiumUtils.close(message, nonce, keyBytes); 14 | 15 | return new Uint8List.fromList(nonce.toList() + s.toList()); 16 | } 17 | 18 | static Uint8List decryptMessage(message, passphrase, salt) { 19 | var keyBytes = SodiumUtils.pwhash(passphrase, salt); 20 | return SodiumUtils.open(message, keyBytes); 21 | } 22 | 23 | static Uint8List signDetached(Uint8List simpleHash, Uint8List key) { 24 | return SodiumUtils.sign(simpleHash, key); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Exceptions to above rules. 44 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 45 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /lib/helper/password_generater.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | 5 | class PasswordGenerator { 6 | 7 | static String generatePassword( 8 | {@required double length, 9 | bool isWithLetters, 10 | bool isWithUppercase, 11 | bool isWithNumbers, 12 | bool isWithSpecial}) { 13 | String _lowerCaseLetters = "abcdefghijklmnopqrstuvwxyz"; 14 | String _upperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 15 | String _numbers = "0123456789"; 16 | String _special = r'!@#$%^&*()+_-=}{[]|:;"/?.><,`~'; 17 | 18 | String _allowedChars = ""; 19 | 20 | _allowedChars += (isWithLetters ? _lowerCaseLetters : ''); 21 | _allowedChars += (isWithUppercase ? _upperCaseLetters : ''); 22 | _allowedChars += (isWithNumbers ? _numbers : ''); 23 | _allowedChars += (isWithSpecial ? _special : ''); 24 | 25 | int i = 0; 26 | String _result = ""; 27 | 28 | while (i < length.round()) { 29 | int randomInt = Random.secure().nextInt(_allowedChars.length); 30 | _result += _allowedChars[randomInt]; 31 | i++; 32 | } 33 | 34 | return _result; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [2.1.2-beta.2] 2 | 3 | * Restore Keys from secret-key 4 | * Restore Keys from mnemonic and derivationPath. 5 | 6 | # [2.1.2-beta.1] 7 | 8 | * Operation Gas fee calculator optimisation 9 | * Michelson grammar improvements 10 | * Minor bug fixes 11 | 12 | # [2.1.2-beta] 13 | 14 | * Support for florence mainnet and testnet 15 | * Operation Gas fee calculator 16 | 17 | # [2.1.1] 18 | 19 | * Deploy contract 20 | * Call contract 21 | * Operation confirmation 22 | * Activating a fundraiser account 23 | * Reveal an account 24 | 25 | # [2.1.1-beta.2] 26 | 27 | * Activating a fundraiser account 28 | * Reveal an account 29 | 30 | # [2.1.1-beta] 31 | 32 | * Fix for revelation of an account Ref #12 33 | 34 | # [2.1.0-beta] 35 | 36 | * Deploy contract 37 | * Call contract 38 | * Operation confirmation 39 | * Fix for revelation of an account Ref #12 40 | 41 | # [2.0.0] 42 | 43 | * Dependencies update. 44 | * Added Get Balance. 45 | * Added Transfer Balance. 46 | * Added Delegate an Account. 47 | 48 | # [1.0.1] 49 | 50 | * Dependencies update. 51 | 52 | # [1.0.0] 53 | 54 | Initial version of library. 55 | 56 | * Functions added: 57 | * Generate mnemonics. 58 | * Generate keys from mnemonic. 59 | * Generate keys from mnemonics and passphrase. 60 | * Sign Operation Group. 61 | * Unlock fundraiser identity. 62 | -------------------------------------------------------------------------------- /lib/helper/http_helper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | class HttpHelper { 5 | static Future performPostRequest(server, command, payload, 6 | {Map headers}) async { 7 | HttpClient httpClient = new HttpClient(); 8 | HttpClientRequest request = await httpClient.postUrl( 9 | command.isEmpty ? Uri.parse(server) : Uri.parse('$server/$command')); 10 | request.headers.set('content-type', 'application/json'); 11 | if (headers == null) { 12 | request.add(utf8.encode(json.encode(payload))); 13 | } else { 14 | headers.entries.forEach( 15 | (header) => request.headers.add(header.key, header.value), 16 | ); 17 | request.headers.add('body', json.encode(payload)); 18 | request.add(utf8.encode(json.encode(payload))); 19 | } 20 | HttpClientResponse response = await request.close(); 21 | String reply = await response.transform(utf8.decoder).join(); 22 | httpClient.close(); 23 | return reply; 24 | } 25 | 26 | static Future performGetRequest(server, command) async { 27 | HttpClient httpClient = new HttpClient(); 28 | HttpClientRequest request = 29 | await httpClient.getUrl(Uri.parse('$server/$command')); 30 | HttpClientResponse response = await request.close(); 31 | String reply = await response.transform(utf8.decoder).join(); 32 | httpClient.close(); 33 | return jsonDecode(reply); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /lib/utils/sodium_utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:flutter_sodium/flutter_sodium.dart'; 4 | 5 | class SodiumUtils { 6 | static Uint8List rand(length) { 7 | return Sodium.randombytesBuf(length); 8 | } 9 | 10 | static Uint8List salt() { 11 | return Uint8List.fromList(rand(Sodium.cryptoPwhashSaltbytes).toList()); 12 | } 13 | 14 | static Uint8List pwhash(String passphrase, Uint8List salt) { 15 | return Sodium.cryptoPwhash( 16 | Sodium.cryptoBoxSeedbytes, 17 | Uint8List.fromList(passphrase.codeUnits), 18 | salt, 19 | 4, 20 | 33554432, 21 | Sodium.cryptoPwhashAlgArgon2i13); 22 | } 23 | 24 | static Uint8List nonce() { 25 | return rand(Sodium.cryptoBoxNoncebytes); 26 | } 27 | 28 | static Uint8List close( 29 | Uint8List message, Uint8List nonce, Uint8List keyBytes) { 30 | return Sodium.cryptoSecretboxEasy(message, nonce, keyBytes); 31 | } 32 | 33 | static Uint8List open(Uint8List nonceAndCiphertext, Uint8List key) { 34 | var nonce = nonceAndCiphertext.sublist(0, Sodium.cryptoSecretboxNoncebytes); 35 | var ciphertext = 36 | nonceAndCiphertext.sublist(Sodium.cryptoSecretboxNoncebytes); 37 | 38 | return Sodium.cryptoSecretboxOpenEasy(ciphertext, nonce, key); 39 | } 40 | 41 | static Uint8List sign(Uint8List simpleHash, Uint8List key) { 42 | return Sodium.cryptoSignDetached(simpleHash, key); 43 | } 44 | 45 | static KeyPair publicKey(Uint8List sk) { 46 | var seed = Sodium.cryptoSignEd25519SkToSeed(sk); 47 | return Sodium.cryptoSignSeedKeypair(seed); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | example 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 | -------------------------------------------------------------------------------- /lib/helper/generateKeys.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | import 'package:blake2b/blake2b_hash.dart'; 3 | import 'package:convert/convert.dart'; 4 | import 'package:bs58check/bs58check.dart' as bs58check; 5 | 6 | class GenerateKeys { 7 | static String computeKeyHash( 8 | Uint8List publicKey, 9 | ) { 10 | Uint8List blake2bHash = Blake2bHash.hashWithDigestSize(160, publicKey); 11 | String uintToString = String.fromCharCodes(blake2bHash); 12 | String stringToHexString = hex.encode(uintToString.codeUnits); 13 | String finalStringToDecode = "06a19f" + stringToHexString; 14 | List listOfHexDecodedInt = hex.decode(finalStringToDecode); 15 | String publicKeyHash = bs58check.encode(listOfHexDecodedInt); 16 | return publicKeyHash; 17 | } 18 | 19 | static String readKeysWithHint( 20 | Uint8List key, 21 | String hint, 22 | ) { 23 | String uint8ListToString = String.fromCharCodes(key); 24 | String stringToHexString = hex.encode(uint8ListToString.codeUnits); 25 | String concatinatingHexStringWithHint = hint + stringToHexString; 26 | List convertingHexStringToListOfInt = 27 | hex.decode(concatinatingHexStringWithHint); 28 | String base58String = bs58check.encode(convertingHexStringToListOfInt); 29 | return base58String; 30 | } 31 | 32 | static Uint8List writeKeyWithHint( 33 | String key, 34 | String hint, 35 | ) { 36 | if (hint == 'edsk' || 37 | hint == 'edpk' || 38 | hint == 'sppk' || 39 | hint == 'p2pk' || 40 | hint == '2bf64e07' || 41 | hint == '0d0f25d9') { 42 | return bs58check.decode(key).sublist(4); 43 | } else 44 | throw Exception("Unrecognized key hint, '$hint'"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | build/ 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Flutter.podspec 62 | **/ios/Flutter/Generated.xcconfig 63 | **/ios/Flutter/app.flx 64 | **/ios/Flutter/app.zip 65 | **/ios/Flutter/flutter_assets/ 66 | **/ios/Flutter/flutter_export_environment.sh 67 | **/ios/ServiceDefinitions.json 68 | **/ios/Runner/GeneratedPluginRegistrant.* 69 | 70 | # Exceptions to above rules. 71 | !**/ios/**/default.mode1v3 72 | !**/ios/**/default.mode2v3 73 | !**/ios/**/default.pbxuser 74 | !**/ios/**/default.perspectivev3 75 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 76 | -------------------------------------------------------------------------------- /lib/reporting/conseil_query_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:tezster_dart/types/tezos/query_types.dart'; 2 | 3 | class ConseilQueryBuilder { 4 | static blankQuery() { 5 | return { 6 | 'fields': new List(), 7 | 'predicates': new List(), 8 | 'orderBy': new List(), 9 | 'aggregation': new List(), 10 | 'limit': 100 11 | }; 12 | } 13 | 14 | static setLimit(query, limit) { 15 | if (limit < 1) { 16 | throw new Exception('Limit cannot be less than one.'); 17 | } 18 | var q = query; 19 | q['limit'] = limit; 20 | return q; 21 | } 22 | 23 | static addOrdering(query, field, {direction}) { 24 | if (direction == null) direction = QueryTypes.conseilSortDirection['ASC']; 25 | var q = query; 26 | q['orderBy'].add({'field': field, 'direction': direction}); 27 | return q; 28 | } 29 | 30 | static addPredicate(query, field, operation, values, 31 | {invert = false, group = ''}) { 32 | if (group == null) group = ''; 33 | if (operation == QueryTypes.conseilOperator['BETWEEN'] && 34 | values.length != 2) { 35 | throw new Exception('BETWEEN operation requires a list of two values.'); 36 | } else if (operation == QueryTypes.conseilOperator['IN'] && 37 | values.length < 2) { 38 | throw new Exception( 39 | 'IN operation requires a list of two or more values.'); 40 | } else if (values.length != 1 && 41 | operation != QueryTypes.conseilOperator['IN'] && 42 | operation != QueryTypes.conseilOperator['BETWEEN'] && 43 | operation != QueryTypes.conseilOperator['ISNULL']) { 44 | throw new Exception('invalid values list for $operation.'); 45 | } 46 | var q = query; 47 | q['predicates'].add({ 48 | 'field': field, 49 | 'operation': operation ?? '', 50 | 'set': values , 51 | 'inverse': invert, 52 | 'group': group ?? '', 53 | }); 54 | return q; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /example/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.example" 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 | -------------------------------------------------------------------------------- /lib/chain/tezos/tezos_node_reader.dart: -------------------------------------------------------------------------------- 1 | import 'package:tezster_dart/helper/http_helper.dart'; 2 | 3 | class TezosNodeReader { 4 | static Future getCounterForAccount(String server, String publicKeyHash, 5 | {String chainid = 'main'}) async { 6 | var response = await HttpHelper.performGetRequest(server, 7 | 'chains/$chainid/blocks/head/context/contracts/$publicKeyHash/counter'); 8 | return int.parse(response.toString().replaceAll('"', ''), radix: 10); 9 | } 10 | 11 | static Future isManagerKeyRevealedForAccount( 12 | String server, String publicKeyHash) async { 13 | var managerKey = 14 | await getAccountManagerForBlock(server, 'head', publicKeyHash); 15 | return managerKey.length > 0; 16 | } 17 | 18 | static Future getAccountManagerForBlock( 19 | String server, String block, String publicKeyHash, 20 | {String chainid = 'main'}) async { 21 | var response = await HttpHelper.performGetRequest(server, 22 | 'chains/$chainid/blocks/$block/context/contracts/$publicKeyHash/manager_key'); 23 | return response != null ? response.toString() : ''; 24 | } 25 | 26 | static Future> getBlockAtOffset( 27 | String server, int offset, 28 | {String chainid = 'main'}) async { 29 | if (offset <= 0) { 30 | return await getBlock(server); 31 | } 32 | var head = await getBlock(server); 33 | var response = await HttpHelper.performGetRequest( 34 | server, 'chains/$chainid/blocks/${head['header']['level'] - offset}'); 35 | return response; 36 | } 37 | 38 | static Future> getBlock(String server, 39 | {String hash = 'head', String chainid = 'main'}) async { 40 | var response = await HttpHelper.performGetRequest( 41 | server, 'chains/$chainid/blocks/$hash'); 42 | return response; 43 | } 44 | 45 | static Future> getContractStorage(server, accountHash, 46 | {block = 'head', chainid = 'main'}) async { 47 | return await HttpHelper.performGetRequest(server, 48 | 'chains/$chainid/blocks/$block/context/contracts/$accountHash/storage'); 49 | } 50 | 51 | static getValueForBigMapKey(String server, String index, String key, 52 | {String block, String chainid}) async { 53 | return await HttpHelper.performGetRequest(server, 54 | 'chains/$chainid/blocks/$block/context/big_maps/$index/$key'); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/soft-signer/soft_signer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:tezster_dart/chain/tezos/tezos_message_utils.dart'; 4 | import 'package:tezster_dart/helper/password_generater.dart'; 5 | import 'package:tezster_dart/utils/crypto_utils.dart'; 6 | 7 | enum SignerCurve { ED25519, SECP256K1, SECP256R1 } 8 | 9 | class SoftSigner { 10 | var _secretKey; 11 | var _lockTimout; 12 | var _passphrase; 13 | var _salt; 14 | var _unlocked; 15 | var _key; 16 | 17 | SoftSigner( 18 | {Uint8List secretKey, 19 | int validity = -1, 20 | String passphrase = '', 21 | Uint8List salt}) { 22 | this._secretKey = secretKey; 23 | this._lockTimout = validity; 24 | this._passphrase = passphrase; 25 | this._salt = salt != null ? salt : Uint8List(0); 26 | this._unlocked = validity < 0; 27 | this._key = Uint8List(0); 28 | if (validity < 0) { 29 | this._key = secretKey; 30 | } 31 | } 32 | 33 | static SoftSigner createSigner(Uint8List secretKey, int validity) { 34 | if (validity >= 0) { 35 | var passphrase = PasswordGenerator.generatePassword( 36 | length: 32, 37 | isWithLetters: true, 38 | isWithNumbers: true, 39 | isWithSpecial: true, 40 | isWithUppercase: true, 41 | ); 42 | var salt = CryptoUtils.generateSaltForPwHash(); 43 | secretKey = CryptoUtils.encryptMessage(secretKey, passphrase, salt); 44 | return SoftSigner( 45 | secretKey: secretKey, 46 | validity: validity, 47 | passphrase: passphrase, 48 | salt: salt, 49 | ); 50 | } else 51 | return new SoftSigner(secretKey: secretKey); 52 | } 53 | 54 | Uint8List getKey() { 55 | if (!_unlocked) { 56 | var k = CryptoUtils.decryptMessage(_secretKey, _passphrase, _salt); 57 | if (_lockTimout == 0) { 58 | return k; 59 | } 60 | _key = k; 61 | _unlocked = true; 62 | if (_lockTimout > 0) { 63 | Future.delayed(Duration(milliseconds: _lockTimout * 1000), () { 64 | _key = Uint8List(0); 65 | _unlocked = false; 66 | }); 67 | } 68 | return _key; 69 | } 70 | return _key; 71 | } 72 | 73 | Uint8List signOperation(Uint8List uint8list) { 74 | return CryptoUtils.signDetached(TezosMessageUtils.simpleHash(uint8list, 32), 75 | Uint8List.fromList(getKey())); 76 | } 77 | 78 | SignerCurve getSignerCurve() { 79 | return SignerCurve.ED25519; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 13 | 20 | 24 | 28 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /lib/reporting/tezos/tezos_conseil_client.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:tezster_dart/reporting/conseil_data_client.dart'; 4 | import 'package:tezster_dart/reporting/conseil_query_builder.dart'; 5 | import 'package:tezster_dart/types/tezos/query_types.dart'; 6 | 7 | var bLOCKS = 'blocks'; 8 | var oPERATIONS = 'operations'; 9 | 10 | class TezosConseilClient { 11 | static awaitOperationConfirmation(serverInfo, network, hash, duration, 12 | {blocktime = 60}) async { 13 | if (blocktime == null) blocktime = 60; 14 | if (duration <= 0) { 15 | throw new Exception('Invalid duration'); 16 | } 17 | var initialLevel = await getBlockHead(serverInfo, network); 18 | initialLevel = initialLevel['level']; 19 | var timeOffset = 180000; 20 | var startTime = new DateTime.now().millisecondsSinceEpoch - timeOffset; 21 | var estimatedEndTime = startTime + timeOffset + duration * blocktime * 1000; 22 | var currentLevel = initialLevel; 23 | var operationQuery = ConseilQueryBuilder.blankQuery(); 24 | operationQuery = ConseilQueryBuilder.addPredicate(operationQuery, 25 | 'operation_group_hash', QueryTypes.conseilOperator['EQ'], [hash], 26 | invert: false); 27 | operationQuery = ConseilQueryBuilder.addPredicate(operationQuery, 28 | 'timestamp', QueryTypes.conseilOperator['AFTER'], [startTime], 29 | invert: false); 30 | operationQuery = ConseilQueryBuilder.setLimit(operationQuery, 1); 31 | while (initialLevel + duration > currentLevel) { 32 | var group = 33 | jsonDecode(await getOperations(serverInfo, network, operationQuery)); 34 | if (group.length > 0) { 35 | return group[0]; 36 | } 37 | currentLevel = (await getBlockHead(serverInfo, network))['level']; 38 | if (initialLevel + duration < currentLevel) { 39 | break; 40 | } 41 | if (new DateTime.now().millisecondsSinceEpoch > estimatedEndTime) { 42 | break; 43 | } 44 | await Future.delayed(Duration(milliseconds: blocktime * 1000)); 45 | } 46 | throw new Exception( 47 | 'Did not observe $hash on $network in $duration block${duration > 1 ? 's' : ''} since $initialLevel'); 48 | } 49 | 50 | static getBlockHead(serverInfo, network) async { 51 | var query = ConseilQueryBuilder.setLimit( 52 | ConseilQueryBuilder.addOrdering( 53 | ConseilQueryBuilder.blankQuery(), 'level', 54 | direction: QueryTypes.conseilSortDirection['DESC']), 55 | 1); 56 | var r = jsonDecode( 57 | await getTezosEntityData(serverInfo, network, bLOCKS, query)); 58 | return r[0]; 59 | } 60 | 61 | static getTezosEntityData(serverInfo, network, entity, query) async { 62 | return await ConseilDataClient.executeEntityQuery( 63 | serverInfo, 'tezos', network, entity, query); 64 | } 65 | 66 | static getOperations(serverInfo, network, query) { 67 | return getTezosEntityData(serverInfo, network, oPERATIONS, query); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /lib/utils/gas_fee_calculator.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | class Estimate { 4 | // ignore: non_constant_identifier_names 5 | var MINIMAL_FEE_MUTEZ = 150; 6 | // ignore: non_constant_identifier_names 7 | var MINIMAL_FEE_PER_BYTE_MUTEZ = 1; 8 | // ignore: non_constant_identifier_names 9 | var MINIMAL_FEE_PER_GAS_MUTEZ = 0.1; 10 | // ignore: non_constant_identifier_names 11 | var GAS_BUFFER = 100; 12 | 13 | var _milligasLimit; 14 | var _storageLimit; 15 | var opSize; 16 | var minimalFeePerStorageByteMutez; 17 | var baseFeeMutez; 18 | 19 | Estimate(this._milligasLimit, this._storageLimit, this.opSize, 20 | this.minimalFeePerStorageByteMutez, this.baseFeeMutez); 21 | 22 | /// @description The number of Mutez that will be burned for the storage of the [operation](https://tezos.gitlab.io/user/glossary.html#operations). (Storage + Allocation fees) 23 | get burnFeeMutez { 24 | return this.roundUp(this.storageLimit * this.minimalFeePerStorageByteMutez); 25 | } 26 | 27 | /// @description The limit on the amount of storage an [operation](https://tezos.gitlab.io/user/glossary.html#operations) can use. 28 | get storageLimit { 29 | var limit = max(int.parse(_storageLimit.toString()), 0); 30 | return limit > 0 ? limit : 0; 31 | } 32 | 33 | /// @description The limit on the amount of [gas](https://tezos.gitlab.io/user/glossary.html#gas) a given operation can consume. 34 | get gasLimit { 35 | return this.roundUp(this._milligasLimit / 1000 + this.GAS_BUFFER); 36 | } 37 | 38 | get operationFeeMutez { 39 | return ((this._milligasLimit / 1000 + this.GAS_BUFFER) * 40 | this.MINIMAL_FEE_PER_GAS_MUTEZ + 41 | this.opSize * this.MINIMAL_FEE_PER_BYTE_MUTEZ) 42 | .round(); 43 | } 44 | 45 | roundUp(nanotez) { 46 | return nanotez.round(); 47 | } 48 | 49 | /// @description Minimum fees for the [operation](https://tezos.gitlab.io/user/glossary.html#operations) according to [baker](https://tezos.gitlab.io/user/glossary.html#baker) defaults. 50 | get minimalFeeMutez { 51 | return this.roundUp(this.MINIMAL_FEE_MUTEZ + this.operationFeeMutez); 52 | } 53 | 54 | /// @description The suggested fee for the operation which includes minimal fees and a small buffer. 55 | get suggestedFeeMutez { 56 | return this.roundUp(this.operationFeeMutez + this.MINIMAL_FEE_MUTEZ * 2); 57 | } 58 | 59 | /// @description Fees according to your specified base fee will ensure that at least minimum fees are used. 60 | 61 | get usingBaseFeeMutez { 62 | return (max( 63 | int.parse(this.baseFeeMutez.toString()), this.MINIMAL_FEE_MUTEZ) + 64 | this.roundUp(this.operationFeeMutez)); 65 | } 66 | 67 | /// @description The sum of `minimalFeeMutez` + `burnFeeMutez`. 68 | 69 | get totalCost { 70 | return int.parse(this.minimalFeeMutez.toString()) + 71 | int.parse(this.burnFeeMutez.toString()); 72 | } 73 | 74 | /// @description Since Delphinet, consumed gas is provided in milligas for more precision. 75 | /// This function returns an estimation of the gas that operation will consume in milligas. 76 | get consumedMilligas { 77 | return int.parse(this._milligasLimit.toString()); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 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 | # tezster_dart: 28 | # git: 29 | # url: git@github.com:TechSatya/Tezster_dart.git 30 | # ref: dev 31 | tezster_dart: 32 | path: ../ 33 | 34 | # The following adds the Cupertino Icons font to your application. 35 | # Use with the CupertinoIcons class for iOS style icons. 36 | cupertino_icons: ^0.1.3 37 | 38 | dev_dependencies: 39 | flutter_test: 40 | sdk: flutter 41 | 42 | # For information on the generic Dart part of this file, see the 43 | # following page: https://dart.dev/tools/pub/pubspec 44 | # The following section is specific to Flutter. 45 | flutter: 46 | # The following line ensures that the Material Icons font is 47 | # included with your application, so that you can use the icons in 48 | # the material Icons class. 49 | uses-material-design: true 50 | # To add assets to your application, add an assets section, like this: 51 | # assets: 52 | # - images/a_dot_burr.jpeg 53 | # - images/a_dot_ham.jpeg 54 | # An image asset can refer to one or more resolution-specific "variants", see 55 | # https://flutter.dev/assets-and-images/#resolution-aware. 56 | # For details regarding adding assets from package dependencies, see 57 | # https://flutter.dev/assets-and-images/#from-packages 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 | -------------------------------------------------------------------------------- /lib/michelson_parser/michelson_parser.dart: -------------------------------------------------------------------------------- 1 | import 'package:tezster_dart/michelson_parser/parser/micheline_grammar.dart'; 2 | import 'package:tezster_dart/michelson_parser/parser/michelson_grammar.dart'; 3 | import 'package:tezster_dart/michelson_parser/parser/nearley.dart'; 4 | 5 | class MichelsonParser { 6 | static String parseMichelson(String code) { 7 | var parser = Nearley(); 8 | parser.parser(Nearley.fromCompiled(MichelsonGrammar().grammar)); 9 | var cleanCode = preProcessMichelsonScript(code); 10 | cleanCode.map((_code) => parser.feed(_code)).toList(); 11 | return parser.results[0]; 12 | } 13 | 14 | static translateMichelineToHex(code) { 15 | var parser = Nearley(); 16 | parser.parser(Nearley.fromCompiled(MichelineGrammar().grammar)); 17 | parser.feed(normalizeMichelineWhiteSpace(code)); 18 | return parser.results.join(''); 19 | } 20 | 21 | static String normalizeMichelineWhiteSpace(String fragment) { 22 | return fragment 23 | .replaceAll(new RegExp(r'\n/g'), ' ') 24 | .replaceAll(new RegExp(r' +'), ' ') 25 | .replaceAll(new RegExp(r'\[{'), '[ {') 26 | .replaceAll(new RegExp(r'}\]'), '} ]') 27 | .replaceAll(new RegExp(r'},{'), '}, {') 28 | .replaceAll(new RegExp(r'\]}'), '] }') 29 | .replaceAll(new RegExp(r'":"'), '": "') 30 | .replaceAll(new RegExp(r'":\['), '": [') 31 | .replaceAll(new RegExp(r'{"'), '{ "') 32 | .replaceAll(new RegExp(r'"}'), '" }') 33 | .replaceAll(new RegExp(r',"'), ', "') 34 | .replaceAll(new RegExp(r'","'), '", "') 35 | .replaceAll(new RegExp(r'\[\['), '[ [') 36 | .replaceAll(new RegExp(r'\]\]'), '] ]') 37 | .replaceAll(new RegExp(r'\["'), '[ "') 38 | .replaceAll(new RegExp(r'"\]'), '" ]') 39 | .replaceAll(new RegExp(r'\[ +\]'), '\[\]') 40 | .trim(); 41 | } 42 | 43 | static List preProcessMichelsonScript(String code) { 44 | var sections = Map(); 45 | sections['parameter'] = code.indexOf(RegExp(r'(^|\s+)parameter')); 46 | sections['storage'] = code.indexOf(RegExp(r'(^|\s+)storage')); 47 | sections['code'] = code.indexOf(RegExp(r'(^|\s+)code')); 48 | var boundaries = sections.values.toList(); 49 | boundaries.sort((a, b) => a - b); 50 | sections[ 51 | sections.keys.firstWhere((key) => sections[key] == boundaries[0])] = 52 | code.substring(boundaries[0] < 0 ? 0 : boundaries[0], 53 | boundaries[1] < 0 ? 0 : boundaries[1]); 54 | sections[ 55 | sections.keys.firstWhere((key) => sections[key] == boundaries[1])] = 56 | code.substring(boundaries[1] < 0 ? 0 : boundaries[1], 57 | boundaries[2] < 0 ? 0 : boundaries[2]); 58 | sections[ 59 | sections.keys.firstWhere((key) => sections[key] == boundaries[2])] = 60 | code.substring(boundaries[2] < 0 ? 0 : boundaries[2]); 61 | var parts = [sections['parameter'], sections['storage'], sections['code']]; 62 | return parts 63 | .map((p) => p 64 | .trim() 65 | .split('\n') 66 | .map( 67 | (l) => l.replaceAll(RegExp(r'\#[\s\S]+$'), '').trim(), 68 | ) 69 | .toList() 70 | .where((e) => e.toString().isNotEmpty) 71 | .toList() 72 | .join(' ')) 73 | .toList(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /lib/models/operation_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:tezster_dart/helper/constants.dart'; 2 | 3 | class OperationModel { 4 | String destination; 5 | String amount; 6 | int storageLimit; 7 | int gasLimit; 8 | int counter; 9 | String fee; 10 | String source; 11 | String kind; 12 | String publicKey; 13 | String delegate; 14 | Map script; 15 | 16 | Map parameters; 17 | 18 | String pkh; 19 | String secret; 20 | 21 | OperationModel({ 22 | this.destination, 23 | this.amount, 24 | this.counter, 25 | this.fee, 26 | this.source, 27 | this.kind = 'transaction', 28 | this.gasLimit = TezosConstants.DefaultTransactionGasLimit, 29 | this.storageLimit = TezosConstants.DefaultTransactionStorageLimit, 30 | this.publicKey, 31 | this.delegate, 32 | this.script, 33 | this.pkh, 34 | this.secret, 35 | }); 36 | 37 | Map toJson() => kind == 'delegation' 38 | ? { 39 | 'counter': counter.toString(), 40 | 'delegate': delegate, 41 | 'fee': fee.toString(), 42 | 'gas_limit': gasLimit.toString(), 43 | 'kind': kind, 44 | 'source': source, 45 | 'storage_limit': storageLimit.toString(), 46 | } 47 | : kind == 'reveal' 48 | ? { 49 | 'kind': kind, 50 | 'source': source, 51 | 'fee': fee, 52 | 'counter': counter.toString(), 53 | 'gas_limit': gasLimit.toString(), 54 | 'storage_limit': storageLimit.toString(), 55 | 'public_key': publicKey 56 | } 57 | : kind == "origination" 58 | ? delegate == null 59 | ? { 60 | 'kind': 'origination', 61 | 'source': source, 62 | 'fee': fee.toString(), 63 | 'counter': counter.toString(), 64 | 'gas_limit': gasLimit.toString(), 65 | 'storage_limit': storageLimit.toString(), 66 | 'balance': amount.toString(), 67 | 'script': script 68 | } 69 | : { 70 | 'kind': 'origination', 71 | 'source': source, 72 | 'fee': fee.toString(), 73 | 'counter': counter.toString(), 74 | 'gas_limit': gasLimit.toString(), 75 | 'storage_limit': storageLimit.toString(), 76 | 'balance': amount.toString(), 77 | 'delegate': delegate, 78 | 'script': script 79 | } 80 | : kind == "activate_account" 81 | ? { 82 | 'kind': kind, 83 | 'pkh': pkh, 84 | 'secret': secret, 85 | } 86 | : parameters == null 87 | ? { 88 | 'destination': destination, 89 | 'amount': amount, 90 | 'storage_limit': storageLimit.toString(), 91 | 'gas_limit': gasLimit.toString(), 92 | 'counter': counter.toString(), 93 | 'fee': fee, 94 | 'source': source, 95 | 'kind': kind, 96 | } 97 | : { 98 | 'destination': destination, 99 | 'amount': amount, 100 | 'storage_limit': storageLimit.toString(), 101 | 'gas_limit': gasLimit.toString(), 102 | 'counter': counter.toString(), 103 | 'fee': fee, 104 | 'source': source, 105 | 'kind': kind, 106 | 'parameters': parameters, 107 | }; 108 | } 109 | -------------------------------------------------------------------------------- /test/tezos_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | import 'package:tezster_dart/tezster_dart.dart'; 3 | 4 | void main() { 5 | String testPrivateKey = 6 | "edskRdVS5H9YCRAG8yqZkX2nUTbGcaDqjYgopkJwRuPUnYzCn3t9ZGksncTLYe33bFjq29pRhpvjQizCCzmugMGhJiXezixvdC"; 7 | String testForgedOperation = 8 | "713cb068fe3ac078351727eb5c34279e22b75b0cf4dc0a8d3d599e27031db136040cb9f9da085607c05cac1ca4c62a3f3cfb8146aa9b7f631e52f877a1d363474404da8130b0b940ee"; 9 | String testMnemonics = 10 | "luxury bulb roast timber sense stove sugar sketch goddess host meadow decorate gather salmon funny person canoe daring machine network camp moment wrong dice"; 11 | 12 | KeyStoreModel _keyStoreModel = KeyStoreModel( 13 | secretKey: 14 | "edskRrDH2TF4DwKU1ETsUjyhxPC8aCTD6ko5YDguNkJjRb3PiBm8Upe4FGFmCrQqzSVMDLfFN22XrQXATcA3v41hWnAhymgQwc", 15 | publicKey: "edpku4ZfXDzF7CjPkX5LS8JFg1Znab3UKdhp18maKq2MrR82Gm9BTc", 16 | publicKeyHash: "tz1aPUfTyjtUcSnCfSvyykT67atDtVu7FePX", 17 | ); 18 | 19 | test('Get Keys From Mnemonics and PassPhrase', () async { 20 | List keys = 21 | await TezsterDart.getKeysFromMnemonic(mnemonic: testMnemonics); 22 | expect(keys[0], 23 | "edskRdVS5H9YCRAG8yqZkX2nUTbGcaDqjYgopkJwRuPUnYzCn3t9ZGksncTLYe33bFjq29pRhpvjQizCCzmugMGhJiXezixvdC"); 24 | expect(keys[1], "edpkuLog552hecagkykJ3fTvop6grTMhfZY4TWbvchDWdYyxCHcrQL"); 25 | expect(keys[2], "tz1g85oYHLFKDpNfDHPeBUbi3S7pUsgCB28q"); 26 | }); 27 | 28 | test('Restore account from secret key', () { 29 | List keys = 30 | TezsterDart.getKeysFromSecretKey(_keyStoreModel.secretKey); 31 | expect(keys[0], _keyStoreModel.secretKey); 32 | expect(keys[1], _keyStoreModel.publicKey); 33 | expect(keys[2], _keyStoreModel.publicKeyHash); 34 | }); 35 | 36 | test('Sign Operation Group', () async { 37 | List keys = await TezsterDart.signOperationGroup( 38 | forgedOperation: testForgedOperation, 39 | privateKey: testPrivateKey, 40 | ); 41 | expect(keys[0], 42 | "edsigtrBnsjSngfP6LULUDeo84eJVks4LWReYrZBUjKQNJjhVsG7bksqZ7CKnRePMceMe3vgRHHbyd2CqRdC8iEAK5NcyNn4iEB"); 43 | expect(keys[1], 44 | "713cb068fe3ac078351727eb5c34279e22b75b0cf4dc0a8d3d599e27031db136040cb9f9da085607c05cac1ca4c62a3f3cfb8146aa9b7f631e52f877a1d363474404da8130b0b940ee8c7ce5bf2968c1204c1c4b2ba98bcbd08fc4ad3cad706d39ac55e4dd61fde5a8496840ce2d377389a4ca7842bf613d3f096fda819c26e43adfb0cad1336a430d"); 45 | }); 46 | 47 | test('Unlock Fundraiser Identity', () async { 48 | List keys = await TezsterDart.unlockFundraiserIdentity( 49 | mnemonic: 50 | "cannon rabbit obvious drama slogan net acoustic donor core acoustic clinic poem travel plunge winter", 51 | email: "lkbpoife.tobqgidu@tezos.example.org", 52 | passphrase: "5tjpU0cimq", 53 | ); 54 | expect(keys[0], 55 | "edskRzNDm2dpqe2yd5zYAw1vmjr8sAwMubfcXajxdCNNr4Ud39BoppeqMAzoCPmb14mzfXRhjtydQjCbqU2VzWrsq6JP4D9GVb"); 56 | expect(keys[1], "edpkvASxrq16v5Awxpz4XPTA2d6QFaCL8expPrPNcVgVbWxT84Kdw2"); 57 | expect(keys[2], "tz1hhkSbaocSWm3wawZUuUdX57L3maSH16Pv"); 58 | }); 59 | 60 | test('Create Soft Signer', () async { 61 | await TezsterDart.createSigner( 62 | TezsterDart.writeKeyWithHint(_keyStoreModel.secretKey, 'edsk')); 63 | }); 64 | 65 | test('send-Transaction-Operation', () async { 66 | var signer = await TezsterDart.createSigner( 67 | TezsterDart.writeKeyWithHint(_keyStoreModel.secretKey, 'edsk')); 68 | const server = 'https://testnet.tezster.tech'; 69 | 70 | var result = await TezsterDart.sendTransactionOperation( 71 | server, 72 | signer, 73 | _keyStoreModel, 74 | 'tz1dTkCS1NQwapmafZwCoqBq1QhXmopKDLcj', 75 | 500000, 76 | 1500, 77 | ); 78 | expect(true, 79 | result['operationGroupID'] != null && result['operationGroupID'] != ''); 80 | }); 81 | 82 | test('send-Delegation-Operation', () async { 83 | var signer = await TezsterDart.createSigner( 84 | TezsterDart.writeKeyWithHint(_keyStoreModel.secretKey, 'edsk')); 85 | const server = 'https://testnet.tezster.tech'; 86 | 87 | var result = await TezsterDart.sendDelegationOperation( 88 | server, 89 | signer, 90 | _keyStoreModel, 91 | 'tz1dTkCS1NQwapmafZwCoqBq1QhXmopKDLcj', 92 | 10000, 93 | ); 94 | 95 | expect(true, 96 | result['operationGroupID'] != null && result['operationGroupID'] != ''); 97 | }); 98 | 99 | test('restore identity from mnemonic', () async { 100 | List keys = await TezsterDart.restoreIdentityFromDerivationPath( 101 | "m/44'/1729'/0'/0'", 102 | "curious roof motor parade analyst riot chronic actor pony random ring slot"); 103 | expect(keys[0], 104 | 'edskRzZLyGkhw9fmibXfqyMuEtEaa8Lxfqz9VBAq7LZbb4AfNQrgbtwW7Tv8qRyr44M89KrTTdLoxML29wEXc2864QuG1xWijP'); 105 | expect(keys[1], 'edpkvPPibVYfQd7uohshcoS7Q2XXTD6vgsJWBrYHmDypkVabWh8czs'); 106 | expect(keys[2], 'tz1Kx6NQZ2M4a9FssBswKyT25USCXWHcTbw7'); 107 | }); 108 | } 109 | -------------------------------------------------------------------------------- /lib/michelson_parser/grammar/michelin_grammar_tokenizer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'michelson_grammar_tokenizer.dart'; 3 | 4 | class MichelinGrammarTokenizer { 5 | Map delimiters; 6 | List tokens = []; 7 | var buffer; 8 | int index; 9 | var line; 10 | var lastLineBreak; 11 | 12 | MichelinGrammarTokenizer(this.delimiters) : super(); 13 | 14 | feed(String text) { 15 | tokenize(text); 16 | return tokens; 17 | } 18 | 19 | var numindex = 0; 20 | 21 | next() { 22 | if (tokens.isEmpty) feed(this.buffer.toString()); 23 | var result = tokens.length > numindex 24 | ? { 25 | "type": tokens[numindex].type, 26 | "value": tokens[numindex].value, 27 | "text": tokens[numindex].value, 28 | "col": tokens[numindex].columnNumber, 29 | "line": '1', 30 | } 31 | : null; 32 | numindex++; 33 | return result; 34 | } 35 | 36 | has(tokenType) { 37 | return true; 38 | } 39 | 40 | reset(data, {state}) { 41 | this.buffer = data; 42 | this.index = 0; 43 | this.line = state != null ? state.line : 1; 44 | this.lastLineBreak = state != null ? -state.col : 0; 45 | } 46 | 47 | save() { 48 | return { 49 | 'line': this.line, 50 | 'col': this.index - this.lastLineBreak, 51 | }; 52 | } 53 | 54 | formatError(token, message) { 55 | var value = token['text']; 56 | int index = token['col']; 57 | var start = max(0, index - token['col']); 58 | var firstLine = this.buffer.substring(start, index + value.length); 59 | message += " at line " + 60 | token['line'] + 61 | " col " + 62 | token['col'].toString() + 63 | ":\n\n"; 64 | message += " " + firstLine + "\n"; 65 | message += 66 | " " + List.generate(token['col'] + 1, (e) => '').join(" ") + "^"; 67 | return message; 68 | } 69 | 70 | tokenize(String chunk) { 71 | for (int i = 0; i < chunk.length; i++) { 72 | final char = chunk[i]; 73 | if (char == ' ') { 74 | var argSeq = getKeyFromValue(char); 75 | tokens.add(GrammarResultModel(argSeq.key, char)..columnNumber = i); 76 | continue; 77 | } 78 | if (isArrayOrStringContaines(char, isRegexCheck: false)) { 79 | var argSeq = getKeyFromValue(char); 80 | tokens.add(GrammarResultModel(argSeq.key, char)..columnNumber = i); 81 | continue; 82 | } 83 | if (isArrayOrStringContaines(char, isRegexCheck: true)) { 84 | var seq = char; 85 | for (int j = i + 1; j < i + 50; j++) { 86 | if (j >= chunk.length - 1) break; 87 | if (isArrayOrStringContaines(seq) && chunk[j] != '_') { 88 | var __seq = chunk[j]; 89 | for (var i = 1; i < 10; i++) { 90 | try { 91 | var ele = chunk[j + i]; 92 | if (isArrayOrStringContaines(seq + __seq)) { 93 | seq += __seq; 94 | break; 95 | } 96 | __seq += ele; 97 | } catch (e) {} 98 | } 99 | var argSeq = getKeyFromValue(seq); 100 | tokens.add(GrammarResultModel(argSeq.key, seq)..columnNumber = i); 101 | i += (seq.length - 1); 102 | seq = ''; 103 | break; 104 | } 105 | seq += chunk[j]; 106 | } 107 | 108 | } 109 | 110 | else if (char == '"') { 111 | var nextIndex = i + 200; 112 | if (nextIndex >= chunk.length - 1) nextIndex = chunk.length; 113 | var data = chunk.substring( 114 | i + 1, 115 | nextIndex, 116 | ); 117 | var rs = chunk.substring(i, i + data.indexOf('"') + 2); 118 | tokens.add(GrammarResultModel('quotedValue', rs)..columnNumber = i); 119 | i += rs.length - 1; 120 | } else { 121 | print("No match found ==> $char"); 122 | } 123 | } 124 | } 125 | 126 | int getEndIndexOfVar(String text) { 127 | for (var i = 0; i < text.length; i++) { 128 | if (isArrayOrStringContaines(text[i]) || text[i] == ' ') { 129 | return i; 130 | } 131 | } 132 | return text.length; 133 | } 134 | 135 | bool isArrayOrStringContaines(char, {isRegexCheck = false}) { 136 | if (delimiters.values.contains(char)) return true; 137 | var result = false; 138 | var _keys = delimiters.keys.toList(); 139 | for (var i = 0; i < _keys.length; i++) { 140 | var value = delimiters[_keys[i]]; 141 | if (value is List) { 142 | result = value.contains(char); 143 | if (result) break; 144 | } 145 | if (value is RegExp && isRegexCheck) { 146 | result = value.hasMatch(char); 147 | if (result) break; 148 | } 149 | } 150 | 151 | return result; 152 | } 153 | 154 | getKeyFromValue(String char, {isRegex = true}) { 155 | var result; 156 | for (var data in delimiters.entries) { 157 | var isContain = data.value is List 158 | ? data.value.contains(char) 159 | : data.value is RegExp && isRegex 160 | ? (data.value as RegExp).hasMatch(char) 161 | : data.value == char; 162 | if (isContain) result = data; 163 | if (result != null) break; 164 | } 165 | return result; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /lib/chain/tezos/tezos_message_codec.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:tezster_dart/chain/tezos/tezos_language_util.dart'; 4 | import 'package:tezster_dart/chain/tezos/tezos_message_utils.dart'; 5 | import 'package:tezster_dart/models/operation_model.dart'; 6 | 7 | class TezosMessageCodec { 8 | static String encodeOperation(OperationModel message) { 9 | if (message.pkh != null && message.secret != null) 10 | return encodeActivation(message); 11 | if (message.kind == 'transaction') return encodeTransaction(message); 12 | if (message.kind == 'reveal') return encodeReveal(message); 13 | if (message.kind == 'delegation') return encodeDelegation(message); 14 | if (message.kind == 'origination') return encodeOrigination(message); 15 | return ''; 16 | } 17 | 18 | static String encodeActivation(activation) { 19 | var hex = TezosMessageUtils.writeInt(4); 20 | hex += TezosMessageUtils.writeAddress(activation.pkh).substring(4); 21 | hex += activation.secret; 22 | return hex; 23 | } 24 | 25 | static String encodeTransaction(OperationModel message) { 26 | String hex = TezosMessageUtils.writeInt(108); 27 | hex += TezosMessageUtils.writeAddress(message.source).substring(2); 28 | hex += TezosMessageUtils.writeInt(int.parse(message.fee)); 29 | hex += TezosMessageUtils.writeInt(message.counter); 30 | hex += TezosMessageUtils.writeInt(message.gasLimit); 31 | hex += TezosMessageUtils.writeInt(message.storageLimit); 32 | hex += TezosMessageUtils.writeInt(int.parse(message.amount)); 33 | hex += TezosMessageUtils.writeAddress(message.destination); 34 | if (message.parameters != null) { 35 | var composite = message.parameters; 36 | var result = TezosLanguageUtil.translateMichelineToHex( 37 | jsonEncode(composite['value'])); 38 | if ((composite['entrypoint'] == 'default' || 39 | composite['entrypoint'] == '') && 40 | result == '030b') { 41 | hex += '00'; 42 | } else { 43 | hex += 'ff'; 44 | if (composite['entrypoint'] == 'default' || 45 | composite['entrypoint'] == '') { 46 | hex += '00'; 47 | } else if (composite['entrypoint'] == 'root') { 48 | hex += '01'; 49 | } else if (composite['entrypoint'] == 'do') { 50 | hex += '02'; 51 | } else if (composite['entrypoint'] == 'set_delegate') { 52 | hex += '03'; 53 | } else if (composite['entrypoint'] == 'remove_delegate') { 54 | hex += '04'; 55 | } else { 56 | var entryStr = '0' + composite['entrypoint'].length.toRadixString(16); 57 | hex += 'ff' + 58 | (entryStr.substring(entryStr.length - 2)) + 59 | composite['entrypoint'] 60 | .split('') 61 | .map((String c) => c.codeUnitAt(0).toRadixString(16)) 62 | .join(''); 63 | } 64 | var resRad = '0000000' + (result.length ~/ 2).toRadixString(16); 65 | hex += resRad.substring(resRad.length - 8) + result; 66 | } 67 | } else { 68 | hex += '00'; 69 | } 70 | return hex; 71 | } 72 | 73 | static String encodeReveal(OperationModel message) { 74 | var hex = TezosMessageUtils.writeInt(107); 75 | hex += TezosMessageUtils.writeAddress(message.source).substring(2); 76 | hex += TezosMessageUtils.writeInt(int.parse(message.fee)); 77 | hex += TezosMessageUtils.writeInt(message.counter); 78 | hex += TezosMessageUtils.writeInt(message.gasLimit); 79 | hex += TezosMessageUtils.writeInt(message.storageLimit); 80 | hex += TezosMessageUtils.writePublicKey(message.publicKey); 81 | return hex; 82 | } 83 | 84 | static String encodeDelegation(OperationModel delegation) { 85 | var hex = TezosMessageUtils.writeInt(110); 86 | hex += TezosMessageUtils.writeAddress(delegation.source).substring(2); 87 | hex += TezosMessageUtils.writeInt(int.parse(delegation.fee)); 88 | hex += TezosMessageUtils.writeInt(delegation.counter); 89 | hex += TezosMessageUtils.writeInt(delegation.gasLimit); 90 | hex += TezosMessageUtils.writeInt(delegation.storageLimit); 91 | if (delegation.delegate != null && delegation.delegate.isNotEmpty) { 92 | hex += TezosMessageUtils.writeBoolean(true); 93 | hex += TezosMessageUtils.writeAddress(delegation.delegate).substring(2); 94 | } else { 95 | hex += TezosMessageUtils.writeBoolean(false); 96 | } 97 | return hex; 98 | } 99 | 100 | static String encodeOrigination(OperationModel origination) { 101 | var hex = TezosMessageUtils.writeInt(109); 102 | hex += TezosMessageUtils.writeAddress(origination.source).substring(2); 103 | hex += TezosMessageUtils.writeInt(int.parse(origination.fee)); 104 | hex += TezosMessageUtils.writeInt(origination.counter); 105 | hex += TezosMessageUtils.writeInt(origination.gasLimit); 106 | hex += TezosMessageUtils.writeInt(origination.storageLimit); 107 | hex += TezosMessageUtils.writeInt(int.parse(origination.amount)); 108 | 109 | if (origination.delegate != null) { 110 | hex += TezosMessageUtils.writeBoolean(true); 111 | hex += TezosMessageUtils.writeAddress(origination.delegate).substring(2); 112 | } else { 113 | hex += TezosMessageUtils.writeBoolean(false); 114 | } 115 | if (origination.script != null) { 116 | var parts = []; 117 | parts.add(origination.script['code']); 118 | parts.add(origination.script['storage']); 119 | hex += parts 120 | .map((p) => jsonEncode(p)) 121 | .map((p) => TezosLanguageUtil.translateMichelineToHex(p)) 122 | .fold('', (m, p) { 123 | var result = ('0000000' + (p.length ~/ 2).toRadixString(16)); 124 | result = result.substring(result.length - 8) + p; 125 | return m + result; 126 | }); 127 | } 128 | 129 | return hex; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.5.0" 11 | bip39: 12 | dependency: "direct main" 13 | description: 14 | name: bip39 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.0.4" 18 | blake2b: 19 | dependency: "direct main" 20 | description: 21 | name: blake2b 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "0.1.2" 25 | boolean_selector: 26 | dependency: transitive 27 | description: 28 | name: boolean_selector 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.1.0" 32 | bs58check: 33 | dependency: "direct main" 34 | description: 35 | name: bs58check 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.0.1" 39 | characters: 40 | dependency: transitive 41 | description: 42 | name: characters 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.1.0" 46 | charcode: 47 | dependency: transitive 48 | description: 49 | name: charcode 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.2.0" 53 | clock: 54 | dependency: transitive 55 | description: 56 | name: clock 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.1.0" 60 | collection: 61 | dependency: transitive 62 | description: 63 | name: collection 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.15.0" 67 | convert: 68 | dependency: "direct main" 69 | description: 70 | name: convert 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "2.1.1" 74 | crypto: 75 | dependency: "direct main" 76 | description: 77 | name: crypto 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "2.1.5" 81 | cryptography: 82 | dependency: transitive 83 | description: 84 | name: cryptography 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.4.1" 88 | ed25519_hd_key: 89 | dependency: "direct main" 90 | description: 91 | name: ed25519_hd_key 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "1.1.0" 95 | fake_async: 96 | dependency: transitive 97 | description: 98 | name: fake_async 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "1.2.0" 102 | ffi: 103 | dependency: transitive 104 | description: 105 | name: ffi 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "0.1.3" 109 | fixnum: 110 | dependency: transitive 111 | description: 112 | name: fixnum 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "0.10.11" 116 | flutter: 117 | dependency: "direct main" 118 | description: flutter 119 | source: sdk 120 | version: "0.0.0" 121 | flutter_sodium: 122 | dependency: "direct main" 123 | description: 124 | name: flutter_sodium 125 | url: "https://pub.dartlang.org" 126 | source: hosted 127 | version: "0.1.11" 128 | flutter_test: 129 | dependency: "direct dev" 130 | description: flutter 131 | source: sdk 132 | version: "0.0.0" 133 | hex: 134 | dependency: transitive 135 | description: 136 | name: hex 137 | url: "https://pub.dartlang.org" 138 | source: hosted 139 | version: "0.1.2" 140 | js: 141 | dependency: transitive 142 | description: 143 | name: js 144 | url: "https://pub.dartlang.org" 145 | source: hosted 146 | version: "0.6.2" 147 | matcher: 148 | dependency: transitive 149 | description: 150 | name: matcher 151 | url: "https://pub.dartlang.org" 152 | source: hosted 153 | version: "0.12.10" 154 | meta: 155 | dependency: transitive 156 | description: 157 | name: meta 158 | url: "https://pub.dartlang.org" 159 | source: hosted 160 | version: "1.3.0" 161 | password_hash: 162 | dependency: "direct main" 163 | description: 164 | name: password_hash 165 | url: "https://pub.dartlang.org" 166 | source: hosted 167 | version: "2.0.0" 168 | path: 169 | dependency: transitive 170 | description: 171 | name: path 172 | url: "https://pub.dartlang.org" 173 | source: hosted 174 | version: "1.8.0" 175 | pointycastle: 176 | dependency: transitive 177 | description: 178 | name: pointycastle 179 | url: "https://pub.dartlang.org" 180 | source: hosted 181 | version: "2.0.1" 182 | sky_engine: 183 | dependency: transitive 184 | description: flutter 185 | source: sdk 186 | version: "0.0.99" 187 | source_span: 188 | dependency: transitive 189 | description: 190 | name: source_span 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "1.8.0" 194 | stack_trace: 195 | dependency: transitive 196 | description: 197 | name: stack_trace 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "1.10.0" 201 | stream_channel: 202 | dependency: transitive 203 | description: 204 | name: stream_channel 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "2.1.0" 208 | string_scanner: 209 | dependency: transitive 210 | description: 211 | name: string_scanner 212 | url: "https://pub.dartlang.org" 213 | source: hosted 214 | version: "1.1.0" 215 | term_glyph: 216 | dependency: transitive 217 | description: 218 | name: term_glyph 219 | url: "https://pub.dartlang.org" 220 | source: hosted 221 | version: "1.2.0" 222 | test_api: 223 | dependency: transitive 224 | description: 225 | name: test_api 226 | url: "https://pub.dartlang.org" 227 | source: hosted 228 | version: "0.2.19" 229 | typed_data: 230 | dependency: transitive 231 | description: 232 | name: typed_data 233 | url: "https://pub.dartlang.org" 234 | source: hosted 235 | version: "1.3.0" 236 | unorm_dart: 237 | dependency: "direct main" 238 | description: 239 | name: unorm_dart 240 | url: "https://pub.dartlang.org" 241 | source: hosted 242 | version: "0.1.2" 243 | vector_math: 244 | dependency: transitive 245 | description: 246 | name: vector_math 247 | url: "https://pub.dartlang.org" 248 | source: hosted 249 | version: "2.1.0" 250 | sdks: 251 | dart: ">=2.12.0-0.0 <3.0.0" 252 | flutter: ">=1.10.0" 253 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.5.0" 11 | bip39: 12 | dependency: transitive 13 | description: 14 | name: bip39 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.0.4" 18 | blake2b: 19 | dependency: transitive 20 | description: 21 | name: blake2b 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "0.1.2" 25 | boolean_selector: 26 | dependency: transitive 27 | description: 28 | name: boolean_selector 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.1.0" 32 | bs58check: 33 | dependency: transitive 34 | description: 35 | name: bs58check 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.0.1" 39 | characters: 40 | dependency: transitive 41 | description: 42 | name: characters 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.1.0" 46 | charcode: 47 | dependency: transitive 48 | description: 49 | name: charcode 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.2.0" 53 | clock: 54 | dependency: transitive 55 | description: 56 | name: clock 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.1.0" 60 | collection: 61 | dependency: transitive 62 | description: 63 | name: collection 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.15.0" 67 | convert: 68 | dependency: transitive 69 | description: 70 | name: convert 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "2.1.1" 74 | crypto: 75 | dependency: transitive 76 | description: 77 | name: crypto 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "2.1.5" 81 | cryptography: 82 | dependency: transitive 83 | description: 84 | name: cryptography 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.4.1" 88 | cupertino_icons: 89 | dependency: "direct main" 90 | description: 91 | name: cupertino_icons 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "0.1.3" 95 | ed25519_hd_key: 96 | dependency: transitive 97 | description: 98 | name: ed25519_hd_key 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "1.1.0" 102 | fake_async: 103 | dependency: transitive 104 | description: 105 | name: fake_async 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "1.2.0" 109 | ffi: 110 | dependency: transitive 111 | description: 112 | name: ffi 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "0.1.3" 116 | fixnum: 117 | dependency: transitive 118 | description: 119 | name: fixnum 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "0.10.11" 123 | flutter: 124 | dependency: "direct main" 125 | description: flutter 126 | source: sdk 127 | version: "0.0.0" 128 | flutter_sodium: 129 | dependency: transitive 130 | description: 131 | name: flutter_sodium 132 | url: "https://pub.dartlang.org" 133 | source: hosted 134 | version: "0.1.11" 135 | flutter_test: 136 | dependency: "direct dev" 137 | description: flutter 138 | source: sdk 139 | version: "0.0.0" 140 | hex: 141 | dependency: transitive 142 | description: 143 | name: hex 144 | url: "https://pub.dartlang.org" 145 | source: hosted 146 | version: "0.1.2" 147 | js: 148 | dependency: transitive 149 | description: 150 | name: js 151 | url: "https://pub.dartlang.org" 152 | source: hosted 153 | version: "0.6.2" 154 | matcher: 155 | dependency: transitive 156 | description: 157 | name: matcher 158 | url: "https://pub.dartlang.org" 159 | source: hosted 160 | version: "0.12.10" 161 | meta: 162 | dependency: transitive 163 | description: 164 | name: meta 165 | url: "https://pub.dartlang.org" 166 | source: hosted 167 | version: "1.3.0" 168 | password_hash: 169 | dependency: transitive 170 | description: 171 | name: password_hash 172 | url: "https://pub.dartlang.org" 173 | source: hosted 174 | version: "2.0.0" 175 | path: 176 | dependency: transitive 177 | description: 178 | name: path 179 | url: "https://pub.dartlang.org" 180 | source: hosted 181 | version: "1.8.0" 182 | pointycastle: 183 | dependency: transitive 184 | description: 185 | name: pointycastle 186 | url: "https://pub.dartlang.org" 187 | source: hosted 188 | version: "2.0.1" 189 | sky_engine: 190 | dependency: transitive 191 | description: flutter 192 | source: sdk 193 | version: "0.0.99" 194 | source_span: 195 | dependency: transitive 196 | description: 197 | name: source_span 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "1.8.0" 201 | stack_trace: 202 | dependency: transitive 203 | description: 204 | name: stack_trace 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "1.10.0" 208 | stream_channel: 209 | dependency: transitive 210 | description: 211 | name: stream_channel 212 | url: "https://pub.dartlang.org" 213 | source: hosted 214 | version: "2.1.0" 215 | string_scanner: 216 | dependency: transitive 217 | description: 218 | name: string_scanner 219 | url: "https://pub.dartlang.org" 220 | source: hosted 221 | version: "1.1.0" 222 | term_glyph: 223 | dependency: transitive 224 | description: 225 | name: term_glyph 226 | url: "https://pub.dartlang.org" 227 | source: hosted 228 | version: "1.2.0" 229 | test_api: 230 | dependency: transitive 231 | description: 232 | name: test_api 233 | url: "https://pub.dartlang.org" 234 | source: hosted 235 | version: "0.2.19" 236 | tezster_dart: 237 | dependency: "direct main" 238 | description: 239 | path: ".." 240 | relative: true 241 | source: path 242 | version: "2.1.2-beta.2" 243 | typed_data: 244 | dependency: transitive 245 | description: 246 | name: typed_data 247 | url: "https://pub.dartlang.org" 248 | source: hosted 249 | version: "1.3.0" 250 | unorm_dart: 251 | dependency: transitive 252 | description: 253 | name: unorm_dart 254 | url: "https://pub.dartlang.org" 255 | source: hosted 256 | version: "0.1.2" 257 | vector_math: 258 | dependency: transitive 259 | description: 260 | name: vector_math 261 | url: "https://pub.dartlang.org" 262 | source: hosted 263 | version: "2.1.0" 264 | sdks: 265 | dart: ">=2.12.0-0.0 <3.0.0" 266 | flutter: ">=1.10.0" 267 | -------------------------------------------------------------------------------- /lib/michelson_parser/grammar/michelson_grammar_tokenizer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | class MichelsonGrammarTokenizer { 4 | Map delimiters; 5 | List tokens = []; 6 | var buffer; 7 | int index; 8 | var line; 9 | var lastLineBreak; 10 | 11 | MichelsonGrammarTokenizer(this.delimiters) : super(); 12 | 13 | feed(String text) { 14 | tokenize(text); 15 | return tokens; 16 | } 17 | 18 | var numindex = 0; 19 | 20 | next() { 21 | if (tokens.isEmpty) feed(this.buffer.toString()); 22 | var result = tokens.length > numindex 23 | ? { 24 | "type": tokens[numindex].type, 25 | "value": tokens[numindex].value, 26 | "text": tokens[numindex].value, 27 | "col": tokens[numindex].columnNumber, 28 | "line": '1', 29 | } 30 | : null; 31 | numindex++; 32 | return result; 33 | } 34 | 35 | has(tokenType) { 36 | return true; 37 | } 38 | 39 | reset(data, {state}) { 40 | this.buffer = data; 41 | this.index = 0; 42 | this.line = state != null ? state.line : 1; 43 | this.lastLineBreak = state != null ? -state.col : 0; 44 | } 45 | 46 | save() { 47 | return { 48 | 'line': this.line, 49 | 'col': this.index - this.lastLineBreak, 50 | }; 51 | } 52 | 53 | formatError(token, message) { 54 | var value = token['text']; 55 | int index = token['col']; 56 | var start = max(0, index - token['col']); 57 | var firstLine = this.buffer.substring(start, index + value.length); 58 | message += " at line " + 59 | token['line'] + 60 | " col " + 61 | token['col'].toString() + 62 | ":\n\n"; 63 | message += " " + firstLine + "\n"; 64 | message += 65 | " " + List.generate(token['col'] + 1, (e) => '').join(" ") + "^"; 66 | return message; 67 | } 68 | 69 | tokenize(String chunk) { 70 | for (int i = 0; i < chunk.length; i++) { 71 | final char = chunk[i]; 72 | if (char == ' ') { 73 | var argSeq = getKeyFromValue(char); 74 | tokens.add(GrammarResultModel(argSeq.key, char)..columnNumber = i); 75 | continue; 76 | } 77 | if (isArrayOrStringContaines(char, isRegexCheck: true)) { 78 | var seq = char; 79 | for (int j = i + 1; j < i + 55; j++) { 80 | if (j >= chunk.length) break; 81 | if (isArrayOrStringContaines(seq) && chunk[j] != '_') { 82 | var __seq = chunk[j]; 83 | for (var i = 1; i < 10; i++) { 84 | try { 85 | var ele = chunk[j + i]; 86 | if (isArrayOrStringContaines(seq + __seq)) { 87 | seq += __seq; 88 | break; 89 | } 90 | __seq += ele; 91 | } catch (e) {} 92 | } 93 | var argSeq = getKeyFromValue(seq); 94 | tokens.add(GrammarResultModel(argSeq.key, seq)..columnNumber = i); 95 | i += (seq.length - 1); 96 | seq = ''; 97 | break; 98 | } 99 | seq += chunk[j]; 100 | } 101 | if (seq.isNotEmpty) { 102 | if (tokens.length > 0 && 103 | tokens.last.type == 'word' && 104 | getKeyFromValue(char, isRegex: false) == null) { 105 | tokens.last = tokens.last..value += char; 106 | } else if (tokens.length > 0 && 107 | tokens.last.type == 'number' && 108 | getKeyFromValue(char, isRegex: true).key == "number") { 109 | tokens.last = tokens.last..value += char; 110 | } else if (isArrayOrStringContaines(seq)) { 111 | var argSeq = getKeyFromValue(seq); 112 | tokens.add(GrammarResultModel(argSeq.key, seq)..columnNumber = i); 113 | i += (seq.length - 1); 114 | } else { 115 | if (seq != char && 116 | isArrayOrStringContaines( 117 | seq.substring(0, 118 | seq.indexOf(' ') == -1 ? seq.length : seq.indexOf(' ')), 119 | isRegexCheck: true) && 120 | seq.length > 10) { 121 | var argSeq = getKeyFromValue( 122 | seq.substring(0, 123 | seq.indexOf(' ') == -1 ? seq.length : seq.indexOf(' ')), 124 | isRegex: true); 125 | if (argSeq.key == 'number') 126 | while (!isNumeric(seq.substring(seq.length - 2))) 127 | seq = seq.substring(0, seq.length - 1); 128 | tokens.add(GrammarResultModel( 129 | argSeq.key, 130 | seq.substring(0, 131 | seq.indexOf(' ') == -1 ? seq.length : seq.indexOf(' '))) 132 | ..columnNumber = i); 133 | i += (seq 134 | .substring( 135 | 0, 136 | seq.indexOf(' ') == -1 137 | ? seq.length 138 | : seq.indexOf(' ')) 139 | .length - 140 | 1); 141 | } else if (char != '%') { 142 | var argSeq = getKeyFromValue(char); 143 | tokens 144 | .add(GrammarResultModel(argSeq.key, char)..columnNumber = i); 145 | } 146 | } 147 | } 148 | } else if (char == '%' || char == ':') { 149 | var nextIndex = i + 30; 150 | if (nextIndex >= chunk.length - 1) nextIndex = chunk.length - 1; 151 | var data = chunk.substring( 152 | i, 153 | nextIndex, 154 | ); 155 | var endIndex = getEndIndexOfVar(data); 156 | tokens.add(GrammarResultModel('annot', data.substring(0, endIndex)) 157 | ..columnNumber = i); 158 | i += endIndex - 1; 159 | } else if (char == '"') { 160 | var nextIndex = i + 200; 161 | if (nextIndex >= chunk.length - 1) nextIndex = chunk.length; 162 | var data = chunk.substring( 163 | i + 1, 164 | nextIndex, 165 | ); 166 | var rs = chunk.substring(i, i + data.indexOf('"') + 2); 167 | tokens.add(GrammarResultModel('string', rs)..columnNumber = i); 168 | i += rs.length - 1; 169 | } else { 170 | print("No match found ==> $char"); 171 | } 172 | } 173 | } 174 | 175 | bool isNumeric(String s) { 176 | if (s == null) { 177 | return false; 178 | } 179 | return double.tryParse(s) != null; 180 | } 181 | 182 | int getEndIndexOfVar(String text) { 183 | for (var i = 0; i < text.length; i++) { 184 | if (isArrayOrStringContaines(text[i]) || text[i] == ' ') { 185 | return i; 186 | } 187 | } 188 | return text.length; 189 | } 190 | 191 | bool isArrayOrStringContaines(char, {isRegexCheck = false}) { 192 | if (delimiters.values.contains(char)) return true; 193 | var result = false; 194 | var _keys = delimiters.keys.toList(); 195 | for (var i = 0; i < _keys.length; i++) { 196 | var value = delimiters[_keys[i]]; 197 | if (value is List) { 198 | result = value.contains(char); 199 | if (result) break; 200 | } 201 | if (value is RegExp && isRegexCheck) { 202 | result = value.hasMatch(char); 203 | if (result) break; 204 | } 205 | } 206 | 207 | return result; 208 | } 209 | 210 | getKeyFromValue(String char, {isRegex = true}) { 211 | var result; 212 | for (var data in delimiters.entries) { 213 | var isContain = data.value is List 214 | ? data.value.contains(char) 215 | : data.value is RegExp && isRegex 216 | ? (data.value as RegExp).hasMatch(char) 217 | : data.value == char; 218 | if (isContain) result = data; 219 | if (result != null) break; 220 | } 221 | return result; 222 | } 223 | } 224 | 225 | class GrammarResultModel { 226 | String type; 227 | String value; 228 | int columnNumber; 229 | 230 | GrammarResultModel(this.type, this.value); 231 | 232 | toJson() => {'type': type, 'value': value}; 233 | } 234 | -------------------------------------------------------------------------------- /lib/chain/tezos/tezos_message_utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:blake2b/blake2b_hash.dart'; 4 | import 'package:bs58check/bs58check.dart' as bs58check; 5 | import 'package:convert/convert.dart'; 6 | import 'package:tezster_dart/chain/tezos/tezos_language_util.dart'; 7 | import 'package:tezster_dart/helper/generateKeys.dart'; 8 | import 'package:tezster_dart/src/soft-signer/soft_signer.dart'; 9 | import 'package:tezster_dart/types/tezos/tezos_chain_types.dart'; 10 | 11 | class TezosMessageUtils { 12 | static String writeBranch(String branch) { 13 | return hex.encode(bs58check.decode(branch).sublist(2).toList()); 14 | } 15 | 16 | static String writeInt(int value) { 17 | if (value < 0) { 18 | throw new Exception('Use writeSignedInt to encode negative numbers'); 19 | } 20 | var byteHexList = Uint8List.fromList(hex.decode(twoByteHex(value))); 21 | for (var i = 0; i < byteHexList.length; i++) { 22 | if (i != 0) byteHexList[i] ^= 0x80; 23 | } 24 | var result = hex.encode(byteHexList.reversed.toList()); 25 | return result; 26 | } 27 | 28 | static String twoByteHex(int n) { 29 | if (n < 128) { 30 | var s = ('0' + n.toRadixString(16)); 31 | return s.substring(s.length - 2); 32 | } 33 | String h = ''; 34 | if (n > 2147483648) { 35 | var r = BigInt.from(n); 36 | while (r.compareTo(BigInt.zero) != 0) { 37 | var _h = ('0' + (r & BigInt.from(127)).toRadixString(16)); 38 | h = _h.substring(_h.length - 2) + h; 39 | r = r >> 7; 40 | } 41 | } else { 42 | var r = n; 43 | while (r > 0) { 44 | var _h = ('0' + (r & 127).toRadixString(16)); 45 | h = _h.substring(_h.length - 2) + h; 46 | r = r >> 7; 47 | } 48 | } 49 | return h; 50 | } 51 | 52 | static String writeAddress(String address) { 53 | var bs58checkdata = bs58check.decode(address).sublist(3); 54 | var _hex = hex.encode(bs58checkdata); 55 | if (address.startsWith("tz1")) { 56 | return "0000" + _hex; 57 | } else if (address.startsWith("tz2")) { 58 | return "0001" + _hex; 59 | } else if (address.startsWith("tz3")) { 60 | return "0002" + _hex; 61 | } else if (address.startsWith("KT1")) { 62 | return "01" + _hex + "00"; 63 | } else { 64 | throw new Exception( 65 | 'Unrecognized address prefix: ${address.substring(0, 3)}'); 66 | } 67 | } 68 | 69 | static String writePublicKey(String publicKey) { 70 | if (publicKey.startsWith("edpk")) { 71 | return "00" + hex.encode(bs58check.decode(publicKey).sublist(4)); 72 | } else if (publicKey.startsWith("sppk")) { 73 | return "01" + hex.encode(bs58check.decode(publicKey).sublist(4)); 74 | } else if (publicKey.startsWith("p2pk")) { 75 | return "02" + hex.encode(bs58check.decode(publicKey).sublist(4)); 76 | } else { 77 | throw new Exception('Unrecognized key type'); 78 | } 79 | } 80 | 81 | static String readPublicKey(String hex, Uint8List b) { 82 | if (hex.length != 66 && hex.length != 68) { 83 | throw new Exception("Incorrect hex length, ${hex.length} to parse a key"); 84 | } 85 | var hint = hex.substring(0, 2); 86 | if (hint == "00") { 87 | return GenerateKeys.readKeysWithHint(b, '0d0f25d9'); 88 | } else if (hint == "01" && hex.length == 68) { 89 | return GenerateKeys.readKeysWithHint(b, '03fee256'); 90 | } else if (hint == "02" && hex.length == 68) { 91 | return GenerateKeys.readKeysWithHint(b, '03b28b7f'); 92 | } else { 93 | throw new Exception('Unrecognized key type'); 94 | } 95 | } 96 | 97 | static dynamic readKeyWithHint(Uint8List b, String hint) { 98 | Uint8List key = !(b.runtimeType == Uint8List) ? Uint8List.fromList(b) : b; 99 | String keyHex = hex.encode(key); 100 | if (hint == 'edsk') { 101 | return GenerateKeys.readKeysWithHint(b, '2bf64e07'); 102 | } else if (hint == 'edpk') { 103 | return readPublicKey("00$keyHex", b); 104 | } else if (hint == 'sppk') { 105 | return readPublicKey("01$keyHex", b); 106 | } else if (hint == 'p2pk') { 107 | return readPublicKey("02$keyHex", b); 108 | } else { 109 | throw new Exception("Unrecognized key hint, $hint"); 110 | } 111 | } 112 | 113 | static Uint8List simpleHash(Uint8List message, int size) { 114 | return Uint8List.fromList(Blake2bHash.hashWithDigestSize(256, message)); 115 | } 116 | 117 | static String readSignatureWithHint(Uint8List opSignature, SignerCurve hint) { 118 | opSignature = Uint8List.fromList(opSignature); 119 | if (hint == SignerCurve.ED25519) { 120 | return GenerateKeys.readKeysWithHint(opSignature, '09f5cd8612'); 121 | } else if (hint == SignerCurve.SECP256K1) { 122 | return GenerateKeys.readKeysWithHint(opSignature, '0d7365133f'); 123 | } else if (hint == SignerCurve.SECP256R1) { 124 | return GenerateKeys.readKeysWithHint(opSignature, '36f02c34'); 125 | } else { 126 | throw Exception('Unrecognized signature hint, "$hint"'); 127 | } 128 | } 129 | 130 | static String writeBoolean(bool b) { 131 | return b ? "ff" : "00"; 132 | } 133 | 134 | static encodeBigMapKey(Uint8List key) { 135 | return readBufferWithHint(simpleHash(key, 32), 'expr'); 136 | } 137 | 138 | static readBufferWithHint(address, hint) { 139 | var buffer = address; 140 | if (hint == 'op') { 141 | return bs58check 142 | .encode(Uint8List.fromList(hex.decode('0574' + hex.encode(buffer)))); 143 | } else if (hint == 'p') { 144 | return bs58check 145 | .encode(Uint8List.fromList(hex.decode('02aa' + hex.encode(buffer)))); 146 | } else if (hint == 'expr') { 147 | return bs58check.encode( 148 | Uint8List.fromList(hex.decode('0d2c401b' + hex.encode(buffer)))); 149 | } else if (hint == '') { 150 | return bs58check.encode(buffer); 151 | } else { 152 | throw new Exception('Unsupported hint $hint'); 153 | } 154 | } 155 | 156 | static String writeSignedInt(value) { 157 | if (value == 0 || value == '0') { 158 | return '00'; 159 | } 160 | var n = BigInt.from(value).abs(); 161 | var l = n.bitLength; 162 | var arr = []; 163 | for (var i = 0; i < l; i += 7) { 164 | var byte = BigInt.zero; 165 | if (i == 0) { 166 | byte = n & BigInt.parse('0x3f'); 167 | n = n >> 6; 168 | } else { 169 | byte = n & BigInt.parse('0x7f'); 170 | n = n >> 7; 171 | } 172 | if (value < 0 && i == 0) { 173 | byte = byte | BigInt.parse('0x40'); 174 | } 175 | if (i + 7 < l) { 176 | byte = byte | BigInt.parse('0x80'); 177 | } 178 | arr.add(byte); 179 | } 180 | if (l % 7 == 0) { 181 | arr[arr.length - 1] = arr[arr.length - 1] | 0x80; 182 | arr.add(1); 183 | } 184 | return arr 185 | .map((w) => ('0' + w.toRadixString(16)) 186 | .substring(0, w.toRadixString(16).length - 2)) 187 | .toList() 188 | .join(''); 189 | } 190 | 191 | static String writeString(value) { 192 | var len = dataLength(value.length); 193 | var text = value 194 | .split('') 195 | .map((String c) => c.codeUnitAt(0).toRadixString(16)) 196 | .toList() 197 | .join(''); 198 | return len + text; 199 | } 200 | 201 | static dataLength(value) { 202 | var data = ('0000000' + (value).toString(16)); 203 | return data.substring(0, data.length - 8); 204 | } 205 | 206 | static readAddress(String hexValue, Uint8List b) { 207 | if (hexValue.length != 44 && hexValue.length != 42) { 208 | throw new Exception("Incorrect hex length to parse an address"); 209 | } 210 | var implicitHint = hexValue.length == 44 211 | ? hexValue.substring(0, 4) 212 | : "00" + hexValue.substring(0, 2); 213 | if (implicitHint == "0000") { 214 | return GenerateKeys.readKeysWithHint(b, '06a19f'); 215 | } else if (implicitHint == "0001") { 216 | return GenerateKeys.readKeysWithHint(b, '06a1a1'); 217 | } else if (implicitHint == "0002") { 218 | return GenerateKeys.readKeysWithHint(b, '06a1a4'); 219 | } else if (hexValue.substring(0, 2) == "01" && hexValue.length == 44) { 220 | return GenerateKeys.readKeysWithHint(b, '025a79'); 221 | } else { 222 | throw new Exception("Unrecognized address type"); 223 | } 224 | } 225 | 226 | static readAddressWithHint(Uint8List b, String hint) { 227 | Uint8List address = 228 | !(b.runtimeType == Uint8List) ? Uint8List.fromList(b) : b; 229 | String hexValue = hex.encode(address); 230 | if (hint == 'tz1') { 231 | return readAddress("0000$hexValue", b); 232 | } else if (hint == 'tz2') { 233 | return readAddress("0001$hexValue", b); 234 | } else if (hint == 'tz3') { 235 | return readAddress("0002$hexValue", b); 236 | } else if (hint == 'kt1') { 237 | return readAddress("01$hexValue}00", b); 238 | } else { 239 | throw new Exception("Unrecognized address hint, '$hint'"); 240 | } 241 | } 242 | 243 | static writePackedData(value, type, format) { 244 | switch (type) { 245 | case 'int': 246 | return '0500' + writeSignedInt(value); 247 | case 'nat': 248 | return '0500' + writeInt(int.parse(value)); 249 | case 'string': 250 | return '0501' + writeString(value); 251 | case 'key_hash': 252 | var address = writeAddress(value).substring(2); 253 | return '050a${dataLength(address.length / 2)}$address'; 254 | case 'address': 255 | var address = writeAddress(value); 256 | return '050a${dataLength(address.length / 2)}$address'; 257 | case 'bytes': 258 | var buffer = hex.encode(value); 259 | return '050a${dataLength(buffer.length / 2)}$buffer'; 260 | default: 261 | try { 262 | if (format == TezosParameterFormat.Micheline) { 263 | return '05${TezosLanguageUtil.translateMichelineToHex(value)}'; 264 | } else if (format == TezosParameterFormat.Michelson) { 265 | var micheline = 266 | TezosLanguageUtil.translateMichelsonToMicheline(value); 267 | return '05${TezosLanguageUtil.translateMichelineToHex(micheline)}'; 268 | } else { 269 | throw new Exception('Unsupported format $format provided'); 270 | } 271 | } catch (e) { 272 | throw new Exception( 273 | 'Unrecognized data type or format: $type, $format : $e'); 274 | } 275 | } 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | // All the functions are called in initState(){} 2 | // For reference please check Debug console for outputs. 3 | // Just run the project you must see the print statement outputs in debug console. It may take few seconds to reflect the output. 4 | 5 | // NOTE: please get the tezster_dart package under pubspec.yaml before running the project 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:tezster_dart/tezster_dart.dart'; 9 | 10 | void main() { 11 | runApp(MyApp()); 12 | } 13 | 14 | class MyApp extends StatefulWidget { 15 | @override 16 | _MyAppState createState() => _MyAppState(); 17 | } 18 | 19 | class _MyAppState extends State { 20 | tezosWalletUtil() async { 21 | //Generate mnemonic 22 | String mnemonic = TezsterDart 23 | .generateMnemonic(); // strength is optional, by default it's 256 ==> Generates 24 words. 24 | print("mnemonic ===> $mnemonic"); 25 | //mnemonic ===> 24 random words, [If strength parameter is changed the words length differs.] 26 | 27 | //Generate keys from mnemonic 28 | List keys = await TezsterDart.getKeysFromMnemonic( 29 | mnemonic: 30 | "luxury bulb roast timber sense stove sugar sketch goddess host meadow decorate gather salmon funny person canoe daring machine network camp moment wrong dice", 31 | ); 32 | print("keys ===> $keys"); 33 | //keys ===> [privateKey, publicKey, publicKeyHash] 34 | //Accessing: private key ===> keys[0] | public key ===> keys[1] | public Key Hash ===> identity[2] all of type string 35 | 36 | //Create / Unlock identity from mnemonic and passphrase. 37 | List identity = await TezsterDart.getKeysFromMnemonicAndPassphrase( 38 | mnemonic: 39 | "cannon rabbit obvious drama slogan net acoustic donor core acoustic clinic poem travel plunge winter", 40 | passphrase: "5tjpU0cimq", 41 | ); 42 | print("identity ===> $identity"); 43 | // identityWithMnemonic ===> [privateKey, publicKey, publicKeyHash] 44 | // Accessing: private key ===> identity[0] | public key ===> identity[1] | public Key Hash ===> identity[2] all of type string. 45 | 46 | //Restore account from secret key 47 | List restoredKeys = TezsterDart.getKeysFromSecretKey( 48 | "edskRrDH2TF4DwKU1ETsUjyhxPC8aCTD6ko5YDguNkJjRb3PiBm8Upe4FGFmCrQqzSVMDLfFN22XrQXATcA3v41hWnAhymgQwc"); 49 | print("Restored account keys ===> $restoredKeys"); 50 | // restoredKeys ===> [privateKey, publicKey, publicKeyHash] 51 | // Accessing: private key ===> restoredKeys[0] | public key ===> restoredKeys[1] | public Key Hash ===> restoredKeys[2] all of type string. 52 | 53 | //Sign operation with public-Key and forged operation 54 | List signOpGrp = await TezsterDart.signOperationGroup( 55 | privateKey: 56 | "edskRdVS5H9YCRAG8yqZkX2nUTbGcaDqjYgopkJwRuPUnYzCn3t9ZGksncTLYe33bFjq29pRhpvjQizCCzmugMGhJiXezixvdC", 57 | forgedOperation: 58 | "713cb068fe3ac078351727eb5c34279e22b75b0cf4dc0a8d3d599e27031db136040cb9f9da085607c05cac1ca4c62a3f3cfb8146aa9b7f631e52f877a1d363474404da8130b0b940ee", 59 | ); 60 | print("signOperationGroup ===> $signOpGrp"); 61 | //signOperationGroup ===> [hexSignature, signedOpBytes] 62 | //Accessing: hex signature ===> signOpGrp[0] | signed Operation bytes ===> signOpGrp[1] all of type string 63 | 64 | //Unlock fundraiser identity. 65 | List identityFundraiser = 66 | await TezsterDart.unlockFundraiserIdentity( 67 | mnemonic: 68 | "cannon rabbit obvious drama slogan net acoustic donor core acoustic clinic poem travel plunge winter", 69 | email: "lkbpoife.tobqgidu@tezos.example.org", 70 | passphrase: "5tjpU0cimq", 71 | ); 72 | print("identityFundraiser ===> $identityFundraiser"); 73 | //identityFundraiser ===> [privateKey, publicKey, publicKeyHash] 74 | //Accessing: private key ===> identityFundraiser[0] | public key ===> identityFundraiser[1] | public Key Hash ===> identityFundraiser[2] all of type string. 75 | 76 | // Get Balance 77 | String balance = 78 | await TezsterDart.getBalance('tz1c....ozGGs', 'your rpc server'); 79 | print("Accoutn Balance ===> $balance"); 80 | 81 | var server = ''; 82 | 83 | var keyStore = KeyStoreModel( 84 | publicKey: 'edpkvQtuhdZQmjdjVfaY9Kf4hHfrRJYugaJErkCGvV3ER1S7XWsrrj', 85 | secretKey: 86 | 'edskRgu8wHxjwayvnmpLDDijzD3VZDoAH7ZLqJWuG4zg7LbxmSWZWhtkSyM5Uby41rGfsBGk4iPKWHSDniFyCRv3j7YFCknyHH', 87 | publicKeyHash: 'tz1QSHaKpTFhgHLbqinyYRjxD5sLcbfbzhxy', 88 | ); 89 | 90 | //Send transaction 91 | var transactionSigner = await TezsterDart.createSigner( 92 | TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk')); 93 | var transactionResult = await TezsterDart.sendTransactionOperation( 94 | server, 95 | transactionSigner, 96 | keyStore, 97 | 'tz1RVcUP9nUurgEJMDou8eW3bVDs6qmP5Lnc', 98 | 500000, 99 | 1500, 100 | ); 101 | print("Applied operation ===> ${transactionResult['appliedOp']}"); 102 | print("Operation groupID ===> ${transactionResult['operationGroupID']}"); 103 | 104 | //Send delegation 105 | var delegationSigner = await TezsterDart.createSigner( 106 | TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk')); 107 | var delegationResult = await TezsterDart.sendDelegationOperation( 108 | server, 109 | delegationSigner, 110 | keyStore, 111 | 'tz1RVcUP9nUurgEJMDou8eW3bVDs6qmP5Lnc', 112 | 10000, 113 | ); 114 | print("Applied operation ===> ${delegationResult['appliedOp']}"); 115 | print("Operation groupID ===> ${delegationResult['operationGroupID']}"); 116 | 117 | // restore identity from derivation path and mnemonic 118 | List revoceredKeys = 119 | await TezsterDart.restoreIdentityFromDerivationPath("m/44'/1729'/0'/0'", 120 | "curious roof motor parade analyst riot chronic actor pony random ring slot"); 121 | print("revoceredKeys ===> $revoceredKeys"); 122 | //revoceredKeys ===> [privateKey, publicKey, publicKeyHash] 123 | //Accessing: private key ===> revoceredKeys[0] | public key ===> revoceredKeys[1] | public Key Hash ===> revoceredKeys[2] all of type string. 124 | 125 | //Deploy a contract 126 | var contract = """parameter string; 127 | storage string; 128 | code { DUP; 129 | DIP { CDR ; NIL string ; SWAP ; CONS } ; 130 | CAR ; CONS ; 131 | CONCAT; 132 | NIL operation; PAIR}"""; 133 | 134 | var storage = '"Sample"'; 135 | var contractOriginationSigner = await TezsterDart.createSigner( 136 | TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk')); 137 | 138 | var resultContractOrigination = 139 | await TezsterDart.sendContractOriginationOperation( 140 | server, 141 | contractOriginationSigner, 142 | keyStore, 143 | 0, 144 | null, 145 | 100000, 146 | 1000, 147 | 100000, 148 | contract, 149 | storage, 150 | codeFormat: TezosParameterFormat.Michelson, 151 | ); 152 | 153 | print( 154 | "Operation groupID ===> ${resultContractOrigination['operationGroupID']}"); 155 | 156 | //Call a contract 157 | var contractInvocationSigner = await TezsterDart.createSigner( 158 | TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk')); 159 | 160 | var contractAddress = 'KT1KA7DqFjShLC4CPtChPX8QtRYECUb99xMY'; 161 | 162 | var resultInvoke = await TezsterDart.sendContractInvocationOperation( 163 | server, 164 | contractInvocationSigner, 165 | keyStore, 166 | contractAddress, 167 | 10000, 168 | 100000, 169 | 1000, 170 | 100000, 171 | '', 172 | '"Cryptonomicon"', 173 | codeFormat: TezosParameterFormat.Michelson); 174 | 175 | print("Operation groupID ===> ${resultInvoke['operationGroupID']}"); 176 | 177 | //Await opration Confirmation 178 | var network = 'carthagenet'; 179 | 180 | var serverInfo = {'url': '', 'apiKey': '', 'network': network}; 181 | 182 | var operationConfirmationSigner = await TezsterDart.createSigner( 183 | TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk')); 184 | 185 | var resultoperationConfirmation = 186 | await TezsterDart.sendContractOriginationOperation( 187 | server, 188 | operationConfirmationSigner, 189 | keyStore, 190 | 0, 191 | null, 192 | 100000, 193 | 1000, 194 | 100000, 195 | contract, 196 | storage, 197 | codeFormat: TezosParameterFormat.Michelson, 198 | ); 199 | 200 | print( 201 | "Operation groupID ===> ${resultoperationConfirmation['operationGroupID']}"); 202 | 203 | var groupId = resultoperationConfirmation['operationGroupID']; 204 | 205 | var conseilResult = await TezsterDart.awaitOperationConfirmation( 206 | serverInfo, network, groupId, 5); 207 | 208 | print('Originated contract at ${conseilResult['originated_contracts']}'); 209 | 210 | //Activating a fundraiser account 211 | var faucetKeyStore = KeyStoreModel( 212 | publicKeyHash: '', 213 | seed: [ 214 | "wife", 215 | "filter", 216 | "wage", 217 | "thunder", 218 | "forget", 219 | "scale", 220 | "punch", 221 | "mammal", 222 | "offer", 223 | "car", 224 | "cash", 225 | "defy", 226 | "vehicle", 227 | "romance", 228 | "green" 229 | ], 230 | secret: '', 231 | email: '', 232 | password: '', 233 | ); 234 | 235 | var faucetKeys = await TezsterDart.unlockFundraiserIdentity( 236 | email: faucetKeyStore.email, 237 | passphrase: faucetKeyStore.password, 238 | mnemonic: faucetKeyStore.seed.join(' ')); 239 | faucetKeyStore 240 | ..publicKey = faucetKeys[1] 241 | ..secretKey = faucetKeys[0] 242 | ..publicKeyHash = faucetKeys[2]; 243 | var activationOperationSigner = await TezsterDart.createSigner( 244 | TezsterDart.writeKeyWithHint(faucetKeyStore.secretKey, 'edsk')); 245 | var activationOperationResult = 246 | await TezsterDart.sendIdentityActivationOperation(server, 247 | activationOperationSigner, faucetKeyStore, faucetKeyStore.secret); 248 | print('${activationOperationResult['operationGroupID']}'); 249 | 250 | //Reveal an account 251 | var keyRevealKeyStore = KeyStoreModel( 252 | publicKeyHash: 'tz1Uey......FDPWW5MHgi', 253 | secretKey: 'edskRpg......EjHx8ebL2B6g', 254 | publicKey: 'edpktt......gYJu2', 255 | ); 256 | 257 | var keyRevealSigner = await TezsterDart.createSigner( 258 | TezsterDart.writeKeyWithHint(keyRevealKeyStore.secretKey, 'edsk')); 259 | 260 | var keyRevealResult = await TezsterDart.sendKeyRevealOperation( 261 | server, keyRevealSigner, keyRevealKeyStore); 262 | 263 | print('${keyRevealResult['operationGroupID']}'); 264 | } 265 | 266 | @override 267 | void initState() { 268 | super.initState(); 269 | tezosWalletUtil(); 270 | } 271 | 272 | @override 273 | Widget build(BuildContext context) { 274 | return MaterialApp( 275 | debugShowCheckedModeBanner: false, 276 | home: Scaffold( 277 | body: Padding( 278 | padding: EdgeInsets.all(8.0), 279 | child: Center( 280 | child: Text( 281 | "Welcome to Tezster_dart package.\n Please check the debug console for the outputs", 282 | textAlign: TextAlign.center, 283 | ), 284 | ), 285 | ), 286 | ), 287 | ); 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /lib/src/tezster_dart.dart: -------------------------------------------------------------------------------- 1 | library tezster_dart; 2 | 3 | import 'dart:async'; 4 | import 'dart:convert'; 5 | import 'dart:typed_data'; 6 | import 'dart:core'; 7 | import 'package:convert/convert.dart'; 8 | import 'package:blake2b/blake2b_hash.dart'; 9 | import 'package:crypto/crypto.dart'; 10 | import 'package:ed25519_hd_key/ed25519_hd_key.dart'; 11 | import 'package:password_hash/password_hash.dart'; 12 | import 'package:bip39/bip39.dart' as bip39; 13 | import 'package:bs58check/bs58check.dart' as bs58check; 14 | import 'package:tezster_dart/chain/tezos/tezos_message_utils.dart'; 15 | import 'package:tezster_dart/chain/tezos/tezos_node_reader.dart'; 16 | import 'package:tezster_dart/chain/tezos/tezos_node_writer.dart'; 17 | import 'package:tezster_dart/helper/constants.dart'; 18 | import 'package:tezster_dart/helper/http_helper.dart'; 19 | import 'package:tezster_dart/reporting/tezos/tezos_conseil_client.dart'; 20 | import 'package:tezster_dart/src/soft-signer/soft_signer.dart'; 21 | import 'package:tezster_dart/tezster_dart.dart'; 22 | import 'package:tezster_dart/types/tezos/tezos_chain_types.dart'; 23 | import 'package:tezster_dart/utils/sodium_utils.dart'; 24 | import "package:unorm_dart/unorm_dart.dart" as unorm; 25 | import 'package:flutter_sodium/flutter_sodium.dart'; 26 | 27 | import 'package:tezster_dart/helper/generateKeys.dart'; 28 | 29 | class TezsterDart { 30 | static String generateMnemonic({int strength = 256}) { 31 | return bip39.generateMnemonic(strength: strength); 32 | } 33 | 34 | static Future> getKeysFromMnemonic({ 35 | String mnemonic, 36 | }) async { 37 | assert(mnemonic != null); 38 | Uint8List seed = bip39.mnemonicToSeed(mnemonic); 39 | Uint8List seedLength32 = seed.sublist(0, 32); 40 | KeyPair keyPair = Sodium.cryptoSignSeedKeypair(seedLength32); 41 | String skKey = GenerateKeys.readKeysWithHint(keyPair.sk, '2bf64e07'); 42 | String pkKey = GenerateKeys.readKeysWithHint(keyPair.pk, '0d0f25d9'); 43 | String pkKeyHash = GenerateKeys.computeKeyHash(keyPair.pk); 44 | return [skKey, pkKey, pkKeyHash]; 45 | } 46 | 47 | static Future> getKeysFromMnemonicAndPassphrase({ 48 | String mnemonic, 49 | String passphrase, 50 | }) async { 51 | assert(mnemonic != null); 52 | assert(passphrase != null); 53 | return await _unlockKeys( 54 | passphrase: passphrase, 55 | mnemonic: mnemonic, 56 | ); 57 | } 58 | 59 | static Future> restoreIdentityFromDerivationPath( 60 | String derivationPath, String mnemonic, 61 | {String password = '', String pkh, bool validate = true}) async { 62 | if (validate) { 63 | if (![12, 15, 18, 21, 24].contains(mnemonic.split(' ').length)) { 64 | throw new Exception("Invalid mnemonic length."); 65 | } 66 | if (!bip39.validateMnemonic(mnemonic)) { 67 | throw new Exception("The given mnemonic could not be validated."); 68 | } 69 | } 70 | 71 | KeyPair keys; 72 | Uint8List seed = bip39.mnemonicToSeed(mnemonic); 73 | 74 | if (derivationPath != null && derivationPath.length > 0) { 75 | KeyData keysource = ED25519_HD_KEY.derivePath(derivationPath, seed); 76 | var combinedKey = Uint8List.fromList(keysource.key + keysource.chainCode); 77 | keys = SodiumUtils.publicKey(combinedKey); 78 | } else { 79 | return await _unlockKeys(mnemonic: mnemonic, passphrase: password); 80 | } 81 | 82 | var secretKey = TezosMessageUtils.readKeyWithHint(keys.sk, "edsk"); 83 | var publicKey = TezosMessageUtils.readKeyWithHint(keys.pk, "edpk"); 84 | var publicKeyHash = GenerateKeys.computeKeyHash(keys.pk); 85 | if (pkh != null && publicKeyHash != pkh) { 86 | throw new Exception( 87 | 'The given mnemonic and passphrase do not correspond to the supplied public key hash'); 88 | } 89 | 90 | return [secretKey, publicKey, publicKeyHash]; 91 | } 92 | 93 | static List getKeysFromSecretKey(String skKey) { 94 | Uint8List secretKeyBytes = GenerateKeys.writeKeyWithHint(skKey, 'edsk'); 95 | KeyPair keys = SodiumUtils.publicKey(secretKeyBytes); 96 | String pkKey = TezosMessageUtils.readKeyWithHint(keys.pk, 'edpk'); 97 | String pkKeyHash = GenerateKeys.computeKeyHash(keys.pk); 98 | return [skKey, pkKey, pkKeyHash]; 99 | } 100 | 101 | static Future> unlockFundraiserIdentity({ 102 | String mnemonic, 103 | String email, 104 | String passphrase = "", 105 | }) async { 106 | assert(mnemonic != null); 107 | assert(email != null); 108 | return await _unlockKeys( 109 | email: email, 110 | passphrase: passphrase, 111 | mnemonic: mnemonic, 112 | ); 113 | } 114 | 115 | static Future> signOperationGroup({ 116 | String privateKey, 117 | String forgedOperation, 118 | }) async { 119 | assert(privateKey != null); 120 | assert(forgedOperation != null); 121 | String watermarkedForgedOperationBytesHex = '03' + forgedOperation; 122 | List hexStringToListOfInt = 123 | hex.decode(watermarkedForgedOperationBytesHex); 124 | Uint8List hashedWatermarkedOpBytes = 125 | Blake2bHash.hashWithDigestSize(256, hexStringToListOfInt); 126 | Uint8List privateKeyBytes = bs58check.decode(privateKey); 127 | List pkB = List.from(privateKeyBytes); 128 | pkB.removeRange(0, 4); 129 | Uint8List finalPKb = Uint8List.fromList(pkB); 130 | Uint8List value = Sodium.cryptoSignDetached( 131 | hashedWatermarkedOpBytes, 132 | finalPKb, 133 | ); 134 | String opSignatureHex = hex.encode(value); 135 | String hexStringToEncode = '09f5cd8612' + opSignatureHex; 136 | Uint8List hexDeco = hex.decode(hexStringToEncode); 137 | String hexSignature = bs58check.encode(hexDeco); 138 | String signedOpBytes = forgedOperation + opSignatureHex; 139 | return [hexSignature, signedOpBytes]; 140 | } 141 | 142 | static Future> _unlockKeys({ 143 | String mnemonic, 144 | String passphrase = "", 145 | String email = "", 146 | }) async { 147 | assert(mnemonic != null); 148 | assert(passphrase != null); 149 | 150 | List stringNormalize(String stringToNormalize) { 151 | String normalizedString = unorm.nfkd(stringToNormalize); 152 | List stringToBuffer = utf8.encode(normalizedString); 153 | return stringToBuffer; 154 | } 155 | 156 | List mnemonicsBuffer = stringNormalize(mnemonic); 157 | String m = String.fromCharCodes(mnemonicsBuffer); 158 | List normalizedPassphrase = stringNormalize("$email" + "$passphrase"); 159 | String normString = String.fromCharCodes(normalizedPassphrase); 160 | String p = "mnemonic" + normString; 161 | Uint8List seed = PBKDF2(hashAlgorithm: sha512).generateKey(m, p, 2048, 32); 162 | KeyPair keyPair = Sodium.cryptoSignSeedKeypair(seed); 163 | String skKey = GenerateKeys.readKeysWithHint(keyPair.sk, '2bf64e07'); 164 | String pkKey = GenerateKeys.readKeysWithHint(keyPair.pk, '0d0f25d9'); 165 | String pkKeyHash = GenerateKeys.computeKeyHash(keyPair.pk); 166 | return [skKey, pkKey, pkKeyHash]; 167 | } 168 | 169 | static Future getBalance(String publicKeyHash, String rpc) async { 170 | assert(publicKeyHash != null); 171 | assert(rpc != null); 172 | var response = await HttpHelper.performGetRequest(rpc, 173 | 'chains/main/blocks/head/context/contracts/$publicKeyHash/balance'); 174 | return response.toString(); 175 | } 176 | 177 | static Uint8List writeKeyWithHint(key, hint) { 178 | assert(key != null); 179 | assert(hint != null); 180 | return GenerateKeys.writeKeyWithHint(key, hint); 181 | } 182 | 183 | static String writeAddress(address) { 184 | assert(address != null); 185 | return TezosMessageUtils.writeAddress(address); 186 | } 187 | 188 | static createSigner(Uint8List secretKey, {int validity = 60}) { 189 | assert(secretKey != null); 190 | return SoftSigner.createSigner(secretKey, validity); 191 | } 192 | 193 | static sendTransactionOperation(String server, SoftSigner signer, 194 | KeyStoreModel keyStore, String to, int amount, int fee, 195 | {int offset = 54}) async { 196 | assert(server != null); 197 | assert(signer != null); 198 | assert(keyStore != null); 199 | assert(keyStore.publicKeyHash != null); 200 | assert(keyStore.publicKey != null); 201 | assert(keyStore.secretKey != null); 202 | assert(to != null); 203 | assert(amount != null); 204 | assert(fee != null); 205 | assert(offset != null); 206 | 207 | return await TezosNodeWriter.sendTransactionOperation( 208 | server, signer, keyStore, to, amount, fee); 209 | } 210 | 211 | static sendDelegationOperation(String server, SoftSigner signer, 212 | KeyStoreModel keyStore, String delegate, int fee, 213 | {offset = 54}) async { 214 | assert(server != null); 215 | assert(signer != null); 216 | assert(keyStore != null); 217 | assert(keyStore.publicKeyHash != null); 218 | assert(keyStore.publicKey != null); 219 | assert(keyStore.secretKey != null); 220 | assert(offset != null); 221 | if (fee == null || fee == 0) fee = TezosConstants.DefaultDelegationFee; 222 | return await TezosNodeWriter.sendDelegationOperation( 223 | server, signer, keyStore, delegate, fee, offset); 224 | } 225 | 226 | static sendContractOriginationOperation( 227 | String server, 228 | SoftSigner signer, 229 | KeyStoreModel keyStore, 230 | int amount, 231 | String delegate, 232 | int fee, 233 | int storageLimit, 234 | int gasLimit, 235 | String code, 236 | String storage, { 237 | TezosParameterFormat codeFormat = TezosParameterFormat.Micheline, 238 | int offset = 54, 239 | }) async { 240 | assert(server != null); 241 | assert(signer != null); 242 | assert(keyStore != null); 243 | assert(keyStore.publicKeyHash != null); 244 | assert(keyStore.publicKey != null); 245 | assert(keyStore.secretKey != null); 246 | assert(amount != null); 247 | assert(fee != null); 248 | assert(storageLimit != null); 249 | assert(gasLimit != null); 250 | assert(code != null); 251 | assert(storage != null); 252 | assert(codeFormat != null); 253 | assert(offset != null); 254 | return await TezosNodeWriter.sendContractOriginationOperation( 255 | server, 256 | signer, 257 | keyStore, 258 | amount, 259 | delegate, 260 | fee, 261 | storageLimit, 262 | gasLimit, 263 | code, 264 | storage, 265 | codeFormat, 266 | offset, 267 | ); 268 | } 269 | 270 | static awaitOperationConfirmation(serverInfo, network, hash, duration, 271 | {blocktime}) async { 272 | assert(serverInfo != null); 273 | assert(network != null); 274 | assert(hash != null); 275 | assert(duration != null); 276 | return await TezosConseilClient.awaitOperationConfirmation( 277 | serverInfo, network, hash, duration, 278 | blocktime: blocktime); 279 | } 280 | 281 | static sendContractInvocationOperation( 282 | String server, 283 | SoftSigner signer, 284 | KeyStoreModel keyStore, 285 | String contract, 286 | int amount, 287 | int fee, 288 | int storageLimit, 289 | int gasLimit, 290 | entrypoint, 291 | String parameters, 292 | {TezosParameterFormat codeFormat = TezosParameterFormat.Micheline, 293 | offset = 54}) async { 294 | assert(server != null); 295 | assert(signer != null); 296 | assert(keyStore != null); 297 | assert(contract != null); 298 | assert(keyStore.publicKeyHash != null); 299 | assert(keyStore.publicKey != null); 300 | assert(keyStore.secretKey != null); 301 | assert(amount != null); 302 | assert(entrypoint != null); 303 | assert(parameters != null); 304 | assert(fee != null); 305 | assert(storageLimit != null); 306 | assert(gasLimit != null); 307 | return await TezosNodeWriter.sendContractInvocationOperation( 308 | server, 309 | signer, 310 | keyStore, 311 | contract, 312 | amount, 313 | fee, 314 | storageLimit, 315 | gasLimit, 316 | entrypoint, 317 | parameters, 318 | parameterFormat: codeFormat ?? TezosParameterFormat.Micheline, 319 | offset: offset ?? 54); 320 | } 321 | 322 | static sendIdentityActivationOperation(String server, SoftSigner signer, 323 | KeyStoreModel keyStore, String activationCode) async { 324 | assert(server != null); 325 | assert(signer != null); 326 | assert(keyStore != null); 327 | assert(activationCode != null); 328 | return await TezosNodeWriter.sendIdentityActivationOperation( 329 | server, signer, keyStore, activationCode); 330 | } 331 | 332 | static sendKeyRevealOperation(String server, signer, KeyStoreModel keyStore, 333 | {fee = TezosConstants.DefaultKeyRevealFee, 334 | offset = TezosConstants.HeadBranchOffset}) async { 335 | assert(server != null); 336 | assert(signer != null); 337 | assert(keyStore != null); 338 | assert(fee != null); 339 | assert(offset != null); 340 | return await TezosNodeWriter.sendKeyRevealOperation( 341 | server, signer, keyStore, fee, offset); 342 | } 343 | 344 | static getContractStorage(String server, String accountHash) async { 345 | assert(server != null); 346 | assert(accountHash != null); 347 | return await TezosNodeReader.getContractStorage(server, accountHash); 348 | } 349 | 350 | static encodeBigMapKey(Uint8List key) { 351 | assert(key != null); 352 | return TezosMessageUtils.encodeBigMapKey(key); 353 | } 354 | 355 | static Uint8List writePackedData(String value, String type, 356 | {format = TezosParameterFormat.Micheline}) { 357 | assert(value != null); 358 | assert(type != null); 359 | assert(format != null); 360 | return Uint8List.fromList( 361 | hex.decode(TezosMessageUtils.writePackedData(value, type, format))); 362 | } 363 | 364 | static getValueForBigMapKey(String server, String index, String key, 365 | {block = 'head', chainid = 'main'}) async { 366 | assert(server != null); 367 | assert(index != null); 368 | assert(key != null); 369 | return await TezosNodeReader.getValueForBigMapKey(server, index, key, 370 | block: 'head', chainid: 'main'); 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /lib/chain/tezos/tezos_node_writer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:convert/convert.dart'; 5 | import 'package:tezster_dart/chain/tezos/tezos_language_util.dart'; 6 | import 'package:tezster_dart/chain/tezos/tezos_message_codec.dart'; 7 | import 'package:tezster_dart/chain/tezos/tezos_message_utils.dart'; 8 | import 'package:tezster_dart/chain/tezos/tezos_node_reader.dart'; 9 | import 'package:tezster_dart/helper/constants.dart'; 10 | import 'package:tezster_dart/helper/http_helper.dart'; 11 | import 'package:tezster_dart/models/key_store_model.dart'; 12 | import 'package:tezster_dart/models/operation_model.dart'; 13 | import 'package:tezster_dart/src/soft-signer/soft_signer.dart'; 14 | import 'package:tezster_dart/types/tezos/tezos_chain_types.dart'; 15 | import 'package:tezster_dart/utils/gas_fee_calculator.dart'; 16 | 17 | class TezosNodeWriter { 18 | static Future> sendTransactionOperation(String server, 19 | SoftSigner signer, KeyStoreModel keyStore, String to, int amount, int fee, 20 | {int offset = 54}) async { 21 | var counter = await TezosNodeReader.getCounterForAccount( 22 | server, keyStore.publicKeyHash) + 23 | 1; 24 | 25 | var estimate = Estimate(TezosConstants.DefaultTransactionGasLimit * 1000, 26 | TezosConstants.DefaultTransactionStorageLimit, 162, 250, fee); 27 | 28 | OperationModel transaction = new OperationModel( 29 | destination: to, 30 | amount: amount.toString(), 31 | counter: counter, 32 | fee: estimate.suggestedFeeMutez.toString(), 33 | source: keyStore.publicKeyHash, 34 | gasLimit: estimate.gasLimit, 35 | storageLimit: estimate.storageLimit, 36 | ); 37 | 38 | var operations = await appendRevealOperation(server, keyStore.publicKey, 39 | keyStore.publicKeyHash, counter - 1, [transaction]); 40 | return sendOperation(server, operations, signer, offset); 41 | } 42 | 43 | static sendDelegationOperation(String server, SoftSigner signer, 44 | KeyStoreModel keyStore, String delegate, int fee, offset) async { 45 | var counter = await TezosNodeReader.getCounterForAccount( 46 | server, keyStore.publicKeyHash) + 47 | 1; 48 | 49 | var estimate = Estimate(TezosConstants.DefaultDelegationGasLimit * 1000, 50 | TezosConstants.DefaultDelegationStorageLimit, 162, 250, fee); 51 | 52 | OperationModel delegation = new OperationModel( 53 | counter: counter, 54 | kind: 'delegation', 55 | fee: estimate.suggestedFeeMutez.toString(), 56 | source: keyStore.publicKeyHash, 57 | delegate: delegate, 58 | gasLimit: estimate.gasLimit, 59 | storageLimit: estimate.storageLimit, 60 | ); 61 | 62 | var operations = await appendRevealOperation(server, keyStore.publicKey, 63 | keyStore.publicKeyHash, counter - 1, [delegation]); 64 | return sendOperation(server, operations, signer, offset); 65 | } 66 | 67 | static sendContractOriginationOperation( 68 | String server, 69 | SoftSigner signer, 70 | KeyStoreModel keyStore, 71 | int amount, 72 | String delegate, 73 | int fee, 74 | int storageLimit, 75 | int gasLimit, 76 | String code, 77 | String storage, 78 | TezosParameterFormat codeFormat, 79 | int offset) async { 80 | var counter = await TezosNodeReader.getCounterForAccount( 81 | server, keyStore.publicKeyHash) + 82 | 1; 83 | var operation = constructContractOriginationOperation( 84 | keyStore, 85 | amount, 86 | delegate, 87 | fee, 88 | storageLimit, 89 | gasLimit, 90 | code, 91 | storage, 92 | codeFormat, 93 | counter); 94 | var operations = await appendRevealOperation(server, keyStore.publicKey, 95 | keyStore.publicKeyHash, counter - 1, [operation]); 96 | return sendOperation(server, operations, signer, offset); 97 | } 98 | 99 | static sendContractInvocationOperation( 100 | String server, 101 | SoftSigner signer, 102 | KeyStoreModel keyStore, 103 | String contract, 104 | int amount, 105 | int fee, 106 | int storageLimit, 107 | int gasLimit, 108 | entrypoint, 109 | String parameters, 110 | {TezosParameterFormat parameterFormat = TezosParameterFormat.Micheline, 111 | offset = 54}) async { 112 | var counter = await TezosNodeReader.getCounterForAccount( 113 | server, keyStore.publicKeyHash) + 114 | 1; 115 | var transaction = constructContractInvocationOperation( 116 | keyStore.publicKeyHash, 117 | counter, 118 | contract, 119 | amount, 120 | fee, 121 | storageLimit, 122 | gasLimit, 123 | entrypoint, 124 | parameters, 125 | parameterFormat); 126 | var operations = await appendRevealOperation(server, keyStore.publicKey, 127 | keyStore.publicKeyHash, counter - 1, [transaction]); 128 | return sendOperation(server, operations, signer, offset); 129 | } 130 | 131 | static sendIdentityActivationOperation(String server, SoftSigner signer, 132 | KeyStoreModel keyStore, String activationCode) async { 133 | var activation = OperationModel( 134 | kind: 'activate_account', 135 | pkh: keyStore.publicKeyHash, 136 | secret: activationCode, 137 | ); 138 | return await sendOperation(server, [activation], signer, 54); 139 | } 140 | 141 | static sendKeyRevealOperation( 142 | String server, signer, KeyStoreModel keyStore, fee, offset) async { 143 | var counter = (await TezosNodeReader.getCounterForAccount( 144 | server, keyStore.publicKeyHash)) + 145 | 1; 146 | var estimate = Estimate(10000 * 1000, 0, 162, 250, fee); 147 | 148 | var revealOp = OperationModel( 149 | kind: 'reveal', 150 | source: keyStore.publicKeyHash, 151 | fee: estimate.suggestedFeeMutez.toString(), 152 | counter: counter, 153 | gasLimit: estimate.gasLimit, 154 | storageLimit: estimate.storageLimit, 155 | publicKey: keyStore.publicKey, 156 | ); 157 | 158 | var operations = [revealOp]; 159 | return sendOperation(server, operations, signer, offset); 160 | } 161 | 162 | static constructContractInvocationOperation( 163 | String publicKeyHash, 164 | int counter, 165 | String contract, 166 | int amount, 167 | int fee, 168 | int storageLimit, 169 | int gasLimit, 170 | entrypoint, 171 | String parameters, 172 | TezosParameterFormat parameterFormat) { 173 | var estimate = Estimate(gasLimit * 1000, storageLimit, 162, 250, fee); 174 | 175 | OperationModel transaction = new OperationModel( 176 | destination: contract, 177 | amount: amount.toString(), 178 | counter: counter, 179 | fee: estimate.suggestedFeeMutez.toString(), 180 | source: publicKeyHash, 181 | gasLimit: estimate.gasLimit, 182 | storageLimit: estimate.storageLimit, 183 | ); 184 | 185 | if (parameters != null) { 186 | if (parameterFormat == TezosParameterFormat.Michelson) { 187 | var michelineParams = 188 | TezosLanguageUtil.translateMichelsonToMicheline(parameters); 189 | transaction.parameters = { 190 | 'entrypoint': entrypoint.isEmpty ? 'default' : entrypoint, 191 | 'value': jsonDecode(michelineParams) 192 | }; 193 | } else if (parameterFormat == TezosParameterFormat.Micheline) { 194 | transaction.parameters = { 195 | 'entrypoint': entrypoint.isEmpty ? 'default' : entrypoint, 196 | 'value': jsonDecode(parameters) 197 | }; 198 | } else if (parameterFormat == TezosParameterFormat.MichelsonLambda) { 199 | var michelineLambda = 200 | TezosLanguageUtil.translateMichelsonToMicheline('code $parameters'); 201 | transaction.parameters = { 202 | 'entrypoint': entrypoint.isEmpty ? 'default' : entrypoint, 203 | 'value': jsonDecode(michelineLambda) 204 | }; 205 | } 206 | 207 | } else if (entrypoint != null) { 208 | transaction.parameters = {'entrypoint': entrypoint, 'value': []}; 209 | } 210 | return transaction; 211 | } 212 | 213 | static Future> appendRevealOperation( 214 | String server, 215 | String publicKey, 216 | String publicKeyHash, 217 | int accountOperationIndex, 218 | List operations) async { 219 | bool isKeyRevealed = await TezosNodeReader.isManagerKeyRevealedForAccount( 220 | server, publicKeyHash); 221 | var counter = accountOperationIndex + 1; 222 | if (!isKeyRevealed) { 223 | var revealOp = OperationModel( 224 | counter: counter, 225 | fee: '0', // Reveal Fee will be covered by the appended operation 226 | source: publicKeyHash, 227 | kind: 'reveal', 228 | gasLimit: TezosConstants.DefaultKeyRevealGasLimit, 229 | storageLimit: TezosConstants.DefaultKeyRevealStorageLimit, 230 | publicKey: publicKey, 231 | ); 232 | for (var index = 0; index < operations.length; index++) { 233 | var c = accountOperationIndex + 2 + index; 234 | operations[index].counter = c; 235 | } 236 | return [revealOp, ...operations]; 237 | } 238 | return operations; 239 | } 240 | 241 | static Future> sendOperation(String server, 242 | List operations, SoftSigner signer, int offset) async { 243 | var blockHead = await TezosNodeReader.getBlockAtOffset(server, offset); 244 | var blockHash = blockHead['hash'].toString().substring(0, 51); 245 | var forgedOperationGroup = forgeOperations(blockHash, operations); 246 | var opSignature = signer.signOperation(Uint8List.fromList(hex.decode( 247 | TezosConstants.OperationGroupWatermark + forgedOperationGroup))); 248 | var signedOpGroup = Uint8List.fromList( 249 | hex.decode(forgedOperationGroup) + opSignature.toList()); 250 | var base58signature = TezosMessageUtils.readSignatureWithHint( 251 | opSignature, signer.getSignerCurve()); 252 | var opPair = {'bytes': signedOpGroup, 'signature': base58signature}; 253 | var appliedOp = await preapplyOperation( 254 | server, blockHash, blockHead['protocol'], operations, opPair); 255 | var injectedOperation = await injectOperation(server, opPair); 256 | 257 | return {'appliedOp': appliedOp[0], 'operationGroupID': injectedOperation}; 258 | } 259 | 260 | static String forgeOperations( 261 | String branch, List operations) { 262 | String encoded = TezosMessageUtils.writeBranch(branch); 263 | operations.forEach((element) { 264 | encoded += TezosMessageCodec.encodeOperation(element); 265 | }); 266 | return encoded; 267 | } 268 | 269 | static preapplyOperation(String server, String branch, protocol, 270 | List operations, Map signedOpGroup, 271 | {String chainid = 'main'}) async { 272 | var payload = [ 273 | { 274 | 'protocol': protocol, 275 | 'branch': branch, 276 | 'contents': operations, 277 | 'signature': signedOpGroup['signature'] 278 | } 279 | ]; 280 | var response = await HttpHelper.performPostRequest(server, 281 | 'chains/$chainid/blocks/head/helpers/preapply/operations', payload); 282 | var json; 283 | try { 284 | json = jsonDecode(response); 285 | } catch (err) { 286 | throw new Exception( 287 | 'Could not parse JSON from response of chains/$chainid/blocks/head/helpers/preapply/operation: $response for $payload'); 288 | } 289 | parseRPCError(jsonDecode(response)); 290 | return json; 291 | } 292 | 293 | static void parseRPCError(json) { 294 | var errors = ''; 295 | try { 296 | var arr = json is List ? json : [json]; 297 | if (json[0]['kind'] != null) { 298 | errors = arr.fold( 299 | '', 300 | (previousValue, element) => 301 | '$previousValue${element['kind']} : ${element['id']}, '); 302 | } else if (arr.length == 1 && 303 | arr[0]['contents'].length == 1 && 304 | arr[0]['contents'][0]['kind'].toString() == "activate_account") { 305 | } else {} 306 | } catch (err) { 307 | if (json.toString().startsWith('Failed to parse the request body: ')) { 308 | errors = json.toString().toString().substring(34); 309 | } else { 310 | var hash = json 311 | .toString() 312 | .replaceFirst('/\"/g', "'") 313 | .replaceFirst('/\n/', "'"); 314 | if (hash.length == 51 && hash[0] == 'o') { 315 | } else { 316 | print( 317 | "failed to parse errors: '$err' from '${json.toString()}'\n, PLEASE report this to the maintainers"); 318 | } 319 | } 320 | } 321 | 322 | if (errors.length > 0) { 323 | print('errors found in response:\n$json'); 324 | throw Exception( 325 | "Status code ==> 200\nResponse ==> $json \n Error ==> $errors"); 326 | } 327 | } 328 | 329 | static String parseRPCOperationResult(result) { 330 | if (result.status == 'failed') { 331 | return "${result.status}: ${result.errors.map((e) => '(${e.kind}: ${e.id})').join(', ')}"; 332 | } else if (result.status == 'applied') { 333 | return ''; 334 | } else { 335 | return result.status; 336 | } 337 | } 338 | 339 | static injectOperation(String server, Map opPair, 340 | {chainid = 'main'}) async { 341 | var response = await HttpHelper.performPostRequest(server, 342 | 'injection/operation?chain=$chainid', hex.encode(opPair['bytes'])); 343 | response = response.toString().replaceAll('"', ''); 344 | return response; 345 | } 346 | 347 | static OperationModel constructContractOriginationOperation( 348 | KeyStoreModel keyStore, 349 | int amount, 350 | String delegate, 351 | int fee, 352 | int storageLimit, 353 | int gasLimit, 354 | String code, 355 | String storage, 356 | TezosParameterFormat codeFormat, 357 | int counter) { 358 | var parsedCode; 359 | var parsedStorage; 360 | if (codeFormat == TezosParameterFormat.Michelson) { 361 | parsedCode = 362 | jsonDecode(TezosLanguageUtil.translateMichelsonToMicheline(code)); 363 | parsedStorage = 364 | jsonDecode(TezosLanguageUtil.translateMichelsonToMicheline(storage)); 365 | } else if (codeFormat == TezosParameterFormat.Micheline) { 366 | parsedCode = jsonDecode(code); 367 | parsedStorage = jsonDecode(storage); 368 | } 369 | var estimate = Estimate(gasLimit * 1000, storageLimit, 162, 250, fee); 370 | 371 | return OperationModel( 372 | kind: 'origination', 373 | source: keyStore.publicKeyHash, 374 | fee: estimate.suggestedFeeMutez.toString(), 375 | counter: counter, 376 | gasLimit: estimate.gasLimit, 377 | storageLimit: estimate.storageLimit, 378 | amount: amount.toString(), 379 | delegate: delegate, 380 | script: { 381 | 'code': parsedCode, 382 | 'storage': parsedStorage, 383 | }, 384 | ); 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tezster_dart 2 | 3 | [![Star on GitHub](https://img.shields.io/github/stars/Tezsure/tezster_dart?style=flat&logo=github&colorB=green&label=stars)](https://github.com/Tezsure/tezster_dart) 4 | [![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT) 5 | [![Github issues](https://img.shields.io/github/issues/Tezsure/tezster_dart)](https://github.com/Tezsure/tezster_dart/issues?q=is%3Aissue+is%3Aopen+) 6 | 7 | [![Tezster banner](https://tezster.s3-ap-southeast-1.amazonaws.com/TEZSTER_CLI/1_jDB5enULQVo2UfeiwD32qA.png)](https://github.com/Tezsure) 8 | A library for building decentralized applications in Flutter, currently focused on the Tezos platform. Tezster_dart package contains all the function that is required to build tezos application. 9 | 10 | ## What is Tezos 11 | 12 | Tezos is a decentralized blockchain that governs itself by establishing a true digital commonwealth. It facilitates formal verification, a technique which mathematically proves the correctness of the code governing transactions and boosts the security of the most sensitive or financially weighted smart contracts. 13 | 14 | ### Features 15 | 16 | * Tezos wallet utilities. 17 | * Get Balance. 18 | * Generate mnemonics. 19 | * Generate keys from mnemonic. 20 | * Generate keys from mnemonics and passphrase. 21 | * Sign Operation Group. 22 | * Unlock fundraiser identity. 23 | * Transfer Balance. 24 | * Delegate an Account. 25 | * Deploy a contract. 26 | * Call a contract. 27 | * Operation confirmation. 28 | * Activating a fundraiser account 29 | * Reveal an account 30 | 31 | ### Getting started 32 | 33 | Check out the [example](https://github.com/Tezsure/tezster_dart/tree/master/example) directory for a sample app for using Tezster_dart. 34 | 35 | ### Import using 36 | 37 | ``` dart 38 | import 'package:tezster_dart/tezster_dart.dart'; 39 | ``` 40 | 41 | ### Usage 42 | 43 | * Get Balance 44 | 45 | ``` dart 46 | String balance = await TezsterDart.getBalance('tz1c....ozGGs', 'your rpc server'); 47 | ``` 48 | 49 | * Generate mnemonic 50 | 51 | ``` dart 52 | String mnemonic = TezsterDart.generateMnemonic(); // sustain laugh capital drop brush artist ahead blossom bread spring motor other mountain thumb volcano engine shed guilt famous loud force hundred same brave 53 | ``` 54 | 55 | * Generate keys from mnemonic 56 | 57 | ``` dart 58 | List keys = await TezsterDart.getKeysFromMnemonic(mnemonic: "Your Mnemonic"); 59 | 60 | /* [edskRdVS5H9YCRAG8yqZkX2nUTbGcaDqjYgopkJwRuPUnYzCn3t9ZGksncTLYe33bFjq29pRhpvjQizCCzmugMGhJiXezixvdC, 61 | edpkuLog552hecagkykJ3fTvop6grTMhfZY4TWbvchDWdYyxCHcrQL, 62 | tz1g85oYHLFKDpNfDHPeBUbi3S7pUsgCB28q] */ 63 | ``` 64 | 65 | * Create / Unlock identity from mnemonic and passphrase. 66 | 67 | ``` dart 68 | List identityWithMnemonic = await TezsterDart.getKeysFromMnemonicAndPassphrase( 69 | mnemonic: "your mnemonic", 70 | passphrase: "pa$\$w0rd"); 71 | 72 | /* [edskS9kdgvCWDiZL1yP1qH5xLCWYHQub4qibfU8DQZjv7wX7BskxSsL6h9j1yDYJ7Y9jDbMULNmfLhw9vBJPqDw3TeVHHd34w7, 73 | edpkuRr9yHChSt2MTWHCeHe2JM3zJZxHgj8vEANwb8WENrZbLxYzbx, 74 | tz1hTe7oxtQr67dg6dWfTX3V44oPY7pzkFZS] */ 75 | ``` 76 | 77 | * Sign operation with private key and forged operation 78 | 79 | ``` dart 80 | List signOperationGroup = await TezsterDart.signOperationGroup( 81 | privateKey: "edskRdV..... .XezixvdA", 82 | forgedOperation: "713cb068fe.... .b940ee"); 83 | 84 | /* [edsigtrBnsjSngfP6LULUDeo84eJVks4LWReYrZBUjKQNJjhVsG7bksqZ7CKnRePMceMe3vgRHHbyd2CqRdC8iEAK5NcyNn4iEB, 85 | 713cb068fe3ac078351727eb5c34279e22b75b0cf4dc0a8d3d599e27031db136040cb9f9da085607c05cac1ca4c62a3f3cfb 86 | 8146aa9b7f631e52f877a1d363474404da8130b0b940ee8c7ce5bf2968c1204c1c4b2ba98bcbd08fc4ad3cad706d39ac55e4 87 | dd61fde5a8496840ce2d377389a4ca7842bf613d3f096fda819c26e43adfb0cad1336a430d] */ 88 | ``` 89 | 90 | * Unlock fundraiser identity. 91 | 92 | ``` dart 93 | List identityFundraiser = await TezsterDart.unlockFundraiserIdentity( 94 | mnemonic: "your mnemonic", 95 | email: "test@example.com", 96 | password: "pa$\$w0rd"); 97 | 98 | /* [edskRzNDm2dpqe2yd5zYAw1vmjr8sAwMubfcXajxdCNNr4Ud39BoppeqMAzoCPmb14mzfXRhjtydQjCbqU2VzWrsq6JP4D9GVb, 99 | edpkvASxrq16v5Awxpz4XPTA2d6QFaCL8expPrPNcVgVbWxT84Kdw2, 100 | tz1hhkSbaocSWm3wawZUuUdX57L3maSH16Pv] */ 101 | ``` 102 | 103 | * Transfer Balance. 104 | * The most basic operation on the chain is the transfer of value between two accounts. In this example we have the account we activated above: tz1QSHaKpTFhgHLbqinyYRjxD5sLcbfbzhxy and some random testnet address to test with: tz1RVcUP9nUurgEJMDou8eW3bVDs6qmP5Lnc. Note all amounts are in µtz, as in micro-tez, hence 0.5tz is represented as 500000. The fee of 1500 was chosen arbitrarily, but some operations have minimum fee requirements. 105 | 106 | ``` dart 107 | var server = ''; 108 | 109 | var keyStore = KeyStoreModel( 110 | publicKey: 'edpkvQtuhdZQmjdjVfaY9Kf4hHfrRJYugaJErkCGvV3ER1S7XWsrrj', 111 | secretKey: 112 | 'edskRgu8wHxjwayvnmpLDDijzD3VZDoAH7ZLqJWuG4zg7LbxmSWZWhtkSyM5Uby41rGfsBGk4iPKWHSDniFyCRv3j7YFCknyHH', 113 | publicKeyHash: 'tz1QSHaKpTFhgHLbqinyYRjxD5sLcbfbzhxy', 114 | ); 115 | 116 | var signer = await TezsterDart.createSigner( 117 | TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk')); 118 | 119 | var result = await TezsterDart.sendTransactionOperation( 120 | server, 121 | signer, 122 | keyStore, 123 | 'tz1RVcUP9nUurgEJMDou8eW3bVDs6qmP5Lnc', 124 | 500000, 125 | 1500, 126 | ); 127 | 128 | print("Applied operation ===> $result['appliedOp']"); 129 | print("Operation groupID ===> $result['operationGroupID']"); 130 | 131 | ``` 132 | 133 | * Delegate an Account. 134 | * One of the most exciting features of Tezos is delegation. This is a means for non-"baker" (non-validator) accounts to participate in the on-chain governance process and receive staking rewards. It is possible to delegate both implicit and originated accounts. For implicit addresses, those starting with tz1, tz2 and tz3, simply call sendDelegationOperation. Originated accounts, that is smart contracts, must explicitly support delegate assignment, but can also be deployed with a delegate already set. 135 | 136 | ``` dart 137 | var server = ''; 138 | 139 | var keyStore = KeyStoreModel( 140 | publicKey: 'edpkvQtuhdZQmjdjVfaY9Kf4hHfrRJYugaJErkCGvV3ER1S7XWsrrj', 141 | secretKey: 142 | 'edskRgu8wHxjwayvnmpLDDijzD3VZDoAH7ZLqJWuG4zg7LbxmSWZWhtkSyM5Uby41rGfsBGk4iPKWHSDniFyCRv3j7YFCknyHH', 143 | publicKeyHash: 'tz1QSHaKpTFhgHLbqinyYRjxD5sLcbfbzhxy', 144 | ); 145 | 146 | var signer = await TezsterDart.createSigner( 147 | TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk')); 148 | 149 | var result = await TezsterDart.sendDelegationOperation( 150 | server, 151 | signer, 152 | keyStore, 153 | 'tz1RVcUP9nUurgEJMDou8eW3bVDs6qmP5Lnc', 154 | 10000, 155 | ); 156 | 157 | print("Applied operation ===> $result['appliedOp']"); 158 | print("Operation groupID ===> $result['operationGroupID']"); 159 | 160 | ``` 161 | 162 | * Deploy a contract. 163 | * With this release we are excited to include the feature of trestles chain interactions, including contract deployment a user can directly write smart contracts in Michelson language and deploy it on Tezos chain using the `sendContractOriginationOperation()` method in return you'll get an origination id of the deployed contract that can be use to track the contract on chain. We have set an example for you below. 164 | 165 | ``` dart 166 | var server = ''; 167 | 168 | var contract = """parameter string; 169 | storage string; 170 | code { DUP; 171 | DIP { CDR ; NIL string ; SWAP ; CONS } ; 172 | CAR ; CONS ; 173 | CONCAT; 174 | NIL operation; PAIR}"""; 175 | 176 | var storage = '"Sample"'; 177 | 178 | var keyStore = KeyStoreModel( 179 | publicKey: 'edpkvQtuhdZQmjdjVfaY9Kf4hHfrRJYugaJErkCGvV3ER1S7XWsrrj', 180 | secretKey: 181 | 'edskRgu8wHxjwayvnmpLDDijzD3VZDoAH7ZLqJWuG4zg7LbxmSWZWhtkSyM5Uby41rGfsBGk4iPKWHSDniFyCRv3j7YFCknyHH', 182 | publicKeyHash: 'tz1QSHaKpTFhgHLbqinyYRjxD5sLcbfbzhxy', 183 | ); 184 | 185 | var signer = await TezsterDart.createSigner( 186 | TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk')); 187 | 188 | var result = await TezsterDart.sendContractOriginationOperation( 189 | server, 190 | signer, 191 | keyStore, 192 | 0, 193 | null, 194 | 100000, 195 | 1000, 196 | 100000, 197 | contract, 198 | storage, 199 | codeFormat: TezosParameterFormat.Michelson, 200 | ); 201 | 202 | print("Operation groupID ===> $result['operationGroupID']"); 203 | 204 | ``` 205 | reference link: `https://github.com/Tezsure/Tezster_dart/blob/master/example/lib/main.dart#L110` 206 |
207 | 208 | * Call a contract. 209 | * We have also included the feature to call or invoke a deployed contract just use the inbuilt `sendContractInvocationOperation()` method in return you'll get an origination id of the invoked contract that can be used to track the contracts on chain. We have set an example for you below. 210 | 211 | ``` dart 212 | var server = ''; 213 | 214 | var keyStore = KeyStoreModel( 215 | publicKey: 'edpkvQtuhdZQmjdjVfaY9Kf4hHfrRJYugaJErkCGvV3ER1S7XWsrrj', 216 | secretKey: 217 | 'edskRgu8wHxjwayvnmpLDDijzD3VZDoAH7ZLqJWuG4zg7LbxmSWZWhtkSyM5Uby41rGfsBGk4iPKWHSDniFyCRv3j7YFCknyHH', 218 | publicKeyHash: 'tz1QSHaKpTFhgHLbqinyYRjxD5sLcbfbzhxy', 219 | ); 220 | 221 | var signer = await TezsterDart.createSigner( 222 | TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk')); 223 | 224 | var contractAddress = 'KT1KA7DqFjShLC4CPtChPX8QtRYECUb99xMY'; 225 | 226 | var resultInvoke = await TezsterDart.sendContractInvocationOperation( 227 | server, 228 | signer, 229 | keyStore, 230 | contractAddress, 231 | 10000, 232 | 100000, 233 | 1000, 234 | 100000, 235 | '', 236 | '"Cryptonomicon"', 237 | codeFormat: TezosParameterFormat.Michelson); 238 | 239 | print("Operation groupID ===> $result['operationGroupID']"); 240 | 241 | ``` 242 | reference link: `https://github.com/Tezsure/Tezster_dart/blob/master/example/lib/main.dart#L141` 243 |
244 | 245 | * Operation confirmation. 246 | * No wonder it's really important to await for confirmation for any on chain interactions. Hence, we have provided `awaitOperationConfirmation()` method with this release that developers can leverage for their advantage to confirm the originated contract's operations id. We have set an example for you how to use it. 247 | 248 | ``` dart 249 | var server = ''; 250 | 251 | var network = 'carthagenet'; 252 | 253 | var serverInfo = { 254 | 'url': '', 255 | 'apiKey': '', 256 | 'network': network 257 | }; 258 | 259 | var contract = """parameter string; 260 | storage string; 261 | code { DUP; 262 | DIP { CDR ; NIL string ; SWAP ; CONS } ; 263 | CAR ; CONS ; 264 | CONCAT; 265 | NIL operation; PAIR}"""; 266 | 267 | var storage = '"Sample"'; 268 | 269 | var keyStore = KeyStoreModel( 270 | publicKey: 'edpkvQtuhdZQmjdjVfaY9Kf4hHfrRJYugaJErkCGvV3ER1S7XWsrrj', 271 | secretKey: 272 | 'edskRgu8wHxjwayvnmpLDDijzD3VZDoAH7ZLqJWuG4zg7LbxmSWZWhtkSyM5Uby41rGfsBGk4iPKWHSDniFyCRv3j7YFCknyHH', 273 | publicKeyHash: 'tz1QSHaKpTFhgHLbqinyYRjxD5sLcbfbzhxy', 274 | ); 275 | 276 | var signer = await TezsterDart.createSigner( 277 | TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk')); 278 | 279 | var result = await TezsterDart.sendContractOriginationOperation( 280 | server, 281 | signer, 282 | keyStore, 283 | 0, 284 | null, 285 | 100000, 286 | 1000, 287 | 100000, 288 | contract, 289 | storage, 290 | codeFormat: TezosParameterFormat.Michelson, 291 | ); 292 | 293 | print("Operation groupID ===> $result['operationGroupID']"); 294 | 295 | var groupId = result['operationGroupID']; 296 | 297 | var conseilResult = await TezsterDart.awaitOperationConfirmation( 298 | serverInfo, network, groupId, 5); 299 | 300 | print('Originated contract at ${conseilResult['originated_contracts']}'); 301 | 302 | ``` 303 | reference link: `https://github.com/Tezsure/Tezster_dart/blob/master/example/lib/main.dart#L162` 304 |
305 | 306 | * Activating a fundraiser account 307 | * A fundraiser account needs to be activated to be used for any operation. Hence, we have included the facility to activate a faucet account. All the user has to do is call the `sendIdentityActivationOperation()` method and viola the faucet or a fundraiser account will be activated. We have set an example for you how to use it. 308 | 309 | ``` dart 310 | var server = ''; 311 | 312 | var faucetKeyStore = KeyStoreModel( 313 | publicKeyHash: 'tz1ga.....trZNA6A', 314 | seed: [ 315 | "wife", 316 | "filter", 317 | "wage", 318 | "thunder", 319 | "forget", 320 | "scale", 321 | "punch", 322 | "mammal", 323 | "offer", 324 | "car", 325 | "cash", 326 | "defy", 327 | "vehicle", 328 | "romance", 329 | "green" 330 | ], 331 | secret: '', 332 | email: '', 333 | password: '', 334 | ); 335 | 336 | var keys = await TezsterDart.unlockFundraiserIdentity( 337 | email: faucetKeyStore.email, 338 | passphrase: faucetKeyStore.password, 339 | mnemonic: faucetKeyStore.seed.join(' ')); 340 | 341 | faucetKeyStore 342 | ..publicKey = keys[1] 343 | ..secretKey = keys[0] 344 | ..publicKeyHash = keys[2]; 345 | 346 | var activationOperationSigner = await TezsterDart.createSigner( 347 | TezsterDart.writeKeyWithHint(faucetKeyStore.secretKey, 'edsk')); 348 | 349 | var activationOperationResult = 350 | await TezsterDart.sendIdentityActivationOperation(server, 351 | activationOperationSigner, faucetKeyStore, faucetKeyStore.secret); 352 | 353 | print('${activationOperationResult['operationGroupID']}'); 354 | ``` 355 |
356 | 357 | * Reveal an account 358 | * Once a fundraiser account has been activated it needs to be revealed on-chain. Hence, we have included the facility to reveal the faucet/fundraiser account all you have to do is call the `sendKeyRevealOperation()` method, and voila it’s revealed. We have set an example for you how to use it. 359 | 360 | ``` dart 361 | var server = ''; 362 | 363 | var keyStore = KeyStoreModel( 364 | publicKeyHash: 'tz1U.....W5MHgi', 365 | secretKey: 366 | 'edskRp......bL2B6g', 367 | publicKey: 'edpktt.....U1gYJu2', 368 | ); 369 | 370 | var signer = await TezsterDart.createSigner( 371 | TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk')); 372 | 373 | var result = 374 | await TezsterDart.sendKeyRevealOperation(server, signer, keyStore); 375 | 376 | print('${result['operationGroupID']}'); 377 | ``` 378 |
379 | 380 | --- 381 | **NOTE:** 382 | Use stable version of flutter to avoid package conflicts. 383 | 384 | --- 385 | 386 | ### Feature requests and bugs 387 | 388 | Please file feature requests and bugs at the [issue tracker](https://github.com/Tezsure/tezster_dart/issues/new). If you want to contribute to this libary, please submit a Pull Request. -------------------------------------------------------------------------------- /lib/michelson_parser/parser/nearley.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:math'; 3 | 4 | import 'package:tezster_dart/michelson_parser/grammar/michelson_grammar_tokenizer.dart'; 5 | 6 | var fail = {}; 7 | 8 | class Nearley { 9 | NearleyGrammar grammar; 10 | var options; 11 | 12 | var lexer; 13 | var lexerState; 14 | var table; 15 | 16 | int current; 17 | var results; 18 | 19 | static NearleyGrammar fromCompiled(Map rules, {start}) { 20 | var lexer = rules['Lexer']; 21 | start = rules['ParserStart']; 22 | var redefinedRules = rules['ParserRules'].toList(); 23 | var _rules = redefinedRules.map((r) { 24 | return (new Rule(r['name'], r['symbols'], r['postprocess'])); 25 | }).toList(); 26 | var g = NearleyGrammar(_rules, start); 27 | g.lexer = lexer; 28 | return g; 29 | } 30 | 31 | void parser(NearleyGrammar rules, {start, options}) { 32 | grammar = rules; 33 | 34 | options = { 35 | 'keepHistory': false, 36 | 'lexer': grammar.lexer ?? new StreamLexer(), 37 | }; 38 | 39 | lexer = options['lexer']; 40 | lexerState = null; 41 | 42 | var column = new Column(grammar, 0); 43 | column.wants[grammar.start] = []; 44 | column.predict(grammar.start); 45 | column.process(); 46 | table = [column]; 47 | current = 0; 48 | } 49 | 50 | feed(chunk) { 51 | var lexer = this.lexer; 52 | lexer.tokens = []; 53 | lexer.numindex = 0; 54 | lexer.reset(chunk); 55 | var token = lexer.next(); 56 | var column; 57 | while (token != null) { 58 | column = table[current]; 59 | 60 | if (current != 0) table[current - 1] = null; 61 | 62 | var n = current + 1; 63 | var nextColumn = new Column(grammar, n); 64 | 65 | var literal = token['text'] != null ? token['text'] : token['value']; 66 | var value = lexer is StreamLexer ? token['value'] : token; 67 | var scannable = column.scannable; 68 | for (var w = scannable.length - 1; 0 <= w; w--) { 69 | var state = scannable[w]; 70 | var expect = state.rule.symbols[state.dot]; 71 | 72 | if (expect is RegExp 73 | ? expect.hasMatch(value['value']) 74 | : expect['type'] != null 75 | ? expect['type'] == token['type'] 76 | : expect['literal'] == literal) { 77 | var next = state.nextState({ 78 | 'data': value, 79 | 'token': token, 80 | 'isToken': true, 81 | 'reference': n - 1 82 | }); 83 | nextColumn.states.add(next); 84 | } 85 | } 86 | 87 | nextColumn.process(); 88 | table.add(nextColumn); 89 | 90 | if (nextColumn.states.length == 0) { 91 | var err = new NearleyError(reportError(token)); 92 | err.offset = this.current; 93 | err.token = token; 94 | throw Exception(err.error); 95 | } 96 | 97 | token = lexer.next(); 98 | current++; 99 | } 100 | 101 | if (column != null) { 102 | this.lexerState = lexer.save(); 103 | } 104 | 105 | results = this.finish(); 106 | 107 | return this; 108 | } 109 | 110 | finish() { 111 | var considerations = []; 112 | var start = this.grammar.start; 113 | var column = this.table[this.table.length - 1]; 114 | column.states.forEach((t) { 115 | if (t.rule.name == start && 116 | t.dot == t.rule.symbols.length && 117 | t.reference == 0 && 118 | t.data != fail) { 119 | considerations.add(t); 120 | } 121 | }); 122 | return considerations.map((c) { 123 | return c.data; 124 | }).toList(); 125 | } 126 | 127 | reportError(token) { 128 | var lines = []; 129 | var tokenDisplay = 130 | (token['type'] != null ? token['type'] + " token: " : "") + 131 | jsonEncode(token['value'] != null ? token['value'] : token); 132 | lines.add(this.lexer.formatError(token, "Syntax error")); 133 | lines.add('Unexpected ' + 134 | tokenDisplay + 135 | '. Instead, I was expecting to see one of the following:\n'); 136 | var lastColumnIndex = this.table.length - 2; 137 | var lastColumn = this.table[lastColumnIndex]; 138 | var expectantStates = lastColumn.states.where((state) { 139 | if (state.rule.symbols.isNotEmpty) { 140 | var nextSymbol = 141 | state.rule.symbols[state.dot - 1 < 0 ? 0 : state.dot - 1]; 142 | return nextSymbol != null && !(nextSymbol is String); 143 | } else { 144 | return false; 145 | } 146 | }).toList(); 147 | 148 | var stateStacks = expectantStates.map((state) { 149 | return this.buildFirstStateStack(state, []); 150 | }).toList(); 151 | 152 | stateStacks.forEach((stateStack) { 153 | var state = stateStack[0]; 154 | var nextSymbol = 155 | state.rule.symbols[state.dot - 1 < 0 ? 0 : state.dot - 1]; 156 | var symbolDisplay = this.getSymbolDisplay(nextSymbol); 157 | lines.add('A ' + symbolDisplay + ' based on:'); 158 | this.displayStateStack(stateStack, lines); 159 | }); 160 | 161 | lines.add(""); 162 | return lines.join("\n"); 163 | } 164 | 165 | String reportLexerError(lexerError) { 166 | var tokenDisplay, lexerMessage; 167 | 168 | var token = lexerError.token; 169 | if (token) { 170 | tokenDisplay = "input " + jsonEncode(token.text[0]) + " (lexer error)"; 171 | lexerMessage = this.lexer.formatError(token, "Syntax error"); 172 | } else { 173 | tokenDisplay = "input (lexer error)"; 174 | lexerMessage = lexerError.message; 175 | } 176 | return this.reportErrorCommon(lexerMessage, tokenDisplay); 177 | } 178 | 179 | reportErrorCommon(lexerMessage, tokenDisplay) { 180 | var lines = []; 181 | lines.add(lexerMessage); 182 | var lastColumnIndex = this.table.length - 1; 183 | var lastColumn = this.table[lastColumnIndex]; 184 | var expectantStates = lastColumn.states.where((state) { 185 | var nextSymbol = state.rule.symbols[state.dot] ?? null; 186 | return nextSymbol != null && !(nextSymbol is String); 187 | }).toList(); 188 | 189 | if (expectantStates.length == 0) { 190 | lines.add('Unexpected ' + 191 | tokenDisplay + 192 | '. I did not expect any more input. Here is the state of my parse table:\n'); 193 | this.displayStateStack(lastColumn.states, lines); 194 | } else { 195 | lines.add('Unexpected ' + 196 | tokenDisplay + 197 | '. Instead, I was expecting to see one of the following:\n'); 198 | 199 | var stateStacks = expectantStates.map((state) { 200 | return this.buildFirstStateStack(state, []) ?? [state]; 201 | }); 202 | 203 | stateStacks.forEach((stateStack) { 204 | var state = stateStack[0]; 205 | var nextSymbol = state.rule.symbols[state.dot]; 206 | var symbolDisplay = this.getSymbolDisplay(nextSymbol); 207 | lines.add('A ' + symbolDisplay + ' based on:'); 208 | this.displayStateStack(stateStack, lines); 209 | }); 210 | } 211 | lines.add(""); 212 | return lines.join("\n"); 213 | } 214 | 215 | buildFirstStateStack(state, visited) { 216 | if (visited.indexOf(state) != -1) { 217 | return null; 218 | } 219 | if (state.wantedBy.length == 0) { 220 | return [state]; 221 | } 222 | var prevState = state.wantedBy[0]; 223 | var childVisited = [state]..addAll(visited); 224 | var childResult = this.buildFirstStateStack(prevState, childVisited); 225 | if (childResult == null) { 226 | return null; 227 | } 228 | return [state]..addAll(childResult); 229 | } 230 | 231 | displayStateStack(stateStack, lines) { 232 | var lastDisplay; 233 | var sameDisplayCount = 0; 234 | for (var j = 0; j < stateStack.length; j++) { 235 | var state = stateStack[j]; 236 | var display = state.rule.toStringWithData(state.dot); 237 | if (display == lastDisplay) { 238 | sameDisplayCount++; 239 | } else { 240 | if (sameDisplayCount > 0) { 241 | lines.add(' ^ ' + 242 | sameDisplayCount.toString() + 243 | ' more lines identical to this'); 244 | } 245 | sameDisplayCount = 0; 246 | lines.add(' ' + display); 247 | } 248 | lastDisplay = display; 249 | } 250 | } 251 | 252 | getSymbolDisplay(symbol) { 253 | return getSymbolLongDisplay(symbol); 254 | } 255 | 256 | getSymbolLongDisplay(symbol) { 257 | if (symbol is String) { 258 | return symbol; 259 | } else if (symbol is Map) { 260 | return jsonEncode(symbol['literal'] ?? ''); 261 | } else if (symbol is RegExp) { 262 | return 'character matching ' + symbol.toString(); 263 | } else if (symbol['type'] != null) { 264 | return symbol['type'] + ' token'; 265 | } else if (symbol['test'] ?? null != null) { 266 | return 'token matching ' + symbol['test'].toString(); 267 | } else { 268 | throw new Exception('Unknown symbol type: ' + symbol.toString()); 269 | } 270 | } 271 | } 272 | 273 | class Column { 274 | NearleyGrammar grammar; 275 | int index; 276 | 277 | List states; 278 | Map wants; 279 | List scannable; 280 | Map completed; 281 | 282 | Column(this.grammar, this.index) { 283 | states = []; 284 | wants = {}; 285 | scannable = []; 286 | completed = {}; 287 | } 288 | 289 | void predict(exp) { 290 | var rules = grammar.byName[exp] ?? []; 291 | for (var i = 0; i < rules.length; i++) { 292 | var r = rules[i]; 293 | var wantBy = wants[exp]; 294 | var s = new ColumnState(r, 0, index, wantBy); 295 | states.add(s); 296 | } 297 | } 298 | 299 | void process() { 300 | var _states = this.states; 301 | var _wants = this.wants; 302 | var _completed = this.completed; 303 | 304 | for (var w = 0; w < _states.length; w++) { 305 | var state = _states[w]; 306 | if (state.isComplete) { 307 | state.finish(); 308 | if (state.data != fail) { 309 | var wantBy = state.wantedBy; 310 | 311 | for (var i = wantBy.length - 1; 0 <= i; i--) { 312 | var left = wantBy[i]; 313 | this.complete(left, state); 314 | } 315 | 316 | if (state.reference == index) { 317 | var exp = state.rule.name; 318 | this.completed[exp] = this.completed[exp] ?? []; 319 | this.completed[exp].add(state); 320 | } 321 | } 322 | } else { 323 | var exp = state.rule.symbols[state.dot]; 324 | if (!(exp is String)) { 325 | this.scannable.add(state); 326 | } 327 | 328 | if (_wants[exp] != null) { 329 | _wants[exp].add(state); 330 | if (_completed.containsKey(exp)) { 331 | var nulls = _completed[exp]; 332 | for (var i = 0; i < nulls.length; i++) { 333 | var right = nulls[i]; 334 | complete(state, right); 335 | } 336 | } 337 | } else { 338 | _wants[exp] = _wants[exp] is List ? _wants[exp].add(state) : [state]; 339 | this.predict(exp); 340 | } 341 | } 342 | } 343 | } 344 | 345 | void complete(left, right) { 346 | var copy = left.nextState(right); 347 | states.add(copy); 348 | } 349 | } 350 | 351 | class ColumnState { 352 | Rule rule; 353 | var dot; 354 | var reference; 355 | var data; 356 | var wantedBy; 357 | var isComplete; 358 | 359 | ColumnState left; 360 | var right; 361 | 362 | ColumnState(this.rule, this.dot, this.reference, this.wantedBy) { 363 | data = []; 364 | isComplete = dot == rule.symbols.length; 365 | } 366 | 367 | void finish() { 368 | if (rule.postprocess != null) { 369 | try { 370 | data = rule.postprocess(data); 371 | } catch (e) { 372 | print("Error In ===> " + rule.name); 373 | } 374 | } 375 | } 376 | 377 | ColumnState nextState(child) { 378 | var state = new ColumnState(rule, dot + 1, reference, wantedBy); 379 | state.left = this; 380 | state.right = child; 381 | if (state.isComplete) { 382 | state.data = state.build(); 383 | } 384 | return state; 385 | } 386 | 387 | List build() { 388 | var children = []; 389 | var node = this; 390 | do { 391 | children.add( 392 | node.right is ColumnState ? node.right.data : node.right['data']); 393 | node = node.left; 394 | } while (node.left != null); 395 | children = children.reversed.toList(); 396 | return children; 397 | } 398 | } 399 | 400 | class NearleyError { 401 | var error; 402 | int offset; 403 | var token; 404 | NearleyError(this.error); 405 | } 406 | 407 | class NearleyGrammar { 408 | List rules; 409 | var start; 410 | 411 | var lexer; 412 | var byName; 413 | 414 | NearleyGrammar(this.rules, this.start) { 415 | this.rules = rules; 416 | this.start = start ?? this.rules[0].name; 417 | var byName = this.byName = {}; 418 | this.rules.forEach((rule) { 419 | if (!byName.containsKey(rule.name)) { 420 | byName[rule.name] = []; 421 | } 422 | byName[rule.name].add(rule); 423 | }); 424 | } 425 | } 426 | 427 | class Rule { 428 | var name; 429 | var symbols; 430 | var postprocess; 431 | Rule(this.name, this.symbols, this.postprocess); 432 | 433 | toStringWithData(withCursorAt) { 434 | stringifySymbolSequence(e) { 435 | return e is Map && e.containsKey('literal') 436 | ? jsonEncode(e['literal']) 437 | : e is Map && e.containsKey('type') 438 | ? '%' + e['type'] 439 | : e.toString(); 440 | } 441 | 442 | var symbolSequence = (withCursorAt == null) 443 | ? this.symbols.map(stringifySymbolSequence).join(' ') 444 | : (this 445 | .symbols 446 | .take(withCursorAt - 1 < 0 ? 0 : withCursorAt - 1) 447 | .map(stringifySymbolSequence) 448 | .join(' ') + 449 | " ● " + 450 | this 451 | .symbols 452 | .take(withCursorAt) 453 | .map(stringifySymbolSequence) 454 | .join(' ')); 455 | return this.name + " → " + symbolSequence; 456 | } 457 | } 458 | 459 | class StreamLexer { 460 | var buffer; 461 | int index; 462 | var line; 463 | var lastLineBreak; 464 | 465 | StreamLexer() { 466 | this.reset(""); 467 | } 468 | 469 | has(tokenType) { 470 | return true; 471 | } 472 | 473 | reset(data, {state}) { 474 | this.buffer = data; 475 | this.index = 0; 476 | this.line = state != null ? state.line : 1; 477 | this.lastLineBreak = state != null ? -state.col : 0; 478 | } 479 | 480 | save() { 481 | return { 482 | 'line': this.line, 483 | 'col': this.index - this.lastLineBreak, 484 | }; 485 | } 486 | 487 | formatError(token, message) { 488 | pad(n, length) { 489 | var s = (n).toString(); 490 | return List.generate(length - s.length + 1, (index) => '').join(" ") + s; 491 | } 492 | 493 | var buffer = this.buffer; 494 | if (buffer is String) { 495 | var lines = buffer.split("\n").sublist(max(0, this.line - 5), this.line); 496 | 497 | var nextLineBreak = buffer.indexOf('\n', this.index); 498 | if (nextLineBreak == -1) nextLineBreak = buffer.length; 499 | var col = this.index - this.lastLineBreak; 500 | var lastLineDigits = (this.line).toString().length; 501 | message += " at line " + 502 | this.line.toString() + 503 | " col " + 504 | col.toString() + 505 | ":\n\n"; 506 | var msg = List(); 507 | for (var i = 0; i < lines.length; i++) { 508 | var line = lines[i]; 509 | msg.add( 510 | pad(this.line - lines.length + i + 1, lastLineDigits) + " " + line); 511 | } 512 | message += msg.join("\n"); 513 | message += "\n" + pad("", lastLineDigits + col) + "^\n"; 514 | return message; 515 | } else { 516 | return message + " at index " + (this.index - 1); 517 | } 518 | } 519 | } 520 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 97C146F11CF9000F007C117D /* Supporting Files */, 94 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 95 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 96 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 97 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 98 | ); 99 | path = Runner; 100 | sourceTree = ""; 101 | }; 102 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | ); 106 | name = "Supporting Files"; 107 | sourceTree = ""; 108 | }; 109 | /* End PBXGroup section */ 110 | 111 | /* Begin PBXNativeTarget section */ 112 | 97C146ED1CF9000F007C117D /* Runner */ = { 113 | isa = PBXNativeTarget; 114 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 115 | buildPhases = ( 116 | 9740EEB61CF901F6004384FC /* Run Script */, 117 | 97C146EA1CF9000F007C117D /* Sources */, 118 | 97C146EB1CF9000F007C117D /* Frameworks */, 119 | 97C146EC1CF9000F007C117D /* Resources */, 120 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 121 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 122 | ); 123 | buildRules = ( 124 | ); 125 | dependencies = ( 126 | ); 127 | name = Runner; 128 | productName = Runner; 129 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 130 | productType = "com.apple.product-type.application"; 131 | }; 132 | /* End PBXNativeTarget section */ 133 | 134 | /* Begin PBXProject section */ 135 | 97C146E61CF9000F007C117D /* Project object */ = { 136 | isa = PBXProject; 137 | attributes = { 138 | LastUpgradeCheck = 1020; 139 | ORGANIZATIONNAME = ""; 140 | TargetAttributes = { 141 | 97C146ED1CF9000F007C117D = { 142 | CreatedOnToolsVersion = 7.3.1; 143 | LastSwiftMigration = 1100; 144 | }; 145 | }; 146 | }; 147 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 148 | compatibilityVersion = "Xcode 9.3"; 149 | developmentRegion = en; 150 | hasScannedForEncodings = 0; 151 | knownRegions = ( 152 | en, 153 | Base, 154 | ); 155 | mainGroup = 97C146E51CF9000F007C117D; 156 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 157 | projectDirPath = ""; 158 | projectRoot = ""; 159 | targets = ( 160 | 97C146ED1CF9000F007C117D /* Runner */, 161 | ); 162 | }; 163 | /* End PBXProject section */ 164 | 165 | /* Begin PBXResourcesBuildPhase section */ 166 | 97C146EC1CF9000F007C117D /* Resources */ = { 167 | isa = PBXResourcesBuildPhase; 168 | buildActionMask = 2147483647; 169 | files = ( 170 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 171 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 172 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 173 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 174 | ); 175 | runOnlyForDeploymentPostprocessing = 0; 176 | }; 177 | /* End PBXResourcesBuildPhase section */ 178 | 179 | /* Begin PBXShellScriptBuildPhase section */ 180 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 181 | isa = PBXShellScriptBuildPhase; 182 | buildActionMask = 2147483647; 183 | files = ( 184 | ); 185 | inputPaths = ( 186 | ); 187 | name = "Thin Binary"; 188 | outputPaths = ( 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | shellPath = /bin/sh; 192 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 193 | }; 194 | 9740EEB61CF901F6004384FC /* Run Script */ = { 195 | isa = PBXShellScriptBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | ); 199 | inputPaths = ( 200 | ); 201 | name = "Run Script"; 202 | outputPaths = ( 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | shellPath = /bin/sh; 206 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 207 | }; 208 | /* End PBXShellScriptBuildPhase section */ 209 | 210 | /* Begin PBXSourcesBuildPhase section */ 211 | 97C146EA1CF9000F007C117D /* Sources */ = { 212 | isa = PBXSourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 216 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | /* End PBXSourcesBuildPhase section */ 221 | 222 | /* Begin PBXVariantGroup section */ 223 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 97C146FB1CF9000F007C117D /* Base */, 227 | ); 228 | name = Main.storyboard; 229 | sourceTree = ""; 230 | }; 231 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 232 | isa = PBXVariantGroup; 233 | children = ( 234 | 97C147001CF9000F007C117D /* Base */, 235 | ); 236 | name = LaunchScreen.storyboard; 237 | sourceTree = ""; 238 | }; 239 | /* End PBXVariantGroup section */ 240 | 241 | /* Begin XCBuildConfiguration section */ 242 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_ANALYZER_NONNULL = YES; 247 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 248 | CLANG_CXX_LIBRARY = "libc++"; 249 | CLANG_ENABLE_MODULES = YES; 250 | CLANG_ENABLE_OBJC_ARC = YES; 251 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 252 | CLANG_WARN_BOOL_CONVERSION = YES; 253 | CLANG_WARN_COMMA = YES; 254 | CLANG_WARN_CONSTANT_CONVERSION = YES; 255 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 257 | CLANG_WARN_EMPTY_BODY = YES; 258 | CLANG_WARN_ENUM_CONVERSION = YES; 259 | CLANG_WARN_INFINITE_RECURSION = YES; 260 | CLANG_WARN_INT_CONVERSION = YES; 261 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 262 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 263 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 264 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 265 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 266 | CLANG_WARN_STRICT_PROTOTYPES = YES; 267 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 268 | CLANG_WARN_UNREACHABLE_CODE = YES; 269 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 270 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 271 | COPY_PHASE_STRIP = NO; 272 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 273 | ENABLE_NS_ASSERTIONS = NO; 274 | ENABLE_STRICT_OBJC_MSGSEND = YES; 275 | GCC_C_LANGUAGE_STANDARD = gnu99; 276 | GCC_NO_COMMON_BLOCKS = YES; 277 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 278 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 279 | GCC_WARN_UNDECLARED_SELECTOR = YES; 280 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 281 | GCC_WARN_UNUSED_FUNCTION = YES; 282 | GCC_WARN_UNUSED_VARIABLE = YES; 283 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 284 | MTL_ENABLE_DEBUG_INFO = NO; 285 | SDKROOT = iphoneos; 286 | SUPPORTED_PLATFORMS = iphoneos; 287 | TARGETED_DEVICE_FAMILY = "1,2"; 288 | VALIDATE_PRODUCT = YES; 289 | }; 290 | name = Profile; 291 | }; 292 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 293 | isa = XCBuildConfiguration; 294 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 295 | buildSettings = { 296 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 297 | CLANG_ENABLE_MODULES = YES; 298 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 299 | ENABLE_BITCODE = NO; 300 | FRAMEWORK_SEARCH_PATHS = ( 301 | "$(inherited)", 302 | "$(PROJECT_DIR)/Flutter", 303 | ); 304 | INFOPLIST_FILE = Runner/Info.plist; 305 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 306 | LIBRARY_SEARCH_PATHS = ( 307 | "$(inherited)", 308 | "$(PROJECT_DIR)/Flutter", 309 | ); 310 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 311 | PRODUCT_NAME = "$(TARGET_NAME)"; 312 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 313 | SWIFT_VERSION = 5.0; 314 | VERSIONING_SYSTEM = "apple-generic"; 315 | }; 316 | name = Profile; 317 | }; 318 | 97C147031CF9000F007C117D /* Debug */ = { 319 | isa = XCBuildConfiguration; 320 | buildSettings = { 321 | ALWAYS_SEARCH_USER_PATHS = NO; 322 | CLANG_ANALYZER_NONNULL = YES; 323 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 324 | CLANG_CXX_LIBRARY = "libc++"; 325 | CLANG_ENABLE_MODULES = YES; 326 | CLANG_ENABLE_OBJC_ARC = YES; 327 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 328 | CLANG_WARN_BOOL_CONVERSION = YES; 329 | CLANG_WARN_COMMA = YES; 330 | CLANG_WARN_CONSTANT_CONVERSION = YES; 331 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 332 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INFINITE_RECURSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 339 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 340 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 341 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 342 | CLANG_WARN_STRICT_PROTOTYPES = YES; 343 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 344 | CLANG_WARN_UNREACHABLE_CODE = YES; 345 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 346 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 347 | COPY_PHASE_STRIP = NO; 348 | DEBUG_INFORMATION_FORMAT = dwarf; 349 | ENABLE_STRICT_OBJC_MSGSEND = YES; 350 | ENABLE_TESTABILITY = YES; 351 | GCC_C_LANGUAGE_STANDARD = gnu99; 352 | GCC_DYNAMIC_NO_PIC = NO; 353 | GCC_NO_COMMON_BLOCKS = YES; 354 | GCC_OPTIMIZATION_LEVEL = 0; 355 | GCC_PREPROCESSOR_DEFINITIONS = ( 356 | "DEBUG=1", 357 | "$(inherited)", 358 | ); 359 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 360 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 361 | GCC_WARN_UNDECLARED_SELECTOR = YES; 362 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 363 | GCC_WARN_UNUSED_FUNCTION = YES; 364 | GCC_WARN_UNUSED_VARIABLE = YES; 365 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 366 | MTL_ENABLE_DEBUG_INFO = YES; 367 | ONLY_ACTIVE_ARCH = YES; 368 | SDKROOT = iphoneos; 369 | TARGETED_DEVICE_FAMILY = "1,2"; 370 | }; 371 | name = Debug; 372 | }; 373 | 97C147041CF9000F007C117D /* Release */ = { 374 | isa = XCBuildConfiguration; 375 | buildSettings = { 376 | ALWAYS_SEARCH_USER_PATHS = NO; 377 | CLANG_ANALYZER_NONNULL = YES; 378 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 379 | CLANG_CXX_LIBRARY = "libc++"; 380 | CLANG_ENABLE_MODULES = YES; 381 | CLANG_ENABLE_OBJC_ARC = YES; 382 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 383 | CLANG_WARN_BOOL_CONVERSION = YES; 384 | CLANG_WARN_COMMA = YES; 385 | CLANG_WARN_CONSTANT_CONVERSION = YES; 386 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 387 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 388 | CLANG_WARN_EMPTY_BODY = YES; 389 | CLANG_WARN_ENUM_CONVERSION = YES; 390 | CLANG_WARN_INFINITE_RECURSION = YES; 391 | CLANG_WARN_INT_CONVERSION = YES; 392 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 393 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 394 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 395 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 396 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 397 | CLANG_WARN_STRICT_PROTOTYPES = YES; 398 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 399 | CLANG_WARN_UNREACHABLE_CODE = YES; 400 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 401 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 402 | COPY_PHASE_STRIP = NO; 403 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 404 | ENABLE_NS_ASSERTIONS = NO; 405 | ENABLE_STRICT_OBJC_MSGSEND = YES; 406 | GCC_C_LANGUAGE_STANDARD = gnu99; 407 | GCC_NO_COMMON_BLOCKS = YES; 408 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 409 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 410 | GCC_WARN_UNDECLARED_SELECTOR = YES; 411 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 412 | GCC_WARN_UNUSED_FUNCTION = YES; 413 | GCC_WARN_UNUSED_VARIABLE = YES; 414 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 415 | MTL_ENABLE_DEBUG_INFO = NO; 416 | SDKROOT = iphoneos; 417 | SUPPORTED_PLATFORMS = iphoneos; 418 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 419 | TARGETED_DEVICE_FAMILY = "1,2"; 420 | VALIDATE_PRODUCT = YES; 421 | }; 422 | name = Release; 423 | }; 424 | 97C147061CF9000F007C117D /* Debug */ = { 425 | isa = XCBuildConfiguration; 426 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 427 | buildSettings = { 428 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 429 | CLANG_ENABLE_MODULES = YES; 430 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 431 | ENABLE_BITCODE = NO; 432 | FRAMEWORK_SEARCH_PATHS = ( 433 | "$(inherited)", 434 | "$(PROJECT_DIR)/Flutter", 435 | ); 436 | INFOPLIST_FILE = Runner/Info.plist; 437 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 438 | LIBRARY_SEARCH_PATHS = ( 439 | "$(inherited)", 440 | "$(PROJECT_DIR)/Flutter", 441 | ); 442 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 443 | PRODUCT_NAME = "$(TARGET_NAME)"; 444 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 445 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 446 | SWIFT_VERSION = 5.0; 447 | VERSIONING_SYSTEM = "apple-generic"; 448 | }; 449 | name = Debug; 450 | }; 451 | 97C147071CF9000F007C117D /* Release */ = { 452 | isa = XCBuildConfiguration; 453 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 454 | buildSettings = { 455 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 456 | CLANG_ENABLE_MODULES = YES; 457 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 458 | ENABLE_BITCODE = NO; 459 | FRAMEWORK_SEARCH_PATHS = ( 460 | "$(inherited)", 461 | "$(PROJECT_DIR)/Flutter", 462 | ); 463 | INFOPLIST_FILE = Runner/Info.plist; 464 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 465 | LIBRARY_SEARCH_PATHS = ( 466 | "$(inherited)", 467 | "$(PROJECT_DIR)/Flutter", 468 | ); 469 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 470 | PRODUCT_NAME = "$(TARGET_NAME)"; 471 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 472 | SWIFT_VERSION = 5.0; 473 | VERSIONING_SYSTEM = "apple-generic"; 474 | }; 475 | name = Release; 476 | }; 477 | /* End XCBuildConfiguration section */ 478 | 479 | /* Begin XCConfigurationList section */ 480 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 481 | isa = XCConfigurationList; 482 | buildConfigurations = ( 483 | 97C147031CF9000F007C117D /* Debug */, 484 | 97C147041CF9000F007C117D /* Release */, 485 | 249021D3217E4FDB00AE95B9 /* Profile */, 486 | ); 487 | defaultConfigurationIsVisible = 0; 488 | defaultConfigurationName = Release; 489 | }; 490 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 491 | isa = XCConfigurationList; 492 | buildConfigurations = ( 493 | 97C147061CF9000F007C117D /* Debug */, 494 | 97C147071CF9000F007C117D /* Release */, 495 | 249021D4217E4FDB00AE95B9 /* Profile */, 496 | ); 497 | defaultConfigurationIsVisible = 0; 498 | defaultConfigurationName = Release; 499 | }; 500 | /* End XCConfigurationList section */ 501 | }; 502 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 503 | } 504 | --------------------------------------------------------------------------------