├── .github └── workflows │ └── codescan.yml ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── africa │ └── ejara │ └── trustdart │ ├── Coin.kt │ ├── Numeric.kt │ ├── TrustdartPlugin.kt │ ├── WalletHandler.kt │ ├── coins │ ├── ADA.kt │ ├── BNB.kt │ ├── BTC.kt │ ├── DOGE.kt │ ├── ETH.kt │ ├── NEAR.kt │ ├── SOL.kt │ ├── TRX.kt │ ├── XLM.kt │ ├── XRP.kt │ └── XTZ.kt │ ├── interfaces │ └── CoinInterface.kt │ └── utils │ ├── Extension.kt │ └── WalletError.kt ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── africa │ │ │ │ │ └── ejara │ │ │ │ │ └── trustdart_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── 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 │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── 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-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ ├── coins.dart │ ├── main.dart │ └── operations.dart ├── pubspec.yaml └── test │ └── widget_test.dart ├── gitignore ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── Coin.swift │ ├── SwiftTrustdartPlugin.swift │ ├── TrustdartPlugin.h │ ├── TrustdartPlugin.m │ ├── WalletHandler.swift │ ├── coins │ │ ├── ADA.swift │ │ ├── BNB.swift │ │ ├── BTC.swift │ │ ├── DOGE.swift │ │ ├── ETH.swift │ │ ├── NEAR.swift │ │ ├── SOL.swift │ │ ├── TRX.swift │ │ ├── XLM.swift │ │ ├── XRP.swift │ │ └── XTZ.swift │ ├── protocols │ │ └── CoinProtocol.swift │ └── utils │ │ ├── Utils.swift │ │ └── WalletError.swift └── trustdart.podspec ├── lib └── trustdart.dart ├── pubspec.yaml ├── sonar-project.properties └── trustdart.iml /.github/workflows/codescan.yml: -------------------------------------------------------------------------------- 1 | name: CodeScan 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | build: 8 | name: CodeScan 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | with: 13 | fetch-depth: 0 14 | - uses: sonarsource/sonarqube-scan-action@master 15 | env: 16 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 17 | SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | .packages 4 | .pub/ 5 | build/ 6 | .idea/ 7 | pubspec.lock 8 | org.eclipse.buildship.core.prefs 9 | android/.classpath 10 | android/.project 11 | example/android/.project 12 | example/android/.classpath 13 | 14 | -------------------------------------------------------------------------------- /.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: b22742018b3edf16c6cadd7b76d9db5e7f9064b5 8 | channel: stable 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.0.1 2 | 3 | This release is a meant to be a proof of concept and to test if this can be installed in another flutter app. 4 | 5 | ### 0.0.2 6 | 7 | Improved readme and added github access tokens for access to trustwallet android package on github maven. 8 | 9 | ### 0.0.3 10 | 11 | Added change logs. 12 | 13 | ### 0.0.4 14 | 15 | Github Access Tokens Added from Dummy user 16 | 17 | ### 0.0.5 18 | 19 | Added instructions for setting up github access tokens since dummy access token did not work. Github deletes access tokens committed to github. 20 | 21 | ### 0.0.6 22 | 23 | Added support for TRON TOKENS, TRX, TRC10, TRC20 and Asset Freezing. 24 | 25 | ### 0.0.7 26 | 27 | Added support for Solana, transfere and receive SOL as well as any token on Solana. 28 | 29 | ### 0.0.8 30 | 31 | Changed return of getPrivateKey, getPublicKey to base64 encoding 32 | 33 | ### 0.0.9 34 | 35 | Improve error handling and checking for null wallet 36 | 37 | ### 0.1.0 38 | 39 | - Replaced `java.util.Base64` with `android.util.Base64` 40 | 41 | - Added support for Near Crypto transfer transactions 42 | 43 | ### 0.1.1 44 | 45 | - Fixed number conversion in Near. 46 | 47 | ### 0.1.2 48 | 49 | - Memory optimization & minor bug fixes 50 | 51 | ### 0.1.3 52 | 53 | - Fixed Base64 encoding issue which causes the app to crash on lower version of Android. 54 | - Upgrade WalletCore to 3.1.31. 55 | - Updated grade version to 7.2.1. 56 | - added new methods on TrustDart; getSeed, getRawPrivateKey & getRawPublicKey. 57 | 58 | ### 0.1.4 59 | 60 | - Fixed `getSeed, getRawPrivateKey & getRawPublicKey` methods returning empty array. 61 | - Added `signDataWithPrivateKey`. 62 | 63 | ### 0.1.5 64 | 65 | - Updated the gradle version in Android. 66 | - Reverted to adding the feeLimit option in TRON (TRX). For instance, it provides the flexibility of setting the feeLimit to 0 for TRX send. 67 | 68 | ### 0.1.6 69 | 70 | - Updated raw sign for tezos 71 | 72 | ### 0.1.7 73 | 74 | - Stellar Integration 75 | 76 | ### 0.1.8 77 | 78 | - Binance chains integrations 79 | 80 | ### 0.1.9 81 | 82 | - Doge coin integrations 83 | 84 | ### 0.1.10 85 | 86 | - Doge coin fixes 87 | 88 | ### 0.1.11 89 | 90 | - Android long/int conversion issues fixed. 91 | 92 | ### 0.2.0 93 | 94 | - Added support for tezos fa2/fa12 95 | 96 | ### 0.2.1 97 | 98 | - Changes for Near update 99 | 100 | ### 0.3.0 101 | 102 | - Added support for Cardano and All Ethereum / ERC20 compatible tokens and blockchains. 103 | 104 | ### 0.3.1 105 | 106 | - Fixed bug in ethereum 107 | 108 | ### 0.3.2 109 | 110 | - Fixed bug in cardano 111 | 112 | ### 0.3.3 113 | 114 | - Add multi input, multi private keys sign for bitcoin 115 | 116 | ### 0.3.4 117 | 118 | - Add support for Stellar testnet transaction signing 119 | 120 | ### 0.3.6 121 | 122 | - WalletCore updates 123 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ejara 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # trustdart 2 | 3 | A dart library that can interact with the trust wallet core library . 4 | 5 | ## Install 6 | 7 | - You first need to setup a personal access token for github as decribed here, . 8 | - You only need to select `read:package` scope. 9 | - Next set the following variables in your environment 10 | - `TRUST_DART_GITHUB_USER` ~ the github username of the account you used to create the access token. 11 | - `TRUST_DART_GITHUB_TOKEN` ~ the github access token you just generated. 12 | - You can install from here . 13 | 14 | ## Overview 15 | 16 | For every blockchain, there are two groups of operations, those that can be done locally and those that require access to a node. 17 | 18 | ### Local Operations 19 | 20 | - Wallet management 21 | - Creating a new multi-coin wallet 22 | - Importing a multi-coin wallet 23 | - Address derivation (receiving) 24 | - Generating the default address for a coin 25 | - Generating an address using a custom derivation path (expert) 26 | - Transaction signing (e.g. for sending) 27 | 28 | ### Operations that require access to a node 29 | 30 | - Query blockchain for transaction/operations history. 31 | - Push newly created transactions to the blockchain. 32 | 33 | #### This libarary focuses on just the local operations. You would have to write your own code to query the blockchain or publish transactions 34 | 35 | #### Refere to the examples folder to see how to use test application 36 | 37 | ### Roadmap 38 | 39 | - Add all the cryptos supported by trustwallet (this initial version only works for Tezos, Ethereum & Bitcoin) 40 | - For Bitcoin transaction signing hasn't been implemented yet. 41 | 42 | ### Approach 43 | 44 | - This flutter plugin makes use of method channels approach. Learn more about the approach here . 45 | 46 | ### Contribution 47 | 48 | - For now there are no strict guidelines. 49 | - Just create your pull request. 50 | - Make sure to add a description what the pull request is about. 51 | - If you have any questions feel free to ask. 52 | - Looking forward contributions. 53 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'africa.ejara.trustdart' 2 | version '1.0-SNAPSHOT' 3 | 4 | 5 | buildscript { 6 | ext.kotlin_version = '1.9.0' 7 | repositories { 8 | google() 9 | jcenter() 10 | } 11 | 12 | dependencies { 13 | classpath 'com.android.tools.build:gradle:8.1.1' 14 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 15 | } 16 | } 17 | 18 | rootProject.allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | maven { 23 | url = uri("https://maven.pkg.github.com/trustwallet/wallet-core") 24 | credentials { 25 | username = System.getenv("TRUST_DART_GITHUB_USER") 26 | password = System.getenv("TRUST_DART_GITHUB_TOKEN") 27 | } 28 | } 29 | } 30 | 31 | 32 | } 33 | 34 | apply plugin: 'com.android.library' 35 | apply plugin: 'kotlin-android' 36 | 37 | kotlin { 38 | jvmToolchain(17) 39 | } 40 | 41 | android { 42 | namespace 'africa.ejara.trustdart' 43 | compileSdkVersion 34 44 | 45 | compileOptions { 46 | sourceCompatibility JavaVersion.VERSION_17 47 | targetCompatibility JavaVersion.VERSION_17 48 | } 49 | 50 | sourceSets { 51 | main.java.srcDirs += 'src/main/kotlin' 52 | } 53 | defaultConfig { 54 | minSdkVersion 23 55 | } 56 | 57 | dependencies { 58 | implementation "com.trustwallet:wallet-core:4.2.14" 59 | } 60 | } 61 | 62 | dependencies { 63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 64 | implementation "org.jetbrains.kotlin:kotlin-script-runtime:1.5.31" 65 | implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" 66 | } 67 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'trustdart' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/Coin.kt: -------------------------------------------------------------------------------- 1 | package africa.ejara.trustdart 2 | 3 | import africa.ejara.trustdart.interfaces.CoinInterface 4 | import africa.ejara.trustdart.utils.base64String 5 | import africa.ejara.trustdart.utils.toHex 6 | import africa.ejara.trustdart.utils.toHexByteArray 7 | import org.json.JSONObject 8 | import wallet.core.java.AnySigner 9 | import wallet.core.jni.CoinType 10 | import wallet.core.jni.HDWallet 11 | 12 | open class Coin(nameOfCoin: String, typeOfCoin: CoinType) : CoinInterface { 13 | 14 | var name: String? = null 15 | var coinType: CoinType? = null 16 | 17 | init { 18 | name = nameOfCoin 19 | coinType = typeOfCoin 20 | } 21 | 22 | override fun generateAddress( 23 | path: String, 24 | mnemonic: String, 25 | passphrase: String 26 | ): Map? { 27 | val wallet = HDWallet(mnemonic, passphrase) 28 | return mapOf("legacy" to coinType!!.deriveAddress(wallet.getKey(coinType, path))) 29 | } 30 | 31 | override fun getPrivateKey(path: String, mnemonic: String, passphrase: String): String? { 32 | val wallet = HDWallet(mnemonic, passphrase) 33 | return wallet.getKey(coinType, path).data().base64String() 34 | } 35 | 36 | override fun getSeed(path: String, mnemonic: String, passphrase: String): ByteArray? { 37 | val data = HDWallet(mnemonic, passphrase).seed() 38 | print("Seed Data: $data") 39 | return data 40 | } 41 | 42 | override fun getRawPrivateKey(path: String, mnemonic: String, passphrase: String): ByteArray? { 43 | val wallet = HDWallet(mnemonic, passphrase) 44 | return wallet.getKey(coinType, path).data() 45 | } 46 | 47 | override fun getPublicKey(path: String, mnemonic: String, passphrase: String): String? { 48 | val wallet = HDWallet(mnemonic, passphrase) 49 | return wallet.getKey(coinType, path).getPublicKeySecp256k1(true).data().base64String() 50 | } 51 | 52 | override fun getRawPublicKey(path: String, mnemonic: String, passphrase: String): ByteArray? { 53 | val wallet = HDWallet(mnemonic, passphrase) 54 | return wallet.getKey(coinType, path).getPublicKeySecp256k1(true).data() 55 | } 56 | 57 | override fun validateAddress(address: String): Boolean { 58 | return coinType!!.validate(address) 59 | } 60 | 61 | override fun signDataWithPrivateKey( 62 | path: String, 63 | mnemonic: String, 64 | passphrase: String, 65 | txData: String 66 | ): String? { 67 | val wallet = HDWallet(mnemonic, passphrase) 68 | val privateKey = wallet.getKey(coinType, path) 69 | return privateKey.sign(txData.toHexByteArray(), coinType!!.curve()).toHex() 70 | } 71 | 72 | override fun signTransaction( 73 | path: String, 74 | txData: Map, 75 | mnemonic: String, 76 | passphrase: String 77 | ): String? { 78 | val wallet = HDWallet(mnemonic, passphrase) 79 | val privateKey = wallet.getKey(coinType, path) 80 | val opJson = JSONObject(txData).toString() 81 | return AnySigner.signJSON(opJson, privateKey.data(), coinType!!.value()) 82 | } 83 | 84 | override fun multiSignTransaction( 85 | txData: Map, 86 | privateKeys: ArrayList 87 | ): String? { 88 | val opJson = JSONObject(txData).toString() 89 | val signatures = mutableListOf() 90 | 91 | for (privateKey in privateKeys) { 92 | val signature = AnySigner.signJSON(opJson, privateKey.toByteArray(), coinType!!.value()) 93 | signatures.add(signature) 94 | } 95 | return signatures.joinToString(",") 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/Numeric.kt: -------------------------------------------------------------------------------- 1 | package africa.ejara.trustdart 2 | 3 | import kotlin.experimental.and 4 | 5 | // TODO: Move to native 6 | object Numeric { 7 | 8 | fun containsHexPrefix(input: String): Boolean { 9 | return input.length > 1 && input[0] == '0' && input[1] == 'x' 10 | } 11 | 12 | fun cleanHexPrefix(input: String): String { 13 | return if (containsHexPrefix(input)) { 14 | input.substring(2) 15 | } else { 16 | input 17 | } 18 | } 19 | 20 | fun hexStringToByteArray(input: String): ByteArray { 21 | val cleanInput = cleanHexPrefix(input) 22 | 23 | val len = cleanInput.length 24 | 25 | if (len == 0) { 26 | return byteArrayOf() 27 | } 28 | 29 | val data: ByteArray 30 | val startIdx: Int 31 | if (len % 2 != 0) { 32 | data = ByteArray(len / 2 + 1) 33 | data[0] = Character.digit(cleanInput.get(0), 16).toByte() 34 | startIdx = 1 35 | } else { 36 | data = ByteArray(len / 2) 37 | startIdx = 0 38 | } 39 | 40 | var i = startIdx 41 | while (i < len) { 42 | data[(i + 1) / 2] = 43 | ((Character.digit( 44 | cleanInput[i], 45 | 16 46 | ) shl 4) + Character.digit(cleanInput[i + 1], 16)).toByte() 47 | i += 2 48 | } 49 | return data 50 | } 51 | 52 | fun toHexString(input: ByteArray?, offset: Int, length: Int, withPrefix: Boolean): String { 53 | val stringBuilder = StringBuilder() 54 | if (withPrefix) { 55 | stringBuilder.append("0x") 56 | } 57 | for (i in offset until offset + length) { 58 | stringBuilder.append(String.format("%02x", input!![i] and 0xFF.toByte())) 59 | } 60 | 61 | return stringBuilder.toString() 62 | } 63 | 64 | fun toHexString(input: ByteArray?): String { 65 | return toHexString(input, 0, input!!.size, false) 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/TrustdartPlugin.kt: -------------------------------------------------------------------------------- 1 | package africa.ejara.trustdart 2 | 3 | import africa.ejara.trustdart.utils.ErrorResponse 4 | import africa.ejara.trustdart.utils.WalletError 5 | import africa.ejara.trustdart.utils.WalletHandlerErrorCodes 6 | import androidx.annotation.NonNull 7 | import io.flutter.embedding.engine.plugins.FlutterPlugin 8 | import io.flutter.plugin.common.MethodCall 9 | import io.flutter.plugin.common.MethodChannel 10 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler 11 | import io.flutter.plugin.common.MethodChannel.Result 12 | import wallet.core.jni.HDWallet 13 | 14 | 15 | /** TrustdartPlugin */ 16 | class TrustdartPlugin : FlutterPlugin, MethodCallHandler { 17 | 18 | init { 19 | System.loadLibrary("TrustWalletCore") 20 | } 21 | 22 | /// The MethodChannel that will the communication between Flutter and native Android 23 | /// 24 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it 25 | /// when the Flutter Engine is detached from the Activity 26 | private lateinit var channel: MethodChannel 27 | 28 | override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { 29 | channel = MethodChannel(flutterPluginBinding.binaryMessenger, "trustdart") 30 | channel.setMethodCallHandler(this) 31 | } 32 | 33 | override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { 34 | when (call.method) { 35 | "generateMnemonic" -> { 36 | var validator = WalletHandler().validate( 37 | WalletError( 38 | WalletHandlerErrorCodes.ArgumentsNull, 39 | "[strength] and [passphrase] are required.", 40 | null 41 | ), arrayOf(call.arguments()) 42 | ) 43 | if (validator.isValid) { 44 | val wallet = WalletHandler().generateMnemonic(128, call.arguments()!!) 45 | validator = WalletHandler().validate( 46 | WalletError( 47 | WalletHandlerErrorCodes.NoWallet, 48 | "Could not generated mnemonic.", 49 | null 50 | ), arrayOf(wallet) 51 | ) 52 | if (validator.isValid) return result.success(wallet) 53 | } 54 | return result.error( 55 | validator.details.errorCode, 56 | validator.details.errorMessage, 57 | validator.details.errorDetails 58 | ) 59 | } 60 | "checkMnemonic" -> { 61 | val mnemonic: String? = call.argument("mnemonic") 62 | val passphrase: String? = call.argument("passphrase") 63 | var validator = WalletHandler().validate( 64 | WalletError( 65 | WalletHandlerErrorCodes.ArgumentsNull, 66 | "[mnemonic] and [passphrase] are required.", 67 | null 68 | ), arrayOf(mnemonic, passphrase) 69 | ) 70 | if (validator.isValid) { 71 | val wallet = WalletHandler().checkMnemonic(mnemonic!!, passphrase!!) 72 | validator = WalletHandler().validate( 73 | WalletError( 74 | WalletHandlerErrorCodes.NoWallet, 75 | "Failed to generate wallet.", 76 | null 77 | ), arrayOf(wallet) 78 | ) 79 | if (validator.isValid) return result.success(true) 80 | } 81 | return result.error( 82 | validator.details.errorCode, 83 | validator.details.errorMessage, 84 | validator.details.errorDetails 85 | ) 86 | } 87 | "generateAddress" -> { 88 | val path: String? = call.argument("path") 89 | val coin: String? = call.argument("coin") 90 | val mnemonic: String? = call.argument("mnemonic") 91 | val passphrase: String? = call.argument("passphrase") 92 | var validator = WalletHandler().validate( 93 | WalletError( 94 | WalletHandlerErrorCodes.ArgumentsNull, 95 | ErrorResponse.argumentsNull, 96 | null 97 | ), arrayOf(path, coin, mnemonic, passphrase) 98 | ) 99 | if (validator.isValid) { 100 | val address = WalletHandler().getCoin(coin) 101 | .generateAddress(path!!, mnemonic!!, passphrase!!) 102 | validator = WalletHandler().validate( 103 | WalletError( 104 | WalletHandlerErrorCodes.AddressNull, 105 | "Could not generate address.", 106 | null 107 | ), arrayOf(address) 108 | ) 109 | if (validator.isValid) return result.success(address) 110 | } 111 | return result.error( 112 | validator.details.errorCode, 113 | validator.details.errorMessage, 114 | validator.details.errorDetails 115 | ) 116 | } 117 | "validateAddress" -> { 118 | val address: String? = call.argument("address") 119 | val coin: String? = call.argument("coin") 120 | val validator = WalletHandler().validate( 121 | WalletError( 122 | WalletHandlerErrorCodes.ArgumentsNull, 123 | "[coin] and [address] are required.", 124 | null 125 | ), arrayOf(coin, address) 126 | ) 127 | if (validator.isValid && WalletHandler().getCoin(coin) 128 | .validateAddress(address!!) 129 | ) return result.success(true) 130 | return result.error( 131 | validator.details.errorCode, 132 | validator.details.errorMessage, 133 | validator.details.errorDetails 134 | ) 135 | } 136 | "signTransaction" -> { 137 | val coin: String? = call.argument("coin") 138 | val path: String? = call.argument("path") 139 | val mnemonic: String? = call.argument("mnemonic") 140 | val passphrase: String? = call.argument("passphrase") 141 | val txData: Map? = call.argument("txData") 142 | var validator = WalletHandler().validate( 143 | WalletError( 144 | WalletHandlerErrorCodes.ArgumentsNull, 145 | "[path], [coin], [mnemonic], [passphrase] and [txData] are required.", 146 | null 147 | ), arrayOf(path, coin, mnemonic, txData, passphrase) 148 | ) 149 | if (validator.isValid) { 150 | val txHash = WalletHandler().getCoin(coin) 151 | .signTransaction(path!!, txData!!, mnemonic!!, passphrase!!) 152 | validator = WalletHandler().validate( 153 | WalletError( 154 | WalletHandlerErrorCodes.TxHashNull, 155 | "Could not sign transaction.", 156 | null 157 | ), arrayOf(txHash) 158 | ) 159 | if (validator.isValid) return result.success(txHash) 160 | } 161 | return result.error( 162 | validator.details.errorCode, 163 | validator.details.errorMessage, 164 | validator.details.errorDetails 165 | ) 166 | } 167 | "multiSignTransaction" -> { 168 | val coin: String? = call.argument("coin") 169 | val txData: Map? = call.argument("txData") 170 | val privateKeys: ArrayList? = call.argument("privateKeys") 171 | var validator = WalletHandler().validate( 172 | WalletError( 173 | WalletHandlerErrorCodes.ArgumentsNull, 174 | "[coin], [privateKeys] and [txData] are required.", 175 | null 176 | ), arrayOf(coin, txData, privateKeys) 177 | ) 178 | if (validator.isValid) { 179 | val txHash = WalletHandler().getCoin(coin) 180 | .multiSignTransaction(txData!!, privateKeys!!) 181 | validator = WalletHandler().validate( 182 | WalletError( 183 | WalletHandlerErrorCodes.TxHashNull, 184 | "Could not sign transaction.", 185 | null 186 | ), arrayOf(txHash) 187 | ) 188 | if (validator.isValid) return result.success(txHash) 189 | } 190 | return result.error( 191 | validator.details.errorCode, 192 | validator.details.errorMessage, 193 | validator.details.errorDetails 194 | ) 195 | } 196 | "signDataWithPrivateKey" -> { 197 | val coin: String? = call.argument("coin") 198 | val path: String? = call.argument("path") 199 | val mnemonic: String? = call.argument("mnemonic") 200 | val passphrase: String? = call.argument("passphrase") 201 | val txData: String? = call.argument("txData") 202 | var validator = WalletHandler().validate( 203 | WalletError( 204 | WalletHandlerErrorCodes.ArgumentsNull, 205 | "[path], [coin], [mnemonic], [passphrase] and [txData] are required.", 206 | null 207 | ), arrayOf(path, coin, mnemonic, txData, passphrase) 208 | ) 209 | if (validator.isValid) { 210 | val txHash = WalletHandler().getCoin(coin) 211 | .signDataWithPrivateKey(path!!, mnemonic!!, passphrase!!, txData!!) 212 | validator = WalletHandler().validate( 213 | WalletError( 214 | WalletHandlerErrorCodes.TxHashNull, 215 | "Could not sign data.", 216 | null 217 | ), arrayOf(txHash) 218 | ) 219 | if (validator.isValid) return result.success(txHash) 220 | } 221 | return result.error( 222 | validator.details.errorCode, 223 | validator.details.errorMessage, 224 | validator.details.errorDetails 225 | ) 226 | } 227 | "getPublicKey" -> { 228 | val path: String? = call.argument("path") 229 | val coin: String? = call.argument("coin") 230 | val mnemonic: String? = call.argument("mnemonic") 231 | val passphrase: String? = call.argument("passphrase") 232 | 233 | var validator = WalletHandler().validate( 234 | WalletError( 235 | WalletHandlerErrorCodes.ArgumentsNull, 236 | ErrorResponse.argumentsNull, 237 | null 238 | ), arrayOf(path, coin, mnemonic, passphrase) 239 | ) 240 | if (validator.isValid) { 241 | val publicKey = 242 | WalletHandler().getCoin(coin).getPublicKey(path!!, mnemonic!!, passphrase!!) 243 | validator = WalletHandler().validate( 244 | WalletError( 245 | WalletHandlerErrorCodes.AddressNull, 246 | ErrorResponse.publicKeyNull, 247 | null 248 | ), arrayOf(publicKey) 249 | ) 250 | if (validator.isValid) return result.success(publicKey) 251 | } 252 | return result.error( 253 | validator.details.errorCode, 254 | validator.details.errorMessage, 255 | validator.details.errorDetails 256 | ) 257 | } 258 | "getRawPublicKey" -> { 259 | val path: String? = call.argument("path") 260 | val coin: String? = call.argument("coin") 261 | val mnemonic: String? = call.argument("mnemonic") 262 | val passphrase: String? = call.argument("passphrase") 263 | 264 | var validator = WalletHandler().validate( 265 | WalletError( 266 | WalletHandlerErrorCodes.ArgumentsNull, 267 | ErrorResponse.argumentsNull, 268 | null 269 | ), arrayOf(path, coin, mnemonic, passphrase) 270 | ) 271 | if (validator.isValid) { 272 | val publicKey = 273 | WalletHandler().getCoin(coin) 274 | .getRawPublicKey(path!!, mnemonic!!, passphrase!!) 275 | if (publicKey != null) { 276 | validator = WalletHandler().validate( 277 | WalletError( 278 | WalletHandlerErrorCodes.AddressNull, 279 | ErrorResponse.publicKeyNull, 280 | null 281 | ), publicKey.toTypedArray() 282 | //), arrayOf(publicKey) 283 | ) 284 | } 285 | if (validator.isValid) return result.success(publicKey) 286 | } 287 | return result.error( 288 | validator.details.errorCode, 289 | validator.details.errorMessage, 290 | validator.details.errorDetails 291 | ) 292 | } 293 | "getSeed" -> { 294 | val path: String? = call.argument("path") 295 | val coin: String? = call.argument("coin") 296 | val mnemonic: String? = call.argument("mnemonic") 297 | val passphrase: String? = call.argument("passphrase") 298 | 299 | var validator = WalletHandler().validate( 300 | WalletError( 301 | WalletHandlerErrorCodes.ArgumentsNull, 302 | ErrorResponse.argumentsNull, 303 | null 304 | ), arrayOf(path, coin, mnemonic, passphrase) 305 | ) 306 | if (validator.isValid) { 307 | val privateKey = WalletHandler().getCoin(coin) 308 | .getSeed(path!!, mnemonic!!, passphrase!!) 309 | validator = WalletHandler().validate( 310 | WalletError( 311 | WalletHandlerErrorCodes.AddressNull, 312 | ErrorResponse.privateKeyNull, 313 | null 314 | ), arrayOf(privateKey) 315 | ) 316 | if (validator.isValid) return result.success(privateKey) 317 | } 318 | return result.error( 319 | validator.details.errorCode, 320 | validator.details.errorMessage, 321 | validator.details.errorDetails 322 | ) 323 | } 324 | "getPrivateKey" -> { 325 | val path: String? = call.argument("path") 326 | val coin: String? = call.argument("coin") 327 | val mnemonic: String? = call.argument("mnemonic") 328 | val passphrase: String? = call.argument("passphrase") 329 | 330 | var validator = WalletHandler().validate( 331 | WalletError( 332 | WalletHandlerErrorCodes.ArgumentsNull, 333 | ErrorResponse.argumentsNull, 334 | null 335 | ), arrayOf(path, coin, mnemonic, passphrase) 336 | ) 337 | if (validator.isValid) { 338 | val privateKey = WalletHandler().getCoin(coin) 339 | .getPrivateKey(path!!, mnemonic!!, passphrase!!) 340 | validator = WalletHandler().validate( 341 | WalletError( 342 | WalletHandlerErrorCodes.AddressNull, 343 | ErrorResponse.privateKeyNull, 344 | null 345 | ), arrayOf(privateKey) 346 | ) 347 | if (validator.isValid) return result.success(privateKey) 348 | } 349 | return result.error( 350 | validator.details.errorCode, 351 | validator.details.errorMessage, 352 | validator.details.errorDetails 353 | ) 354 | } 355 | "getRawPrivateKey" -> { 356 | val path: String? = call.argument("path") 357 | val coin: String? = call.argument("coin") 358 | val mnemonic: String? = call.argument("mnemonic") 359 | val passphrase: String? = call.argument("passphrase") 360 | 361 | var validator = WalletHandler().validate( 362 | WalletError( 363 | WalletHandlerErrorCodes.ArgumentsNull, 364 | ErrorResponse.argumentsNull, 365 | null 366 | ), arrayOf(path, coin, mnemonic, passphrase) 367 | ) 368 | if (validator.isValid) { 369 | val privateKey = WalletHandler().getCoin(coin) 370 | .getRawPrivateKey(path!!, mnemonic!!, passphrase!!) 371 | validator = WalletHandler().validate( 372 | WalletError( 373 | WalletHandlerErrorCodes.AddressNull, 374 | ErrorResponse.privateKeyNull, 375 | null 376 | ), privateKey!!.toTypedArray() 377 | ) 378 | if (validator.isValid) return result.success(privateKey) 379 | } 380 | return result.error( 381 | validator.details.errorCode, 382 | validator.details.errorMessage, 383 | validator.details.errorDetails 384 | ) 385 | } 386 | else -> result.notImplemented() 387 | } 388 | } 389 | 390 | override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { 391 | channel.setMethodCallHandler(null) 392 | } 393 | 394 | } 395 | -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/WalletHandler.kt: -------------------------------------------------------------------------------- 1 | package africa.ejara.trustdart 2 | 3 | import BTC 4 | import ETH 5 | import NEAR 6 | import SOL 7 | import TRX 8 | import XTZ 9 | import XLM 10 | import BNB 11 | import DOGE 12 | import ADA 13 | import XRP 14 | 15 | import africa.ejara.trustdart.utils.WalletError 16 | import africa.ejara.trustdart.utils.WalletValidateResponse 17 | import wallet.core.jni.HDWallet 18 | 19 | class WalletHandler { 20 | 21 | companion object { 22 | val coins = mapOf( 23 | "BTC" to BTC(), 24 | "ETH" to ETH(), 25 | "XTZ" to XTZ(), 26 | "TRX" to TRX(), 27 | "SOL" to SOL(), 28 | "NEAR" to NEAR(), 29 | "XLM" to XLM(), 30 | "BNB" to BNB(), 31 | "DOGE" to DOGE(), 32 | "ADA" to ADA(), 33 | "XRP" to XRP(), 34 | ) 35 | } 36 | 37 | fun getCoin(coin: String?): Coin { 38 | return coins[coin]!! 39 | } 40 | 41 | fun generateMnemonic(strength: Int, passphrase: String): String? { 42 | return HDWallet(strength, passphrase).mnemonic() 43 | } 44 | 45 | fun checkMnemonic(mnemonic: String, passphrase: String): HDWallet { 46 | return HDWallet(mnemonic, passphrase) 47 | } 48 | 49 | fun validate(walletError: WalletError, data: Array): WalletValidateResponse { 50 | var isValid = true 51 | for (_data in data) { 52 | if (_data == null) { 53 | isValid = false 54 | } 55 | } 56 | return WalletValidateResponse(isValid, walletError) 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/coins/ADA.kt: -------------------------------------------------------------------------------- 1 | import africa.ejara.trustdart.Coin 2 | import africa.ejara.trustdart.Numeric 3 | import africa.ejara.trustdart.utils.toLong 4 | import com.google.protobuf.ByteString 5 | import wallet.core.jni.CoinType 6 | import wallet.core.jni.HDWallet 7 | import wallet.core.jni.proto.Cardano 8 | import wallet.core.java.AnySigner 9 | 10 | 11 | class ADA : Coin("ADA", CoinType.CARDANO) { 12 | 13 | override fun signTransaction( 14 | path: String, 15 | txData: Map, 16 | mnemonic: String, 17 | passphrase: String 18 | ): String? { 19 | val wallet = HDWallet(mnemonic, passphrase) 20 | val privateKey = wallet.getKey(coinType, path) 21 | val listOfAllUtxos = mutableListOf(); 22 | val utxos: List> = txData["utxos"] as List> 23 | val message = Cardano.Transfer.newBuilder() 24 | .setToAddress(txData["receiverAddress"] as String) 25 | .setChangeAddress(txData["senderAddress"] as String) 26 | .setAmount(txData["amount"]!!.toLong()) 27 | .build() 28 | val input = Cardano.SigningInput.newBuilder() 29 | .setTransferMessage(message) 30 | .setTtl(txData["ttl"]!!.toLong()) 31 | 32 | input.addPrivateKey(ByteString.copyFrom(privateKey.data())) 33 | for (utx in utxos) { 34 | val outpoint = Cardano.OutPoint.newBuilder() 35 | .setTxHash(ByteString.copyFrom(Numeric.hexStringToByteArray(utx["txid"] as String))) 36 | .setOutputIndex(utx["index"]!!.toLong()) 37 | .build() 38 | val utxo = Cardano.TxInput.newBuilder() 39 | .setOutPoint(outpoint) 40 | .setAddress(utx["senderAddress"] as String) 41 | .setAmount(utx["balance"]!!.toLong()) 42 | .build() 43 | listOfAllUtxos.add(utxo) 44 | } 45 | input.addAllUtxos(listOfAllUtxos) 46 | 47 | val output = AnySigner.sign(input.build(), coinType, Cardano.SigningOutput.parser()) 48 | return Numeric.toHexString(output.encoded.toByteArray()) 49 | } 50 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/coins/BNB.kt: -------------------------------------------------------------------------------- 1 | import africa.ejara.trustdart.Coin 2 | import com.google.protobuf.ByteString 3 | import wallet.core.jni.* 4 | import wallet.core.jni.CoinType 5 | import wallet.core.java.AnySigner 6 | import wallet.core.jni.proto.Binance 7 | import africa.ejara.trustdart.Numeric 8 | import africa.ejara.trustdart.utils.toLong 9 | 10 | 11 | class BNB : Coin("BNB", CoinType.BINANCE) { 12 | 13 | override fun signTransaction( 14 | path: String, 15 | txData: Map, 16 | mnemonic: String, 17 | passphrase: String 18 | ): String? { 19 | val wallet = HDWallet(mnemonic, passphrase) 20 | val privateKey = wallet.getKey(coinType, path) 21 | val publicKey = txData["fromAddress"] as String 22 | val signingInput = Binance.SigningInput.newBuilder() 23 | signingInput.chainId = txData["chainID"] as String 24 | signingInput.accountNumber = txData["accountNumber"]!!.toLong() 25 | signingInput.sequence = txData["sequence"]!!.toLong() 26 | signingInput.source = txData["source"]!!.toLong() 27 | if (txData["memo"] != null) { 28 | signingInput.memo = txData["memo"] as String 29 | } 30 | signingInput.privateKey = ByteString.copyFrom(privateKey.data()) 31 | 32 | val token = Binance.SendOrder.Token.newBuilder() 33 | token.denom = name 34 | token.amount = txData["amount"]!!.toLong() 35 | 36 | val input = Binance.SendOrder.Input.newBuilder() 37 | input.address = ByteString.copyFrom(AnyAddress(publicKey, coinType).data()) 38 | input.addAllCoins(listOf(token.build())) 39 | 40 | val output = Binance.SendOrder.Output.newBuilder() 41 | output.address = 42 | ByteString.copyFrom(AnyAddress(txData["toAddress"] as String, coinType).data()) 43 | output.addAllCoins(listOf(token.build())) 44 | 45 | val sendOrder = Binance.SendOrder.newBuilder() 46 | sendOrder.addAllInputs(listOf(input.build())) 47 | sendOrder.addAllOutputs(listOf(output.build())) 48 | 49 | signingInput.sendOrder = sendOrder.build() 50 | 51 | val sign: Binance.SigningOutput = 52 | AnySigner.sign(signingInput.build(), coinType, Binance.SigningOutput.parser()) 53 | return Numeric.toHexString(sign.encoded.toByteArray()) 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/coins/BTC.kt: -------------------------------------------------------------------------------- 1 | import africa.ejara.trustdart.Coin 2 | import africa.ejara.trustdart.Numeric 3 | import africa.ejara.trustdart.utils.toLong 4 | import com.google.protobuf.ByteString 5 | import wallet.core.java.AnySigner 6 | import wallet.core.jni.BitcoinAddress 7 | import wallet.core.jni.BitcoinScript 8 | import wallet.core.jni.CoinType 9 | import wallet.core.jni.HDWallet 10 | import wallet.core.jni.proto.Bitcoin 11 | 12 | 13 | class BTC : Coin("BTC", CoinType.BITCOIN) { 14 | 15 | override fun generateAddress( 16 | path: String, 17 | mnemonic: String, 18 | passphrase: String 19 | ): Map? { 20 | val wallet = HDWallet(mnemonic, passphrase) 21 | val privateKey = wallet.getKey(coinType, path) 22 | val publicKey = privateKey.getPublicKeySecp256k1(true) 23 | val address = BitcoinAddress(publicKey, coinType!!.p2pkhPrefix()) 24 | return mapOf( 25 | "legacy" to address.description(), 26 | "segwit" to coinType!!.deriveAddress(privateKey) 27 | ) 28 | } 29 | 30 | override fun signTransaction( 31 | path: String, 32 | txData: Map, 33 | mnemonic: String, 34 | passphrase: String 35 | ): String? { 36 | val wallet = HDWallet(mnemonic, passphrase) 37 | val privateKey = wallet.getKey(coinType, path) 38 | val utxos: List> = txData["utxos"] as List> 39 | 40 | val input = Bitcoin.SigningInput.newBuilder() 41 | .setAmount(txData["amount"]!!.toLong()) 42 | .setHashType(BitcoinScript.hashTypeForCoin(coinType)) 43 | .setToAddress(txData["toAddress"] as String) 44 | .setChangeAddress(txData["changeAddress"] as String) 45 | .setByteFee(1) 46 | .addPrivateKey(ByteString.copyFrom(privateKey.data())) 47 | 48 | for (utx in utxos) { 49 | val txHash = Numeric.hexStringToByteArray(utx["txid"] as String) 50 | txHash.reverse() 51 | val outPoint = Bitcoin.OutPoint.newBuilder() 52 | .setHash(ByteString.copyFrom(txHash)) 53 | .setIndex(utx["vout"] as Int) 54 | .setSequence(Long.MAX_VALUE.toInt()) 55 | .build() 56 | val txScript = Numeric.hexStringToByteArray(utx["script"] as String) 57 | val utxo = Bitcoin.UnspentTransaction.newBuilder() 58 | .setAmount(utx["value"]!!.toLong()) 59 | .setOutPoint(outPoint) 60 | .setScript(ByteString.copyFrom(txScript)) 61 | .build() 62 | input.addUtxo(utxo) 63 | } 64 | 65 | var output = AnySigner.sign(input.build(), coinType, Bitcoin.SigningOutput.parser()) 66 | // since we want to set our own fee 67 | // but such functionality is not obvious in the trustwalletcore library 68 | // a hack is used for now to calculate the byteFee 69 | val size = output.encoded.toByteArray().size 70 | val fees = txData["fees"]!!.toLong() 71 | if (size > 0) { // prevent division by zero 72 | val byteFee = fees.div(size) // this gives the fee per byte truncated to Long 73 | // now we set new byte size 74 | if (byteFee > 1) input.byteFee = byteFee 75 | } 76 | output = AnySigner.sign(input.build(), coinType, Bitcoin.SigningOutput.parser()) 77 | return Numeric.toHexString(output.encoded.toByteArray()) 78 | } 79 | 80 | 81 | override fun multiSignTransaction( 82 | txData: Map, 83 | privateKeys: ArrayList 84 | ): String? { 85 | val utxos: List> = txData["utxos"] as List> 86 | 87 | val input = Bitcoin.SigningInput.newBuilder() 88 | .setAmount(txData["amount"]!!.toLong()) 89 | .setHashType(BitcoinScript.hashTypeForCoin(coinType)) 90 | .setToAddress(txData["toAddress"] as String) 91 | .setChangeAddress(txData["changeAddress"] as String) 92 | .setByteFee(1) 93 | 94 | val byteStrings: MutableList = 95 | privateKeys.map { ByteString.copyFrom(Numeric.hexStringToByteArray(it)) } 96 | .toMutableList() 97 | 98 | input.addAllPrivateKey(byteStrings); 99 | 100 | for (utx in utxos) { 101 | val txHash = Numeric.hexStringToByteArray(utx["txid"] as String) 102 | txHash.reverse() 103 | val outPoint = Bitcoin.OutPoint.newBuilder() 104 | .setHash(ByteString.copyFrom(txHash)) 105 | .setIndex(utx["vout"] as Int) 106 | .setSequence(Long.MAX_VALUE.toInt()) 107 | .build() 108 | val txScript = Numeric.hexStringToByteArray(utx["script"] as String) 109 | val utxo = Bitcoin.UnspentTransaction.newBuilder() 110 | .setAmount(utx["value"]!!.toLong()) 111 | .setOutPoint(outPoint) 112 | .setScript(ByteString.copyFrom(txScript)) 113 | .build() 114 | input.addUtxo(utxo) 115 | } 116 | 117 | var output = AnySigner.sign(input.build(), coinType, Bitcoin.SigningOutput.parser()) 118 | 119 | // since we want to set our own fee 120 | // but such functionality is not obvious in the trustwalletcore library 121 | // a hack is used for now to calculate the byteFee 122 | val size = output.encoded.toByteArray().size 123 | val fees = txData["fees"]!!.toLong() 124 | if (size > 0) { // prevent division by zero 125 | val byteFee = fees.div(size) // this gives the fee per byte truncated to Long 126 | // now we set new byte size 127 | if (byteFee > 1) input.byteFee = byteFee 128 | } 129 | output = AnySigner.sign(input.build(), coinType, Bitcoin.SigningOutput.parser()) 130 | return Numeric.toHexString(output.encoded.toByteArray()) 131 | } 132 | 133 | }; 134 | -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/coins/DOGE.kt: -------------------------------------------------------------------------------- 1 | import africa.ejara.trustdart.Coin 2 | import africa.ejara.trustdart.Numeric 3 | import africa.ejara.trustdart.utils.toLong 4 | import com.google.protobuf.ByteString 5 | import wallet.core.java.AnySigner 6 | import wallet.core.jni.BitcoinAddress 7 | import wallet.core.jni.BitcoinScript 8 | import wallet.core.jni.BitcoinSigHashType 9 | import wallet.core.jni.CoinType 10 | import wallet.core.jni.HDWallet 11 | import wallet.core.jni.proto.Bitcoin 12 | 13 | class DOGE : Coin("DOGE", CoinType.DOGECOIN) { 14 | 15 | override fun signTransaction( 16 | path: String, 17 | txData: Map, 18 | mnemonic: String, 19 | passphrase: String 20 | ): String? { 21 | val wallet = HDWallet(mnemonic, passphrase) 22 | val privateKey = wallet.getKey(coinType, path) 23 | val publicKey = privateKey.getPublicKeySecp256k1(true) 24 | val address = BitcoinAddress(publicKey, coinType!!.p2pkhPrefix()) 25 | val utxos: List> = txData["utxos"] as List> 26 | 27 | val script = BitcoinScript.lockScriptForAddress(address.description(), coinType) 28 | 29 | val input = Bitcoin.SigningInput.newBuilder() 30 | .setAmount(txData["amount"]!!.toLong()) 31 | .setHashType(BitcoinSigHashType.ALL.value()) 32 | .setToAddress(txData["toAddress"] as String) 33 | .setChangeAddress(txData["changeAddress"] as String) 34 | .setByteFee(txData["fees"]!!.toLong()) 35 | .setCoinType(CoinType.DOGECOIN.value()) 36 | .addPrivateKey(ByteString.copyFrom(privateKey.data())) 37 | 38 | 39 | for (utx in utxos) { 40 | val txId = Numeric.hexStringToByteArray(utx["txid"] as String) 41 | val outPoint = Bitcoin.OutPoint.newBuilder() 42 | .setHash(ByteString.copyFrom(txId.reversedArray())) 43 | .setIndex(utx["vout"] as Int) 44 | .build() 45 | 46 | val utxo = Bitcoin.UnspentTransaction.newBuilder() 47 | .setAmount(utx["value"]!!.toLong()) 48 | .setOutPoint(outPoint) 49 | .setScript(ByteString.copyFrom(script.data())) 50 | .build() 51 | 52 | input.addUtxo(utxo) 53 | } 54 | 55 | val output = AnySigner.sign(input.build(), coinType, Bitcoin.SigningOutput.parser()) 56 | 57 | return Numeric.toHexString(output.encoded.toByteArray()) 58 | } 59 | 60 | } 61 | 62 | -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/coins/ETH.kt: -------------------------------------------------------------------------------- 1 | import africa.ejara.trustdart.Coin 2 | import com.google.protobuf.ByteString 3 | import wallet.core.java.AnySigner 4 | import africa.ejara.trustdart.Numeric 5 | import africa.ejara.trustdart.utils.toHexByteArray 6 | import wallet.core.jni.CoinType 7 | import wallet.core.jni.HDWallet 8 | import wallet.core.jni.proto.Ethereum.SigningOutput 9 | import wallet.core.jni.proto.Ethereum 10 | 11 | open class ETH : Coin("ETH", CoinType.ETHEREUM) { 12 | 13 | override fun signTransaction( 14 | path: String, 15 | txData: Map, 16 | mnemonic: String, 17 | passphrase: String 18 | ): String? { 19 | val wallet = HDWallet(mnemonic, passphrase) 20 | val cmd: String? = txData["cmd"] as? String 21 | val signingInput = Ethereum.SigningInput.newBuilder() 22 | 23 | signingInput.apply { 24 | privateKey = ByteString.copyFrom(wallet.getKey(coinType, path).data()) 25 | chainId = ByteString.copyFrom((txData["chainId"] as String).toHexByteArray()) 26 | nonce = ByteString.copyFrom((txData["nonce"] as String).toHexByteArray()) 27 | gasPrice = ByteString.copyFrom((txData["gasPrice"] as String).toHexByteArray()) 28 | gasLimit = ByteString.copyFrom((txData["gasLimit"] as String).toHexByteArray()) 29 | } 30 | when (cmd) { 31 | "ERC20" -> { 32 | 33 | var transaction = Ethereum.Transaction.newBuilder().apply { 34 | erc20Transfer = Ethereum.Transaction.ERC20Transfer.newBuilder().apply { 35 | to = txData["toAddress"] as String 36 | amount = ByteString.copyFrom((txData["amount"] as String).toHexByteArray()) 37 | }.build() 38 | }.build() 39 | signingInput.setToAddress(txData["contractAddress"] as String) 40 | signingInput.setTransaction(transaction) 41 | 42 | } 43 | 44 | else -> { 45 | var transaction = Ethereum.Transaction.newBuilder().apply { 46 | transfer = Ethereum.Transaction.Transfer.newBuilder().apply { 47 | amount = ByteString.copyFrom((txData["amount"] as String).toHexByteArray()) 48 | }.build() 49 | }.build() 50 | 51 | signingInput.setToAddress(txData["toAddress"] as String) 52 | signingInput.setTransaction(transaction) 53 | } 54 | } 55 | val sign = AnySigner.sign(signingInput.build(), coinType, SigningOutput.parser()) 56 | return Numeric.toHexString(sign.encoded.toByteArray()) 57 | 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/coins/NEAR.kt: -------------------------------------------------------------------------------- 1 | import com.google.protobuf.ByteString 2 | import wallet.core.java.AnySigner 3 | import wallet.core.jni.CoinType 4 | import wallet.core.jni.proto.NEAR 5 | import wallet.core.jni.proto.NEAR.SigningOutput 6 | import wallet.core.jni.Base58 7 | import wallet.core.jni.HDWallet 8 | import africa.ejara.trustdart.Coin 9 | import africa.ejara.trustdart.Numeric 10 | import africa.ejara.trustdart.utils.base64String 11 | import africa.ejara.trustdart.utils.toLong 12 | 13 | class NEAR : Coin("NEAR", CoinType.NEAR) { 14 | 15 | override fun getPublicKey(path: String, mnemonic: String, passphrase: String): String? { 16 | val wallet = HDWallet(mnemonic, passphrase) 17 | return wallet.getKey(coinType, path).publicKeyEd25519.data().base64String() 18 | } 19 | 20 | override fun getRawPublicKey(path: String, mnemonic: String, passphrase: String): ByteArray? { 21 | val wallet = HDWallet(mnemonic, passphrase) 22 | return wallet.getKey(coinType, path).publicKeyEd25519.data() 23 | } 24 | 25 | override fun signTransaction( 26 | path: String, 27 | txData: Map, 28 | mnemonic: String, 29 | passphrase: String 30 | ): String? { 31 | val wallet = HDWallet(mnemonic, passphrase) 32 | val secretKey = wallet.getKey(coinType, path) 33 | val transferAction = NEAR.Transfer.newBuilder().apply { 34 | deposit = 35 | ByteString.copyFrom(Numeric.hexStringToByteArray((txData["amount"] as String))) 36 | }.build() 37 | val signingInput = NEAR.SigningInput.newBuilder().apply { 38 | signerId = txData["signerID"] as String 39 | nonce = txData["nonce"]!!.toLong() 40 | receiverId = txData["receiverID"] as String 41 | addActions(NEAR.Action.newBuilder().apply { 42 | transfer = transferAction 43 | }) 44 | blockHash = ByteString.copyFrom(Base58.decodeNoCheck(txData["blockHash"] as String)) 45 | privateKey = ByteString.copyFrom(secretKey.data()) 46 | }.build() 47 | 48 | val output = AnySigner.sign(signingInput, CoinType.NEAR, SigningOutput.parser()) 49 | return Numeric.toHexString(output.signedTransaction.toByteArray()) 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/coins/SOL.kt: -------------------------------------------------------------------------------- 1 | import africa.ejara.trustdart.Coin 2 | import africa.ejara.trustdart.utils.base64String 3 | import wallet.core.jni.CoinType 4 | import wallet.core.jni.HDWallet 5 | 6 | 7 | class SOL : Coin("SOL", CoinType.SOLANA) { 8 | 9 | override fun getPublicKey(path: String, mnemonic: String, passphrase: String): String? { 10 | val wallet = HDWallet(mnemonic, passphrase) 11 | return wallet.getKey(coinType, path).publicKeyEd25519.data().base64String() 12 | } 13 | 14 | override fun getRawPublicKey(path: String, mnemonic: String, passphrase: String): ByteArray? { 15 | val wallet = HDWallet(mnemonic, passphrase) 16 | return wallet.getKey(coinType, path).publicKeyEd25519.data() 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/coins/TRX.kt: -------------------------------------------------------------------------------- 1 | import africa.ejara.trustdart.Coin 2 | import africa.ejara.trustdart.Numeric 3 | import africa.ejara.trustdart.utils.toLong 4 | import com.google.protobuf.ByteString 5 | import wallet.core.java.AnySigner 6 | import wallet.core.jni.CoinType 7 | import wallet.core.jni.HDWallet 8 | import wallet.core.jni.proto.Tron 9 | 10 | class TRX : Coin("TRX", CoinType.TRON) { 11 | 12 | override fun signTransaction( 13 | path: String, 14 | txData: Map, 15 | mnemonic: String, 16 | passphrase: String 17 | ): String? { 18 | val cmd = txData["cmd"] as String 19 | val wallet = HDWallet(mnemonic, passphrase) 20 | val privateKey = wallet.getKey(coinType, path) 21 | val txHash: String? 22 | when (cmd) { 23 | "TRC20" -> { 24 | val trc20Contract = Tron.TransferTRC20Contract.newBuilder() 25 | .setOwnerAddress(txData["ownerAddress"] as String) 26 | .setContractAddress(txData["contractAddress"] as String) 27 | .setToAddress(txData["toAddress"] as String) 28 | .setAmount(ByteString.copyFrom(Numeric.hexStringToByteArray((txData["amount"] as String)))) 29 | 30 | val blockHeader = Tron.BlockHeader.newBuilder() 31 | .setTimestamp(txData["blockTime"]!!.toLong()) 32 | .setTxTrieRoot(ByteString.copyFrom(Numeric.hexStringToByteArray((txData["txTrieRoot"] as String)))) 33 | .setParentHash(ByteString.copyFrom(Numeric.hexStringToByteArray((txData["parentHash"] as String)))) 34 | .setNumber(txData["number"]!!.toLong()) 35 | .setWitnessAddress(ByteString.copyFrom(Numeric.hexStringToByteArray((txData["witnessAddress"] as String)))) 36 | .setVersion(txData["version"] as Int) 37 | .build() 38 | 39 | val transaction = Tron.Transaction.newBuilder() 40 | .setTransferTrc20Contract(trc20Contract) 41 | .setTimestamp(txData["timestamp"]!!.toLong()) 42 | .setFeeLimit(txData["feeLimit"]!!.toLong()) 43 | .setBlockHeader(blockHeader) 44 | .build() 45 | 46 | val signingInput = Tron.SigningInput.newBuilder() 47 | .setTransaction(transaction) 48 | .setPrivateKey(ByteString.copyFrom(privateKey.data())) 49 | 50 | val output = 51 | AnySigner.sign(signingInput.build(), coinType, Tron.SigningOutput.parser()) 52 | txHash = output.json 53 | } 54 | 55 | "TRC10" -> { 56 | val trc10Contract = Tron.TransferAssetContract.newBuilder() 57 | .setOwnerAddress(txData["ownerAddress"] as String) 58 | .setAssetName(txData["assetName"] as String) 59 | .setToAddress(txData["toAddress"] as String) 60 | .setAmount(txData["amount"]!!.toLong()) 61 | 62 | val blockHeader = Tron.BlockHeader.newBuilder() 63 | .setTimestamp(txData["blockTime"]!!.toLong()) 64 | .setTxTrieRoot(ByteString.copyFrom(Numeric.hexStringToByteArray((txData["txTrieRoot"] as String)))) 65 | .setParentHash(ByteString.copyFrom(Numeric.hexStringToByteArray((txData["parentHash"] as String)))) 66 | .setNumber(txData["number"]!!.toLong()) 67 | .setWitnessAddress(ByteString.copyFrom(Numeric.hexStringToByteArray((txData["witnessAddress"] as String)))) 68 | .setVersion(txData["version"] as Int) 69 | .build() 70 | 71 | val transaction = Tron.Transaction.newBuilder() 72 | .setTransferAsset(trc10Contract) 73 | .setTimestamp(txData["timestamp"]!!.toLong()) 74 | .setBlockHeader(blockHeader) 75 | .build() 76 | 77 | val signingInput = Tron.SigningInput.newBuilder() 78 | .setTransaction(transaction) 79 | .setPrivateKey(ByteString.copyFrom(privateKey.data())) 80 | 81 | val output = 82 | AnySigner.sign(signingInput.build(), coinType, Tron.SigningOutput.parser()) 83 | txHash = output.json 84 | } 85 | 86 | "TRX" -> { 87 | val transfer = Tron.TransferContract.newBuilder() 88 | .setOwnerAddress(txData["ownerAddress"] as String) 89 | .setToAddress(txData["toAddress"] as String) 90 | .setAmount(txData["amount"]!!.toLong()) 91 | 92 | val blockHeader = Tron.BlockHeader.newBuilder() 93 | .setTimestamp(txData["blockTime"]!!.toLong()) 94 | .setTxTrieRoot(ByteString.copyFrom(Numeric.hexStringToByteArray((txData["txTrieRoot"] as String)))) 95 | .setParentHash(ByteString.copyFrom(Numeric.hexStringToByteArray((txData["parentHash"] as String)))) 96 | .setNumber(txData["number"]!!.toLong()) 97 | .setWitnessAddress(ByteString.copyFrom(Numeric.hexStringToByteArray((txData["witnessAddress"] as String)))) 98 | .setVersion(txData["version"] as Int) 99 | .build() 100 | 101 | val transaction = Tron.Transaction.newBuilder() 102 | .setTimestamp(txData["timestamp"]!!.toLong()) 103 | .setFeeLimit(txData["feeLimit"]!!.toLong()) 104 | .setTransfer(transfer) 105 | .setBlockHeader(blockHeader) 106 | .build() 107 | 108 | val signingInput = Tron.SigningInput.newBuilder() 109 | .setTransaction(transaction) 110 | .setPrivateKey(ByteString.copyFrom(privateKey.data())) 111 | 112 | val output = 113 | AnySigner.sign(signingInput.build(), coinType, Tron.SigningOutput.parser()) 114 | txHash = output.json 115 | } 116 | 117 | "FREEZE" -> { 118 | val freezeContract = Tron.FreezeBalanceContract.newBuilder() 119 | .setOwnerAddress(txData["ownerAddress"] as String) 120 | .setResource(txData["resource"] as String) 121 | .setFrozenDuration(txData["frozenDuration"]!!.toLong()) 122 | .setFrozenBalance(txData["frozenBalance"]!!.toLong()) 123 | 124 | val blockHeader = Tron.BlockHeader.newBuilder() 125 | .setTimestamp(txData["blockTime"]!!.toLong()) 126 | .setTxTrieRoot(ByteString.copyFrom(Numeric.hexStringToByteArray((txData["txTrieRoot"] as String)))) 127 | .setParentHash(ByteString.copyFrom(Numeric.hexStringToByteArray((txData["parentHash"] as String)))) 128 | .setNumber(txData["number"]!!.toLong()) 129 | .setWitnessAddress(ByteString.copyFrom(Numeric.hexStringToByteArray((txData["witnessAddress"] as String)))) 130 | .setVersion(txData["version"] as Int) 131 | .build() 132 | 133 | val transaction = Tron.Transaction.newBuilder() 134 | .setTimestamp(txData["timestamp"]!!.toLong()) 135 | .setFreezeBalance(freezeContract) 136 | .setBlockHeader(blockHeader) 137 | .build() 138 | 139 | val signingInput = Tron.SigningInput.newBuilder() 140 | .setTransaction(transaction) 141 | .setPrivateKey(ByteString.copyFrom(privateKey.data())) 142 | 143 | val output = 144 | AnySigner.sign(signingInput.build(), coinType, Tron.SigningOutput.parser()) 145 | txHash = output.json 146 | } 147 | 148 | "CONTRACT" -> { 149 | txHash = null 150 | } 151 | 152 | else -> txHash = null 153 | } 154 | return txHash 155 | } 156 | 157 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/coins/XLM.kt: -------------------------------------------------------------------------------- 1 | import com.google.protobuf.ByteString 2 | import wallet.core.java.AnySigner 3 | import wallet.core.jni.CoinType 4 | import wallet.core.jni.HDWallet 5 | import africa.ejara.trustdart.Coin 6 | import africa.ejara.trustdart.utils.base64String 7 | import africa.ejara.trustdart.utils.toLong 8 | import wallet.core.jni.proto.Stellar 9 | import wallet.core.jni.proto.Stellar.SigningOutput 10 | 11 | 12 | class XLM : Coin("XLM", CoinType.STELLAR) { 13 | 14 | enum class NetworkType(val passphrase: String) { 15 | MAINNET("Public Global Stellar Network ; September 2015"), 16 | TESTNET("Test SDF Network ; September 2015"); 17 | 18 | companion object { 19 | fun fromString(network: String?): NetworkType { 20 | return when (network?.lowercase()) { 21 | "testnet" -> TESTNET 22 | else -> MAINNET // Default to mainnet 23 | } 24 | } 25 | } 26 | } 27 | 28 | override fun getPublicKey(path: String, mnemonic: String, passphrase: String): String? { 29 | val wallet = HDWallet(mnemonic, passphrase) 30 | return wallet.getKey(coinType, path).publicKeyEd25519.data().base64String() 31 | } 32 | 33 | override fun getRawPublicKey(path: String, mnemonic: String, passphrase: String): ByteArray? { 34 | val wallet = HDWallet(mnemonic, passphrase) 35 | return wallet.getKey(coinType, path).publicKeyEd25519.data() 36 | } 37 | 38 | override fun signTransaction( 39 | path: String, 40 | txData: Map, 41 | mnemonic: String, 42 | pass_phrase: String 43 | ): String? { 44 | val cmd = txData["cmd"] as String 45 | val networkType: NetworkType = { 46 | val network = txData["network"] as? String 47 | NetworkType.fromString(network) 48 | }() 49 | 50 | val secretKey = HDWallet(mnemonic, pass_phrase).getKey(coinType, path) 51 | val txHash: String? 52 | when (cmd) { 53 | "ChangeTrust" -> { 54 | val assetUsdt = Stellar.Asset.newBuilder() 55 | assetUsdt.apply { 56 | issuer = txData["toAddress"] as String 57 | alphanum4 = txData["assetCode"] as String 58 | } 59 | val operation = Stellar.OperationChangeTrust.newBuilder() 60 | operation.apply { 61 | asset = assetUsdt.build() 62 | validBefore = 63 | txData["validBefore"]!!.toLong() // till when the asset trust is valid (timestamp) 64 | } 65 | val signingInput = Stellar.SigningInput.newBuilder() 66 | signingInput.apply { 67 | account = txData["ownerAddress"] as String 68 | fee = txData["fee"] as Int 69 | sequence = txData["sequence"]!!.toLong() 70 | passphrase = networkType.passphrase 71 | opChangeTrust = operation.build() 72 | privateKey = ByteString.copyFrom(secretKey.data()) 73 | } 74 | val output = AnySigner.sign(signingInput.build(), coinType, SigningOutput.parser()) 75 | txHash = output.signature 76 | } 77 | 78 | "Payment" -> { 79 | val stellarAsset = Stellar.Asset.newBuilder() 80 | if (txData["asset"] != null && txData["issuer"] != null) { 81 | stellarAsset.apply { 82 | issuer = txData["issuer"] as String 83 | alphanum4 = txData["asset"] as String 84 | } 85 | } 86 | val operation = Stellar.OperationPayment.newBuilder() 87 | operation.apply { 88 | destination = txData["toAddress"] as String 89 | amount = txData["amount"]!!.toLong() 90 | if (txData["asset"] != null) { 91 | asset = stellarAsset.build() 92 | } 93 | } 94 | val signingInput = Stellar.SigningInput.newBuilder() 95 | signingInput.apply { 96 | account = txData["ownerAddress"] as String 97 | fee = txData["fee"] as Int 98 | sequence = txData["sequence"]!!.toLong() 99 | passphrase = networkType.passphrase 100 | opPayment = operation.build() 101 | privateKey = ByteString.copyFrom(secretKey.data()) 102 | if (txData["memo"] != null) { 103 | memoId = Stellar.MemoId.newBuilder().setId(txData["memo"]!!.toLong()) 104 | .build() 105 | } 106 | } 107 | val output = AnySigner.sign(signingInput.build(), coinType, SigningOutput.parser()) 108 | txHash = output.signature 109 | 110 | } 111 | 112 | else -> txHash = null 113 | } 114 | return txHash 115 | } 116 | 117 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/coins/XRP.kt: -------------------------------------------------------------------------------- 1 | import africa.ejara.trustdart.Coin 2 | import com.google.protobuf.ByteString 3 | import wallet.core.java.AnySigner 4 | import africa.ejara.trustdart.Numeric 5 | import africa.ejara.trustdart.utils.toInt 6 | import africa.ejara.trustdart.utils.toLong 7 | import wallet.core.jni.CoinType 8 | import wallet.core.jni.HDWallet 9 | import wallet.core.jni.proto.Ethereum.SigningOutput 10 | import wallet.core.jni.proto.Ripple 11 | 12 | class XRP: Coin("XRP", CoinType.XRP) { 13 | 14 | override fun signTransaction( 15 | path: String, 16 | txData: Map, 17 | mnemonic: String, 18 | passphrase: String 19 | ): String? { 20 | val wallet = HDWallet(mnemonic, passphrase) 21 | val signingInput = Ripple.SigningInput.newBuilder() 22 | val operation = Ripple.OperationPayment.newBuilder() 23 | 24 | operation.apply { 25 | amount = txData["amount"]!!.toLong() 26 | destination = txData["receiverAddress"] as String 27 | if (txData["memo"] != null) { 28 | destinationTag = txData["memo"]!!.toLong() 29 | } 30 | } 31 | 32 | signingInput.apply { 33 | account = txData["senderAddress" ] as String 34 | fee = txData["fee"]!!.toLong() 35 | sequence = txData["sequence"]!!.toInt() 36 | lastLedgerSequence = txData["lastLedgerSequence"]!!.toInt() 37 | privateKey = ByteString.copyFrom(wallet.getKey(coinType, path).data()) 38 | opPayment = operation.build() 39 | } 40 | 41 | val sign = AnySigner.sign(signingInput.build(), coinType, SigningOutput.parser()) 42 | return Numeric.toHexString(sign.encoded.toByteArray()) 43 | 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/coins/XTZ.kt: -------------------------------------------------------------------------------- 1 | import africa.ejara.trustdart.Coin 2 | import africa.ejara.trustdart.Numeric 3 | import africa.ejara.trustdart.utils.base64String 4 | import africa.ejara.trustdart.utils.toHex 5 | import africa.ejara.trustdart.utils.toHexBytes 6 | import africa.ejara.trustdart.utils.toLong 7 | import com.google.protobuf.ByteString 8 | import org.json.JSONObject 9 | import wallet.core.java.AnySigner 10 | import wallet.core.jni.CoinType 11 | import wallet.core.jni.HDWallet 12 | import wallet.core.jni.Hash 13 | import wallet.core.jni.proto.Tezos.* 14 | 15 | 16 | class XTZ : Coin("XTZ", CoinType.TEZOS) { 17 | 18 | override fun getPublicKey(path: String, mnemonic: String, passphrase: String): String? { 19 | val wallet = HDWallet(mnemonic, passphrase) 20 | return wallet.getKey(coinType, path).publicKeyEd25519.data().base64String() 21 | } 22 | 23 | override fun getRawPublicKey(path: String, mnemonic: String, passphrase: String): ByteArray? { 24 | val wallet = HDWallet(mnemonic, passphrase) 25 | return wallet.getKey(coinType, path) 26 | .publicKeyEd25519.data() 27 | } 28 | 29 | override fun signDataWithPrivateKey( 30 | path: String, 31 | mnemonic: String, 32 | passphrase: String, 33 | txData: String 34 | ): String? { 35 | val wallet = HDWallet(mnemonic, passphrase) 36 | val privateKey = wallet.getKey(coinType, path) 37 | val hash: ByteArray? = Hash.blake2b(txData.toHexBytes(), 32) 38 | return privateKey.sign(hash, coinType!!.curve()).toHex() 39 | } 40 | 41 | override fun signTransaction( 42 | path: String, 43 | txData: Map, 44 | mnemonic: String, 45 | passphrase: String 46 | ): String? { 47 | val cmd: String? = txData["cmd"] as String? 48 | val wallet = HDWallet(mnemonic, passphrase) 49 | val privateKey = wallet.getKey(coinType, path) 50 | val txHash: String? 51 | val publicKey = privateKey.publicKeyEd25519.data() 52 | val isRevealed = txData["isRevealed"] as Boolean? 53 | var revealOperation: Operation 54 | val listOfAllOperations = mutableListOf(); 55 | 56 | if (isRevealed == false) { 57 | val revealOperationData = RevealOperationData.newBuilder() 58 | .setPublicKey(ByteString.copyFrom(publicKey)) 59 | 60 | revealOperation = Operation.newBuilder() 61 | .setSource(txData["source"] as String) 62 | .setFee(txData["reveal_fee"]!!.toLong()) 63 | .setCounter(txData["reveal_counter"]!!.toLong()) 64 | .setGasLimit(txData["reveal_gasLimit"]!!.toLong()) 65 | .setStorageLimit(txData["reveal_storageLimit"]!!.toLong()) 66 | .setKind(Operation.OperationKind.REVEAL) 67 | .setRevealOperationData(revealOperationData) 68 | .build() 69 | 70 | listOfAllOperations.add(revealOperation) 71 | } 72 | 73 | when (cmd) { 74 | "FA2" -> { 75 | val transferInfos = Txs.newBuilder() 76 | .setAmount(txData["amount"] as String) 77 | .setTokenId(txData["tokenId"] as String) 78 | .setTo(txData["toAddress"] as String) 79 | .build() 80 | 81 | val txObj = TxObject.newBuilder() 82 | .setFrom(txData["senderAddress"] as String) 83 | .addTxs(transferInfos) 84 | .build() 85 | 86 | val fa2 = FA2Parameters.newBuilder() 87 | .setEntrypoint("transfer") 88 | .addTxsObject(txObj) 89 | .build() 90 | 91 | val parameters = OperationParameters.newBuilder() 92 | .setFa2Parameters(fa2) 93 | .build() 94 | 95 | val transactionData = TransactionOperationData.newBuilder() 96 | .setAmount(txData["transactionAmount"]!!.toLong()) 97 | .setDestination(txData["destination"] as String) 98 | .setParameters(parameters) 99 | .build() 100 | 101 | val transaction = Operation.newBuilder() 102 | .setSource(txData["source"] as String) 103 | .setFee(txData["fee"]!!.toLong()) 104 | .setCounter(txData["counter"]!!.toLong()) 105 | .setGasLimit(txData["gasLimit"]!!.toLong()) 106 | .setStorageLimit(txData["storageLimit"]!!.toLong()) 107 | .setKind(Operation.OperationKind.TRANSACTION) 108 | .setTransactionOperationData(transactionData) 109 | .build() 110 | 111 | listOfAllOperations.add(transaction) 112 | 113 | val operationList = OperationList.newBuilder() 114 | .setBranch(txData["branch"] as String) 115 | .addAllOperations(listOfAllOperations) 116 | .build(); 117 | 118 | val signingInput = SigningInput.newBuilder() 119 | .setPrivateKey(ByteString.copyFrom(privateKey.data())) 120 | .setOperationList(operationList) 121 | .build() 122 | 123 | val result = AnySigner.sign(signingInput, coinType, SigningOutput.parser()) 124 | txHash = Numeric.toHexString(result.encoded.toByteArray()) 125 | } 126 | 127 | "FA12" -> { 128 | val fa12 = FA12Parameters.newBuilder() 129 | .setEntrypoint("transfer") 130 | .setFrom(txData["senderAddress"] as String) 131 | .setTo(txData["toAddress"] as String) 132 | .setValue(txData["value"] as String) 133 | .build() 134 | 135 | val parameters = OperationParameters.newBuilder() 136 | .setFa12Parameters(fa12) 137 | .build() 138 | 139 | val transactionData = TransactionOperationData.newBuilder() 140 | .setAmount(txData["transactionAmount"]!!.toLong()) 141 | .setDestination(txData["destination"] as String) 142 | .setParameters(parameters) 143 | .build() 144 | 145 | val transaction = Operation.newBuilder() 146 | .setSource(txData["source"] as String) 147 | .setFee(txData["fee"]!!.toLong()) 148 | .setCounter(txData["counter"]!!.toLong()) 149 | .setGasLimit(txData["gasLimit"]!!.toLong()) 150 | .setStorageLimit(txData["storageLimit"]!!.toLong()) 151 | .setKind(Operation.OperationKind.TRANSACTION) 152 | .setTransactionOperationData(transactionData) 153 | .build() 154 | 155 | listOfAllOperations.add(transaction) 156 | 157 | val operationList = OperationList.newBuilder() 158 | .setBranch(txData["branch"] as String) 159 | .addAllOperations(listOfAllOperations) 160 | .build(); 161 | 162 | val signingInput = SigningInput.newBuilder() 163 | .setPrivateKey(ByteString.copyFrom(privateKey.data())) 164 | .setOperationList(operationList) 165 | .build() 166 | 167 | val result = AnySigner.sign(signingInput, coinType, SigningOutput.parser()) 168 | txHash = Numeric.toHexString(result.encoded.toByteArray()) 169 | 170 | } 171 | 172 | else -> { 173 | val opJson = JSONObject(txData).toString() 174 | val result = AnySigner.signJSON(opJson, privateKey.data(), coinType!!.value()) 175 | txHash = result 176 | } 177 | } 178 | return txHash 179 | } 180 | 181 | 182 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/interfaces/CoinInterface.kt: -------------------------------------------------------------------------------- 1 | package africa.ejara.trustdart.interfaces 2 | 3 | import com.google.protobuf.ByteString 4 | 5 | interface CoinInterface { 6 | fun generateAddress(path: String, mnemonic: String, passphrase: String): Map? 7 | fun getSeed(path: String, mnemonic: String, passphrase: String): ByteArray? 8 | fun getPrivateKey(path: String, mnemonic: String, passphrase: String): String? 9 | fun getRawPrivateKey(path: String, mnemonic: String, passphrase: String): ByteArray? 10 | fun getPublicKey(path: String, mnemonic: String, passphrase: String): String? 11 | fun getRawPublicKey(path: String, mnemonic: String, passphrase: String): ByteArray? 12 | fun validateAddress(address: String): Boolean 13 | fun signDataWithPrivateKey(path: String, mnemonic: String, passphrase: String, txData: String): String? 14 | fun signTransaction(path: String, txData: Map, mnemonic: String, passphrase: String): String? 15 | fun multiSignTransaction( 16 | txData: Map, 17 | privateKeys: ArrayList 18 | ): String? 19 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/utils/Extension.kt: -------------------------------------------------------------------------------- 1 | package africa.ejara.trustdart.utils 2 | 3 | import africa.ejara.trustdart.Numeric 4 | import android.util.Base64 5 | import com.google.protobuf.ByteString 6 | 7 | fun ByteArray.toHex(): String { 8 | return Numeric.toHexString(this) 9 | } 10 | 11 | fun String.toHexBytes(): ByteArray { 12 | return Numeric.hexStringToByteArray(this) 13 | } 14 | 15 | fun String.toHexByteArray(): ByteArray { 16 | return Numeric.hexStringToByteArray(this) 17 | } 18 | 19 | fun String.toByteString(): ByteString { 20 | return ByteString.copyFrom(this, Charsets.UTF_8) 21 | } 22 | 23 | fun String.toHexBytesInByteString(): ByteString { 24 | return ByteString.copyFrom(this.toHexBytes()) 25 | } 26 | 27 | fun ByteArray.base64String(): String { 28 | return Base64.encodeToString(this, Base64.DEFAULT).toString() 29 | } 30 | 31 | fun Any.toLong(): Long { 32 | return when (this) { 33 | is Int -> this.toLong() 34 | is Long -> this 35 | is String -> this.toLongOrNull() ?: throw NumberFormatException("Cannot convert $this to Long") 36 | else -> throw IllegalArgumentException("Unsupported type") 37 | } 38 | } 39 | 40 | fun Any.toInt(): Int { 41 | return when (this) { 42 | is Int -> this.toInt() 43 | is String -> this.toIntOrNull() ?: throw NumberFormatException("Cannot convert $this to Long") 44 | else -> throw IllegalArgumentException("Unsupported type") 45 | } 46 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/africa/ejara/trustdart/utils/WalletError.kt: -------------------------------------------------------------------------------- 1 | package africa.ejara.trustdart.utils 2 | 3 | 4 | class WalletError( 5 | errorCode: WalletHandlerErrorCodes, 6 | val errorMessage: String?, 7 | val errorDetails: Any? 8 | ) { 9 | val errorCode: String = errorCode.value 10 | } 11 | 12 | data class WalletValidateResponse(val isValid: Boolean, val details: WalletError) 13 | 14 | enum class WalletHandlerErrorCodes(val value: String) { 15 | NoWallet("no_wallet"), 16 | AddressNull("address_null"), 17 | ArgumentsNull("arguments_null"), 18 | TxHashNull("txhash_null") 19 | } 20 | 21 | class ErrorResponse { 22 | companion object { 23 | const val argumentsNull = "[path], [coin], [mnemonic] and [passphrase] are required." 24 | const val privateKeyNull = "Could not generate private key." 25 | const val publicKeyNull = "Could not generate public key." 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /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 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | 48 | ios/build/Pods.build/ 49 | ios/Pods/ 50 | ios/Podfile.lock 51 | android/.settings/ 52 | android/app/.classpath 53 | android/app/.project 54 | pubspec.lock 55 | .fvm/ 56 | .zshrc 57 | 58 | # Code scanning tool 59 | /.scannerwork/ 60 | */.scannerwork/* 61 | -------------------------------------------------------------------------------- /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: b22742018b3edf16c6cadd7b76d9db5e7f9064b5 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # trustdart_example 2 | 3 | Demonstrates how to use the trustdart plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | analyzer: 4 | language: 5 | strict-inference: true 6 | errors: 7 | prefer_const_constructors: error 8 | avoid_empty_else: error 9 | avoid_unused_constructor_parameters: error 10 | always_put_required_named_parameters_first: error 11 | avoid_relative_lib_imports: error 12 | always_use_package_imports: error 13 | # lines_longer_than_80_chars: error 14 | 15 | 16 | 17 | 18 | linter: 19 | rules: 20 | prefer_const_declarations: true 21 | avoid_empty_else: true 22 | avoid_unused_constructor_parameters: true 23 | always_put_required_named_parameters_first: true 24 | avoid_relative_lib_imports: true 25 | always_use_package_imports: true 26 | prefer_const_constructors: true 27 | lines_longer_than_80_chars: true -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | kotlin { 26 | jvmToolchain(17) 27 | } 28 | 29 | android { 30 | namespace 'africa.ejara.trustdart_example' 31 | compileSdkVersion 34 32 | 33 | sourceSets { 34 | main.java.srcDirs += 'src/main/kotlin' 35 | } 36 | 37 | compileOptions { 38 | sourceCompatibility JavaVersion.VERSION_17 39 | targetCompatibility JavaVersion.VERSION_17 40 | } 41 | 42 | defaultConfig { 43 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 44 | applicationId "africa.ejara.trustdart_example" 45 | minSdkVersion 23 46 | targetSdkVersion 34 47 | versionCode flutterVersionCode.toInteger() 48 | versionName flutterVersionName 49 | } 50 | 51 | buildTypes { 52 | release { 53 | // TODO: Add your own signing config for the release build. 54 | // Signing with the debug keys for now, so `flutter run --release` works. 55 | signingConfig signingConfigs.debug 56 | } 57 | } 58 | } 59 | 60 | flutter { 61 | source '../..' 62 | } -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 14 | 18 | 22 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/africa/ejara/trustdart_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package africa.ejara.trustdart_example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | 7 | subprojects { 8 | afterEvaluate { project -> 9 | if (project.hasProperty('android')) { 10 | project.android { 11 | if (namespace == null) { 12 | namespace project.group 13 | } 14 | } 15 | } 16 | } 17 | } 18 | } 19 | 20 | rootProject.buildDir = '../build' 21 | subprojects { 22 | project.buildDir = "${rootProject.buildDir}/${project.name}" 23 | } 24 | subprojects { 25 | project.evaluationDependsOn(':app') 26 | } 27 | 28 | tasks.register("clean", Delete) { 29 | delete rootProject.buildDir 30 | } -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Nov 23 10:00:56 GMT 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.1.1" apply false 22 | id "org.jetbrains.kotlin.android" version "1.9.0" apply false 23 | } 24 | 25 | include ":app" -------------------------------------------------------------------------------- /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/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /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/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '12.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 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.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/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/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/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /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/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 | trustdart_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 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/coins.dart: -------------------------------------------------------------------------------- 1 | class Coin { 2 | String code; 3 | String path; 4 | 5 | Coin({ 6 | required this.code, 7 | required this.path, 8 | }); 9 | } 10 | 11 | List coinList = [ 12 | Coin(code: 'BTC', path: "m/44'/0'/0'/0/0"), 13 | Coin(code: 'ETH', path: "m/44'/60'/0'/0/0"), 14 | Coin(code: 'XTZ', path: "m/44'/1729'/0'/0'"), 15 | Coin(code: 'SOL', path: "m/44'/501'/0'/0/0"), 16 | Coin(code: 'NEAR', path: "m/44'/397'/0'/0/0"), 17 | Coin(code: 'TRX', path: "m/44'/195'/0'/0/0"), 18 | Coin(code: 'XLM', path: "m/44'/148'/0'"), 19 | Coin(code: 'BNB', path: "m/44'/714'/0'/0/0"), 20 | Coin(code: 'DOGE', path: "m/44'/3'/0'/0/0"), 21 | Coin(code: 'ADA', path: "m/1852'/1815'/0'/0/0"), 22 | Coin(code: 'XRP', path: "m/44'/144'/0'/0/0"), 23 | ]; 24 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:trustdart_example/operations.dart'; 5 | 6 | void main() { 7 | runApp(MyApp()); 8 | } 9 | 10 | class MyApp extends StatefulWidget { 11 | @override 12 | _MyAppState createState() => _MyAppState(); 13 | } 14 | 15 | class _MyAppState extends State { 16 | @override 17 | void initState() { 18 | super.initState(); 19 | initPlatformState(); 20 | } 21 | 22 | // Platform messages are asynchronous, so we initialize in an async method. 23 | Future initPlatformState() async { 24 | runOperations(); 25 | 26 | // If the widget was removed from the tree while the asynchronous platform 27 | // message was in flight, we want to discard the reply rather than calling 28 | // setState to update our non-existent appearance. 29 | if (!mounted) return; 30 | } 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | return MaterialApp( 35 | home: Scaffold( 36 | appBar: AppBar( 37 | title: const Text('Plugin example app'), 38 | ), 39 | body: Center( 40 | child: Column( 41 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 42 | children: [ 43 | Container( 44 | child: TextButton( 45 | style: TextButton.styleFrom( 46 | foregroundColor: Colors.black, 47 | padding: const EdgeInsets.all(16.0), 48 | backgroundColor: Colors.red, 49 | textStyle: const TextStyle(fontSize: 20), 50 | ), 51 | onPressed: () {}, 52 | child: const Text('Create a new multi-coin wallet'), 53 | ), 54 | ), 55 | Container( 56 | child: TextButton( 57 | style: TextButton.styleFrom( 58 | foregroundColor: Colors.black, 59 | padding: const EdgeInsets.all(16.0), 60 | backgroundColor: Colors.yellow, 61 | textStyle: const TextStyle(fontSize: 20), 62 | ), 63 | onPressed: () {}, 64 | child: const Text('Generate the default addresses.'), 65 | ), 66 | ), 67 | Container( 68 | child: TextButton( 69 | style: TextButton.styleFrom( 70 | foregroundColor: Colors.black, 71 | padding: const EdgeInsets.all(16.0), 72 | backgroundColor: Colors.green, 73 | textStyle: const TextStyle(fontSize: 20), 74 | ), 75 | onPressed: () {}, 76 | child: const Text('Sign transactions for sending.'), 77 | ), 78 | ), 79 | ], 80 | ), 81 | ), 82 | ), 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /example/lib/operations.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:trustdart/trustdart.dart'; 4 | import 'package:trustdart_example/coins.dart'; 5 | 6 | Map operations = { 7 | 'XTZ': { 8 | "operationList": { 9 | "branch": "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp", 10 | "operations": [ 11 | { 12 | "source": "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW", 13 | "fee": 1272, 14 | "counter": 30738, 15 | "gasLimit": 10100, 16 | "storageLimit": 257, 17 | "kind": 107, 18 | "revealOperationData": { 19 | "publicKey": "8z6GkG6TaQVnpYr2gc6r8Q/mS7m0Qf6Ef9VinW8mKXM=" 20 | } 21 | }, 22 | { 23 | "source": "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW", 24 | "fee": 1272, 25 | "counter": 30739, 26 | "gasLimit": 10100, 27 | "storageLimit": 257, 28 | "kind": 108, 29 | "transactionOperationData": { 30 | "destination": "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW", 31 | "amount": 1 32 | } 33 | } 34 | ] 35 | } 36 | }, 37 | 38 | // 'XTZ': { 39 | // "cmd": "FA2", 40 | // "isRevealed": false, 41 | // "amount": "10", 42 | // "tokenId": "1", 43 | // "toAddress": "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP", 44 | // "senderAddress": "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP", 45 | // "destination": "KT1DYk1XDzHredJq1EyNkDindiWDqZyekXGj", 46 | // "transactionAmount": 0, 47 | // "source": "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP", 48 | // "fee": 100000, 49 | // "counter": 2993174, 50 | // "gasLimit": 100000, 51 | // "storageLimit": 0, 52 | // "branch": "BKvEAX9HXfJZWYfTQbR1C7B3ADoKY6a1aKVRF7qQqvc9hS8Rr3m", 53 | // "reveal_fee": 100000, 54 | // "reveal_counter": 2993173, 55 | // "reveal_gasLimit": 100000, 56 | // "reveal_storageLimit": 0, 57 | // }, 58 | // 'XTZ': { 59 | // "cmd": "FA12", 60 | // "isRevealed": false, 61 | // "branch": "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp", 62 | // "transactionAmount": 0, 63 | // "destination": "KT1DYk1XDzHredJq1EyNkDindiWDqZyekXGj", 64 | // "senderAddress": "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP", 65 | // "toAddress": "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP", 66 | // "value": "123", 67 | // "source": "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP", 68 | // "fee": 100000, 69 | // "counter": 2993173, 70 | // "gasLimit": 100000, 71 | // "storageLimit": 0, 72 | // "reveal_fee": 100000, 73 | // "reveal_counter": 2993173, 74 | // "reveal_gasLimit": 100000, 75 | // "reveal_storageLimit": 0, 76 | // }, 77 | 'ETH': { 78 | "chainId": "0x01", 79 | "nonce": "0x00", 80 | "gasPrice": "0x07FF684650", 81 | "gasLimit": "0x5208", 82 | "toAddress": "0xC894F1dCE55358ef44D760d8B1fb3397F5b1c24b", 83 | "amount": "0x0DE0B6B3A7640000", 84 | }, 85 | 'BTC': { 86 | // https://blockchain.info/unspent?active=35oxCr5Edc2VjoQkX65TPzxUVGXJ7r4Uny 87 | // https://blockchain.info/unspent?active=bc1qxjth4cj6j2v04s07au935547qk9tzd635hkt3n 88 | "utxos": [ 89 | { 90 | "txid": 91 | "fce42021fd2d2fa793dc3d5d6520fc853e327e5c2c638c3a0be7529c559d3536", 92 | "vout": 1, 93 | "value": 4500, 94 | "script": "001434977ae25a9298fac1feef0b1a52be058ab13751", 95 | }, 96 | ], 97 | "toAddress": "15o5bzVX58t1NRvLchBUGuHscCs1sumr2R", 98 | "amount": 3000, 99 | "fees": 1000, 100 | "changeAddress": "15o5bzVX58t1NRvLchBUGuHscCs1sumr2R", 101 | "change": 500, 102 | "privateKeys": [ 103 | "a321c4996143e0add05864bbb694ceb399fbe5d0884d721b1a04755f9f7497a9", 104 | "bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866", 105 | "619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9", 106 | "eae04f225475e7630e58efdbefe50a003efd7e2ade3e67e171e023e9278b6ea4" 107 | ] 108 | }, 109 | 'TRX': { 110 | "cmd": "TRC20", 111 | // can be TRC20 | TRX | TRC10 | CONTRACT | FREEZE 112 | "ownerAddress": "TYjYrDy7yE9vyJfnF5S3EfPrzfXM3eehri", 113 | // from address 114 | "toAddress": "TJpQNJZSktSZQgEthhBapH3zmvg3RaCbKW", 115 | // to address 116 | "contractAddress": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", 117 | // in case of Trc20 (Tether USDT) 118 | "timestamp": DateTime.now().millisecondsSinceEpoch, 119 | "amount": "000F4240", 120 | // 27 * 1000000, // "004C4B40", // "000F4240" = 1000000 sun hex 2's signed complement 121 | // (https://www.rapidtables.com/convert/number/hex-to-decimal.html) 122 | // for asset TRC20 | integer for any other in SUN, 1000000 SUN = 1 TRX 123 | "feeLimit": 10000000, 124 | // reference block data to be obtained by querying the blockchain 125 | "blockTime": 1638519600000, 126 | // timestamp of block to be included milliseconds 127 | "txTrieRoot": 128 | "5807aea383e7de836af95c8b36e22654e4df33e5b92768e55fb936f8a7ae5304", 129 | // trie root of block 130 | "witnessAddress": "41e5e572797a3d479030e2596a239bd142a890a305", 131 | // address of witness that signed block 132 | "parentHash": 133 | "0000000002254183f6d15ba4115b3a5e8a8359adc663f7e6f02fa2bd51c07055", 134 | // parent hash of block 135 | "version": 23, 136 | // block version 137 | "number": 35996036, 138 | // block number 139 | // freezing 140 | "frozenDuration": 3, 141 | // frozen duration 142 | "frozenBalance": 10000000, 143 | // frozen balance in SUN 144 | "resource": "ENERGY", 145 | // Resource type: BANDWIDTH | ENERGY 146 | "assetName": "ALLOW_SAME_TOKEN_NAME" 147 | }, 148 | 'SOL': { 149 | // return { 150 | // "recentBlockhash": "C6oRG8fykBeM7sL5eYyqRSZp9m2QdkGHQqtE8nTszURZ", 151 | // "transferTransaction": {"recipient": "CiFADrjcd1acfVqg7hU1jpbNsdNkiUAexY9mRutsQUoR", "value": "250000"} 152 | // }; 153 | "recentBlockhash": "EjUjs69fQ7JG1aHwrzMET2YqTe6PMMbtbHvCEtzvDZsJ", 154 | "tokenTransferTransaction": { 155 | "tokenMintAddress": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", 156 | "senderTokenAddress": "7LKVpn2ZP9L7PkyFGApri9xEqUv4N8U8QCRyMHrCZqju", 157 | "recipientTokenAddress": "88mvV5z4gvbn7ZXcKvnCDJuoUaALis54auijidhjTbJT", 158 | "amount": "200000", 159 | "decimals": "6" 160 | } 161 | }, 162 | 'NEAR': { 163 | 'signerID': 164 | '434c894cacb459ca4eeadefc7e9868c2eb68b33c0ba81f8434f2bb435b4bbb7b', 165 | // (account ID of the transaction originator) 166 | 'receiverID': 167 | '434c894cacb459ca4eeadefc7e9868c2eb68b33c0ba81f8434f2bb435b4bbb7b', 168 | // (account ID of the transaction recipient) 169 | 'nonce': 1, 170 | // (increments for every new tx) 171 | 'amount': '01000000000000000000000000000000', 172 | // // uint128_t / little endian byte order 173 | 'blockHash': '244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM', 174 | // 175 | }, 176 | // 'XLM': { 177 | // "cmd": 'Payment', 178 | // "ownerAddress": "GCHYGAVOESDOZONH2UDWHTTEF3FQIZSQFBSMK3ZJ5Z5QS37TXNPN2LWI", 179 | // "toAddress": "GBPT3GVKY727GYXTO6QAEVET3AW3EUVZZCZOCCO5B5PJXRVS3S4GD2AY", 180 | // "asset": "USDT", 181 | // "amount": 900000, 182 | // "fee": 10000, 183 | // "sequence": 184158241918287877, 184 | // }, 185 | 'XLM': { 186 | "cmd": 'Payment', 187 | "ownerAddress": "GCPP3J7CE23VF3EONOIDXDL6QODYTI3YWJ7PNMHTO77WSEXGK2TT4QPV", 188 | "toAddress": "GBPT3GVKY727GYXTO6QAEVET3AW3EUVZZCZOCCO5B5PJXRVS3S4GD2AY", 189 | "issuer": "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", 190 | "network": "testnet", 191 | "validBefore": 1717806538278, 192 | "amount": 2000000, 193 | "fee": 10000, 194 | "sequence": 183629192141733925, 195 | "asset": "USDC", 196 | "memo": "3476840067250060816" 197 | }, 198 | // 'XLM': { 199 | // "cmd": "ChangeTrust", 200 | // "ownerAddress": 201 | // "GBPT3GVKY727GYXTO6QAEVET3AW3EUVZZCZOCCO5B5PJXRVS3S4GD2AY", // 202 | // "toAddress": "GCHYGAVOESDOZONH2UDWHTTEF3FQIZSQFBSMK3ZJ5Z5QS37TXNPN2LWI", // 203 | // "assetCode": "USDT", 204 | // "validBefore": 1695723258, 205 | // "fee": 10000, 206 | // "sequence": 184070843628781573 207 | // } 208 | 'BNB': { 209 | "chainID": "Binance-Chain-Tigris", 210 | "accountNumber": 7321705, 211 | "sequence": 6, 212 | "source": 0, 213 | "memo": "532127419", 214 | "fromAddress": "bnb19fy0e8m8zwqa3wn7dly7lyp9vl6ealhg4hkvtw", 215 | "toAddress": "bnb136ns6lfw4zs5hg4n85vdthaad7hq5m4gtkgf23", 216 | "amount": 10000, 217 | }, 218 | // 'ETH': { 219 | // "chainId": "0x38", 220 | // "nonce": "0x05", 221 | // "gasPrice": "0x012a05f200", 222 | // "gasLimit": "0x5208", 223 | // "toAddress": "0xAca4830231E74a9087EFB56a0561f8e1D87776e8", 224 | // "amount": "0x00de0b6b3a7640", 225 | // }, 226 | 'DOGE': { 227 | "utxos": [ 228 | { 229 | "txid": 230 | "ec6cc99e0084361ada185f059d53ad4db12d2a716299dcb3f74e6dfdd87cc2cb", 231 | "vout": 1, 232 | "value": 2840100000, 233 | }, 234 | ], 235 | "toAddress": "DBVdaWiPdsHxMrfQynRtCe9yEXomxM2Xui", 236 | "amount": 300000, 237 | "fees": 5000, 238 | "changeAddress": "D9pvhnWknRza2HTXhY5WT29D4kvYzTZQAF", 239 | }, 240 | // 'ETH': { 241 | // "chainId": "0x89", 242 | // "nonce": "0x00", 243 | // "gasPrice": "0x07FF684650", 244 | // "gasLimit": "0x5208", 245 | // "toAddress": "0xC894F1dCE55358ef44D760d8B1fb3397F5b1c24b", 246 | // "amount": "0x0DE0B6B3A7640000", 247 | // }, 248 | //............Polygon USDC test........ 249 | // 'ETH': { 250 | // "cmd": "ERC20", 251 | // "chainId": "0x89", 252 | // "nonce": "0x00", 253 | // "gasPrice": "0x07FF684650", 254 | // "gasLimit": "0x5208", 255 | // "toAddress": "0xC894F1dCE55358ef44D760d8B1fb3397F5b1c24b", 256 | // "amount": "0x0DE0B6B3A7640000", 257 | // "contractAddress": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174" 258 | // }, 259 | 260 | //............Polygon USDT test........ 261 | // 'ETH': { 262 | // "cmd": "ERC20", 263 | // "chainId": "0x89", 264 | // "nonce": "0x00", 265 | // "gasPrice": "0x07FF684650", 266 | // "gasLimit": "0x5208", 267 | // "toAddress": "0xC894F1dCE55358ef44D760d8B1fb3397F5b1c24b", 268 | // "amount": "0x0DE0B6B3A7640000", 269 | // "contractAddress": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F" 270 | // }, 271 | 'ADA': { 272 | "senderAddress": 273 | "addr1q9evp7aqelh4epkacgyeqweqgkvqsl8gdp54mxew5kdvuyhqhuqa6ngy0jrdcnknurcvjgtv4jd84pd7xllgmdz0wtrqgfz5l4", 274 | "receiverAddress": 275 | "addr1qyk022rpw85g7c0f0wuq6zpkakgjwsftmpd99wqjj4xcsjc74pfgs7t76yuehca7hn4pcl37lsl06ccey0epe5sp4lwslxsyrw", 276 | "amount": 40000, 277 | "ttl": 53333333, 278 | "utxos": [ 279 | { 280 | "senderAddress": 281 | "addr1q9evp7aqelh4epkacgyeqweqgkvqsl8gdp54mxew5kdvuyhqhuqa6ngy0jrdcnknurcvjgtv4jd84pd7xllgmdz0wtrqgfz5l4", 282 | "txid": 283 | "76608917328b3768b3985d057e613c7e8f14cb1f27b132a750a363ee64363a57", 284 | "index": 0, 285 | "balance": 16900000, 286 | }, 287 | ], 288 | }, 289 | 'XRP': { 290 | "senderAddress": "r9HvPkgsUvi8pfZuGKgstvMVW3RUZbvuv7", 291 | "receiverAddress": "rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF", 292 | "amount": 10, 293 | "fee": 10, 294 | "sequence": 32268248, 295 | "lastLedgerSequence": 32268269, 296 | "memo": "418225058" 297 | } 298 | }; 299 | 300 | // ignore: inference_failure_on_function_return_type 301 | runOperations() async { 302 | // Platform messages may fail, so we use a try/catch PlatformException. 303 | // We also handle the message potentially returning null. 304 | try { 305 | String mnemonic = await Trustdart.generateMnemonic(); 306 | print('Here is our mnemonic: \n$mnemonic'); 307 | 308 | String dondo = 309 | "imitate embody law mammal exotic transfer roof hope price swift ordinary uncle"; 310 | // dondo = "a d f d s e w q t y u l"; 311 | bool wallet = await Trustdart.checkMnemonic(dondo); 312 | print(wallet); 313 | // https://github.com/satoshilabs/slips/blob/master/slip-0044.md 314 | String dataToSign = 315 | "03f5a22ee15cd6434751ea528328dd071738e1cfb39fb3c60d306528ff6f46b6e06c00bf43035fb548c011acf2efc84106075b8aa100038827bafcd916904ede02e80701a15985af2de2c555defbac9b8675efd9563285d400ffff07636f6c6c65637400000004008ea010"; 316 | 317 | for (Coin coin in coinList) { 318 | print('Check for ${coin.code} on path ${coin.path} ...'); 319 | print('======================================================='); 320 | String privKey = await Trustdart.getPrivateKey( 321 | dondo, 322 | coin.code, 323 | coin.path, 324 | ); 325 | print('Private Key Check ...'); 326 | print([ 327 | privKey, 328 | ]); 329 | 330 | Uint8List rawPrivKey = await Trustdart.getRawPrivateKey( 331 | dondo, 332 | coin.code, 333 | coin.path, 334 | ); 335 | print('Raw Private Key Check ...'); 336 | print([ 337 | rawPrivKey, 338 | ]); 339 | 340 | String pubKey = await Trustdart.getPublicKey( 341 | dondo, 342 | coin.code, 343 | coin.path, 344 | ); 345 | print('Publick Key Check ...'); 346 | print([pubKey]); 347 | 348 | Uint8List rawPubKey = await Trustdart.getRawPublicKey( 349 | dondo, 350 | coin.code, 351 | coin.path, 352 | ); 353 | print('Raw Publick Key Check ...'); 354 | // ignore: avoid_print 355 | print([ 356 | rawPubKey, 357 | ]); 358 | 359 | Map address = await Trustdart.generateAddress( 360 | dondo, 361 | coin.code, 362 | coin.path, 363 | ); 364 | print('Address Check ...'); 365 | print([ 366 | address, 367 | ]); 368 | 369 | Uint8List seed = await Trustdart.getSeed( 370 | dondo, 371 | coin.code, 372 | coin.path, 373 | ); 374 | print('Seed Check ...'); 375 | print([ 376 | seed, 377 | ]); 378 | 379 | bool valid = await Trustdart.validateAddress( 380 | coin.code, 381 | address['legacy']!, 382 | ); 383 | print([valid]); 384 | 385 | bool invalid = await Trustdart.validateAddress( 386 | coin.code, 387 | address['legacy']! + '0', 388 | ); 389 | print('Invalid Check ...'); 390 | print(coin.code); 391 | print([invalid]); 392 | 393 | String tx = await Trustdart.signTransaction( 394 | dondo, 395 | coin.code, 396 | coin.path, 397 | operations[coin.code], 398 | ); 399 | print('Transaction Check ...'); 400 | print([tx]); 401 | 402 | print(operations[coin.code]["privateKeys"]); 403 | String multiTxSign = await Trustdart.multiSignTransaction(coin.code, 404 | operations[coin.code], operations[coin.code]["privateKeys"]); 405 | print('MultiSig Transaction Check ...'); 406 | print([multiTxSign]); 407 | 408 | String signedData = (await Trustdart.signDataWithPrivateKey( 409 | dondo, 410 | coin.code, 411 | coin.path, 412 | dataToSign, 413 | )); 414 | print('Sign Data with Priv Key Check ...'); 415 | print(signedData); 416 | print(''); 417 | } 418 | } catch (e) { 419 | print(e); 420 | } 421 | } 422 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: trustdart_example 2 | description: Demonstrates how to use the trustdart plugin. 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 | environment: 9 | sdk: ">=2.12.0 <3.0.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | 15 | trustdart: 16 | # When depending on this package from a real application you should use: 17 | # trustdart: ^x.y.z 18 | # See https://dart.dev/tools/pub/dependencies#version-constraints 19 | # The example app is bundled with the plugin so we use a path dependency on 20 | # the parent directory to use the current plugin's version. 21 | path: ../ 22 | flutter_lints: ^1.0.4 23 | 24 | # The following adds the Cupertino Icons font to your application. 25 | # Use with the CupertinoIcons class for iOS style icons. 26 | cupertino_icons: ^1.0.2 27 | 28 | dev_dependencies: 29 | flutter_test: 30 | sdk: flutter 31 | 32 | # For information on the generic Dart part of this file, see the 33 | # following page: https://dart.dev/tools/pub/pubspec 34 | 35 | # The following section is specific to Flutter. 36 | flutter: 37 | 38 | # The following line ensures that the Material Icons font is 39 | # included with your application, so that you can use the icons in 40 | # the material Icons class. 41 | uses-material-design: true 42 | 43 | # To add assets to your application, add an assets section, like this: 44 | # assets: 45 | # - images/a_dot_burr.jpeg 46 | # - images/a_dot_ham.jpeg 47 | 48 | # An image asset can refer to one or more resolution-specific "variants", see 49 | # https://flutter.dev/assets-and-images/#resolution-aware. 50 | 51 | # For details regarding adding assets from package dependencies, see 52 | # https://flutter.dev/assets-and-images/#from-packages 53 | 54 | # To add custom fonts to your application, add a fonts section here, 55 | # in this "flutter" section. Each entry in this list should have a 56 | # "family" key with the font family name, and a "fonts" key with a 57 | # list giving the asset and other descriptors for the font. For 58 | # example: 59 | # fonts: 60 | # - family: Schyler 61 | # fonts: 62 | # - asset: fonts/Schyler-Regular.ttf 63 | # - asset: fonts/Schyler-Italic.ttf 64 | # style: italic 65 | # - family: Trajan Pro 66 | # fonts: 67 | # - asset: fonts/TrajanPro.ttf 68 | # - asset: fonts/TrajanPro_Bold.ttf 69 | # weight: 700 70 | # 71 | # For details regarding fonts from package dependencies, 72 | # see https://flutter.dev/custom-fonts/#from-packages 73 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | import 'package:trustdart_example/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Verify Platform version', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(MyApp()); 16 | 17 | // Verify that platform version is retrieved. 18 | expect( 19 | find.byWidgetPredicate( 20 | (Widget widget) => 21 | widget is Text && widget.data!.startsWith('Running on:'), 22 | ), 23 | findsOneWidget, 24 | ); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | package-lock.json 25 | 26 | # Android/IntelliJ 27 | # 28 | build/ 29 | .idea 30 | .gradle 31 | local.properties 32 | *.iml 33 | .vscode 34 | 35 | # node.js 36 | # 37 | node_modules/ 38 | .tmp/ 39 | npm-debug.log 40 | yarn-error.log 41 | 42 | # BUCK 43 | buck-out/ 44 | \.buckd/ 45 | *.keystore 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 53 | 54 | fastlane/report.xml 55 | fastlane/Preview.html 56 | fastlane/screenshots 57 | 58 | 59 | #added personally cuz of linux caching files 60 | /55e96e5581a23965923ee1f2e8820034** 61 | /haste-map* 62 | /react-native-package* 63 | /*.babelrc 64 | /*.swp 65 | /*.com 66 | /*.save 67 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/ephemeral/ 38 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EjaraApp/trustdart/9b3c2d1a2d29210519b12dcc05f3e7857faf7787/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/Coin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Coin.swift 3 | // Pods-Runner 4 | // 5 | // Created by Jay on 3/29/22. 6 | // 7 | import WalletCore 8 | import Flutter 9 | 10 | class Coin: CoinProtocol { 11 | 12 | let name: String 13 | let coinType: CoinType 14 | 15 | init(name: String, coinType: CoinType){ 16 | self.name = name 17 | self.coinType = coinType 18 | } 19 | 20 | func getName() -> String { 21 | return self.name 22 | } 23 | 24 | func generateAddress(path: String, mnemonic: String, passphrase: String) -> [String: String]? { 25 | let privateKey = HDWallet(mnemonic: mnemonic, passphrase: passphrase)?.getKey(coin: self.coinType, derivationPath: path) 26 | if privateKey != nil { 27 | return ["legacy": self.coinType.deriveAddress(privateKey: privateKey!)] 28 | } 29 | return nil 30 | } 31 | 32 | func getPrivateKey(path: String, mnemonic: String, passphrase: String) -> String? { 33 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 34 | let privateKey: String? = wallet!.getKey(coin: self.coinType, derivationPath: path).data.base64EncodedString() 35 | return privateKey 36 | } 37 | 38 | 39 | func getRawPrivateKey(path: String, mnemonic: String, passphrase: String) -> FlutterStandardTypedData { 40 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 41 | return FlutterStandardTypedData(bytes: wallet!.getKey(coin: self.coinType, derivationPath: path).data) 42 | } 43 | 44 | func getSeed(path: String, mnemonic: String, passphrase: String) -> FlutterStandardTypedData { 45 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 46 | return FlutterStandardTypedData(bytes: wallet!.seed) 47 | } 48 | 49 | func getPublicKey(path: String, mnemonic: String, passphrase: String) -> String? { 50 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 51 | let publicKey: String? = wallet!.getKey(coin: self.coinType, derivationPath: path).getPublicKeySecp256k1(compressed: true).data.base64EncodedString() 52 | return publicKey 53 | } 54 | 55 | func getRawPublicKey(path: String, mnemonic: String, passphrase: String) -> FlutterStandardTypedData { 56 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 57 | return FlutterStandardTypedData(bytes: wallet!.getKey(coin: self.coinType, derivationPath: path).getPublicKeySecp256k1(compressed: true).data) 58 | } 59 | 60 | func validateAddress(address: String) -> Bool { 61 | return self.coinType.validate(address: address) 62 | } 63 | 64 | func signDataWithPrivateKey(path: String, mnemonic: String, passphrase: String, txData: String) -> String? { 65 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 66 | let privateKey = wallet?.getKey(coin: self.coinType, derivationPath: path) 67 | if privateKey != nil { 68 | return privateKey?.sign(digest: Data(hexString: txData)!, curve: self.coinType.curve)?.hexString 69 | } 70 | return nil 71 | } 72 | 73 | func signTransaction(path: String, txData: [String: Any], mnemonic: String, passphrase: String) -> String? { 74 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 75 | let privateKey = wallet?.getKey(coin: self.coinType, derivationPath: path) 76 | let opJson = Utils.objToJson(from: txData) 77 | if privateKey != nil { 78 | return AnySigner.signJSON(opJson!, key: privateKey!.data, coin: self.coinType) 79 | } 80 | return nil 81 | } 82 | 83 | 84 | 85 | func multiSignTransaction(txData: [String: Any], privateKeys: [String]) -> String? { 86 | let opJson = Utils.objToJson(from: txData) 87 | var signatures = [String]() 88 | 89 | for privateKey in privateKeys { 90 | let signature = AnySigner.signJSON(opJson!, key: privateKey.data(using: .utf8)!, coin: self.coinType) 91 | signatures.append(signature) 92 | } 93 | if signatures.isEmpty { 94 | return nil 95 | } else { 96 | return signatures.joined(separator: ",") 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /ios/Classes/SwiftTrustdartPlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import WalletCore 4 | 5 | public class SwiftTrustdartPlugin: NSObject, FlutterPlugin { 6 | public static func register(with registrar: FlutterPluginRegistrar) { 7 | let channel = FlutterMethodChannel(name: "trustdart", binaryMessenger: registrar.messenger()) 8 | let instance = SwiftTrustdartPlugin() 9 | registrar.addMethodCallDelegate(instance, channel: channel) 10 | } 11 | 12 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 13 | switch call.method { 14 | case "generateMnemonic": 15 | let wallet = WalletHandler().generateMnemonic(strength: 128, passphrase: call.arguments as! String) 16 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .noWallet, message: "Could not generate wallet", details: nil), wallet) 17 | if isValid { 18 | result(wallet) 19 | }else { 20 | result(err.details) 21 | } 22 | case "checkMnemonic": 23 | let args = call.arguments as! [String: String] 24 | let wallet = WalletHandler().checkMnemonic(mnemonic: args["mnemonic"], passphrase: args["passphrase"]) 25 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .argumentsNull, message: "Could not validate mnemonic", details: nil), wallet) 26 | if isValid { 27 | result(isValid) 28 | }else { 29 | result(err.details) 30 | } 31 | case "generateAddress": 32 | let args = call.arguments as! [String: String] 33 | let path: String? = args["path"] 34 | let coin: String? = args["coin"] 35 | let mnemonic: String? = args["mnemonic"] 36 | let passphrase: String? = args["passphrase"] 37 | 38 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .argumentsNull, message: ErrorResponse.addressNull, details: nil), path, coin, mnemonic, passphrase) 39 | if isValid { 40 | // generate address 41 | let address = WalletHandler().getCoin(coin!).generateAddress(path: path!, mnemonic: mnemonic!, passphrase: passphrase!) 42 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .addressNull, message: "Failed to generate address.", details: nil), path, coin, mnemonic, passphrase) 43 | if isValid { 44 | result(address) 45 | }else { 46 | result(err.details) 47 | } 48 | }else { 49 | result(err.details) 50 | } 51 | case "validateAddress": 52 | let args = call.arguments as! [String: String] 53 | let address: String? = args["address"] 54 | let coin: String? = args["coin"] 55 | 56 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .argumentsNull, message: "[coin] and [address] are required.", details: nil), address, coin) 57 | 58 | if isValid { 59 | result(WalletHandler().getCoin(coin!).validateAddress(address: address!)) 60 | }else { 61 | result(err.details) 62 | } 63 | case "signTransaction": 64 | let args = call.arguments as! [String: Any] 65 | let coin: String? = args["coin"] as? String 66 | let path: String? = args["path"] as? String 67 | let txData: [String: Any]? = args["txData"] as? [String: Any] 68 | let mnemonic: String? = args["mnemonic"] as? String 69 | let passphrase: String? = args["passphrase"] as? String 70 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .argumentsNull, message: "[coin], [path], [mnemonic] and [passphrase] are required.", details: nil), coin, path, mnemonic, passphrase) 71 | 72 | if isValid { 73 | let txHash = WalletHandler().getCoin(coin!).signTransaction(path: path!, txData: txData!, mnemonic: mnemonic!, passphrase: passphrase!) 74 | 75 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .txHashNull, message: "Failed to sign transaction.", details: nil), txHash) 76 | if isValid { 77 | result(txHash) 78 | }else { 79 | result(err.details) 80 | } 81 | }else { 82 | result(err.details) 83 | } 84 | 85 | case "multiSignTransaction": 86 | let args = call.arguments as! [String: Any] 87 | let coin: String? = args["coin"] as? String 88 | let txData: [String: Any]? = args["txData"] as? [String: Any] 89 | let privateKeys: [String]? = args["privateKeys"] as? [String] 90 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .argumentsNull, message: "[coin] are required.", details: nil), coin) 91 | 92 | if isValid { 93 | let txHash = WalletHandler().getCoin(coin!).multiSignTransaction(txData: txData!, privateKeys: privateKeys ?? [] ) 94 | 95 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .txHashNull, message: "Failed to sign transaction.", details: nil), txHash) 96 | if isValid { 97 | result(txHash) 98 | }else { 99 | result(err.details) 100 | } 101 | }else { 102 | result(err.details) 103 | } 104 | 105 | case "signDataWithPrivateKey": 106 | let args = call.arguments as! [String: Any] 107 | let coin: String? = args["coin"] as? String 108 | let path: String? = args["path"] as? String 109 | let txData: String? = args["txData"] as? String 110 | let mnemonic: String? = args["mnemonic"] as? String 111 | let passphrase: String? = args["passphrase"] as? String 112 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .argumentsNull, message: "[coin], [path], [mnemonic] and [passphrase] are required.", details: nil), coin, path, mnemonic, passphrase) 113 | 114 | if isValid { 115 | let txHash = WalletHandler().getCoin(coin!).signDataWithPrivateKey(path: path!, mnemonic: mnemonic!, passphrase: passphrase!, txData: txData!) 116 | 117 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .txHashNull, message: "Failed to sign data.", details: nil), txHash) 118 | if isValid { 119 | result(txHash) 120 | }else { 121 | result(err.details) 122 | } 123 | }else { 124 | result(err.details) 125 | } 126 | case "getPublicKey": 127 | let args = call.arguments as! [String: String] 128 | let path: String? = args["path"] 129 | let coin: String? = args["coin"] 130 | let mnemonic: String? = args["mnemonic"] 131 | let passphrase: String? = args["passphrase"] 132 | 133 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .argumentsNull, message: ErrorResponse.addressNull, details: nil), path, coin, mnemonic, passphrase) 134 | if isValid { 135 | // generate address 136 | let publicKey = WalletHandler().getCoin(coin!).getPublicKey(path: path!, mnemonic: mnemonic!, passphrase: passphrase!) 137 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .addressNull, message: "Failed to generate public key.", details: nil), path, coin, mnemonic, passphrase) 138 | if isValid { 139 | result(publicKey) 140 | }else { 141 | result(err.details) 142 | } 143 | }else { 144 | result(err.details) 145 | } 146 | case "getRawPublicKey": 147 | let args = call.arguments as! [String: String] 148 | let path: String? = args["path"] 149 | let coin: String? = args["coin"] 150 | let mnemonic: String? = args["mnemonic"] 151 | let passphrase: String? = args["passphrase"] 152 | 153 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .argumentsNull, message: ErrorResponse.addressNull, details: nil), path, coin, mnemonic, passphrase) 154 | if isValid { 155 | // generate address 156 | let publicKey = WalletHandler().getCoin(coin!).getRawPublicKey(path: path!, mnemonic: mnemonic!, passphrase: passphrase!) 157 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .addressNull, message: "Failed to generate public key.", details: nil), path, coin, mnemonic, passphrase) 158 | if isValid { 159 | result(publicKey) 160 | }else { 161 | result(err.details) 162 | } 163 | }else { 164 | result(err.details) 165 | } 166 | case "getPrivateKey": 167 | let args = call.arguments as! [String: String] 168 | let path: String? = args["path"] 169 | let coin: String? = args["coin"] 170 | let mnemonic: String? = args["mnemonic"] 171 | let passphrase: String? = args["passphrase"] 172 | 173 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .argumentsNull, message: ErrorResponse.addressNull, details: nil), path, coin, mnemonic, passphrase) 174 | if isValid { 175 | // generate address 176 | let privateKey = WalletHandler().getCoin(coin!).getPrivateKey(path: path!, mnemonic: mnemonic!, passphrase: passphrase!) 177 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .addressNull, message: "Failed to generate private key.", details: nil), path, coin, mnemonic, passphrase) 178 | if isValid { 179 | result(privateKey) 180 | }else { 181 | result(err.details) 182 | } 183 | }else { 184 | result(err.details) 185 | } 186 | case "getRawPrivateKey": 187 | let args = call.arguments as! [String: String] 188 | let path: String? = args["path"] 189 | let coin: String? = args["coin"] 190 | let mnemonic: String? = args["mnemonic"] 191 | let passphrase: String? = args["passphrase"] 192 | 193 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .argumentsNull, message: ErrorResponse.addressNull, details: nil), path, coin, mnemonic, passphrase) 194 | if isValid { 195 | // generate address 196 | let privateKey = WalletHandler().getCoin(coin!).getRawPrivateKey(path: path!, mnemonic: mnemonic!, passphrase: passphrase!) 197 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .addressNull, message: "Failed to generate private key.", details: nil), path, coin, mnemonic, passphrase) 198 | if isValid { 199 | result(privateKey) 200 | }else { 201 | result(err.details) 202 | } 203 | }else { 204 | result(err.details) 205 | } 206 | case "getSeed": 207 | let args = call.arguments as! [String: String] 208 | let path: String? = args["path"] 209 | let coin: String? = args["coin"] 210 | let mnemonic: String? = args["mnemonic"] 211 | let passphrase: String? = args["passphrase"] 212 | 213 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .argumentsNull, message: ErrorResponse.addressNull, details: nil), path, coin, mnemonic, passphrase) 214 | if isValid { 215 | // generate address 216 | let privateKey = WalletHandler().getCoin(coin!).getSeed(path: path!, mnemonic: mnemonic!, passphrase: passphrase!) 217 | let (isValid, err) = WalletHandler.validate(walletError: WalletError(code: .addressNull, message: "Failed to generate private key.", details: nil), path, coin, mnemonic, passphrase) 218 | if isValid { 219 | result(privateKey) 220 | }else { 221 | result(err.details) 222 | } 223 | }else { 224 | result(err.details) 225 | } 226 | default: 227 | result(FlutterMethodNotImplemented) 228 | } 229 | } 230 | 231 | } 232 | -------------------------------------------------------------------------------- /ios/Classes/TrustdartPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface TrustdartPlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /ios/Classes/TrustdartPlugin.m: -------------------------------------------------------------------------------- 1 | #import "TrustdartPlugin.h" 2 | #if __has_include() 3 | #import 4 | #else 5 | // Support project import fallback if the generated compatibility header 6 | // is not copied when this plugin is created as a library. 7 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 8 | #import "trustdart-Swift.h" 9 | #endif 10 | 11 | @implementation TrustdartPlugin 12 | + (void)registerWithRegistrar:(NSObject*)registrar { 13 | [SwiftTrustdartPlugin registerWithRegistrar:registrar]; 14 | } 15 | @end 16 | -------------------------------------------------------------------------------- /ios/Classes/WalletHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WalletHandler.swift 3 | // Pods-Runner 4 | // 5 | // Created by Jay on 3/29/22. 6 | // 7 | 8 | import WalletCore 9 | import Flutter 10 | 11 | class WalletHandler { 12 | static let coins: [String: Coin] = [ 13 | "BTC" : BTC(), 14 | "ETH" : ETH(), 15 | "XTZ" : XTZ(), 16 | "TRX" : TRX(), 17 | "SOL" : SOL(), 18 | "NEAR" : NEAR(), 19 | "XLM" : XLM(), 20 | "BNB" : BNB(), 21 | "DOGE" : DOGE(), 22 | "ADA" : ADA(), 23 | "XRP" : XRP(), 24 | ] 25 | 26 | func getCoin(_ coin: String) -> Coin { 27 | return WalletHandler.coins[coin]! 28 | } 29 | 30 | func generateMnemonic(strength: Int32, passphrase: String) -> String? { 31 | return HDWallet(strength: strength, passphrase: passphrase)?.mnemonic 32 | } 33 | 34 | func checkMnemonic(mnemonic: String?, passphrase: String?) -> HDWallet?{ 35 | return HDWallet(mnemonic: mnemonic!, passphrase: passphrase!) 36 | } 37 | 38 | static func validate(walletError: WalletError, _ data: T?...) -> (Bool, WalletError) { 39 | var isValid = true 40 | data.forEach { _data in 41 | if _data == nil { 42 | isValid = false 43 | } 44 | } 45 | return (isValid, walletError.self) 46 | } 47 | } 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /ios/Classes/coins/ADA.swift: -------------------------------------------------------------------------------- 1 | /* 2 | ADA 3 | */ 4 | 5 | import WalletCore 6 | 7 | class ADA: Coin { 8 | init() { 9 | super.init(name: "ADA", coinType: .cardano) 10 | } 11 | 12 | override func signTransaction(path: String, txData: [String : Any], mnemonic: String, passphrase: String) -> String? { 13 | let privateKey = HDWallet(mnemonic: mnemonic, passphrase: passphrase)?.getKey(coin: coinType, derivationPath: path) 14 | let utxos: [[String: Any]] = txData["utxos"] as! [[String: Any]] 15 | var listOfUtxos: [CardanoTxInput] = [] 16 | 17 | for utx in utxos { 18 | listOfUtxos.append(CardanoTxInput.with { 19 | $0.outPoint.txHash = Data(hexString: (utx["txid"] as! String))! 20 | $0.outPoint.outputIndex = utx["index"] as! UInt64 21 | $0.address = utx["senderAddress"] as! String 22 | $0.amount = utx["balance"] as! UInt64 23 | }) 24 | } 25 | 26 | let input = CardanoSigningInput.with { 27 | $0.transferMessage.toAddress = txData["receiverAddress"] as! String 28 | $0.transferMessage.changeAddress = txData["senderAddress"] as! String 29 | $0.transferMessage.amount = txData["amount"] as! UInt64 30 | $0.ttl = txData["ttl"] as! UInt64 31 | $0.privateKey = [privateKey!.data] 32 | $0.utxos = listOfUtxos 33 | } 34 | 35 | let output: CardanoSigningOutput = AnySigner.sign(input: input, coin: .cardano) 36 | return output.encoded.hexString 37 | } 38 | } 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ios/Classes/coins/BNB.swift: -------------------------------------------------------------------------------- 1 | import WalletCore 2 | 3 | class BNB: Coin { 4 | init() { 5 | super.init(name: "BNB", coinType: .binance) 6 | } 7 | 8 | override func signTransaction(path: String, txData: [String : Any], mnemonic: String, passphrase: String) -> String? { 9 | 10 | let privateKey = HDWallet(mnemonic: mnemonic, passphrase: passphrase)?.getKey(coin: self.coinType, derivationPath: path) 11 | let publicKey = privateKey?.getPublicKeySecp256k1(compressed: true) 12 | 13 | let token = BinanceSendOrder.Token.with { 14 | $0.denom = "BNB" // BNB or BEP2 token symbol 15 | $0.amount = txData["amount"] as! Int64 16 | } 17 | 18 | let orderInput = BinanceSendOrder.Input.with { 19 | $0.address = AnyAddress(publicKey: publicKey!, coin: .binance).data 20 | $0.coins = [token] 21 | } 22 | 23 | let orderOutput = BinanceSendOrder.Output.with { 24 | $0.address = AnyAddress(string: txData["toAddress"] as! String, coin: .binance)!.data 25 | $0.coins = [token] 26 | } 27 | 28 | let input = BinanceSigningInput.with { 29 | $0.chainID = txData["chainID"] as! String // Chain id (network id) 30 | $0.accountNumber = txData["accountNumber"] as! Int64 // On chain account / address number 31 | $0.sequence = txData["sequence"] as! Int64 // Sequence number, plus 1 for new order 32 | $0.source = txData["source"] as! Int64 // BEP10 source id 33 | $0.privateKey = privateKey!.data 34 | if (txData["memo"] != nil) { 35 | $0.memo = txData["memo"] as! String 36 | } 37 | $0.sendOrder = BinanceSendOrder.with { 38 | $0.inputs = [orderInput] 39 | $0.outputs = [orderOutput] 40 | } 41 | } 42 | 43 | let output: BinanceSigningOutput = AnySigner.sign(input: input, coin: .binance) 44 | return output.encoded.hexString 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /ios/Classes/coins/BTC.swift: -------------------------------------------------------------------------------- 1 | /* 2 | BTC 3 | */ 4 | 5 | import WalletCore 6 | 7 | class BTC: Coin { 8 | init() { 9 | super.init(name: "BTC", coinType: .bitcoin) 10 | } 11 | 12 | override func generateAddress(path: String, mnemonic: String, passphrase: String) -> [String : String]? { 13 | let privateKey = HDWallet(mnemonic: mnemonic, passphrase: passphrase)?.getKey(coin: self.coinType, derivationPath: path) 14 | if privateKey != nil { 15 | let publicKey = privateKey!.getPublicKeySecp256k1(compressed: true) 16 | let legacyAddress = BitcoinAddress(publicKey: publicKey, prefix: 0) 17 | let scriptHashAddress = BitcoinAddress(publicKey: publicKey, prefix: 5) 18 | return ["legacy": legacyAddress!.description, 19 | "segwit": self.coinType.deriveAddress(privateKey: privateKey!), 20 | "p2sh": scriptHashAddress!.description, 21 | ] 22 | }else { 23 | return nil 24 | } 25 | 26 | } 27 | 28 | override func signTransaction(path: String, txData: [String : Any], mnemonic: String, passphrase: String) -> String? { 29 | let privateKey = HDWallet(mnemonic: mnemonic, passphrase: passphrase)?.getKey(coin: self.coinType, derivationPath: path) 30 | let utxos: [[String: Any]] = txData["utxos"] as! [[String: Any]] 31 | var unspent: [BitcoinUnspentTransaction] = [] 32 | 33 | if privateKey != nil { 34 | for utx in utxos { 35 | unspent.append(BitcoinUnspentTransaction.with { 36 | $0.outPoint.hash = Data.reverse(hexString: utx["txid"] as! String) 37 | $0.outPoint.index = utx["vout"] as! UInt32 38 | $0.outPoint.sequence = UINT32_MAX 39 | $0.amount = utx["value"] as! Int64 40 | $0.script = Data(hexString: utx["script"] as! String)! 41 | }) 42 | } 43 | let input: BitcoinSigningInput = BitcoinSigningInput.with { 44 | $0.hashType = BitcoinScript.hashTypeForCoin(coinType: .bitcoin) 45 | $0.amount = txData["amount"] as! Int64 46 | $0.toAddress = txData["toAddress"] as! String 47 | $0.changeAddress = txData["changeAddress"] as! String // can be same sender address 48 | $0.privateKey = [privateKey!.data] 49 | $0.plan = BitcoinTransactionPlan.with { 50 | $0.amount = txData["amount"] as! Int64 51 | $0.fee = txData["fees"] as! Int64 52 | $0.change = txData["change"] as! Int64 53 | $0.utxos = unspent 54 | } 55 | } 56 | 57 | let output: BitcoinSigningOutput = AnySigner.sign(input: input, coin: .bitcoin) 58 | return output.encoded.hexString 59 | }else { 60 | return nil 61 | } 62 | 63 | } 64 | 65 | 66 | 67 | override func multiSignTransaction(txData: [String : Any], privateKeys: [String]) -> String? { 68 | let utxos: [[String: Any]] = txData["utxos"] as! [[String: Any]] 69 | var unspent: [BitcoinUnspentTransaction] = [] 70 | 71 | for utx in utxos { 72 | unspent.append(BitcoinUnspentTransaction.with { 73 | $0.outPoint.hash = Data.reverse(hexString: utx["txid"] as! String) 74 | $0.outPoint.index = utx["vout"] as! UInt32 75 | $0.outPoint.sequence = UINT32_MAX 76 | $0.amount = utx["value"] as! Int64 77 | $0.script = Data(hexString: utx["script"] as! String)! 78 | }) 79 | } 80 | let privateKeyDataArray = privateKeys.compactMap { privateKey in 81 | return Data(hexString: privateKey) 82 | } 83 | 84 | let input: BitcoinSigningInput = BitcoinSigningInput.with { 85 | $0.hashType = BitcoinScript.hashTypeForCoin(coinType: .bitcoin) 86 | $0.amount = txData["amount"] as! Int64 87 | $0.toAddress = txData["toAddress"] as! String 88 | $0.changeAddress = txData["changeAddress"] as! String // can be same sender address 89 | $0.privateKey = privateKeyDataArray 90 | $0.plan = BitcoinTransactionPlan.with { 91 | $0.amount = txData["amount"] as! Int64 92 | $0.fee = txData["fees"] as! Int64 93 | $0.change = txData["change"] as! Int64 94 | $0.utxos = unspent 95 | } 96 | } 97 | 98 | let output: BitcoinSigningOutput = AnySigner.sign(input: input, coin: .bitcoin) 99 | return output.encoded.hexString 100 | 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /ios/Classes/coins/DOGE.swift: -------------------------------------------------------------------------------- 1 | /* 2 | DOGE 3 | */ 4 | 5 | import WalletCore 6 | 7 | class DOGE: Coin { 8 | init() { 9 | super.init(name: "DOGE", coinType: .dogecoin) 10 | } 11 | 12 | override func signTransaction(path: String, txData: [String : Any], mnemonic: String, passphrase: String) -> String? { 13 | let privateKey = HDWallet(mnemonic: mnemonic, passphrase: passphrase)?.getKey(coin: coinType, derivationPath: path) 14 | let publicKey = privateKey!.getPublicKeySecp256k1(compressed: true) 15 | let address = BitcoinAddress(publicKey: publicKey, prefix: coinType.p2pkhPrefix) 16 | let script = BitcoinScript.lockScriptForAddress(address: address!.description, coin: coinType) 17 | let utxos: [[String: Any]] = txData["utxos"] as! [[String: Any]] 18 | var unspent: [BitcoinUnspentTransaction] = [] 19 | 20 | if privateKey != nil { 21 | for utx in utxos { 22 | unspent.append(BitcoinUnspentTransaction.with { 23 | $0.outPoint.hash = Data.reverse(hexString: utx["txid"] as! String) 24 | $0.outPoint.index = UInt32(utx["vout"] as! Int) 25 | $0.outPoint.sequence = UINT32_MAX 26 | $0.amount = utx["value"] as! Int64 27 | $0.script = script.data 28 | }) 29 | } 30 | 31 | 32 | let input: BitcoinSigningInput = BitcoinSigningInput.with { 33 | $0.hashType = BitcoinScript.hashTypeForCoin(coinType: coinType) 34 | $0.toAddress = txData["toAddress"] as! String 35 | $0.changeAddress = txData["changeAddress"] as! String 36 | $0.privateKey = [privateKey!.data] 37 | $0.amount = txData["amount"] as! Int64 38 | $0.coinType = coinType.rawValue 39 | $0.byteFee = txData["fees"] as! Int64 40 | $0.utxo = unspent 41 | } 42 | 43 | let output: BitcoinSigningOutput = AnySigner.sign(input: input, coin: coinType) 44 | return output.encoded.hexString 45 | }else { 46 | return nil 47 | } 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ios/Classes/coins/ETH.swift: -------------------------------------------------------------------------------- 1 | /* 2 | ETH 3 | */ 4 | import WalletCore 5 | 6 | class ETH: Coin { 7 | init() { 8 | super.init(name: "ETH", coinType: .ethereum) 9 | } 10 | override func signTransaction(path: String, txData: [String : Any], mnemonic: String, passphrase: String) -> String? { 11 | let privateKey = HDWallet(mnemonic: mnemonic, passphrase: passphrase)?.getKey(coin: self.coinType, derivationPath: path) 12 | let cmd = txData["cmd"] as? String 13 | 14 | if privateKey != nil { 15 | var input = EthereumSigningInput.with { 16 | $0.chainID = Data(hexString: txData["chainId"] as! String)! 17 | $0.nonce = Data(hexString: (txData["nonce"] as! String))! 18 | $0.gasPrice = Data(hexString: (txData["gasPrice"] as! String))! 19 | $0.gasLimit = Data(hexString: (txData["gasLimit"] as! String))! 20 | $0.toAddress = txData["toAddress"] as! String 21 | $0.privateKey = privateKey!.data 22 | } 23 | switch cmd { 24 | case "ERC20": 25 | input.toAddress = txData["contractAddress"] as! String 26 | input.transaction = EthereumTransaction.with { 27 | $0.erc20Transfer = EthereumTransaction.ERC20Transfer.with { 28 | $0.to = txData["toAddress"] as! String 29 | $0.amount = Data(hexString: (txData["amount"] as! String))! 30 | } 31 | } 32 | 33 | default: 34 | input.toAddress = txData["toAddress"] as! String 35 | input.transaction = EthereumTransaction.with { 36 | $0.transfer = EthereumTransaction.Transfer.with { 37 | $0.amount = Data(hexString: (txData["amount"] as! String))! 38 | } 39 | } 40 | 41 | } 42 | let sign: EthereumSigningOutput = AnySigner.sign(input: input, coin: coinType) 43 | return sign.encoded.hexString 44 | } 45 | return nil 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ios/Classes/coins/NEAR.swift: -------------------------------------------------------------------------------- 1 | /* 2 | NEAR 3 | */ 4 | import WalletCore 5 | 6 | class NEAR: Coin { 7 | init() { 8 | super.init(name: "NEAR", coinType: .near) 9 | } 10 | 11 | override func getPublicKey(path: String, mnemonic: String, passphrase: String) -> String? { 12 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 13 | return wallet!.getKey(coin: self.coinType, derivationPath: path).getPublicKeyEd25519().data.base64EncodedString() 14 | } 15 | 16 | override func getRawPublicKey(path: String, mnemonic: String, passphrase: String) -> FlutterStandardTypedData { 17 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 18 | return FlutterStandardTypedData(bytes: wallet!.getKey(coin: self.coinType, derivationPath: path).getPublicKeyEd25519().data) 19 | } 20 | 21 | override func signTransaction(path: String, txData: [String : Any], mnemonic: String, passphrase: String) -> String? { 22 | let privateKey = HDWallet(mnemonic: mnemonic, passphrase: passphrase)?.getKey(coin: self.coinType, derivationPath: path) 23 | 24 | let input = NEARSigningInput.with { 25 | $0.signerID = txData["signerID"] as! String 26 | $0.nonce = txData["nonce"] as! UInt64 27 | $0.receiverID = txData["receiverID"] as! String 28 | 29 | $0.actions = [ 30 | NEARAction.with({ 31 | $0.transfer = NEARTransfer.with { 32 | // uint128_t / little endian byte order 33 | $0.deposit = Data(hexString: txData["amount"] as! String)! 34 | } 35 | }), 36 | ] 37 | 38 | $0.blockHash = Base58.decodeNoCheck(string: txData["blockHash"] as! String)! 39 | $0.privateKey = privateKey!.data 40 | } 41 | let output: NEARSigningOutput = AnySigner.sign(input: input, coin: .near) 42 | return output.signedTransaction.hexString 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ios/Classes/coins/SOL.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SOL.swift 3 | // trustdart 4 | // 5 | // Created by Jay on 3/29/22. 6 | // 7 | import WalletCore 8 | 9 | class SOL: Coin { 10 | init() { 11 | super.init(name: "SOL", coinType: .solana) 12 | } 13 | 14 | override func getPublicKey(path: String, mnemonic: String, passphrase: String) -> String? { 15 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 16 | return wallet!.getKey(coin: self.coinType, derivationPath: path).getPublicKeyEd25519().data.base64EncodedString() 17 | } 18 | 19 | override func getRawPublicKey(path: String, mnemonic: String, passphrase: String) -> FlutterStandardTypedData { 20 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 21 | return FlutterStandardTypedData(bytes: wallet!.getKey(coin: self.coinType, derivationPath: path).getPublicKeyEd25519().data) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /ios/Classes/coins/TRX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TRX.swift 3 | // trustdart 4 | // 5 | // Created by Jay on 3/29/22. 6 | // 7 | 8 | import WalletCore 9 | 10 | class TRX: Coin { 11 | init() { 12 | super.init(name: "TRX", coinType: .tron) 13 | } 14 | 15 | override func signTransaction(path: String, txData: [String : Any], mnemonic: String, passphrase: String) -> String? { 16 | let cmd = txData["cmd"] as! String 17 | var txHash: String? 18 | let privateKey = HDWallet(mnemonic: mnemonic, passphrase: passphrase)?.getKey(coin: self.coinType, derivationPath: path) 19 | if privateKey != nil { 20 | switch cmd { 21 | case "TRC20": 22 | let contract = TronTransferTRC20Contract.with { 23 | $0.ownerAddress = txData["ownerAddress"] as! String 24 | $0.toAddress = txData["toAddress"] as! String 25 | $0.contractAddress = txData["contractAddress"] as! String 26 | $0.amount = Data(hexString: txData["amount"] as! String)! 27 | } 28 | 29 | let input = TronSigningInput.with { 30 | $0.transaction = TronTransaction.with { 31 | $0.transferTrc20Contract = contract 32 | $0.feeLimit = txData["feeLimit"] as! Int64 33 | $0.timestamp = txData["timestamp"] as! Int64 34 | $0.blockHeader = TronBlockHeader.with { 35 | $0.timestamp = txData["blockTime"] as! Int64 36 | $0.number = txData["number"] as! Int64 37 | $0.version = txData["version"] as! Int32 38 | $0.txTrieRoot = Data(hexString: txData["txTrieRoot"] as! String)! 39 | $0.parentHash = Data(hexString: txData["parentHash"] as! String)! 40 | $0.witnessAddress = Data(hexString: txData["witnessAddress"] as! String)! 41 | } 42 | } 43 | $0.privateKey = privateKey!.data 44 | } 45 | let output: TronSigningOutput = AnySigner.sign(input: input, coin: self.coinType) 46 | txHash = output.json 47 | case "TRC10": 48 | let transferAsset = TronTransferAssetContract.with { 49 | $0.ownerAddress = txData["ownerAddress"] as! String 50 | $0.toAddress = txData["toAddress"] as! String 51 | $0.amount = txData["amount"] as! Int64 52 | $0.assetName = txData["assetName"] as! String 53 | } 54 | let input = TronSigningInput.with { 55 | $0.transaction = TronTransaction.with { 56 | $0.transferAsset = transferAsset 57 | $0.timestamp = txData["timestamp"] as! Int64 58 | $0.blockHeader = TronBlockHeader.with { 59 | $0.timestamp = txData["blockTime"] as! Int64 60 | $0.number = txData["number"] as! Int64 61 | $0.version = txData["version"] as! Int32 62 | $0.txTrieRoot = Data(hexString: txData["txTrieRoot"] as! String)! 63 | $0.parentHash = Data(hexString: txData["parentHash"] as! String)! 64 | $0.witnessAddress = Data(hexString: txData["witnessAddress"] as! String)! 65 | } 66 | } 67 | $0.privateKey = privateKey!.data 68 | } 69 | let output: TronSigningOutput = AnySigner.sign(input: input, coin: self.coinType) 70 | txHash = output.json 71 | case "TRX": 72 | let transfer = TronTransferContract.with { 73 | $0.ownerAddress = txData["ownerAddress"] as! String 74 | $0.toAddress = txData["toAddress"] as! String 75 | $0.amount = txData["amount"] as! Int64 76 | } 77 | let input = TronSigningInput.with { 78 | $0.transaction = TronTransaction.with { 79 | $0.transfer = transfer 80 | $0.feeLimit = txData["feeLimit"] as! Int64 81 | $0.timestamp = txData["timestamp"] as! Int64 82 | $0.blockHeader = TronBlockHeader.with { 83 | $0.timestamp = txData["blockTime"] as! Int64 84 | $0.number = txData["number"] as! Int64 85 | $0.version = txData["version"] as! Int32 86 | $0.txTrieRoot = Data(hexString: txData["txTrieRoot"] as! String)! 87 | $0.parentHash = Data(hexString: txData["parentHash"] as! String)! 88 | $0.witnessAddress = Data(hexString: txData["witnessAddress"] as! String)! 89 | } 90 | } 91 | $0.privateKey = privateKey!.data 92 | } 93 | let output: TronSigningOutput = AnySigner.sign(input: input, coin: self.coinType) 94 | txHash = output.json 95 | case "CONTRACT": 96 | txHash = "" 97 | case "FREEZE": 98 | let contract = TronFreezeBalanceContract.with { 99 | $0.frozenBalance = txData["frozenBalance"] as! Int64 100 | $0.frozenDuration = txData["frozenDuration"] as! Int64 101 | $0.ownerAddress = txData["ownerAddress"] as! String 102 | $0.resource = txData["resource"] as! String 103 | } 104 | let input = TronSigningInput.with { 105 | $0.transaction = TronTransaction.with { 106 | $0.freezeBalance = contract 107 | $0.timestamp = txData["timestamp"] as! Int64 108 | $0.blockHeader = TronBlockHeader.with { 109 | $0.timestamp = txData["blockTime"] as! Int64 110 | $0.number = txData["number"] as! Int64 111 | $0.version = txData["version"] as! Int32 112 | $0.txTrieRoot = Data(hexString: txData["txTrieRoot"] as! String)! 113 | $0.parentHash = Data(hexString: txData["parentHash"] as! String)! 114 | $0.witnessAddress = Data(hexString: txData["witnessAddress"] as! String)! 115 | } 116 | } 117 | $0.privateKey = privateKey!.data 118 | } 119 | let output: TronSigningOutput = AnySigner.sign(input: input, coin: self.coinType) 120 | txHash = output.json 121 | default: 122 | txHash = nil 123 | } 124 | return txHash 125 | } 126 | return nil 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /ios/Classes/coins/XLM.swift: -------------------------------------------------------------------------------- 1 | /* 2 | XLM 3 | */ 4 | import WalletCore 5 | 6 | class XLM: Coin { 7 | enum NetworkType: String { 8 | case mainnet 9 | case testnet 10 | 11 | var passphrase: String { 12 | switch self { 13 | case .mainnet: 14 | return "Public Global Stellar Network ; September 2015" 15 | case .testnet: 16 | return "Test SDF Network ; September 2015" 17 | } 18 | } 19 | } 20 | 21 | init() { 22 | super.init(name: "XLM", coinType: .stellar) 23 | } 24 | 25 | override func getPublicKey(path: String, mnemonic: String, passphrase: String) -> String? { 26 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 27 | return wallet!.getKey(coin: self.coinType, derivationPath: path).getPublicKeyEd25519().data.base64EncodedString() 28 | } 29 | 30 | override func getRawPublicKey(path: String, mnemonic: String, passphrase: String) -> FlutterStandardTypedData { 31 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 32 | return FlutterStandardTypedData(bytes: wallet!.getKey(coin: self.coinType, derivationPath: path).getPublicKeyEd25519().data) 33 | } 34 | 35 | override func signTransaction(path: String, txData: [String : Any], mnemonic: String, passphrase: String) -> String? { 36 | let privateKey = HDWallet(mnemonic: mnemonic, passphrase: passphrase)?.getKey(coin: self.coinType, derivationPath: path) 37 | let cmd = txData["cmd"] as! String 38 | var txHash: String? 39 | 40 | let networkType: NetworkType = { 41 | if let network = txData["network"], let type = NetworkType(rawValue: network as! String) { 42 | return type 43 | } else { 44 | return .mainnet // Default to mainnet if network is not provided or invalid 45 | } 46 | }() 47 | 48 | switch(cmd){ 49 | case "ChangeTrust": 50 | let asset = StellarAsset.with { 51 | $0.issuer = txData["toAddress"] as! String 52 | $0.alphanum4 = txData["assetCode"] as! String 53 | } 54 | 55 | let operation = StellarOperationChangeTrust.with { 56 | $0.asset = asset 57 | $0.validBefore = txData["validBefore"] as! Int64 58 | } 59 | 60 | let signingInput = StellarSigningInput.with { 61 | $0.account = txData["ownerAddress"] as! String 62 | $0.fee = txData["fee"] as! Int32 63 | $0.sequence = txData["sequence"] as! Int64 64 | $0.passphrase = networkType.passphrase 65 | $0.opChangeTrust = operation 66 | $0.privateKey = privateKey!.data 67 | if (txData["memo"] != nil) { 68 | $0.memoID = StellarMemoId.with { 69 | $0.id = Int64(txData["memo"] as! String)! 70 | } 71 | } 72 | } 73 | 74 | let output: StellarSigningOutput = AnySigner.sign(input: signingInput, coin: self.coinType) 75 | txHash = output.signature 76 | case "Payment": 77 | var asset = StellarAsset() 78 | if let assetString = txData["asset"] as? String, let issuer = txData["issuer"] as? String { 79 | asset.alphanum4 = assetString 80 | asset.issuer = issuer 81 | } 82 | 83 | let operation = StellarOperationPayment.with { 84 | $0.destination = txData["toAddress"] as! String 85 | $0.amount = txData["amount"] as! Int64 86 | if (txData["asset"] != nil) { 87 | $0.asset = asset 88 | } 89 | } 90 | 91 | let signingInput = StellarSigningInput.with { 92 | $0.account = txData["ownerAddress"] as! String 93 | $0.fee = txData["fee"] as! Int32 94 | $0.sequence = txData["sequence"] as! Int64 95 | $0.passphrase = networkType.passphrase 96 | $0.opPayment = operation 97 | $0.privateKey = privateKey!.data 98 | if (txData["memo"] != nil) { 99 | $0.memoID = StellarMemoId.with { 100 | $0.id = Int64(txData["memo"] as! String)! 101 | } 102 | } 103 | } 104 | 105 | let output: StellarSigningOutput = AnySigner.sign(input: signingInput, coin: self.coinType) 106 | txHash = output.signature 107 | 108 | default: 109 | txHash = nil 110 | } 111 | 112 | return txHash 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /ios/Classes/coins/XRP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XRP.swift 3 | // trustdart 4 | // 5 | // Created by Jay on 3/11/25. 6 | // 7 | import WalletCore 8 | 9 | class XRP: Coin { 10 | init() { 11 | super.init(name: "XRP", coinType: .xrp) 12 | } 13 | 14 | override func signTransaction(path: String, txData: [String : Any], mnemonic: String, passphrase: String) -> String? { 15 | let privateKey = HDWallet(mnemonic: mnemonic, passphrase: passphrase)?.getKey(coin: self.coinType, derivationPath: path) 16 | 17 | let operation = RippleOperationPayment.with { 18 | $0.destination = txData["receiverAddress"] as! String 19 | $0.amount = txData["amount"] as! Int64 20 | if (txData["memo"] != nil) { 21 | $0.destinationTag = Int64(txData["memo"] as! String)! 22 | } 23 | } 24 | let input = RippleSigningInput.with { 25 | $0.fee = txData["fee"] as! Int64 26 | $0.sequence = txData["sequence"] as! Int32 27 | $0.lastLedgerSequence = txData["lastLedgerSequence"] as! Int32 28 | $0.account = txData["senderAddress" ] as! String 29 | $0.privateKey = privateKey!.data 30 | $0.opPayment = operation 31 | } 32 | 33 | let output: RippleSigningOutput = AnySigner.sign(input: input, coin: .xrp) 34 | return output.encoded.hexString 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /ios/Classes/coins/XTZ.swift: -------------------------------------------------------------------------------- 1 | /* 2 | XTZ 3 | */ 4 | import WalletCore 5 | 6 | class XTZ: Coin { 7 | init() { 8 | super.init(name: "XTZ", coinType: .tezos) 9 | } 10 | 11 | override func getPublicKey(path: String, mnemonic: String, passphrase: String) -> String? { 12 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 13 | return wallet!.getKey(coin: self.coinType, derivationPath: path).getPublicKeyEd25519().data.base64EncodedString() 14 | } 15 | 16 | override func getRawPublicKey(path: String, mnemonic: String, passphrase: String) -> FlutterStandardTypedData { 17 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 18 | return FlutterStandardTypedData(bytes: wallet!.getKey(coin: self.coinType, derivationPath: path).getPublicKeyEd25519().data) 19 | } 20 | 21 | override func signDataWithPrivateKey(path: String, mnemonic: String, passphrase: String, txData: String) -> String? { 22 | let wallet = HDWallet(mnemonic: mnemonic, passphrase: passphrase) 23 | let privateKey = wallet?.getKey(coin: self.coinType, derivationPath: path) 24 | let hash = Hash.blake2b(data: Data(hexString: txData)!, size: 32) 25 | if privateKey != nil { 26 | return privateKey?.sign(digest: hash, curve: self.coinType.curve)?.hexString 27 | } 28 | return nil 29 | } 30 | 31 | override func signTransaction(path: String, txData: [String : Any], mnemonic: String, passphrase: String) -> String? { 32 | var txHash: String? = nil 33 | let cmd = txData["cmd"] as! String? 34 | 35 | let privateKey = HDWallet(mnemonic: mnemonic, passphrase: passphrase)!.getKey(coin: self.coinType, derivationPath: path) 36 | let publicKey = privateKey.getPublicKeyEd25519().data 37 | let isRevealed = txData["isRevealed"] as! Bool? 38 | var revealOperation: TezosOperation = TezosOperation() 39 | var listOfAllOperations: [TezosOperation] = [] 40 | if(isRevealed == false){ 41 | 42 | var revealOperationData = TezosRevealOperationData() 43 | revealOperationData.publicKey = publicKey 44 | 45 | revealOperation = TezosOperation() 46 | revealOperation.source = txData["source"] as! String; 47 | revealOperation.fee = txData["reveal_fee"] as! Int64 48 | revealOperation.counter = txData["reveal_counter"] as! Int64 49 | revealOperation.gasLimit = txData["reveal_gasLimit"] as! Int64 50 | revealOperation.storageLimit = txData["reveal_storageLimit"] as! Int64 51 | revealOperation.kind = .reveal 52 | revealOperation.revealOperationData = revealOperationData 53 | 54 | listOfAllOperations.append(revealOperation) 55 | } 56 | 57 | switch(cmd){ 58 | case "FA2": 59 | var operationList = TezosOperationList() 60 | operationList.branch = txData["branch"] as! String 61 | 62 | let transferInfos = TezosTxs.with{ 63 | $0.to = txData["toAddress"] as! String 64 | $0.tokenID = txData["tokenId"] as! String 65 | $0.amount = txData["amount"] as! String 66 | } 67 | 68 | let transactionOperationData = TezosTransactionOperationData.with { 69 | $0.amount = txData["transactionAmount"] as! Int64 70 | $0.destination = txData["destination"] as! String 71 | $0.parameters.fa2Parameters.entrypoint = "transfer"; 72 | $0.parameters.fa2Parameters.txsObject = [TezosTxObject.with{ 73 | $0.from = txData["senderAddress"] as! String 74 | $0.txs = [transferInfos] 75 | }] 76 | } 77 | 78 | let transactionOperation = TezosOperation.with { 79 | $0.source = txData["source"] as! String 80 | $0.fee = txData["fee"] as! Int64 81 | $0.counter = txData["counter"] as! Int64 82 | $0.gasLimit = txData["gasLimit"] as! Int64 83 | $0.storageLimit = txData["storageLimit"] as! Int64 84 | $0.kind = .transaction 85 | $0.transactionOperationData = transactionOperationData 86 | } 87 | 88 | listOfAllOperations.append(transactionOperation) 89 | operationList.operations = listOfAllOperations 90 | 91 | 92 | let input = TezosSigningInput.with { 93 | $0.operationList = operationList 94 | $0.privateKey = privateKey.data 95 | } 96 | 97 | let output: TezosSigningOutput = AnySigner.sign(input: input, coin: .tezos) 98 | txHash = output.encoded.hexString 99 | 100 | case "FA12": 101 | var operationList = TezosOperationList() 102 | operationList.branch = txData["branch"] as! String 103 | 104 | let transactionOperationData = TezosTransactionOperationData.with { 105 | $0.amount = txData["transactionAmount"] as! Int64 106 | $0.destination = txData["destination"] as! String 107 | $0.parameters.fa12Parameters.entrypoint = "transfer"; 108 | $0.parameters.fa12Parameters.from = txData["senderAddress"] as! String; 109 | $0.parameters.fa12Parameters.to = txData["toAddress"] as! String; 110 | $0.parameters.fa12Parameters.value = txData["value"] as! String; 111 | } 112 | 113 | let transactionOperation = TezosOperation.with { 114 | $0.source = txData["source"] as! String; 115 | $0.fee = txData["fee"] as! Int64 116 | $0.counter = txData["counter"] as! Int64 117 | $0.gasLimit = txData["gasLimit"] as! Int64 118 | $0.storageLimit = txData["storageLimit"] as! Int64 119 | $0.kind = .transaction 120 | $0.transactionOperationData = transactionOperationData 121 | } 122 | 123 | listOfAllOperations.append(transactionOperation) 124 | operationList.operations = listOfAllOperations 125 | 126 | let input = TezosSigningInput.with { 127 | $0.operationList = operationList 128 | $0.privateKey = privateKey.data 129 | } 130 | 131 | let output: TezosSigningOutput = AnySigner.sign(input: input, coin: .tezos) 132 | txHash = output.encoded.hexString 133 | 134 | default: 135 | let opJson = Utils.objToJson(from: txData) 136 | let output = AnySigner.signJSON(opJson!, key: privateKey.data, coin: self.coinType) 137 | txHash = output 138 | } 139 | return txHash 140 | 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /ios/Classes/protocols/CoinProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoinInterface.swift 3 | // Pods-Runner 4 | // 5 | // Created by Jay on 3/29/22. 6 | // 7 | 8 | import WalletCore 9 | import Flutter 10 | 11 | protocol CoinProtocol { 12 | func generateAddress(path: String, mnemonic: String, passphrase: String) -> [String: String]? 13 | func getPrivateKey(path: String, mnemonic: String, passphrase: String) -> String? 14 | func getRawPrivateKey(path: String, mnemonic: String, passphrase: String) -> FlutterStandardTypedData 15 | func getSeed(path: String, mnemonic: String, passphrase: String) -> FlutterStandardTypedData 16 | func getPublicKey(path: String, mnemonic: String, passphrase: String) -> String? 17 | func getRawPublicKey(path: String, mnemonic: String, passphrase: String) -> FlutterStandardTypedData 18 | func validateAddress(address: String) -> Bool 19 | func signDataWithPrivateKey(path: String, mnemonic: String, passphrase: String, txData: String) -> String? 20 | func signTransaction(path: String, txData: [String: Any], mnemonic: String, passphrase: String) -> String? 21 | func multiSignTransaction(txData: [String: Any], privateKeys: [String]) -> String? 22 | 23 | } 24 | -------------------------------------------------------------------------------- /ios/Classes/utils/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // trustdart 4 | // 5 | // Created by Jay on 3/30/22. 6 | // 7 | 8 | struct Utils { 9 | static func objToJson(from object:Any) -> String? { 10 | guard let data = try? JSONSerialization.data(withJSONObject: object, options: []) else { 11 | return nil 12 | } 13 | return String(data: data, encoding: String.Encoding.utf8) 14 | } 15 | 16 | } 17 | 18 | extension Data { 19 | var bytes: [UInt8] { 20 | var byteArray = [UInt8](repeating: 0, count: self.count) 21 | self.copyBytes(to: &byteArray, count: self.count) 22 | return byteArray 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ios/Classes/utils/WalletError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WalletError.swift 3 | // trustdart 4 | // 5 | // Created by Jay on 4/5/22. 6 | // 7 | 8 | import Flutter 9 | 10 | class WalletError { 11 | let details: FlutterError 12 | required init(code: WalletHandlerErrorCodes, message: String?, details: Any?){ 13 | self.details = FlutterError(code: code.rawValue, message: message, details: details) 14 | } 15 | } 16 | 17 | enum WalletHandlerErrorCodes: String { 18 | case noWallet = "no_wallet" 19 | case addressNull = "address_null" 20 | case argumentsNull = "arguments_null" 21 | case txHashNull = "txhash_null" 22 | } 23 | 24 | 25 | struct ErrorResponse { 26 | static let addressNull = "[path], [coin], [mnemonic] and [passphrase] are required." 27 | } 28 | -------------------------------------------------------------------------------- /ios/trustdart.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint trustdart.podspec` to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'trustdart' 7 | s.version = '0.0.1' 8 | s.summary = 'A dart library that can interact with the trust wallet core library.' 9 | s.description = <<-DESC 10 | A dart library that can interact with the trust wallet core library. 11 | DESC 12 | s.homepage = 'https://github.com/EjaraApp/trustdart' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Ejara' => 'baah.kusi@ejara.africa' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.dependency 'Flutter' 18 | s.dependency 'TrustWalletCore' 19 | s.platform = :ios, '12.0' 20 | 21 | # Flutter.framework does not contain a i386 slice. 22 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 23 | s.swift_version = '5.0' 24 | end 25 | -------------------------------------------------------------------------------- /lib/trustdart.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/services.dart'; 4 | 5 | /// Has static members that handle various core crypto functionalies 6 | /// 7 | /// All the members are static and each takes care of a particular crypto 8 | /// Interaction with the native ios/android library. 9 | /// 10 | class Trustdart { 11 | static const MethodChannel _channel = const MethodChannel('trustdart'); 12 | 13 | static Future generateMnemonic([String passphrase = ""]) async { 14 | try { 15 | final String mnemonic = 16 | await _channel.invokeMethod('generateMnemonic', passphrase); 17 | return mnemonic; 18 | } catch (e) { 19 | return ""; 20 | } 21 | } 22 | 23 | static Future checkMnemonic(String mnemonic, 24 | [String passphrase = ""]) async { 25 | try { 26 | final bool importStatus = 27 | await _channel.invokeMethod('checkMnemonic', { 28 | 'mnemonic': mnemonic, 29 | 'passphrase': passphrase, 30 | }); 31 | return importStatus; 32 | } catch (e) { 33 | return false; 34 | } 35 | } 36 | 37 | /// generates an address for a particular coin 38 | static Future generateAddress(String mnemonic, String coin, String path, 39 | [String passphrase = ""]) async { 40 | try { 41 | final Map address = 42 | await _channel.invokeMethod('generateAddress', { 43 | 'coin': coin, 44 | 'path': path, 45 | 'mnemonic': mnemonic, 46 | 'passphrase': passphrase, 47 | }); 48 | return address; 49 | } catch (e) { 50 | return {'legacy': ''}; 51 | } 52 | } 53 | 54 | /// validates address belonging to a particular crypto 55 | static Future validateAddress(String coin, String address) async { 56 | try { 57 | final bool isAddressValid = 58 | await _channel.invokeMethod('validateAddress', { 59 | 'coin': coin, 60 | 'address': address, 61 | }); 62 | return isAddressValid; 63 | } catch (e) { 64 | return false; 65 | } 66 | } 67 | 68 | /// Returns the hex string format of the public key. 69 | static Future getPublicKey(String mnemonic, String coin, String path, 70 | [String passphrase = ""]) async { 71 | try { 72 | final String publicKey = 73 | await _channel.invokeMethod('getPublicKey', { 74 | 'coin': coin, 75 | 'path': path, 76 | 'mnemonic': mnemonic, 77 | 'passphrase': passphrase, 78 | }); 79 | return publicKey; 80 | } catch (e) { 81 | return ''; 82 | } 83 | } 84 | 85 | /// Returns the hex string format of the public key. 86 | static Future getRawPublicKey( 87 | String mnemonic, String coin, String path, 88 | [String passphrase = ""]) async { 89 | try { 90 | final Uint8List publicKey = 91 | await _channel.invokeMethod('getRawPublicKey', { 92 | 'coin': coin, 93 | 'path': path, 94 | 'mnemonic': mnemonic, 95 | 'passphrase': passphrase, 96 | }); 97 | return publicKey; 98 | } catch (e) { 99 | return Uint8List(0); 100 | } 101 | } 102 | 103 | /// Returns the hex string format of the private key. 104 | static Future getPrivateKey(String mnemonic, String coin, String path, 105 | [String passphrase = ""]) async { 106 | try { 107 | final String privateKey = 108 | await _channel.invokeMethod('getPrivateKey', { 109 | 'coin': coin, 110 | 'path': path, 111 | 'mnemonic': mnemonic, 112 | 'passphrase': passphrase, 113 | }); 114 | return privateKey; 115 | } catch (e) { 116 | return ''; 117 | } 118 | } 119 | 120 | /// Returns the hex string format of the private key. 121 | static Future getRawPrivateKey( 122 | String mnemonic, String coin, String path, 123 | [String passphrase = ""]) async { 124 | try { 125 | final Uint8List privateKey = 126 | await _channel.invokeMethod('getRawPrivateKey', { 127 | 'coin': coin, 128 | 'path': path, 129 | 'mnemonic': mnemonic, 130 | 'passphrase': passphrase, 131 | }); 132 | return privateKey; 133 | } catch (e) { 134 | return Uint8List(0); 135 | } 136 | } 137 | 138 | static Future getSeed(String mnemonic, String coin, String path, 139 | [String passphrase = ""]) async { 140 | try { 141 | final Uint8List privateKey = 142 | await _channel.invokeMethod('getSeed', { 143 | 'coin': coin, 144 | 'path': path, 145 | 'mnemonic': mnemonic, 146 | 'passphrase': passphrase, 147 | }); 148 | return privateKey; 149 | } catch (e) { 150 | return Uint8List(0); 151 | } 152 | } 153 | 154 | ///signs a transaction 155 | static Future signTransaction( 156 | String mnemonic, String coin, String path, Map txData, 157 | [String passphrase = ""]) async { 158 | try { 159 | final String txHash = 160 | await _channel.invokeMethod('signTransaction', { 161 | 'coin': coin, 162 | 'txData': txData, 163 | 'path': path, 164 | 'mnemonic': mnemonic, 165 | 'passphrase': passphrase, 166 | }); 167 | return txHash; 168 | } catch (e) { 169 | return ''; 170 | } 171 | } 172 | 173 | static Future multiSignTransaction( 174 | String coin, Map txData, List? privateKeys) async { 175 | try { 176 | final String txHash = await _channel.invokeMethod( 177 | 'multiSignTransaction', { 178 | 'coin': coin, 179 | 'txData': txData, 180 | 'privateKeys': privateKeys 181 | }); 182 | return txHash; 183 | } catch (e) { 184 | return ''; 185 | } 186 | } 187 | 188 | static Future signDataWithPrivateKey( 189 | String mnemonic, String coin, String path, String txData, 190 | [String passphrase = ""]) async { 191 | try { 192 | final String txHash = await _channel 193 | .invokeMethod('signDataWithPrivateKey', { 194 | 'coin': coin, 195 | 'txData': txData, 196 | 'path': path, 197 | 'mnemonic': mnemonic, 198 | 'passphrase': passphrase, 199 | }); 200 | return txHash; 201 | } catch (e) { 202 | return ''; 203 | } 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: trustdart 2 | description: A dart library that can interact with the trust wallet core library. 3 | version: 0.3.6 4 | homepage: https://github.com/EjaraApp/trustdart 5 | 6 | environment: 7 | sdk: ^2.12.0 8 | flutter: ">=1.20.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | dev_dependencies: 15 | flutter_test: 16 | sdk: flutter 17 | 18 | # For information on the generic Dart part of this file, see the 19 | # following page: https://dart.dev/tools/pub/pubspec 20 | 21 | # The following section is specific to Flutter. 22 | flutter: 23 | # This section identifies this Flutter project as a plugin project. 24 | # The 'pluginClass' and Android 'package' identifiers should not ordinarily 25 | # be modified. They are used by the tooling to maintain consistency when 26 | # adding or updating assets for this project. 27 | plugin: 28 | platforms: 29 | android: 30 | package: africa.ejara.trustdart 31 | pluginClass: TrustdartPlugin 32 | ios: 33 | pluginClass: TrustdartPlugin 34 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=EjaraApp_trustdart_AYN5YH4SQ-aWMkOq5Itw 2 | -------------------------------------------------------------------------------- /trustdart.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | --------------------------------------------------------------------------------