Such Wow, Many Cake. Om nom

wownero test
dsc 2 years ago
parent b58b0002b0
commit 13816cf3b5

5
.gitignore vendored

@ -116,10 +116,15 @@ cw_shared_external/ios/External/
cw_haven/ios/External/
cw_haven/android/.externalNativeBuild/
cw_haven/android/.cxx/
# cw_wownero/**
cw_wownero/ios/External/
cw_wownero/android/.externalNativeBuild/
cw_wownero/android/.cxx/
lib/bitcoin/bitcoin.dart
lib/monero/monero.dart
lib/haven/haven.dart
lib/haven/wownero.dart
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_180.png
ios/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_120.png

@ -0,0 +1,11 @@
package com.cakewallet.wownero;
import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class Application extends FlutterApplication implements PluginRegistrantCallback {
@Override
public void registerWith(PluginRegistry registry) {}
}

@ -0,0 +1,90 @@
package com.cakewallet.wownero;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterFragmentActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.view.WindowManager;
import com.unstoppabledomains.resolution.DomainResolution;
import com.unstoppabledomains.resolution.Resolution;
import java.security.SecureRandom;
public class MainActivity extends FlutterFragmentActivity {
final String UTILS_CHANNEL = "com.cake_wallet/native_utils";
final int UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK = 24;
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
MethodChannel utilsChannel =
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(),
UTILS_CHANNEL);
utilsChannel.setMethodCallHandler(this::handle);
}
private void handle(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
Handler handler = new Handler(Looper.getMainLooper());
try {
switch (call.method) {
case "enableWakeScreen":
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
handler.post(() -> result.success(true));
break;
case "disableWakeScreen":
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
handler.post(() -> result.success(true));
break;
case "sec_random":
int count = call.argument("count");
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[count];
random.nextBytes(bytes);
handler.post(() -> result.success(bytes));
break;
case "getUnstoppableDomainAddress":
int version = Build.VERSION.SDK_INT;
if (version >= UNSTOPPABLE_DOMAIN_MIN_VERSION_SDK) {
getUnstoppableDomainAddress(call, result);
} else {
handler.post(() -> result.success(""));
}
break;
default:
handler.post(() -> result.notImplemented());
}
} catch (Exception e) {
handler.post(() -> result.error("UNCAUGHT_ERROR", e.getMessage(), null));
}
}
private void getUnstoppableDomainAddress(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
DomainResolution resolution = new Resolution();
Handler handler = new Handler(Looper.getMainLooper());
String domain = call.argument("domain");
String ticker = call.argument("ticker");
AsyncTask.execute(() -> {
try {
String address = resolution.getAddress(domain, ticker);
handler.post(() -> result.success(address));
} catch (Exception e) {
System.out.println("Expected Address, but got " + e.getMessage());
handler.post(() -> result.success(""));
}
});
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

@ -22,10 +22,9 @@
-
uri: node.imonero.org:18081
is_default: false
-
uri: node.c3pool.com:18081
is_default: false
-
uri: xmr.prprpr.icu:18081
is_default: false

@ -0,0 +1,24 @@
-
uri: eu-west-2.wow.xmr.pm:34568
is_default: true
useSSL: false
-
uri: eu-west-1.wow.xmr.pm:34568
is_default: false
useSSL: false
-
uri: eu-west-6.wow.xmr.pm:34568
is_default: false
useSSL: false
-
uri: node.wownero.club:34568
is_default: false
useSSL: false
-
uri: node.suchwow.xyz:34568
is_default: false
useSSL: false
-
uri: idontwanttogototoronto.wow.fail:34568
is_default: false
useSSL: false

@ -24,7 +24,8 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
CryptoCurrency.usdterc20,
CryptoCurrency.xlm,
CryptoCurrency.xrp,
CryptoCurrency.xhv
CryptoCurrency.xhv,
CryptoCurrency.wow
];
static const xmr = CryptoCurrency(title: 'XMR', raw: 0);
static const ada = CryptoCurrency(title: 'ADA', raw: 1);
@ -58,6 +59,8 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
static const xnzd = CryptoCurrency(title: 'XNZD', raw: 28);
static const xusd = CryptoCurrency(title: 'XUSD', raw: 29);
static const wow = CryptoCurrency(title: 'WOW', raw: 30);
static CryptoCurrency deserialize({int raw}) {
switch (raw) {
case 0:
@ -120,6 +123,8 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
return CryptoCurrency.xnzd;
case 29:
return CryptoCurrency.xusd;
case 30:
return CryptoCurrency.wow;
default:
return null;
}
@ -187,6 +192,8 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
return CryptoCurrency.xnzd;
case 'xusd':
return CryptoCurrency.xusd;
case 'wow':
return CryptoCurrency.wow;
default:
return null;
}

@ -11,6 +11,8 @@ CryptoCurrency currencyForWalletType(WalletType type) {
return CryptoCurrency.ltc;
case WalletType.haven:
return CryptoCurrency.xhv;
case WalletType.wownero:
return CryptoCurrency.wow;
default:
return null;
}

@ -62,6 +62,8 @@ class Node extends HiveObject with Keyable {
return createUriFromElectrumAddress(uriRaw);
case WalletType.haven:
return Uri.http(uriRaw, '');
case WalletType.wownero:
return Uri.http(uriRaw, '');
default:
return null;
}
@ -83,6 +85,7 @@ class Node extends HiveObject with Keyable {
try {
switch (type) {
case WalletType.monero:
case WalletType.wownero:
return requestMoneroNode();
case WalletType.bitcoin:
return requestElectrumServer();

@ -7,7 +7,8 @@ const walletTypes = [
WalletType.monero,
WalletType.bitcoin,
WalletType.litecoin,
WalletType.haven
WalletType.haven,
WalletType.wownero
];
const walletTypeTypeId = 5;
@ -26,7 +27,10 @@ enum WalletType {
litecoin,
@HiveField(4)
haven
haven,
@HiveField(5)
wownero
}
int serializeToInt(WalletType type) {
@ -39,6 +43,8 @@ int serializeToInt(WalletType type) {
return 2;
case WalletType.haven:
return 3;
case WalletType.wownero:
return 4;
default:
return -1;
}
@ -54,6 +60,8 @@ WalletType deserializeFromInt(int raw) {
return WalletType.litecoin;
case 3:
return WalletType.haven;
case 4:
return WalletType.wownero;
default:
return null;
}
@ -69,6 +77,8 @@ String walletTypeToString(WalletType type) {
return 'Litecoin';
case WalletType.haven:
return 'Haven';
case WalletType.wownero:
return 'Wownero';
default:
return '';
}
@ -84,6 +94,8 @@ String walletTypeToDisplayName(WalletType type) {
return 'Litecoin (Electrum)';
case WalletType.haven:
return 'Haven';
case WalletType.wownero:
return 'Wownero';
default:
return '';
}
@ -99,6 +111,8 @@ CryptoCurrency walletTypeToCryptoCurrency(WalletType type) {
return CryptoCurrency.ltc;
case WalletType.haven:
return CryptoCurrency.xhv;
case WalletType.wownero:
return CryptoCurrency.wow;
default:
return null;
}

@ -0,0 +1,11 @@
.DS_Store
.dart_tool/
.packages
.pub/
build/
ios/External/
android/.externalNativeBuild/
android/.cxx/

@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 398e4272a2e43d7daab75f225a13422e384ee0cd
channel: dev
project_type: plugin

@ -0,0 +1,3 @@
## 0.0.1
* TODO: wow.

@ -0,0 +1 @@
TODO: Add your license here.

@ -0,0 +1,5 @@
# cw_wownero
This project is part of Cake Wallet app.
Copyright (c) 2022 Cake Technologies LLC.

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>

@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>cw_wownero</name>
<comment>Project android created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>

@ -0,0 +1,13 @@
arguments=
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(6.0-20191016123526+0000))
connection.project.dir=../../android
eclipse.preferences.version=1
gradle.user.home=
java.home=
jvm.arguments=
offline.mode=false
override.workspace.settings=true
show.console.view=true
show.executions.view=true

@ -0,0 +1,244 @@
cmake_minimum_required(VERSION 3.4.1)
add_library( cw_wownero
SHARED
./jni/wownero_jni.cpp
../ios/Classes/wownero_api.cpp)
find_library( log-lib log )
set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/../../cw_shared_external/ios/External/android)
############
# libsodium
############
add_library(sodium STATIC IMPORTED)
set_target_properties(sodium PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libsodium.a)
############
# OpenSSL
############
add_library(crypto STATIC IMPORTED)
set_target_properties(crypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libcrypto.a)
add_library(ssl STATIC IMPORTED)
set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libssl.a)
############
# wownero-seed
############
add_library(wownero-seed STATIC IMPORTED)
set_target_properties(wownero-seed PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libwownero-seed.a)
############
# Boost
############
add_library(boost_chrono STATIC IMPORTED)
set_target_properties(boost_chrono PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_chrono.a)
add_library(boost_date_time STATIC IMPORTED)
set_target_properties(boost_date_time PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_date_time.a)
add_library(boost_filesystem STATIC IMPORTED)
set_target_properties(boost_filesystem PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_filesystem.a)
add_library(boost_program_options STATIC IMPORTED)
set_target_properties(boost_program_options PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_program_options.a)
add_library(boost_regex STATIC IMPORTED)
set_target_properties(boost_regex PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_regex.a)
add_library(boost_serialization STATIC IMPORTED)
set_target_properties(boost_serialization PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_serialization.a)
add_library(boost_system STATIC IMPORTED)
set_target_properties(boost_system PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_system.a)
add_library(boost_thread STATIC IMPORTED)
set_target_properties(boost_thread PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_thread.a)
add_library(boost_wserialization STATIC IMPORTED)
set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/libboost_wserialization.a)
#############
# Wownero
#############
add_library(wallet_api STATIC IMPORTED)
set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libwallet_api.a)
add_library(wallet STATIC IMPORTED)
set_target_properties(wallet PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libwallet.a)
add_library(cryptonote_core STATIC IMPORTED)
set_target_properties(cryptonote_core PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libcryptonote_core.a)
add_library(cryptonote_basic STATIC IMPORTED)
set_target_properties(cryptonote_basic PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libcryptonote_basic.a)
add_library(cryptonote_format_utils_basic STATIC IMPORTED)
set_target_properties(cryptonote_format_utils_basic PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libcryptonote_format_utils_basic.a)
add_library(mnemonics STATIC IMPORTED)
set_target_properties(mnemonics PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libmnemonics.a)
add_library(common STATIC IMPORTED)
set_target_properties(common PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libcommon.a)
add_library(cncrypto STATIC IMPORTED)
set_target_properties(cncrypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libcncrypto.a)
add_library(ringct STATIC IMPORTED)
set_target_properties(ringct PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libringct.a)
add_library(ringct_basic STATIC IMPORTED)
set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libringct_basic.a)
add_library(blockchain_db STATIC IMPORTED)
set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libblockchain_db.a)
add_library(lmdb STATIC IMPORTED)
set_target_properties(lmdb PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/liblmdb.a)
add_library(easylogging STATIC IMPORTED)
set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libeasylogging.a)
add_library(unbound STATIC IMPORTED)
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libunbound.a)
add_library(epee STATIC IMPORTED)
set_target_properties(epee PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libepee.a)
add_library(blocks STATIC IMPORTED)
set_target_properties(blocks PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libblocks.a)
add_library(checkpoints STATIC IMPORTED)
set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libcheckpoints.a)
add_library(device STATIC IMPORTED)
set_target_properties(device PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libdevice.a)
add_library(device_trezor STATIC IMPORTED)
set_target_properties(device_trezor PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libdevice_trezor.a)
add_library(multisig STATIC IMPORTED)
set_target_properties(multisig PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libmultisig.a)
add_library(version STATIC IMPORTED)
set_target_properties(version PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libversion.a)
add_library(net STATIC IMPORTED)
set_target_properties(net PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libnet.a)
add_library(hardforks STATIC IMPORTED)
set_target_properties(hardforks PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libhardforks.a)
add_library(randomx STATIC IMPORTED)
set_target_properties(randomx PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/librandomx.a)
add_library(rpc_base STATIC IMPORTED)
set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/librpc_base.a)
add_library(wallet-crypto STATIC IMPORTED)
set_target_properties(wallet-crypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/lib/wownero/libwallet-crypto.a)
set(WALLET_CRYPTO "")
if(${ANDROID_ABI} STREQUAL "x86_64")
set(WALLET_CRYPTO "wallet-crypto")
endif()
include_directories( ${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/include )
target_link_libraries( cw_wownero
wallet_api
wallet
cryptonote_core
cryptonote_basic
cryptonote_format_utils_basic
mnemonics
ringct
ringct_basic
net
common
cncrypto
blockchain_db
lmdb
easylogging
unbound
epee
blocks
checkpoints
device
device_trezor
multisig
version
randomx
hardforks
rpc_base
${WALLET_CRYPTO}
boost_chrono
boost_date_time
boost_filesystem
boost_program_options
boost_regex
boost_serialization
boost_system
boost_thread
boost_wserialization
ssl
crypto
sodium
-Wl,--whole-archive
wownero-seed
-Wl,--no-whole-archive
${log-lib} )

@ -0,0 +1,49 @@
group 'com.cakewallet.wownero'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
rootProject.allprojects {
repositories {
google()
jcenter()
}
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 28
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
minSdkVersion 21
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

@ -0,0 +1,4 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip

@ -0,0 +1,74 @@
#include <string.h>
#include <jni.h>
#include "../../ios/Classes/wownero_api.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL
Java_com_cakewallet_wownero_WowneroApi_setNodeAddressJNI(
JNIEnv *env,
jobject inst,
jstring uri,
jstring login,
jstring password,
jboolean use_ssl,
jboolean is_light_wallet) {
const char *_uri = env->GetStringUTFChars(uri, 0);
const char *_login = "";
const char *_password = "";
char *error;
if (login != NULL) {
_login = env->GetStringUTFChars(login, 0);
}
if (password != NULL) {
_password = env->GetStringUTFChars(password, 0);
}
char *__uri = (char*) _uri;
char *__login = (char*) _login;
char *__password = (char*) _password;
bool inited = setup_node(__uri, __login, __password, false, false, error);
if (!inited) {
env->ThrowNew(env->FindClass("java/lang/Exception"), error);
}
}
JNIEXPORT void JNICALL
Java_com_cakewallet_wownero_WowneroApi_connectToNodeJNI(
JNIEnv *env,
jobject inst) {
char *error;
bool is_connected = connect_to_node(error);
if (!is_connected) {
env->ThrowNew(env->FindClass("java/lang/Exception"), error);
}
}
JNIEXPORT void JNICALL
Java_com_cakewallet_wownero_WowneroApi_startSyncJNI(
JNIEnv *env,
jobject inst) {
start_refresh();
}
JNIEXPORT void JNICALL
Java_com_cakewallet_wownero_WowneroApi_loadWalletJNI(
JNIEnv *env,
jobject inst,
jstring path,
jstring password) {
char *_path = (char *) env->GetStringUTFChars(path, 0);
char *_password = (char *) env->GetStringUTFChars(password, 0);
load_wallet(_path, _password, 0);
}
#ifdef __cplusplus
}
#endif

@ -0,0 +1 @@
rootProject.name = 'cw_wownero'

@ -0,0 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cakewallet.wownero">
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

@ -0,0 +1,74 @@
package com.cakewallet.cw_wownero
import android.app.Activity
import android.os.AsyncTask
import android.os.Looper
import android.os.Handler
import android.os.Process
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
class doAsync(val handler: () -> Unit) : AsyncTask<Void, Void, Void>() {
override fun doInBackground(vararg params: Void?): Void? {
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
handler()
return null
}
}
class CwWowneroPlugin: MethodCallHandler {
companion object {
// val wowneroApi = WowneroApi()
val main = Handler(Looper.getMainLooper());
init {
System.loadLibrary("cw_wownero")
}
@JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "cw_wownero")
channel.setMethodCallHandler(CwWowneroPlugin())
}
}
override fun onMethodCall(call: MethodCall, result: Result) {
if (call.method == "setupNode") {
val uri = call.argument("address") ?: ""
val login = call.argument("login") ?: ""
val password = call.argument("password") ?: ""
val useSSL = false
val isLightWallet = false
// doAsync {
// try {
// wowneroApi.setNodeAddressJNI(uri, login, password, useSSL, isLightWallet)
// main.post({
// result.success(true)
// });
// } catch(e: Throwable) {
// main.post({
// result.error("CONNECTION_ERROR", e.message, null)
// });
// }
// }.execute()
}
if (call.method == "startSync") {
// doAsync {
// wowneroApi.startSyncJNI()
// main.post({
// result.success(true)
// });
// }.execute()
}
if (call.method == "loadWallet") {
val path = call.argument("path") ?: ""
val password = call.argument("password") ?: ""
// wowneroApi.loadWalletJNI(path, password)
result.success(true)
}
}
}

@ -0,0 +1,37 @@
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
.generated/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/Generated.xcconfig
/Flutter/flutter_export_environment.sh

@ -0,0 +1,23 @@
#include <stdint.h>
struct CWWowneroWalletListener;
typedef int8_t (*on_new_block_callback)(uint64_t height);
typedef int8_t (*on_need_to_refresh_callback)();
typedef struct CWWowneroWalletListener
{
// on_money_spent_callback *on_money_spent;
// on_money_received_callback *on_money_received;
// on_unconfirmed_money_received_callback *on_unconfirmed_money_received;
// on_new_block_callback *on_new_block;
// on_updated_callback *on_updated;
// on_refreshed_callback *on_refreshed;
on_new_block_callback on_new_block;
} CWWowneroWalletListener;
struct TestListener {
// int8_t x;
on_new_block_callback on_new_block;
};

@ -0,0 +1,4 @@
#import <Flutter/Flutter.h>
@interface CwWowneroPlugin : NSObject<FlutterPlugin>
@end

@ -0,0 +1,8 @@
#import "CwWowneroPlugin.h"
#import <cw_wownero/cw_wownero-Swift.h>
@implementation CwWowneroPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[SwiftCwWowneroPlugin registerWithRegistrar:registrar];
}
@end

@ -0,0 +1,14 @@
import Flutter
import UIKit
public class SwiftCwWowneroPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "cw_wownero", binaryMessenger: registrar.messenger())
let instance = SwiftCwWowneroPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
result("iOS " + UIDevice.current.systemVersion)
}
}

@ -0,0 +1,823 @@
#include <stdint.h>
#include "cstdlib"
#include <chrono>
#include <functional>
#include <iostream>
#include <unistd.h>
#include <mutex>
#include "thread"
#include "CwWalletListener.h"
#include <sstream>
#include <wownero_seed/wownero_seed.hpp>
#if __APPLE__
// Fix for randomx on ios
void __clear_cache(void* start, void* end) { }
#include "../External/ios/include/wallet2_api.h"
#else
#include "../External/android/include/wallet2_api.h"
#endif
using namespace std::chrono_literals;
#ifdef __cplusplus
extern "C"
{
#endif
const uint64_t MONERO_BLOCK_SIZE = 1000;
struct Utf8Box
{
char *value;
Utf8Box(char *_value)
{
value = _value;
}
};
struct SubaddressRow
{
uint64_t id;
char *address;
char *label;
SubaddressRow(std::size_t _id, char *_address, char *_label)
{
id = static_cast<uint64_t>(_id);
address = _address;
label = _label;
}
};
struct AccountRow
{
uint64_t id;
char *label;
AccountRow(std::size_t _id, char *_label)
{
id = static_cast<uint64_t>(_id);
label = _label;
}
};
struct MoneroWalletListener : Monero::WalletListener
{
uint64_t m_height;
bool m_need_to_refresh;
bool m_new_transaction;
MoneroWalletListener()
{
m_height = 0;
m_need_to_refresh = false;
m_new_transaction = false;
}
void moneySpent(const std::string &txId, uint64_t amount)
{
m_new_transaction = true;
}
void moneyReceived(const std::string &txId, uint64_t amount)
{
m_new_transaction = true;
}
void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount)
{
m_new_transaction = true;
}
void newBlock(uint64_t height)
{
m_height = height;
}
void updated()
{
m_new_transaction = true;
}
void refreshed(bool success)
{
m_need_to_refresh = true;
}
void resetNeedToRefresh()
{
m_need_to_refresh = false;
}
bool isNeedToRefresh()
{
return m_need_to_refresh;
}
bool isNewTransactionExist()
{
return m_new_transaction;
}
void resetIsNewTransactionExist()
{
m_new_transaction = false;
}
uint64_t height()
{
return m_height;
}
};
struct TransactionInfoRow
{
uint64_t amount;
uint64_t fee;
uint64_t blockHeight;
uint64_t confirmations;
uint32_t subaddrAccount;
int8_t direction;
int8_t isPending;
uint32_t subaddrIndex;
char *hash;
char *paymentId;
int64_t datetime;
TransactionInfoRow(Monero::TransactionInfo *transaction)
{
amount = transaction->amount();
fee = transaction->fee();
blockHeight = transaction->blockHeight();
subaddrAccount = transaction->subaddrAccount();
std::set<uint32_t>::iterator it = transaction->subaddrIndex().begin();
subaddrIndex = *it;
confirmations = transaction->confirmations();
datetime = static_cast<int64_t>(transaction->timestamp());
direction = transaction->direction();
isPending = static_cast<int8_t>(transaction->isPending());
std::string *hash_str = new std::string(transaction->hash());
hash = strdup(hash_str->c_str());
paymentId = strdup(transaction->paymentId().c_str());
}
};
struct PendingTransactionRaw
{
uint64_t amount;
uint64_t fee;
char *hash;
Monero::PendingTransaction *transaction;
PendingTransactionRaw(Monero::PendingTransaction *_transaction)
{
transaction = _transaction;
amount = _transaction->amount();
fee = _transaction->fee();
hash = strdup(_transaction->txid()[0].c_str());
}
};
Monero::Wallet *m_wallet;
Monero::TransactionHistory *m_transaction_history;
MoneroWalletListener *m_listener;
Monero::Subaddress *m_subaddress;
Monero::SubaddressAccount *m_account;
uint64_t m_last_known_wallet_height;
uint64_t m_cached_syncing_blockchain_height = 0;
std::mutex store_lock;
bool is_storing = false;
void change_current_wallet(Monero::Wallet *wallet)
{
m_wallet = wallet;
m_listener = nullptr;
if (wallet != nullptr)
{
m_transaction_history = wallet->history();
}
else
{
m_transaction_history = nullptr;
}
if (wallet != nullptr)
{
m_account = wallet->subaddressAccount();
}
else
{
m_account = nullptr;
}
if (wallet != nullptr)
{
m_subaddress = wallet->subaddress();
}
else
{
m_subaddress = nullptr;
}
}
Monero::Wallet *get_current_wallet()
{
return m_wallet;
}
bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error)
{
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager();
time_t time = std::time(nullptr);
wownero_seed wow_seed(time, "wownero");
std::stringstream seed_stream;
seed_stream << wow_seed;
std::string seed = seed_stream.str();
std::stringstream key_stream;
key_stream << wow_seed.key();
std::string spendKey = key_stream.str();
uint64_t restoreHeight = wow_seed.blockheight();
Monero::Wallet *wallet = walletManager->createDeterministicWalletFromSpendKey(
std::string(path),
std::string(password),
std::string(language),
static_cast<Monero::NetworkType>(_networkType),
(uint64_t)restoreHeight,
spendKey,
1);
wallet->setCacheAttribute("cake.seed", seed);
int status;
std::string errorString;
wallet->statusWithErrorString(status, errorString);
if (wallet->status() != Monero::Wallet::Status_Ok)
{
error = strdup(wallet->errorString().c_str());
return false;
}
change_current_wallet(wallet);
return true;
}
bool restore_wallet_from_seed(char *path, char *password, char *seed, int32_t networkType, char *error)
{
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager();
wownero_seed wow_seed(seed, "wownero");
std::stringstream seed_stream;
seed_stream << wow_seed;
std::string seed_str = seed_stream.str();
std::stringstream key_stream;
key_stream << wow_seed.key();
std::string spendKey = key_stream.str();
uint64_t restoreHeight = wow_seed.blockheight();
Monero::Wallet *wallet = walletManager->createDeterministicWalletFromSpendKey(
std::string(path),
std::string(password),
"English",
static_cast<Monero::NetworkType>(_networkType),
(uint64_t)restoreHeight,
spendKey,
1);
wallet->setCacheAttribute("cake.seed", seed_str);
int status;
std::string errorString;
wallet->statusWithErrorString(status, errorString);
if (status != Monero::Wallet::Status_Ok || !errorString.empty())
{
error = strdup(errorString.c_str());
return false;
}
change_current_wallet(wallet);
return true;
}
bool restore_wallet_from_keys(char *path, char *password, char *language, char *address, char *viewKey, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error)
{
// this function is not used, restoring from keys is disabled for Wownero
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->createWalletFromKeys(
std::string(path),
std::string(password),
std::string(language),
_networkType,
(uint64_t)restoreHeight,
std::string(address),
std::string(viewKey),
std::string(spendKey));
int status;
std::string errorString;
wallet->statusWithErrorString(status, errorString);
if (status != Monero::Wallet::Status_Ok || !errorString.empty())
{
error = strdup(errorString.c_str());
return false;
}
change_current_wallet(wallet);
return true;
}
bool load_wallet(char *path, char *password, int32_t nettype)
{
nice(19);
Monero::NetworkType networkType = static_cast<Monero::NetworkType>(nettype);
Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager();
Monero::Wallet *wallet = walletManager->openWallet(std::string(path), std::string(password), networkType);
int status;
std::string errorString;
wallet->statusWithErrorString(status, errorString);
change_current_wallet(wallet);
return !(status != Monero::Wallet::Status_Ok || !errorString.empty());
}
char *error_string() {
return strdup(get_current_wallet()->errorString().c_str());
}
bool is_wallet_exist(char *path)
{
return Monero::WalletManagerFactory::getWalletManager()->walletExists(std::string(path));
}
void close_current_wallet()
{
Monero::WalletManagerFactory::getWalletManager()->closeWallet(get_current_wallet());
change_current_wallet(nullptr);
}
char *get_filename()
{
return strdup(get_current_wallet()->filename().c_str());
}
char *secret_view_key()
{
return strdup(get_current_wallet()->secretViewKey().c_str());
}
char *public_view_key()
{
return strdup(get_current_wallet()->publicViewKey().c_str());
}
char *secret_spend_key()
{
return strdup(get_current_wallet()->secretSpendKey().c_str());
}
char *public_spend_key()
{
return strdup(get_current_wallet()->publicSpendKey().c_str());
}
char *get_address(uint32_t account_index, uint32_t address_index)
{
return strdup(get_current_wallet()->address(account_index, address_index).c_str());
}
const char *seed()
{
return strdup(get_current_wallet()->getCacheAttribute("cake.seed").c_str());
}
uint64_t get_full_balance(uint32_t account_index)
{
return get_current_wallet()->balance(account_index);
}
uint64_t get_unlocked_balance(uint32_t account_index)
{
return get_current_wallet()->unlockedBalance(account_index);
}
uint64_t get_current_height()
{
return get_current_wallet()->blockChainHeight();
}
uint64_t get_node_height()
{
return get_current_wallet()->daemonBlockChainHeight();
}
uint64_t get_seed_height(char *seed)
{
wownero_seed wow_seed(seed, "wownero");
return wow_seed.blockheight();
}
bool connect_to_node(char *error)
{
nice(19);
bool is_connected = get_current_wallet()->connectToDaemon();
if (!is_connected)
{
error = strdup(get_current_wallet()->errorString().c_str());
}
return is_connected;
}
bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *error)
{
nice(19);
Monero::Wallet *wallet = get_current_wallet();
std::string _login = "";
std::string _password = "";
if (login != nullptr)
{
_login = std::string(login);
}
if (password != nullptr)
{
_password = std::string(password);
}
bool inited = wallet->init(std::string(address), 0, _login, _password, true, is_light_wallet);
if (!inited)
{
error = strdup(wallet->errorString().c_str());
} else if (!wallet->connectToDaemon()) {
error = strdup(wallet->errorString().c_str());
}
wallet->setTrustedDaemon(true);
return inited;
}
bool is_connected()
{
return get_current_wallet()->connected();
}
void start_refresh()
{
get_current_wallet()->refreshAsync();
get_current_wallet()->startRefresh();
}
void set_refresh_from_block_height(uint64_t height)
{
get_current_wallet()->setRefreshFromBlockHeight(height);
}
void set_recovering_from_seed(bool is_recovery)
{
get_current_wallet()->setRecoveringFromSeed(is_recovery);
}
void store(char *path)
{
store_lock.lock();
if (is_storing) {
return;
}
is_storing = true;
get_current_wallet()->store(std::string(path));
is_storing = false;
store_lock.unlock();
}
bool transaction_create(char *address, char *payment_id, char *amount,
uint8_t priority_raw, uint32_t subaddr_account, Utf8Box &error, PendingTransactionRaw &pendingTransaction)
{
nice(19);
auto priority = static_cast<Monero::PendingTransaction::Priority>(priority_raw);
std::string _payment_id;
Monero::PendingTransaction *transaction;
if (payment_id != nullptr)
{
_payment_id = std::string(payment_id);
}
if (amount != nullptr)
{
uint64_t _amount = Monero::Wallet::amountFromString(std::string(amount));
transaction = m_wallet->createTransaction(std::string(address), _payment_id, _amount, m_wallet->defaultMixin(), priority, subaddr_account);
}
else
{
transaction = m_wallet->createTransaction(std::string(address), _payment_id, Monero::optional<uint64_t>(), m_wallet->defaultMixin(), priority, subaddr_account);
}
int status = transaction->status();
if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical)
{
error = Utf8Box(strdup(transaction->errorString().c_str()));
return false;
}
if (m_listener != nullptr) {
m_listener->m_new_transaction = true;
}
pendingTransaction = PendingTransactionRaw(transaction);
return true;
}
bool transaction_create_mult_dest(char **addresses, char *payment_id, char **amounts, uint32_t size,
uint8_t priority_raw, uint32_t subaddr_account, Utf8Box &error, PendingTransactionRaw &pendingTransaction)
{
nice(19);
std::vector<std::string> _addresses;
std::vector<uint64_t> _amounts;
for (int i = 0; i < size; i++) {
_addresses.push_back(std::string(*addresses));
_amounts.push_back(Monero::Wallet::amountFromString(std::string(*amounts)));
addresses++;
amounts++;
}
auto priority = static_cast<Monero::PendingTransaction::Priority>(priority_raw);
std::string _payment_id;
Monero::PendingTransaction *transaction;
if (payment_id != nullptr)
{
_payment_id = std::string(payment_id);
}
transaction = m_wallet->createTransactionMultDest(_addresses, _payment_id, _amounts, m_wallet->defaultMixin(), priority, subaddr_account);
int status = transaction->status();
if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical)
{
error = Utf8Box(strdup(transaction->errorString().c_str()));
return false;
}
if (m_listener != nullptr) {
m_listener->m_new_transaction = true;
}
pendingTransaction = PendingTransactionRaw(transaction);
return true;
}
bool transaction_commit(PendingTransactionRaw *transaction, Utf8Box &error)
{
bool committed = transaction->transaction->commit();
if (!committed)
{
error = Utf8Box(strdup(transaction->transaction->errorString().c_str()));
} else if (m_listener != nullptr) {
m_listener->m_new_transaction = true;
}
return committed;
}
uint64_t get_node_height_or_update(uint64_t base_eight)
{
if (m_cached_syncing_blockchain_height < base_eight) {
m_cached_syncing_blockchain_height = base_eight;
}
return m_cached_syncing_blockchain_height;
}
uint64_t get_syncing_height()
{
if (m_listener == nullptr) {
return 0;
}
uint64_t height = m_listener->height();
if (height <= 1) {
return 0;
}
if (height != m_last_known_wallet_height)
{
m_last_known_wallet_height = height;
}
return height;
}
uint64_t is_needed_to_refresh()
{
if (m_listener == nullptr) {
return false;
}
bool should_refresh = m_listener->isNeedToRefresh();
if (should_refresh) {
m_listener->resetNeedToRefresh();
}
return should_refresh;
}
uint8_t is_new_transaction_exist()
{
if (m_listener == nullptr) {
return false;
}
bool is_new_transaction_exist = m_listener->isNewTransactionExist();
if (is_new_transaction_exist)
{
m_listener->resetIsNewTransactionExist();
}
return is_new_transaction_exist;
}
void set_listener()
{
m_last_known_wallet_height = 0;
if (m_listener != nullptr)
{
free(m_listener);
}
m_listener = new MoneroWalletListener();
get_current_wallet()->setListener(m_listener);
}
int64_t *subaddrress_get_all()
{
std::vector<Monero::SubaddressRow *> _subaddresses = m_subaddress->getAll();
size_t size = _subaddresses.size();
int64_t *subaddresses = (int64_t *)malloc(size * sizeof(int64_t));
for (int i = 0; i < size; i++)
{
Monero::SubaddressRow *row = _subaddresses[i];
SubaddressRow *_row = new SubaddressRow(row->getRowId(), strdup(row->getAddress().c_str()), strdup(row->getLabel().c_str()));
subaddresses[i] = reinterpret_cast<int64_t>(_row);
}
return subaddresses;
}
int32_t subaddrress_size()
{
std::vector<Monero::SubaddressRow *> _subaddresses = m_subaddress->getAll();
return _subaddresses.size();
}
void subaddress_add_row(uint32_t accountIndex, char *label)
{
m_subaddress->addRow(accountIndex, std::string(label));
}
void subaddress_set_label(uint32_t accountIndex, uint32_t addressIndex, char *label)
{
m_subaddress->setLabel(accountIndex, addressIndex, std::string(label));
}
void subaddress_refresh(uint32_t accountIndex)
{
m_subaddress->refresh(accountIndex);
}
int32_t account_size()
{
std::vector<Monero::SubaddressAccountRow *> _accocunts = m_account->getAll();
return _accocunts.size();
}
int64_t *account_get_all()
{
std::vector<Monero::SubaddressAccountRow *> _accocunts = m_account->getAll();
size_t size = _accocunts.size();
int64_t *accocunts = (int64_t *)malloc(size * sizeof(int64_t));
for (int i = 0; i < size; i++)
{
Monero::SubaddressAccountRow *row = _accocunts[i];
AccountRow *_row = new AccountRow(row->getRowId(), strdup(row->getLabel().c_str()));
accocunts[i] = reinterpret_cast<int64_t>(_row);
}
return accocunts;
}
void account_add_row(char *label)
{
m_account->addRow(std::string(label));
}
void account_set_label_row(uint32_t account_index, char *label)
{
m_account->setLabel(account_index, label);
}
void account_refresh()
{
m_account->refresh();
}
int64_t *transactions_get_all()
{
std::vector<Monero::TransactionInfo *> transactions = m_transaction_history->getAll();
size_t size = transactions.size();
int64_t *transactionAddresses = (int64_t *)malloc(size * sizeof(int64_t));
for (int i = 0; i < size; i++)
{
Monero::TransactionInfo *row = transactions[i];
TransactionInfoRow *tx = new TransactionInfoRow(row);
transactionAddresses[i] = reinterpret_cast<int64_t>(tx);
}
return transactionAddresses;
}
void transactions_refresh()
{
m_transaction_history->refresh();
}
int64_t transactions_count()
{
return m_transaction_history->count();
}
int LedgerExchange(
unsigned char *command,
unsigned int cmd_len,
unsigned char *response,
unsigned int max_resp_len)
{
return -1;
}
int LedgerFind(char *buffer, size_t len)
{
return -1;
}
void on_startup()
{
Monero::Utils::onStartup();
Monero::WalletManagerFactory::setLogLevel(4);
}
void rescan_blockchain()
{
m_wallet->rescanBlockchainAsync();
}
char * get_tx_key(char * txId)
{
return strdup(m_wallet->getTxKey(std::string(txId)).c_str());
}
char *get_subaddress_label(uint32_t accountIndex, uint32_t addressIndex)
{
return strdup(get_current_wallet()->getSubaddressLabel(accountIndex, addressIndex).c_str());
}
#ifdef __cplusplus
}
#endif

@ -0,0 +1,36 @@
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include "CwWalletListener.h"
#ifdef __cplusplus
extern "C" {
#endif
bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error);
bool restore_wallet_from_seed(char *path, char *password, char *seed, int32_t networkType, uint64_t restoreHeight, char *error);
bool restore_wallet_from_keys(char *path, char *password, char *language, char *address, char *viewKey, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error);
void load_wallet(char *path, char *password, int32_t nettype);
bool is_wallet_exist(char *path);
char *get_filename();
const char *seed();
char *get_address(uint32_t account_index, uint32_t address_index);
uint64_t get_full_balance(uint32_t account_index);
uint64_t get_unlocked_balance(uint32_t account_index);
uint64_t get_current_height();
uint64_t get_node_height();
uint64_t get_seed_height(char *seed);
bool is_connected();
bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *error);
bool connect_to_node(char *error);
void start_refresh();
void set_refresh_from_block_height(uint64_t height);
void set_recovering_from_seed(bool is_recovery);
void store(char *path);
#ifdef __cplusplus
}
#endif

@ -0,0 +1,50 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint cw_wownero.podspec' to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'cw_wownero'
s.version = '0.0.2'
s.summary = 'Cake Wallet Wownero'
s.description = 'Cake Wallet wrapper over Wownero project'
s.homepage = 'http://cakewallet.com'
s.license = { :file => '../LICENSE' }
s.author = { 'CakeWallet' => 'support@cakewallet.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h, Classes/*.h, ../shared_external/ios/libs/monero/include/src/**/*.h, ../shared_external/ios/libs/monero/include/contrib/**/*.h, ../shared_external/ios/libs/monero/include/../shared_external/ios/**/*.h'
s.dependency 'Flutter'
s.dependency 'cw_shared_external'
s.platform = :ios, '10.0'
s.swift_version = '5.0'
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS' => 'arm64', 'ENABLE_BITCODE' => 'NO' }
s.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/Classes/*.h" }
s.subspec 'OpenSSL' do |openssl|
openssl.preserve_paths = '../../../../../cw_shared_external/ios/External/ios/include/**/*.h'
openssl.vendored_libraries = '../../../../../cw_shared_external/ios/External/ios/lib/libcrypto.a', '../../../../../cw_shared_external/ios/External/ios/lib/libssl.a'
openssl.libraries = 'ssl', 'crypto'
openssl.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/ios/include/**" }
end
s.subspec 'Sodium' do |sodium|
sodium.preserve_paths = '../../../../../cw_shared_external/ios/External/ios/include/**/*.h'
sodium.vendored_libraries = '../../../../../cw_shared_external/ios/External/ios/lib/libsodium.a'
sodium.libraries = 'sodium'
sodium.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/ios/include/**" }
end
s.subspec 'Boost' do |boost|
boost.preserve_paths = '../../../../../cw_shared_external/ios/External/ios/include/**/*.h',
boost.vendored_libraries = '../../../../../cw_shared_external/ios/External/ios/lib/libboost.a',
boost.libraries = 'boost'
boost.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/ios/include/**" }
end
s.subspec 'Wownero' do |wownero|
wownero.preserve_paths = 'External/ios/include/**/*.h'
wownero.vendored_libraries = 'External/ios/lib/libwownero.a'
wownero.libraries = 'wownero'
wownero.xcconfig = { 'HEADER_SEARCH_PATHS' => "${PODS_ROOT}/#{s.name}/External/ios/include" }
end
end

@ -0,0 +1,83 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:cw_wownero/api/signatures.dart';
import 'package:cw_wownero/api/types.dart';
import 'package:cw_wownero/api/wownero_api.dart';
import 'package:cw_wownero/api/structs/account_row.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_wownero/api/wallet.dart';
final accountSizeNative = wowneroApi
.lookup<NativeFunction<account_size>>('account_size')
.asFunction<SubaddressSize>();
final accountRefreshNative = wowneroApi
.lookup<NativeFunction<account_refresh>>('account_refresh')
.asFunction<AccountRefresh>();
final accountGetAllNative = wowneroApi
.lookup<NativeFunction<account_get_all>>('account_get_all')
.asFunction<AccountGetAll>();
final accountAddNewNative = wowneroApi
.lookup<NativeFunction<account_add_new>>('account_add_row')
.asFunction<AccountAddNew>();
final accountSetLabelNative = wowneroApi
.lookup<NativeFunction<account_set_label>>('account_set_label_row')
.asFunction<AccountSetLabel>();
bool isUpdating = false;
void refreshAccounts() {
try {
isUpdating = true;
accountRefreshNative();
isUpdating = false;
} catch (e) {
isUpdating = false;
rethrow;
}
}
List<AccountRow> getAllAccount() {
final size = accountSizeNative();
final accountAddressesPointer = accountGetAllNative();
final accountAddresses = accountAddressesPointer.asTypedList(size);
return accountAddresses
.map((addr) => Pointer<AccountRow>.fromAddress(addr).ref)
.toList();
}
void addAccountSync({String label}) {
final labelPointer = Utf8.toUtf8(label);
accountAddNewNative(labelPointer);
free(labelPointer);
}
void setLabelForAccountSync({int accountIndex, String label}) {
final labelPointer = Utf8.toUtf8(label);
accountSetLabelNative(accountIndex, labelPointer);
free(labelPointer);
}
void _addAccount(String label) => addAccountSync(label: label);
void _setLabelForAccount(Map<String, dynamic> args) {
final label = args['label'] as String;
final accountIndex = args['accountIndex'] as int;
setLabelForAccountSync(label: label, accountIndex: accountIndex);
}
Future<void> addAccount({String label}) async {
await compute(_addAccount, label);
await store();
}
Future<void> setLabelForAccount({int accountIndex, String label}) async {
await compute(
_setLabelForAccount, {'accountIndex': accountIndex, 'label': label});
await store();
}

@ -0,0 +1,8 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
String convertUTF8ToString({Pointer<Utf8> pointer}) {
final str = Utf8.fromUtf8(pointer);
free(pointer);
return str;
}

@ -0,0 +1,5 @@
class ConnectionToNodeException implements Exception {
ConnectionToNodeException({this.message});
final String message;
}

@ -0,0 +1,8 @@
class CreationTransactionException implements Exception {
CreationTransactionException({this.message});
final String message;
@override
String toString() => message;
}

@ -0,0 +1,5 @@
class SetupWalletException implements Exception {
SetupWalletException({this.message});
final String message;
}

@ -0,0 +1,8 @@
class WalletCreationException implements Exception {
WalletCreationException({this.message});
final String message;
@override
String toString() => message;
}

@ -0,0 +1,8 @@
class WalletOpeningException implements Exception {
WalletOpeningException({this.message});
final String message;
@override
String toString() => message;
}

@ -0,0 +1,5 @@
class WalletRestoreFromKeysException implements Exception {
WalletRestoreFromKeysException({this.message});
final String message;
}

@ -0,0 +1,5 @@
class WalletRestoreFromSeedException implements Exception {
WalletRestoreFromSeedException({this.message});
final String message;
}

@ -0,0 +1,128 @@
import 'dart:ffi';
import 'package:cw_wownero/api/structs/pending_transaction.dart';
import 'package:cw_wownero/api/structs/ut8_box.dart';
import 'package:ffi/ffi.dart';
typedef create_wallet = Int8 Function(
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Pointer<Utf8>);
typedef restore_wallet_from_seed = Int8 Function(
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Pointer<Utf8>);
typedef restore_wallet_from_keys = Int8 Function(Pointer<Utf8>, Pointer<Utf8>,
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
typedef is_wallet_exist = Int8 Function(Pointer<Utf8>);
typedef load_wallet = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Int8);
typedef error_string = Pointer<Utf8> Function();
typedef get_filename = Pointer<Utf8> Function();
typedef get_seed = Pointer<Utf8> Function();
typedef get_address = Pointer<Utf8> Function(Int32, Int32);
typedef get_full_balanace = Int64 Function(Int32);
typedef get_unlocked_balanace = Int64 Function(Int32);
typedef get_current_height = Int64 Function();
typedef get_node_height = Int64 Function();
typedef get_seed_height = Int64 Function(Pointer<Utf8>);
typedef is_connected = Int8 Function();
typedef setup_node = Int8 Function(
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int8, Int8, Pointer<Utf8>);
typedef start_refresh = Void Function();
typedef connect_to_node = Int8 Function();
typedef set_refresh_from_block_height = Void Function(Int64);
typedef set_recovering_from_seed = Void Function(Int8);
typedef store_c = Void Function(Pointer<Utf8>);
typedef set_listener = Void Function();
typedef get_syncing_height = Int64 Function();
typedef is_needed_to_refresh = Int8 Function();
typedef is_new_transaction_exist = Int8 Function();
typedef subaddrress_size = Int32 Function();
typedef subaddrress_refresh = Void Function(Int32);
typedef subaddress_get_all = Pointer<Int64> Function();
typedef subaddress_add_new = Void Function(
Int32 accountIndex, Pointer<Utf8> label);
typedef subaddress_set_label = Void Function(
Int32 accountIndex, Int32 addressIndex, Pointer<Utf8> label);
typedef account_size = Int32 Function();
typedef account_refresh = Void Function();
typedef account_get_all = Pointer<Int64> Function();
typedef account_add_new = Void Function(Pointer<Utf8> label);
typedef account_set_label = Void Function(
Int32 accountIndex, Pointer<Utf8> label);
typedef transactions_refresh = Void Function();
typedef get_tx_key = Pointer<Utf8> Function(Pointer<Utf8> txId);
typedef transactions_count = Int64 Function();
typedef transactions_get_all = Pointer<Int64> Function();
typedef transaction_create = Int8 Function(
Pointer<Utf8> address,
Pointer<Utf8> paymentId,
Pointer<Utf8> amount,
Int8 priorityRaw,
Int32 subaddrAccount,
Pointer<Utf8Box> error,
Pointer<PendingTransactionRaw> pendingTransaction);
typedef transaction_create_mult_dest = Int8 Function(
Pointer<Pointer<Utf8>> addresses,
Pointer<Utf8> paymentId,
Pointer<Pointer<Utf8>> amounts,
Int32 size,
Int8 priorityRaw,
Int32 subaddrAccount,
Pointer<Utf8Box> error,
Pointer<PendingTransactionRaw> pendingTransaction);
typedef transaction_commit = Int8 Function(Pointer<PendingTransactionRaw>, Pointer<Utf8Box>);
typedef secret_view_key = Pointer<Utf8> Function();
typedef public_view_key = Pointer<Utf8> Function();
typedef secret_spend_key = Pointer<Utf8> Function();
typedef public_spend_key = Pointer<Utf8> Function();
typedef close_current_wallet = Void Function();
typedef on_startup = Void Function();
typedef rescan_blockchain = Void Function();
typedef get_subaddress_label = Pointer<Utf8> Function(
Int32 accountIndex,
Int32 addressIndex);

@ -0,0 +1,11 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class AccountRow extends Struct {
@Int64()
int id;
Pointer<Utf8> label;
String getLabel() => Utf8.fromUtf8(label);
int getId() => id;
}

@ -0,0 +1,23 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class PendingTransactionRaw extends Struct {
@Int64()
int amount;
@Int64()
int fee;
Pointer<Utf8> hash;
String getHash() => Utf8.fromUtf8(hash);
}
class PendingTransactionDescription {
PendingTransactionDescription({this.amount, this.fee, this.hash, this.pointerAddress});
final int amount;
final int fee;
final String hash;
final int pointerAddress;
}

@ -0,0 +1,13 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class SubaddressRow extends Struct {
@Int64()
int id;
Pointer<Utf8> address;
Pointer<Utf8> label;
String getLabel() => Utf8.fromUtf8(label);
String getAddress() => Utf8.fromUtf8(address);
int getId() => id;
}

@ -0,0 +1,41 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class TransactionInfoRow extends Struct {
@Uint64()
int amount;
@Uint64()
int fee;
@Uint64()
int blockHeight;
@Uint64()
int confirmations;
@Uint32()
int subaddrAccount;
@Int8()
int direction;
@Int8()
int isPending;
@Uint32()
int subaddrIndex;
Pointer<Utf8> hash;
Pointer<Utf8> paymentId;
@Int64()
int datetime;
int getDatetime() => datetime;
int getAmount() => amount >= 0 ? amount : amount * -1;
bool getIsPending() => isPending != 0;
String getHash() => Utf8.fromUtf8(hash);
String getPaymentId() => Utf8.fromUtf8(paymentId);
}

@ -0,0 +1,8 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class Utf8Box extends Struct {
Pointer<Utf8> value;
String getValue() => Utf8.fromUtf8(value);
}

@ -0,0 +1,97 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_wownero/api/signatures.dart';
import 'package:cw_wownero/api/types.dart';
import 'package:cw_wownero/api/wownero_api.dart';
import 'package:cw_wownero/api/structs/subaddress_row.dart';
import 'package:cw_wownero/api/wallet.dart';
final subaddressSizeNative = wowneroApi
.lookup<NativeFunction<subaddrress_size>>('subaddrress_size')
.asFunction<SubaddressSize>();
final subaddressRefreshNative = wowneroApi
.lookup<NativeFunction<subaddrress_refresh>>('subaddress_refresh')
.asFunction<SubaddressRefresh>();
final subaddrressGetAllNative = wowneroApi
.lookup<NativeFunction<subaddress_get_all>>('subaddrress_get_all')
.asFunction<SubaddressGetAll>();
final subaddrressAddNewNative = wowneroApi
.lookup<NativeFunction<subaddress_add_new>>('subaddress_add_row')
.asFunction<SubaddressAddNew>();
final subaddrressSetLabelNative = wowneroApi
.lookup<NativeFunction<subaddress_set_label>>('subaddress_set_label')
.asFunction<SubaddressSetLabel>();
bool isUpdating = false;
void refreshSubaddresses({@required int accountIndex}) {
try {
isUpdating = true;
subaddressRefreshNative(accountIndex);
isUpdating = false;
} catch (e) {
isUpdating = false;
rethrow;
}
}
List<SubaddressRow> getAllSubaddresses() {
final size = subaddressSizeNative();
final subaddressAddressesPointer = subaddrressGetAllNative();
final subaddressAddresses = subaddressAddressesPointer.asTypedList(size);
return subaddressAddresses
.map((addr) => Pointer<SubaddressRow>.fromAddress(addr).ref)
.toList();
}
void addSubaddressSync({int accountIndex, String label}) {
final labelPointer = Utf8.toUtf8(label);
subaddrressAddNewNative(accountIndex, labelPointer);
free(labelPointer);
}
void setLabelForSubaddressSync(
{int accountIndex, int addressIndex, String label}) {
final labelPointer = Utf8.toUtf8(label);
subaddrressSetLabelNative(accountIndex, addressIndex, labelPointer);
free(labelPointer);
}
void _addSubaddress(Map<String, dynamic> args) {
final label = args['label'] as String;
final accountIndex = args['accountIndex'] as int;
addSubaddressSync(accountIndex: accountIndex, label: label);
}
void _setLabelForSubaddress(Map<String, dynamic> args) {
final label = args['label'] as String;
final accountIndex = args['accountIndex'] as int;
final addressIndex = args['addressIndex'] as int;
setLabelForSubaddressSync(
accountIndex: accountIndex, addressIndex: addressIndex, label: label);
}
Future addSubaddress({int accountIndex, String label}) async {
await compute<Map<String, Object>, void>(
_addSubaddress, {'accountIndex': accountIndex, 'label': label});
await store();
}
Future setLabelForSubaddress(
{int accountIndex, int addressIndex, String label}) async {
await compute<Map<String, Object>, void>(_setLabelForSubaddress, {
'accountIndex': accountIndex,
'addressIndex': addressIndex,
'label': label
});
await store();
}

@ -0,0 +1,230 @@
import 'dart:ffi';
import 'package:cw_wownero/api/convert_utf8_to_string.dart';
import 'package:cw_wownero/api/wownero_output.dart';
import 'package:cw_wownero/api/structs/ut8_box.dart';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_wownero/api/signatures.dart';
import 'package:cw_wownero/api/types.dart';
import 'package:cw_wownero/api/wownero_api.dart';
import 'package:cw_wownero/api/structs/transaction_info_row.dart';
import 'package:cw_wownero/api/structs/pending_transaction.dart';
import 'package:cw_wownero/api/exceptions/creation_transaction_exception.dart';
final transactionsRefreshNative = wowneroApi
.lookup<NativeFunction<transactions_refresh>>('transactions_refresh')
.asFunction<TransactionsRefresh>();
final transactionsCountNative = wowneroApi
.lookup<NativeFunction<transactions_count>>('transactions_count')
.asFunction<TransactionsCount>();
final transactionsGetAllNative = wowneroApi
.lookup<NativeFunction<transactions_get_all>>('transactions_get_all')
.asFunction<TransactionsGetAll>();
final transactionCreateNative = wowneroApi
.lookup<NativeFunction<transaction_create>>('transaction_create')
.asFunction<TransactionCreate>();
final transactionCreateMultDestNative = wowneroApi
.lookup<NativeFunction<transaction_create_mult_dest>>('transaction_create_mult_dest')
.asFunction<TransactionCreateMultDest>();
final transactionCommitNative = wowneroApi
.lookup<NativeFunction<transaction_commit>>('transaction_commit')
.asFunction<TransactionCommit>();
final getTxKeyNative = wowneroApi
.lookup<NativeFunction<get_tx_key>>('get_tx_key')
.asFunction<GetTxKey>();
String getTxKey(String txId) {
final txIdPointer = Utf8.toUtf8(txId);
final keyPointer = getTxKeyNative(txIdPointer);
free(txIdPointer);
if (keyPointer != null) {
return convertUTF8ToString(pointer: keyPointer);
}
return null;
}
void refreshTransactions() => transactionsRefreshNative();
int countOfTransactions() => transactionsCountNative();
List<TransactionInfoRow> getAllTransations() {
final size = transactionsCountNative();
final transactionsPointer = transactionsGetAllNative();
final transactionsAddresses = transactionsPointer.asTypedList(size);
return transactionsAddresses
.map((addr) => Pointer<TransactionInfoRow>.fromAddress(addr).ref)
.toList();
}
PendingTransactionDescription createTransactionSync(
{String address,
String paymentId,
String amount,
int priorityRaw,
int accountIndex = 0}) {
final addressPointer = Utf8.toUtf8(address);
final paymentIdPointer = Utf8.toUtf8(paymentId);
final amountPointer = amount != null ? Utf8.toUtf8(amount) : nullptr;
final errorMessagePointer = allocate<Utf8Box>();
final pendingTransactionRawPointer = allocate<PendingTransactionRaw>();
final created = transactionCreateNative(
addressPointer,
paymentIdPointer,
amountPointer,
priorityRaw,
accountIndex,
errorMessagePointer,
pendingTransactionRawPointer) !=
0;
free(addressPointer);
free(paymentIdPointer);
if (amountPointer != nullptr) {
free(amountPointer);
}
if (!created) {
final message = errorMessagePointer.ref.getValue();
free(errorMessagePointer);
throw CreationTransactionException(message: message);
}
return PendingTransactionDescription(
amount: pendingTransactionRawPointer.ref.amount,
fee: pendingTransactionRawPointer.ref.fee,
hash: pendingTransactionRawPointer.ref.getHash(),
pointerAddress: pendingTransactionRawPointer.address);
}
PendingTransactionDescription createTransactionMultDestSync(
{List<MoneroOutput> outputs,
String paymentId,
int priorityRaw,
int accountIndex = 0}) {
final int size = outputs.length;
final List<Pointer<Utf8>> addressesPointers = outputs.map((output) =>
Utf8.toUtf8(output.address)).toList();
final Pointer<Pointer<Utf8>> addressesPointerPointer = allocate(count: size);
final List<Pointer<Utf8>> amountsPointers = outputs.map((output) =>
Utf8.toUtf8(output.amount)).toList();
final Pointer<Pointer<Utf8>> amountsPointerPointer = allocate(count: size);
for (int i = 0; i < size; i++) {
addressesPointerPointer[i] = addressesPointers[i];
amountsPointerPointer[i] = amountsPointers[i];
}
final paymentIdPointer = Utf8.toUtf8(paymentId);
final errorMessagePointer = allocate<Utf8Box>();
final pendingTransactionRawPointer = allocate<PendingTransactionRaw>();
final created = transactionCreateMultDestNative(
addressesPointerPointer,
paymentIdPointer,
amountsPointerPointer,
size,
priorityRaw,
accountIndex,
errorMessagePointer,
pendingTransactionRawPointer) !=
0;
free(addressesPointerPointer);
free(amountsPointerPointer);
addressesPointers.forEach((element) => free(element));
amountsPointers.forEach((element) => free(element));
free(paymentIdPointer);
if (!created) {
final message = errorMessagePointer.ref.getValue();
free(errorMessagePointer);
throw CreationTransactionException(message: message);
}
return PendingTransactionDescription(
amount: pendingTransactionRawPointer.ref.amount,
fee: pendingTransactionRawPointer.ref.fee,
hash: pendingTransactionRawPointer.ref.getHash(),
pointerAddress: pendingTransactionRawPointer.address);
}
void commitTransactionFromPointerAddress({int address}) => commitTransaction(
transactionPointer: Pointer<PendingTransactionRaw>.fromAddress(address));
void commitTransaction({Pointer<PendingTransactionRaw> transactionPointer}) {
final errorMessagePointer = allocate<Utf8Box>();
final isCommited =
transactionCommitNative(transactionPointer, errorMessagePointer) != 0;
if (!isCommited) {
final message = errorMessagePointer.ref.getValue();
free(errorMessagePointer);
throw CreationTransactionException(message: message);
}
}
PendingTransactionDescription _createTransactionSync(Map args) {
final address = args['address'] as String;
final paymentId = args['paymentId'] as String;
final amount = args['amount'] as String;
final priorityRaw = args['priorityRaw'] as int;
final accountIndex = args['accountIndex'] as int;
return createTransactionSync(
address: address,
paymentId: paymentId,
amount: amount,
priorityRaw: priorityRaw,
accountIndex: accountIndex);
}
PendingTransactionDescription _createTransactionMultDestSync(Map args) {
final outputs = args['outputs'] as List<MoneroOutput>;
final paymentId = args['paymentId'] as String;
final priorityRaw = args['priorityRaw'] as int;
final accountIndex = args['accountIndex'] as int;
return createTransactionMultDestSync(
outputs: outputs,
paymentId: paymentId,
priorityRaw: priorityRaw,
accountIndex: accountIndex);
}
Future<PendingTransactionDescription> createTransaction(
{String address,
String paymentId = '',
String amount,
int priorityRaw,
int accountIndex = 0}) =>
compute(_createTransactionSync, {
'address': address,
'paymentId': paymentId,
'amount': amount,
'priorityRaw': priorityRaw,
'accountIndex': accountIndex
});
Future<PendingTransactionDescription> createTransactionMultDest(
{List<MoneroOutput> outputs,
String paymentId = '',
int priorityRaw,
int accountIndex = 0}) =>
compute(_createTransactionMultDestSync, {
'outputs': outputs,
'paymentId': paymentId,
'priorityRaw': priorityRaw,
'accountIndex': accountIndex
});

@ -0,0 +1,126 @@
import 'dart:ffi';
import 'package:cw_wownero/api/structs/pending_transaction.dart';
import 'package:cw_wownero/api/structs/ut8_box.dart';
import 'package:ffi/ffi.dart';
typedef CreateWallet = int Function(
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, int, Pointer<Utf8>);
typedef RestoreWalletFromSeed = int Function(
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, int, Pointer<Utf8>);
typedef RestoreWalletFromKeys = int Function(Pointer<Utf8>, Pointer<Utf8>,
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, int, int, Pointer<Utf8>);
typedef IsWalletExist = int Function(Pointer<Utf8>);
typedef LoadWallet = int Function(Pointer<Utf8>, Pointer<Utf8>, int);
typedef ErrorString = Pointer<Utf8> Function();
typedef GetFilename = Pointer<Utf8> Function();
typedef GetSeed = Pointer<Utf8> Function();
typedef GetAddress = Pointer<Utf8> Function(int, int);
typedef GetFullBalance = int Function(int);
typedef GetUnlockedBalance = int Function(int);
typedef GetCurrentHeight = int Function();
typedef GetNodeHeight = int Function();
typedef GetSeedHeight = int Function(Pointer<Utf8>);
typedef IsConnected = int Function();
typedef SetupNode = int Function(
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, int, int, Pointer<Utf8>);
typedef StartRefresh = void Function();
typedef ConnectToNode = int Function();
typedef SetRefreshFromBlockHeight = void Function(int);
typedef SetRecoveringFromSeed = void Function(int);
typedef Store = void Function(Pointer<Utf8>);
typedef SetListener = void Function();
typedef GetSyncingHeight = int Function();
typedef IsNeededToRefresh = int Function();
typedef IsNewTransactionExist = int Function();
typedef SubaddressSize = int Function();
typedef SubaddressRefresh = void Function(int);
typedef SubaddressGetAll = Pointer<Int64> Function();
typedef SubaddressAddNew = void Function(int accountIndex, Pointer<Utf8> label);
typedef SubaddressSetLabel = void Function(
int accountIndex, int addressIndex, Pointer<Utf8> label);
typedef AccountSize = int Function();
typedef AccountRefresh = void Function();
typedef AccountGetAll = Pointer<Int64> Function();
typedef AccountAddNew = void Function(Pointer<Utf8> label);
typedef AccountSetLabel = void Function(int accountIndex, Pointer<Utf8> label);
typedef TransactionsRefresh = void Function();
typedef GetTxKey = Pointer<Utf8> Function(Pointer<Utf8> txId);
typedef TransactionsCount = int Function();
typedef TransactionsGetAll = Pointer<Int64> Function();
typedef TransactionCreate = int Function(
Pointer<Utf8> address,
Pointer<Utf8> paymentId,
Pointer<Utf8> amount,
int priorityRaw,
int subaddrAccount,
Pointer<Utf8Box> error,
Pointer<PendingTransactionRaw> pendingTransaction);
typedef TransactionCreateMultDest = int Function(
Pointer<Pointer<Utf8>> addresses,
Pointer<Utf8> paymentId,
Pointer<Pointer<Utf8>> amounts,
int size,
int priorityRaw,
int subaddrAccount,
Pointer<Utf8Box> error,
Pointer<PendingTransactionRaw> pendingTransaction);
typedef TransactionCommit = int Function(Pointer<PendingTransactionRaw>, Pointer<Utf8Box>);
typedef SecretViewKey = Pointer<Utf8> Function();
typedef PublicViewKey = Pointer<Utf8> Function();
typedef SecretSpendKey = Pointer<Utf8> Function();
typedef PublicSpendKey = Pointer<Utf8> Function();
typedef CloseCurrentWallet = void Function();
typedef OnStartup = void Function();
typedef RescanBlockchainAsync = void Function();
typedef GetSubaddressLabel = Pointer<Utf8> Function(
int accountIndex,
int addressIndex);

@ -0,0 +1,348 @@
import 'dart:async';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:cw_wownero/api/convert_utf8_to_string.dart';
import 'package:cw_wownero/api/signatures.dart';
import 'package:cw_wownero/api/types.dart';
import 'package:cw_wownero/api/wownero_api.dart';
import 'package:cw_wownero/api/exceptions/setup_wallet_exception.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
int _boolToInt(bool value) => value ? 1 : 0;
final getFileNameNative = wowneroApi
.lookup<NativeFunction<get_filename>>('get_filename')
.asFunction<GetFilename>();
final getSeedNative =
wowneroApi.lookup<NativeFunction<get_seed>>('seed').asFunction<GetSeed>();
final getAddressNative = wowneroApi
.lookup<NativeFunction<get_address>>('get_address')
.asFunction<GetAddress>();
final getFullBalanceNative = wowneroApi
.lookup<NativeFunction<get_full_balanace>>('get_full_balance')
.asFunction<GetFullBalance>();
final getUnlockedBalanceNative = wowneroApi
.lookup<NativeFunction<get_unlocked_balanace>>('get_unlocked_balance')
.asFunction<GetUnlockedBalance>();
final getCurrentHeightNative = wowneroApi
.lookup<NativeFunction<get_current_height>>('get_current_height')
.asFunction<GetCurrentHeight>();
final getNodeHeightNative = wowneroApi
.lookup<NativeFunction<get_node_height>>('get_node_height')
.asFunction<GetNodeHeight>();
final getSeedHeightNative = wowneroApi
.lookup<NativeFunction<get_seed_height>>('get_seed_height')
.asFunction<GetSeedHeight>();
final isConnectedNative = wowneroApi
.lookup<NativeFunction<is_connected>>('is_connected')
.asFunction<IsConnected>();
final setupNodeNative = wowneroApi
.lookup<NativeFunction<setup_node>>('setup_node')
.asFunction<SetupNode>();
final startRefreshNative = wowneroApi
.lookup<NativeFunction<start_refresh>>('start_refresh')
.asFunction<StartRefresh>();
final connecToNodeNative = wowneroApi
.lookup<NativeFunction<connect_to_node>>('connect_to_node')
.asFunction<ConnectToNode>();
final setRefreshFromBlockHeightNative = wowneroApi
.lookup<NativeFunction<set_refresh_from_block_height>>(
'set_refresh_from_block_height')
.asFunction<SetRefreshFromBlockHeight>();
final setRecoveringFromSeedNative = wowneroApi
.lookup<NativeFunction<set_recovering_from_seed>>(
'set_recovering_from_seed')
.asFunction<SetRecoveringFromSeed>();
final storeNative =
wowneroApi.lookup<NativeFunction<store_c>>('store').asFunction<Store>();
final setListenerNative = wowneroApi
.lookup<NativeFunction<set_listener>>('set_listener')
.asFunction<SetListener>();
final getSyncingHeightNative = wowneroApi
.lookup<NativeFunction<get_syncing_height>>('get_syncing_height')
.asFunction<GetSyncingHeight>();
final isNeededToRefreshNative = wowneroApi
.lookup<NativeFunction<is_needed_to_refresh>>('is_needed_to_refresh')
.asFunction<IsNeededToRefresh>();
final isNewTransactionExistNative = wowneroApi
.lookup<NativeFunction<is_new_transaction_exist>>(
'is_new_transaction_exist')
.asFunction<IsNewTransactionExist>();
final getSecretViewKeyNative = wowneroApi
.lookup<NativeFunction<secret_view_key>>('secret_view_key')
.asFunction<SecretViewKey>();
final getPublicViewKeyNative = wowneroApi
.lookup<NativeFunction<public_view_key>>('public_view_key')
.asFunction<PublicViewKey>();
final getSecretSpendKeyNative = wowneroApi
.lookup<NativeFunction<secret_spend_key>>('secret_spend_key')
.asFunction<SecretSpendKey>();
final getPublicSpendKeyNative = wowneroApi
.lookup<NativeFunction<secret_view_key>>('public_spend_key')
.asFunction<PublicSpendKey>();
final closeCurrentWalletNative = wowneroApi
.lookup<NativeFunction<close_current_wallet>>('close_current_wallet')
.asFunction<CloseCurrentWallet>();
final onStartupNative = wowneroApi
.lookup<NativeFunction<on_startup>>('on_startup')
.asFunction<OnStartup>();
final rescanBlockchainAsyncNative = wowneroApi
.lookup<NativeFunction<rescan_blockchain>>('rescan_blockchain')
.asFunction<RescanBlockchainAsync>();
final getSubaddressLabelNative = wowneroApi
.lookup<NativeFunction<get_subaddress_label>>('get_subaddress_label')
.asFunction<GetSubaddressLabel>();
int getSyncingHeight() => getSyncingHeightNative();
bool isNeededToRefresh() => isNeededToRefreshNative() != 0;
bool isNewTransactionExist() => isNewTransactionExistNative() != 0;
String getFilename() => convertUTF8ToString(pointer: getFileNameNative());
String getSeed() => convertUTF8ToString(pointer: getSeedNative());
String getAddress({int accountIndex = 0, int addressIndex = 0}) =>
convertUTF8ToString(pointer: getAddressNative(accountIndex, addressIndex));
int getFullBalance({int accountIndex = 0}) =>
getFullBalanceNative(accountIndex);
int getUnlockedBalance({int accountIndex = 0}) =>
getUnlockedBalanceNative(accountIndex);
int getCurrentHeight() => getCurrentHeightNative();
int getNodeHeightSync() => getNodeHeightNative();
int getSeedHeightSync(String seed) {
final seedPointer = Utf8.toUtf8(seed);
final ret = getSeedHeightNative(seedPointer);
free(seedPointer);
return ret;
}
bool isConnectedSync() => isConnectedNative() != 0;
bool setupNodeSync(
{String address,
String login,
String password,
bool useSSL = false,
bool isLightWallet = false}) {
final addressPointer = Utf8.toUtf8(address);
Pointer<Utf8> loginPointer;
Pointer<Utf8> passwordPointer;
if (login != null) {
loginPointer = Utf8.toUtf8(login);
}
if (password != null) {
passwordPointer = Utf8.toUtf8(password);
}
final errorMessagePointer = allocate<Utf8>();
final isSetupNode = setupNodeNative(
addressPointer,
loginPointer,
passwordPointer,
_boolToInt(useSSL),
_boolToInt(isLightWallet),
errorMessagePointer) !=
0;
free(addressPointer);
free(loginPointer);
free(passwordPointer);
if (!isSetupNode) {
throw SetupWalletException(
message: convertUTF8ToString(pointer: errorMessagePointer));
}
return isSetupNode;
}
void startRefreshSync() => startRefreshNative();
Future<bool> connectToNode() async => connecToNodeNative() != 0;
void setRefreshFromBlockHeight({int height}) =>
setRefreshFromBlockHeightNative(height);
void setRecoveringFromSeed({bool isRecovery}) =>
setRecoveringFromSeedNative(_boolToInt(isRecovery));
void storeSync() {
final pathPointer = Utf8.toUtf8('');
storeNative(pathPointer);
free(pathPointer);
}
void closeCurrentWallet() => closeCurrentWalletNative();
String getSecretViewKey() =>
convertUTF8ToString(pointer: getSecretViewKeyNative());
String getPublicViewKey() =>
convertUTF8ToString(pointer: getPublicViewKeyNative());
String getSecretSpendKey() =>
convertUTF8ToString(pointer: getSecretSpendKeyNative());
String getPublicSpendKey() =>
convertUTF8ToString(pointer: getPublicSpendKeyNative());
class SyncListener {
SyncListener(this.onNewBlock, this.onNewTransaction) {
_cachedBlockchainHeight = 0;
_lastKnownBlockHeight = 0;
_initialSyncHeight = 0;
}
void Function(int, int, double) onNewBlock;
void Function() onNewTransaction;
Timer _updateSyncInfoTimer;
int _cachedBlockchainHeight;
int _lastKnownBlockHeight;
int _initialSyncHeight;
Future<int> getNodeHeightOrUpdate(int baseHeight) async {
if (_cachedBlockchainHeight < baseHeight || _cachedBlockchainHeight == 0) {
_cachedBlockchainHeight = await getNodeHeight();
}
return _cachedBlockchainHeight;
}
void start() {
_cachedBlockchainHeight = 0;
_lastKnownBlockHeight = 0;
_initialSyncHeight = 0;
_updateSyncInfoTimer ??=
Timer.periodic(Duration(milliseconds: 1200), (_) async {
if (isNewTransactionExist()) {
onNewTransaction?.call();
}
var syncHeight = getSyncingHeight();
if (syncHeight <= 0) {
syncHeight = getCurrentHeight();
}
if (_initialSyncHeight <= 0) {
_initialSyncHeight = syncHeight;
}
final bchHeight = await getNodeHeightOrUpdate(syncHeight);
if (_lastKnownBlockHeight == syncHeight || syncHeight == null) {
return;
}
_lastKnownBlockHeight = syncHeight;
final track = bchHeight - _initialSyncHeight;
final diff = track - (bchHeight - syncHeight);
final ptc = diff <= 0 ? 0.0 : diff / track;
final left = bchHeight - syncHeight;
if (syncHeight < 0 || left < 0) {
return;
}
// 1. Actual new height; 2. Blocks left to finish; 3. Progress in percents;
onNewBlock?.call(syncHeight, left, ptc);
});
}
void stop() => _updateSyncInfoTimer?.cancel();
}
SyncListener setListeners(void Function(int, int, double) onNewBlock,
void Function() onNewTransaction) {
final listener = SyncListener(onNewBlock, onNewTransaction);
setListenerNative();
return listener;
}
void onStartup() => onStartupNative();
void _storeSync(Object _) => storeSync();
bool _setupNodeSync(Map args) {
final address = args['address'] as String;
final login = (args['login'] ?? '') as String;
final password = (args['password'] ?? '') as String;
final useSSL = args['useSSL'] as bool;
final isLightWallet = args['isLightWallet'] as bool;
return setupNodeSync(
address: address,
login: login,
password: password,
useSSL: useSSL,
isLightWallet: isLightWallet);
}
bool _isConnected(Object _) => isConnectedSync();
int _getNodeHeight(Object _) => getNodeHeightSync();
void startRefresh() => startRefreshSync();
Future setupNode(
{String address,
String login,
String password,
bool useSSL = false,
bool isLightWallet = false}) =>
compute<Map<String, Object>, void>(_setupNodeSync, {
'address': address,
'login': login,
'password': password,
'useSSL': useSSL,
'isLightWallet': isLightWallet
});
Future store() => compute<int, void>(_storeSync, 0);
Future<bool> isConnected() => compute(_isConnected, 0);
Future<int> getNodeHeight() => compute(_getNodeHeight, 0);
void rescanBlockchainAsync() => rescanBlockchainAsyncNative();
String getSubaddressLabel(int accountIndex, int addressIndex) {
return convertUTF8ToString(pointer: getSubaddressLabelNative(accountIndex, addressIndex));
}

@ -0,0 +1,247 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_wownero/api/convert_utf8_to_string.dart';
import 'package:cw_wownero/api/signatures.dart';
import 'package:cw_wownero/api/types.dart';
import 'package:cw_wownero/api/wownero_api.dart';
import 'package:cw_wownero/api/wallet.dart';
import 'package:cw_wownero/api/exceptions/wallet_opening_exception.dart';
import 'package:cw_wownero/api/exceptions/wallet_creation_exception.dart';
import 'package:cw_wownero/api/exceptions/wallet_restore_from_keys_exception.dart';
import 'package:cw_wownero/api/exceptions/wallet_restore_from_seed_exception.dart';
final createWalletNative = wowneroApi
.lookup<NativeFunction<create_wallet>>('create_wallet')
.asFunction<CreateWallet>();
final restoreWalletFromSeedNative = wowneroApi
.lookup<NativeFunction<restore_wallet_from_seed>>(
'restore_wallet_from_seed')
.asFunction<RestoreWalletFromSeed>();
final restoreWalletFromKeysNative = wowneroApi
.lookup<NativeFunction<restore_wallet_from_keys>>(
'restore_wallet_from_keys')
.asFunction<RestoreWalletFromKeys>();
final isWalletExistNative = wowneroApi
.lookup<NativeFunction<is_wallet_exist>>('is_wallet_exist')
.asFunction<IsWalletExist>();
final loadWalletNative = wowneroApi
.lookup<NativeFunction<load_wallet>>('load_wallet')
.asFunction<LoadWallet>();
final errorStringNative = wowneroApi
.lookup<NativeFunction<error_string>>('error_string')
.asFunction<ErrorString>();
void createWalletSync(
{String path, String password, String language, int nettype = 0}) {
final pathPointer = Utf8.toUtf8(path);
final passwordPointer = Utf8.toUtf8(password);
final languagePointer = Utf8.toUtf8(language);
final errorMessagePointer = allocate<Utf8>();
final isWalletCreated = createWalletNative(pathPointer, passwordPointer,
languagePointer, nettype, errorMessagePointer) !=
0;
free(pathPointer);
free(passwordPointer);
free(languagePointer);
if (!isWalletCreated) {
throw WalletCreationException(
message: convertUTF8ToString(pointer: errorMessagePointer));
}
// setupNodeSync(address: "node.wowneroworld.com:18089");
}
bool isWalletExistSync({String path}) {
final pathPointer = Utf8.toUtf8(path);
final isExist = isWalletExistNative(pathPointer) != 0;
free(pathPointer);
return isExist;
}
void restoreWalletFromSeedSync(
{String path,
String password,
String seed,
int nettype = 0,
int restoreHeight = 0}) {
final pathPointer = Utf8.toUtf8(path);
final passwordPointer = Utf8.toUtf8(password);
final seedPointer = Utf8.toUtf8(seed);
final errorMessagePointer = allocate<Utf8>();
final isWalletRestored = restoreWalletFromSeedNative(
pathPointer,
passwordPointer,
seedPointer,
nettype,
errorMessagePointer) !=
0;
free(pathPointer);
free(passwordPointer);
free(seedPointer);
if (!isWalletRestored) {
throw WalletRestoreFromSeedException(
message: convertUTF8ToString(pointer: errorMessagePointer));
}
}
void restoreWalletFromKeysSync(
{String path,
String password,
String language,
String address,
String viewKey,
String spendKey,
int nettype = 0,
int restoreHeight = 0}) {
final pathPointer = Utf8.toUtf8(path);
final passwordPointer = Utf8.toUtf8(password);
final languagePointer = Utf8.toUtf8(language);
final addressPointer = Utf8.toUtf8(address);
final viewKeyPointer = Utf8.toUtf8(viewKey);
final spendKeyPointer = Utf8.toUtf8(spendKey);
final errorMessagePointer = allocate<Utf8>();
final isWalletRestored = restoreWalletFromKeysNative(
pathPointer,
passwordPointer,
languagePointer,
addressPointer,
viewKeyPointer,
spendKeyPointer,
nettype,
restoreHeight,
errorMessagePointer) !=
0;
free(pathPointer);
free(passwordPointer);
free(languagePointer);
free(addressPointer);
free(viewKeyPointer);
free(spendKeyPointer);
if (!isWalletRestored) {
throw WalletRestoreFromKeysException(
message: convertUTF8ToString(pointer: errorMessagePointer));
}
}
void loadWallet({String path, String password, int nettype = 0}) {
final pathPointer = Utf8.toUtf8(path);
final passwordPointer = Utf8.toUtf8(password);
final loaded = loadWalletNative(pathPointer, passwordPointer, nettype) != 0;
free(pathPointer);
free(passwordPointer);
if (!loaded) {
throw WalletOpeningException(
message: convertUTF8ToString(pointer: errorStringNative()));
}
}
void _createWallet(Map<String, dynamic> args) {
final path = args['path'] as String;
final password = args['password'] as String;
final language = args['language'] as String;
createWalletSync(path: path, password: password, language: language);
}
void _restoreFromSeed(Map<String, dynamic> args) {
final path = args['path'] as String;
final password = args['password'] as String;
final seed = args['seed'] as String;
final restoreHeight = args['restoreHeight'] as int;
restoreWalletFromSeedSync(
path: path, password: password, seed: seed, restoreHeight: restoreHeight);
}
void _restoreFromKeys(Map<String, dynamic> args) {
final path = args['path'] as String;
final password = args['password'] as String;
final language = args['language'] as String;
final restoreHeight = args['restoreHeight'] as int;
final address = args['address'] as String;
final viewKey = args['viewKey'] as String;
final spendKey = args['spendKey'] as String;
restoreWalletFromKeysSync(
path: path,
password: password,
language: language,
restoreHeight: restoreHeight,
address: address,
viewKey: viewKey,
spendKey: spendKey);
}
Future<void> _openWallet(Map<String, String> args) async =>
loadWallet(path: args['path'], password: args['password']);
bool _isWalletExist(String path) => isWalletExistSync(path: path);
void openWallet({String path, String password, int nettype = 0}) async =>
loadWallet(path: path, password: password, nettype: nettype);
Future<void> openWalletAsync(Map<String, String> args) async =>
compute(_openWallet, args);
Future<void> createWallet(
{String path,
String password,
String language,
int nettype = 0}) async =>
compute(_createWallet, {
'path': path,
'password': password,
'language': language,
'nettype': nettype
});
Future restoreFromSeed(
{String path,
String password,
String seed,
int nettype = 0,
int restoreHeight = 0}) async =>
compute<Map<String, Object>, void>(_restoreFromSeed, {
'path': path,
'password': password,
'seed': seed,
'nettype': nettype,
'restoreHeight': restoreHeight
});
Future restoreFromKeys(
{String path,
String password,
String language,
String address,
String viewKey,
String spendKey,
int nettype = 0,
int restoreHeight = 0}) async =>
compute<Map<String, Object>, void>(_restoreFromKeys, {
'path': path,
'password': password,
'language': language,
'address': address,
'viewKey': viewKey,
'spendKey': spendKey,
'nettype': nettype,
'restoreHeight': restoreHeight
});
Future<bool> isWalletExist({String path}) => compute(_isWalletExist, path);

@ -0,0 +1,6 @@
import 'dart:ffi';
import 'dart:io';
final DynamicLibrary wowneroApi = Platform.isAndroid
? DynamicLibrary.open("libcw_wownero.so")
: DynamicLibrary.open("cw_wownero.framework/cw_wownero");

@ -0,0 +1,8 @@
import 'package:flutter/foundation.dart';
class MoneroOutput {
MoneroOutput({@required this.address, @required this.amount});
final String address;
final String amount;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,48 @@
import 'package:cw_wownero/api/structs/pending_transaction.dart';
import 'package:cw_wownero/api/transaction_history.dart'
as wownero_transaction_history;
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/core/amount_converter.dart';
import 'package:cw_core/pending_transaction.dart';
class DoubleSpendException implements Exception {
DoubleSpendException();
@override
String toString() =>
'This transaction cannot be committed. This can be due to many reasons including the wallet not being synced, there is not enough WOW in your available balance, or previous transactions are not yet fully processed.';
}
class PendingWowneroTransaction with PendingTransaction {
PendingWowneroTransaction(this.pendingTransactionDescription);
final PendingTransactionDescription pendingTransactionDescription;
@override
String get id => pendingTransactionDescription.hash;
@override
String get amountFormatted => AmountConverter.amountIntToString(
CryptoCurrency.wow, pendingTransactionDescription.amount);
@override
String get feeFormatted => AmountConverter.amountIntToString(
CryptoCurrency.wow, pendingTransactionDescription.fee);
@override
Future<void> commit() async {
try {
wownero_transaction_history.commitTransactionFromPointerAddress(
address: pendingTransactionDescription.pointerAddress);
} catch (e) {
final message = e.toString();
if (message.contains('Reason: double spend')) {
throw DoubleSpendException();
}
rethrow;
}
}
}

@ -0,0 +1,78 @@
import 'package:mobx/mobx.dart';
import 'package:cw_core/account.dart';
import 'package:cw_wownero/api/account_list.dart' as account_list;
part 'wownero_account_list.g.dart';
class WowneroAccountList = WowneroAccountListBase with _$WowneroAccountList;
abstract class WowneroAccountListBase with Store {
WowneroAccountListBase()
: accounts = ObservableList<Account>(),
_isRefreshing = false,
_isUpdating = false {
refresh();
print(account_list.accountSizeNative());
}
@observable
ObservableList<Account> accounts;
bool _isRefreshing;
bool _isUpdating;
void update() async {
if (_isUpdating) {
return;
}
try {
_isUpdating = true;
refresh();
final accounts = getAll();
if (accounts.isNotEmpty) {
this.accounts.clear();
this.accounts.addAll(accounts);
}
_isUpdating = false;
} catch (e) {
_isUpdating = false;
rethrow;
}
}
List<Account> getAll() => account_list
.getAllAccount()
.map((accountRow) => Account(
id: accountRow.getId(),
label: accountRow.getLabel()))
.toList();
Future addAccount({String label}) async {
await account_list.addAccount(label: label);
update();
}
Future setLabelAccount({int accountIndex, String label}) async {
await account_list.setLabelForAccount(
accountIndex: accountIndex, label: label);
update();
}
void refresh() {
if (_isRefreshing) {
return;
}
try {
_isRefreshing = true;
account_list.refreshAccounts();
_isRefreshing = false;
} catch (e) {
_isRefreshing = false;
print(e);
rethrow;
}
}
}

@ -0,0 +1,18 @@
import 'package:intl/intl.dart';
import 'package:cw_core/crypto_amount_format.dart';
const wowneroAmountLength = 11;
const wowneroAmountDivider = 100000000000;
final wowneroAmountFormat = NumberFormat()
..maximumFractionDigits = wowneroAmountLength
..minimumFractionDigits = 1;
String wowneroAmountToString({int amount}) => wowneroAmountFormat
.format(cryptoAmountToDouble(amount: amount, divider: wowneroAmountDivider))
.replaceAll(',', '');
double wowneroAmountToDouble({int amount}) =>
cryptoAmountToDouble(amount: amount, divider: wowneroAmountDivider);
int wowneroParseAmount({String amount}) =>
(double.parse(amount) * wowneroAmountDivider).toInt();

@ -0,0 +1,30 @@
import 'package:cw_core/balance.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_wownero/wownero_amount_format.dart';
class WowneroBalance extends Balance {
WowneroBalance({@required this.fullBalance, @required this.unlockedBalance})
: formattedFullBalance = wowneroAmountToString(amount: fullBalance),
formattedUnlockedBalance =
wowneroAmountToString(amount: unlockedBalance),
super(unlockedBalance, fullBalance);
WowneroBalance.fromString(
{@required this.formattedFullBalance,
@required this.formattedUnlockedBalance})
: fullBalance = wowneroParseAmount(amount: formattedFullBalance),
unlockedBalance = wowneroParseAmount(amount: formattedUnlockedBalance),
super(wowneroParseAmount(amount: formattedUnlockedBalance),
wowneroParseAmount(amount: formattedFullBalance));
final int fullBalance;
final int unlockedBalance;
final String formattedFullBalance;
final String formattedUnlockedBalance;
@override
String get formattedAvailableBalance => formattedUnlockedBalance;
@override
String get formattedAdditionalBalance => formattedFullBalance;
}

@ -0,0 +1,90 @@
import 'package:cw_wownero/api/structs/subaddress_row.dart';
import 'package:flutter/services.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_wownero/api/subaddress_list.dart' as subaddress_list;
import 'package:cw_core/subaddress.dart';
part 'wownero_subaddress_list.g.dart';
class WowneroSubaddressList = WowneroSubaddressListBase
with _$WowneroSubaddressList;
abstract class WowneroSubaddressListBase with Store {
WowneroSubaddressListBase() {
_isRefreshing = false;
_isUpdating = false;
subaddresses = ObservableList<Subaddress>();
}
@observable
ObservableList<Subaddress> subaddresses;
bool _isRefreshing;
bool _isUpdating;
void update({int accountIndex}) {
if (_isUpdating) {
return;
}
try {
_isUpdating = true;
refresh(accountIndex: accountIndex);
subaddresses.clear();
subaddresses.addAll(getAll());
_isUpdating = false;
} catch (e) {
_isUpdating = false;
rethrow;
}
}
List<Subaddress> getAll() {
var subaddresses = subaddress_list.getAllSubaddresses();
if (subaddresses.length > 2) {
final primary = subaddresses.first;
final rest = subaddresses.sublist(1).reversed;
subaddresses = [primary] + rest.toList();
}
return subaddresses
.map((subaddressRow) => Subaddress(
id: subaddressRow.getId(),
address: subaddressRow.getAddress(),
label: subaddressRow.getId() == 0 &&
subaddressRow.getLabel().toLowerCase() == 'Primary account'.toLowerCase()
? 'Primary address'
: subaddressRow.getLabel()))
.toList();
}
Future addSubaddress({int accountIndex, String label}) async {
await subaddress_list.addSubaddress(
accountIndex: accountIndex, label: label);
update(accountIndex: accountIndex);
}
Future setLabelSubaddress(
{int accountIndex, int addressIndex, String label}) async {
await subaddress_list.setLabelForSubaddress(
accountIndex: accountIndex, addressIndex: addressIndex, label: label);
update(accountIndex: accountIndex);
}
void refresh({int accountIndex}) {
if (_isRefreshing) {
return;
}
try {
_isRefreshing = true;
subaddress_list.refreshSubaddresses(accountIndex: accountIndex);
_isRefreshing = false;
} on PlatformException catch (e) {
_isRefreshing = false;
print(e);
rethrow;
}
}
}

@ -0,0 +1,9 @@
import 'package:cw_core/monero_transaction_priority.dart';
import 'package:cw_core/output_info.dart';
class WowneroTransactionCreationCredentials {
WowneroTransactionCreationCredentials({this.outputs, this.priority});
final List<OutputInfo> outputs;
final MoneroTransactionPriority priority;
}

@ -0,0 +1,8 @@
class WowneroTransactionCreationException implements Exception {
WowneroTransactionCreationException(this.message);
final String message;
@override
String toString() => message;
}

@ -0,0 +1,27 @@
import 'dart:core';
import 'package:mobx/mobx.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_wownero/wownero_transaction_info.dart';
part 'wownero_transaction_history.g.dart';
class WowneroTransactionHistory = WowneroTransactionHistoryBase
with _$WowneroTransactionHistory;
abstract class WowneroTransactionHistoryBase
extends TransactionHistoryBase<WowneroTransactionInfo> with Store {
WowneroTransactionHistoryBase() {
transactions = ObservableMap<String, WowneroTransactionInfo>();
}
@override
Future<void> save() async {}
@override
void addOne(WowneroTransactionInfo transaction) =>
transactions[transaction.id] = transaction;
@override
void addMany(Map<String, WowneroTransactionInfo> transactions) =>
this.transactions.addAll(transactions);
}

@ -0,0 +1,80 @@
import 'package:cw_core/transaction_info.dart';
import 'package:cw_wownero/wownero_amount_format.dart';
import 'package:cw_wownero/api/structs/transaction_info_row.dart';
import 'package:cw_core/parseBoolFromString.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/format_amount.dart';
import 'package:cw_wownero/api/transaction_history.dart';
class WowneroTransactionInfo extends TransactionInfo {
WowneroTransactionInfo(this.id, this.height, this.direction, this.date,
this.isPending, this.amount, this.accountIndex, this.addressIndex, this.fee);
WowneroTransactionInfo.fromMap(Map map)
: id = (map['hash'] ?? '') as String,
height = (map['height'] ?? 0) as int,
direction =
parseTransactionDirectionFromNumber(map['direction'] as String) ??
TransactionDirection.incoming,
date = DateTime.fromMillisecondsSinceEpoch(
(int.parse(map['timestamp'] as String) ?? 0) * 1000),
isPending = parseBoolFromString(map['isPending'] as String),
amount = map['amount'] as int,
accountIndex = int.parse(map['accountIndex'] as String),
addressIndex = map['addressIndex'] as int,
key = getTxKey((map['hash'] ?? '') as String),
fee = map['fee'] as int ?? 0 {
additionalInfo = {
'key': key,
'accountIndex': accountIndex,
'addressIndex': addressIndex
};
}
WowneroTransactionInfo.fromRow(TransactionInfoRow row)
: id = row.getHash(),
height = row.blockHeight,
direction = parseTransactionDirectionFromInt(row.direction) ??
TransactionDirection.incoming,
date = DateTime.fromMillisecondsSinceEpoch(row.getDatetime() * 1000),
isPending = row.isPending != 0,
amount = row.getAmount(),
accountIndex = row.subaddrAccount,
addressIndex = row.subaddrIndex,
key = getTxKey(row.getHash()),
fee = row.fee {
additionalInfo = {
'key': key,
'accountIndex': accountIndex,
'addressIndex': addressIndex
};
}
final String id;
final int height;
final TransactionDirection direction;
final DateTime date;
final int accountIndex;
final bool isPending;
final int amount;
final int fee;
final int addressIndex;
String recipientAddress;
String key;
String _fiatAmount;
@override
String amountFormatted() =>
'${formatAmount(wowneroAmountToString(amount: amount))} WOW';
@override
String fiatAmount() => _fiatAmount ?? '';
@override
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
@override
String feeFormatted() =>
'${formatAmount(wowneroAmountToString(amount: fee))} WOW';
}

@ -0,0 +1,428 @@
import 'dart:async';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_wownero/wownero_transaction_creation_exception.dart';
import 'package:cw_wownero/wownero_transaction_info.dart';
import 'package:cw_wownero/wownero_wallet_addresses.dart';
import 'package:cw_core/monero_wallet_utils.dart';
import 'package:cw_wownero/api/structs/pending_transaction.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_wownero/api/transaction_history.dart'
as wownero_transaction_history;
import 'package:cw_wownero/api/wallet.dart';
import 'package:cw_wownero/api/wallet.dart' as wownero_wallet;
import 'package:cw_wownero/api/transaction_history.dart' as transaction_history;
import 'package:cw_wownero/wownero_amount_format.dart';
import 'package:cw_wownero/api/wownero_output.dart';
import 'package:cw_wownero/wownero_transaction_creation_credentials.dart';
import 'package:cw_wownero/pending_wownero_transaction.dart';
import 'package:cw_wownero/wownero_balance.dart';
import 'package:cw_wownero/wownero_transaction_history.dart';
import 'package:cw_core/monero_wallet_keys.dart';
import 'package:cw_core/account.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/monero_transaction_priority.dart';
import 'package:cw_core/crypto_currency.dart';
part 'wownero_wallet.g.dart';
const wowneroBlockSize = 1000;
class WowneroWallet = WowneroWalletBase with _$WowneroWallet;
abstract class WowneroWalletBase extends WalletBase<WowneroBalance,
WowneroTransactionHistory, WowneroTransactionInfo> with Store {
WowneroWalletBase({WalletInfo walletInfo})
: super(walletInfo) {
transactionHistory = WowneroTransactionHistory();
balance = ObservableMap<CryptoCurrency, WowneroBalance>.of({
CryptoCurrency.wow: WowneroBalance(
fullBalance: wownero_wallet.getFullBalance(accountIndex: 0),
unlockedBalance: wownero_wallet.getFullBalance(accountIndex: 0))
});
_isTransactionUpdating = false;
_hasSyncAfterStartup = false;
walletAddresses = WowneroWalletAddresses(walletInfo);
_onAccountChangeReaction = reaction((_) => walletAddresses.account,
(Account account) {
balance = ObservableMap<CryptoCurrency, WowneroBalance>.of(
<CryptoCurrency, WowneroBalance>{
currency: WowneroBalance(
fullBalance: wownero_wallet.getFullBalance(accountIndex: account.id),
unlockedBalance:
wownero_wallet.getUnlockedBalance(accountIndex: account.id))
});
walletAddresses.updateSubaddressList(accountIndex: account.id);
});
}
static const int _autoSaveInterval = 30;
@override
WowneroWalletAddresses walletAddresses;
@override
@observable
SyncStatus syncStatus;
@override
@observable
ObservableMap<CryptoCurrency, WowneroBalance> balance;
@override
String get seed => wownero_wallet.getSeed();
@override
MoneroWalletKeys get keys => MoneroWalletKeys(
privateSpendKey: wownero_wallet.getSecretSpendKey(),
privateViewKey: wownero_wallet.getSecretViewKey(),
publicSpendKey: wownero_wallet.getPublicSpendKey(),
publicViewKey: wownero_wallet.getPublicViewKey());
SyncListener _listener;
ReactionDisposer _onAccountChangeReaction;
bool _isTransactionUpdating;
bool _hasSyncAfterStartup;
Timer _autoSaveTimer;
Future<void> init() async {
await walletAddresses.init();
balance = ObservableMap<CryptoCurrency, WowneroBalance>.of(
<CryptoCurrency, WowneroBalance>{
currency: WowneroBalance(
fullBalance: wownero_wallet.getFullBalance(accountIndex: walletAddresses.account.id),
unlockedBalance: wownero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id))
});
_setListeners();
await updateTransactions();
if (walletInfo.isRecovery) {
wownero_wallet.setRecoveringFromSeed(isRecovery: walletInfo.isRecovery);
if (wownero_wallet.getCurrentHeight() <= 1) {
wownero_wallet.setRefreshFromBlockHeight(
height: walletInfo.restoreHeight);
}
}
_autoSaveTimer = Timer.periodic(
Duration(seconds: _autoSaveInterval),
(_) async => await save());
}
@override
void close() {
_listener?.stop();
_onAccountChangeReaction?.reaction?.dispose();
_autoSaveTimer?.cancel();
}
@override
Future<void> connectToNode({@required Node node}) async {
try {
syncStatus = ConnectingSyncStatus();
await wownero_wallet.setupNode(
address: node.uri.toString(),
login: node.login,
password: node.password,
useSSL: node.isSSL,
isLightWallet: false); // FIXME: hardcoded value
syncStatus = ConnectedSyncStatus();
} catch (e) {
syncStatus = FailedSyncStatus();
print(e);
}
}
@override
Future<void> startSync() async {
try {
_setInitialHeight();
} catch (_) {}
try {
syncStatus = StartingSyncStatus();
wownero_wallet.startRefresh();
_setListeners();
_listener?.start();
} catch (e) {
syncStatus = FailedSyncStatus();
print(e);
rethrow;
}
}
@override
Future<PendingTransaction> createTransaction(Object credentials) async {
final _credentials = credentials as WowneroTransactionCreationCredentials;
final outputs = _credentials.outputs;
final hasMultiDestination = outputs.length > 1;
final unlockedBalance =
wownero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id);
PendingTransactionDescription pendingTransactionDescription;
if (!(syncStatus is SyncedSyncStatus)) {
throw WowneroTransactionCreationException('The wallet is not synced.');
}
if (hasMultiDestination) {
if (outputs.any((item) => item.sendAll
|| item.formattedCryptoAmount <= 0)) {
throw WowneroTransactionCreationException('Wrong balance. Not enough WOW on your balance.');
}
final int totalAmount = outputs.fold(0, (acc, value) =>
acc + value.formattedCryptoAmount);
if (unlockedBalance < totalAmount) {
throw WowneroTransactionCreationException('Wrong balance. Not enough WOW on your balance.');
}
final wowneroOutputs = outputs.map((output) {
final outputAddress = output.isParsedAddress
? output.extractedAddress
: output.address;
return MoneroOutput(
address: outputAddress,
amount: output.cryptoAmount.replaceAll(',', '.'));
}).toList();
pendingTransactionDescription =
await transaction_history.createTransactionMultDest(
outputs: wowneroOutputs,
priorityRaw: _credentials.priority.serialize(),
accountIndex: walletAddresses.account.id);
} else {
final output = outputs.first;
final address = output.isParsedAddress
? output.extractedAddress
: output.address;
final amount = output.sendAll
? null
: output.cryptoAmount.replaceAll(',', '.');
final formattedAmount = output.sendAll
? null
: output.formattedCryptoAmount;
if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
(formattedAmount == null && unlockedBalance <= 0)) {
final formattedBalance = wowneroAmountToString(amount: unlockedBalance);
throw WowneroTransactionCreationException(
'Incorrect unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
}
pendingTransactionDescription =
await transaction_history.createTransaction(
address: address,
amount: amount,
priorityRaw: _credentials.priority.serialize(),
accountIndex: walletAddresses.account.id);
}
return PendingWowneroTransaction(pendingTransactionDescription);
}
@override
int calculateEstimatedFee(TransactionPriority priority, int amount) {
// FIXME: hardcoded value;
if (priority is MoneroTransactionPriority) {
switch (priority) {
case MoneroTransactionPriority.slow:
return 24590000;
case MoneroTransactionPriority.regular:
return 123050000;
case MoneroTransactionPriority.medium:
return 245029999;
case MoneroTransactionPriority.fast:
return 614530000;
case MoneroTransactionPriority.fastest:
return 26021600000;
}
}
return 0;
}
@override
Future<void> save() async {
await walletAddresses.updateAddressesInBox();
await backupWalletFiles(name);
await wownero_wallet.store();
}
Future<int> getNodeHeight() async => wownero_wallet.getNodeHeight();
int getSeedHeight(String seed) => wownero_wallet.getSeedHeightSync(seed);
Future<bool> isConnected() async => wownero_wallet.isConnected();
Future<void> setAsRecovered() async {
walletInfo.isRecovery = false;
await walletInfo.save();
}
@override
Future<void> rescan({int height}) async {
walletInfo.restoreHeight = height;
walletInfo.isRecovery = true;
wownero_wallet.setRefreshFromBlockHeight(height: height);
wownero_wallet.rescanBlockchainAsync();
await startSync();
_askForUpdateBalance();
walletAddresses.accountList.update();
await _askForUpdateTransactionHistory();
await save();
await walletInfo.save();
}
String getTransactionAddress(int accountIndex, int addressIndex) =>
wownero_wallet.getAddress(
accountIndex: accountIndex,
addressIndex: addressIndex);
@override
Future<Map<String, WowneroTransactionInfo>> fetchTransactions() async {
wownero_transaction_history.refreshTransactions();
return _getAllTransactions(null).fold<Map<String, WowneroTransactionInfo>>(
<String, WowneroTransactionInfo>{},
(Map<String, WowneroTransactionInfo> acc, WowneroTransactionInfo tx) {
acc[tx.id] = tx;
return acc;
});
}
Future<void> updateTransactions() async {
try {
if (_isTransactionUpdating) {
return;
}
_isTransactionUpdating = true;
final transactions = await fetchTransactions();
transactionHistory.addMany(transactions);
await transactionHistory.save();
_isTransactionUpdating = false;
} catch (e) {
print(e);
_isTransactionUpdating = false;
}
}
String getSubaddressLabel(int accountIndex, int addressIndex) {
return wownero_wallet.getSubaddressLabel(accountIndex, addressIndex);
}
List<WowneroTransactionInfo> _getAllTransactions(dynamic _) =>
wownero_transaction_history
.getAllTransations()
.map((row) => WowneroTransactionInfo.fromRow(row))
.toList();
void _setListeners() {
_listener?.stop();
_listener = wownero_wallet.setListeners(_onNewBlock, _onNewTransaction);
}
void _setInitialHeight() {
if (walletInfo.isRecovery) {
return;
}
final currentHeight = getCurrentHeight();
if (currentHeight <= 1) {
final height = _getHeightByDate(walletInfo.date);
wownero_wallet.setRecoveringFromSeed(isRecovery: true);
wownero_wallet.setRefreshFromBlockHeight(height: height);
}
}
int _getHeightDistance(DateTime date) {
final distance =
DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch;
final distance_sec = distance / 1000;
final daysTmp = (distance_sec / 86400).round();
final days = daysTmp < 1 ? 1 : daysTmp;
return days * 2000;
}
int _getHeightByDate(DateTime date) {
final nodeHeight = wownero_wallet.getNodeHeightSync();
final heightDistance = _getHeightDistance(date);
if (nodeHeight <= 0) {
return 0;
}
return nodeHeight - heightDistance;
}
void _askForUpdateBalance() {
final unlockedBalance = _getUnlockedBalance();
final fullBalance = _getFullBalance();
if (balance[currency].fullBalance != fullBalance ||
balance[currency].unlockedBalance != unlockedBalance) {
balance[currency] = WowneroBalance(
fullBalance: fullBalance, unlockedBalance: unlockedBalance);
}
}
Future<void> _askForUpdateTransactionHistory() async =>
await updateTransactions();
int _getFullBalance() =>
wownero_wallet.getFullBalance(accountIndex: walletAddresses.account.id);
int _getUnlockedBalance() =>
wownero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id);
void _onNewBlock(int height, int blocksLeft, double ptc) async {
try {
if (walletInfo.isRecovery) {
await _askForUpdateTransactionHistory();
_askForUpdateBalance();
walletAddresses.accountList.update();
}
if (blocksLeft < 100) {
await _askForUpdateTransactionHistory();
_askForUpdateBalance();
walletAddresses.accountList.update();
syncStatus = SyncedSyncStatus();
if (!_hasSyncAfterStartup) {
_hasSyncAfterStartup = true;
await save();
}
if (walletInfo.isRecovery) {
await setAsRecovered();
}
} else {
syncStatus = SyncingSyncStatus(blocksLeft, ptc);
}
} catch (e) {
print(e.toString());
}
}
void _onNewTransaction() async {
try {
await _askForUpdateTransactionHistory();
_askForUpdateBalance();
await Future<void>.delayed(Duration(seconds: 1));
} catch (e) {
print(e.toString());
}
}
}

@ -0,0 +1,85 @@
import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/account.dart';
import 'package:cw_wownero/wownero_account_list.dart';
import 'package:cw_wownero/wownero_subaddress_list.dart';
import 'package:cw_core/subaddress.dart';
import 'package:mobx/mobx.dart';
part 'wownero_wallet_addresses.g.dart';
class WowneroWalletAddresses = WowneroWalletAddressesBase
with _$WowneroWalletAddresses;
abstract class WowneroWalletAddressesBase extends WalletAddresses with Store {
WowneroWalletAddressesBase(WalletInfo walletInfo) : super(walletInfo) {
accountList = WowneroAccountList();
subaddressList = WowneroSubaddressList();
}
@override
@observable
String address;
@observable
Account account;
@observable
Subaddress subaddress;
WowneroSubaddressList subaddressList;
WowneroAccountList accountList;
@override
Future<void> init() async {
accountList.update();
account = accountList.accounts.first;
updateSubaddressList(accountIndex: account.id ?? 0);
await updateAddressesInBox();
}
@override
Future<void> updateAddressesInBox() async {
try {
final _subaddressList = WowneroSubaddressList();
addressesMap.clear();
accountList.accounts.forEach((account) {
_subaddressList.update(accountIndex: account.id);
_subaddressList.subaddresses.forEach((subaddress) {
addressesMap[subaddress.address] = subaddress.label;
});
});
await saveAddressesInBox();
} catch (e) {
print(e.toString());
}
}
bool validate() {
accountList.update();
final accountListLength = accountList.accounts?.length ?? 0;
if (accountListLength <= 0) {
return false;
}
subaddressList.update(accountIndex: accountList.accounts.first.id);
final subaddressListLength = subaddressList.subaddresses?.length ?? 0;
if (subaddressListLength <= 0) {
return false;
}
return true;
}
void updateSubaddressList({int accountIndex}) {
subaddressList.update(accountIndex: accountIndex);
subaddress = subaddressList.subaddresses.first;
address = subaddress.address;
}
}

@ -0,0 +1,232 @@
import 'dart:io';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/monero_wallet_utils.dart';
import 'package:hive/hive.dart';
import 'package:cw_wownero/api/wallet_manager.dart' as wownero_wallet_manager;
import 'package:cw_wownero/api/wallet.dart' as wownero_wallet;
import 'package:cw_wownero/api/exceptions/wallet_opening_exception.dart';
import 'package:cw_wownero/wownero_wallet.dart';
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
class WowneroNewWalletCredentials extends WalletCredentials {
WowneroNewWalletCredentials({String name, String password, this.language})
: super(name: name, password: password);
final String language;
}
class WowneroRestoreWalletFromSeedCredentials extends WalletCredentials {
WowneroRestoreWalletFromSeedCredentials(
{String name, String password, int height, this.mnemonic})
: super(name: name, password: password, height: height);
final String mnemonic;
}
class WowneroWalletLoadingException implements Exception {
@override
String toString() => 'Failure to load the wallet.';
}
class WowneroRestoreWalletFromKeysCredentials extends WalletCredentials {
WowneroRestoreWalletFromKeysCredentials(
{String name,
String password,
this.language,
this.address,
this.viewKey,
this.spendKey,
int height})
: super(name: name, password: password, height: height);
final String language;
final String address;
final String viewKey;
final String spendKey;
}
class WowneroWalletService extends WalletService<
WowneroNewWalletCredentials,
WowneroRestoreWalletFromSeedCredentials,
WowneroRestoreWalletFromKeysCredentials> {
WowneroWalletService(this.walletInfoSource);
final Box<WalletInfo> walletInfoSource;
static bool walletFilesExist(String path) =>
!File(path).existsSync() && !File('$path.keys').existsSync();
@override
WalletType getType() => WalletType.wownero;
@override
Future<WowneroWallet> create(WowneroNewWalletCredentials credentials) async {
try {
final path = await pathForWallet(name: credentials.name, type: getType());
await wownero_wallet_manager.createWallet(
path: path,
password: credentials.password,
language: credentials.language);
final wallet = WowneroWallet(walletInfo: credentials.walletInfo);
await wallet.init();
return wallet;
} catch (e) {
// TODO: Implement Exception for wallet list service.
print('WowneroWalletsManager Error: ${e.toString()}');
rethrow;
}
}
@override
Future<bool> isWalletExit(String name) async {
try {
final path = await pathForWallet(name: name, type: getType());
return wownero_wallet_manager.isWalletExist(path: path);
} catch (e) {
// TODO: Implement Exception for wallet list service.
print('WowneroWalletsManager Error: $e');
rethrow;
}
}
@override
Future<WowneroWallet> openWallet(String name, String password) async {
try {
final path = await pathForWallet(name: name, type: getType());
if (walletFilesExist(path)) {
await repairOldAndroidWallet(name);
}
await wownero_wallet_manager
.openWalletAsync({'path': path, 'password': password});
final walletInfo = walletInfoSource.values.firstWhere(
(info) => info.id == WalletBase.idFor(name, getType()),
orElse: () => null);
final wallet = WowneroWallet(walletInfo: walletInfo);
final isValid = wallet.walletAddresses.validate();
if (!isValid) {
await restoreOrResetWalletFiles(name);
wallet.close();
return openWallet(name, password);
}
await wallet.init();
return wallet;
} catch (e) {
// TODO: Implement Exception for wallet list service.
if ((e.toString().contains('bad_alloc') ||
(e is WalletOpeningException &&
(e.message == 'std::bad_alloc' ||
e.message.contains('bad_alloc')))) ||
(e.toString().contains('does not correspond') ||
(e is WalletOpeningException &&
e.message.contains('does not correspond')))) {
await restoreOrResetWalletFiles(name);
return openWallet(name, password);
}
rethrow;
}
}
@override
Future<void> remove(String wallet) async {
final path = await pathForWalletDir(name: wallet, type: getType());
final file = Directory(path);
final isExist = file.existsSync();
if (isExist) {
await file.delete(recursive: true);
}
}
@override
Future<WowneroWallet> restoreFromKeys(
WowneroRestoreWalletFromKeysCredentials credentials) async {
try {
final path = await pathForWallet(name: credentials.name, type: getType());
await wownero_wallet_manager.restoreFromKeys(
path: path,
password: credentials.password,
language: credentials.language,
restoreHeight: credentials.height,
address: credentials.address,
viewKey: credentials.viewKey,
spendKey: credentials.spendKey);
final wallet = WowneroWallet(walletInfo: credentials.walletInfo);
wallet.walletInfo.isRecovery = true;
await wallet.init();
return wallet;
} catch (e) {
// TODO: Implement Exception for wallet list service.
print('WowneroWalletsManager Error: $e');
rethrow;
}
}
@override
Future<WowneroWallet> restoreFromSeed(
WowneroRestoreWalletFromSeedCredentials credentials) async {
try {
final path = await pathForWallet(name: credentials.name, type: getType());
await wownero_wallet_manager.restoreFromSeed(
path: path,
password: credentials.password,
seed: credentials.mnemonic,
restoreHeight: credentials.height);
final wallet = WowneroWallet(walletInfo: credentials.walletInfo);
wallet.walletInfo.isRecovery = true;
wallet.walletInfo.restoreHeight = wallet.getSeedHeight(credentials.mnemonic);
await wallet.init();
return wallet;
} catch (e) {
// TODO: Implement Exception for wallet list service.
print('WowneroWalletsManager Error: $e');
rethrow;
}
}
Future<void> repairOldAndroidWallet(String name) async {
try {
if (!Platform.isAndroid) {
return;
}
final oldAndroidWalletDirPath =
await outdatedAndroidPathForWalletDir(name: name);
final dir = Directory(oldAndroidWalletDirPath);
if (!dir.existsSync()) {
return;
}
final newWalletDirPath =
await pathForWalletDir(name: name, type: getType());
dir.listSync().forEach((f) {
final file = File(f.path);
final name = f.path.split('/').last;
final newPath = newWalletDirPath + '/$name';
final newFile = File(newPath);
if (!newFile.existsSync()) {
newFile.createSync();
}
newFile.writeAsBytesSync(file.readAsBytesSync());
});
} catch (e) {
print(e.toString());
}
}
}

@ -0,0 +1,546 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "14.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "0.41.2"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.0"
asn1lib:
dependency: transitive
description:
name: asn1lib
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.1"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
build:
dependency: transitive
description:
name: build
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.2"
build_config:
dependency: transitive
description:
name: build_config
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.6"
build_daemon:
dependency: transitive
description:
name: build_daemon
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.10"
build_resolvers:
dependency: "direct dev"
description:
name: build_resolvers
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.3"
build_runner:
dependency: "direct dev"
description:
name: build_runner
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.5"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.10"
built_collection:
dependency: transitive
description:
name: built_collection
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
url: "https://pub.dartlang.org"
source: hosted
version: "8.1.3"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.5"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
code_builder:
dependency: transitive
description:
name: code_builder
url: "https://pub.dartlang.org"
source: hosted
version: "3.7.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.5"
cw_core:
dependency: "direct main"
description:
path: "../cw_core"
relative: true
source: path
version: "0.0.1"
dart_style:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.12"
dartx:
dependency: transitive
description:
name: dartx
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
encrypt:
dependency: transitive
description:
name: encrypt
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.0"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
ffi:
dependency: "direct main"
description:
name: ffi
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.2"
fixnum:
dependency: transitive
description:
name: fixnum
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_mobx:
dependency: "direct main"
description:
name: flutter_mobx
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0+2"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
glob:
dependency: transitive
description:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
graphs:
dependency: transitive
description:
name: graphs
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
hive:
dependency: transitive
description:
name: hive
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.4+1"
hive_generator:
dependency: "direct dev"
description:
name: hive_generator
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.2"
http:
dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.2"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.4"
intl:
dependency: "direct main"
description:
name: intl
url: "https://pub.dartlang.org"
source: hosted
version: "0.17.0"
io:
dependency: transitive
description:
name: io
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.5"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
json_annotation:
dependency: transitive
description:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.1"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
mobx:
dependency: "direct main"
description:
name: mobx
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1+4"
mobx_codegen:
dependency: "direct dev"
description:
name: mobx_codegen
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
package_config:
dependency: transitive
description:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.3"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
path_provider:
dependency: "direct main"
description:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.1"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
pointycastle:
dependency: transitive
description:
name: pointycastle
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
pool:
dependency: transitive
description:
name: pool
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.0"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.8"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.9"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.4+1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_gen:
dependency: transitive
description:
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.10+3"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
stream_transform:
dependency: transitive
description:
name: stream_transform
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.19"
time:
dependency: transitive
description:
name: time
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.1"
timing:
dependency: transitive
description:
name: timing
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.1+3"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.12.0 <3.0.0"
flutter: ">=1.20.0"

@ -0,0 +1,78 @@
name: cw_wownero
description: A new flutter plugin project.
version: 0.0.1
publish_to: none
author: Cake Wallet
homepage: https://cakewallet.com
environment:
sdk: ">=2.6.0 <3.0.0"
flutter: ">=1.20.0"
dependencies:
flutter:
sdk: flutter
ffi: ^0.1.3
path_provider: ^1.4.0
http: ^0.12.0+2
mobx: ^1.2.1+2
flutter_mobx: ^1.1.0+2
intl: ^0.17.0
cw_core:
path: ../cw_core
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^1.10.3
build_resolvers: ^1.3.10
mobx_codegen: ^1.1.0+1
hive_generator: ^0.8.1
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# This section identifies this Flutter project as a plugin project.
# The androidPackage and pluginClass identifiers should not ordinarily
# be modified. They are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin:
platforms:
android:
package: com.cakewallet.cw_wownero
pluginClass: CwWowneroPlugin
ios:
pluginClass: CwWowneroPlugin
# To add assets to your plugin package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# To add custom fonts to your plugin package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages

@ -61,6 +61,7 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.xnok:
case CryptoCurrency.xnzd:
case CryptoCurrency.xusd:
case CryptoCurrency.wow:
return '[0-9a-zA-Z]';
default:
return '[0-9a-zA-Z]';
@ -117,6 +118,8 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.xnzd:
case CryptoCurrency.xusd:
return [98, 99, 106];
case CryptoCurrency.wow:
return [97];
default:
return [];
}

@ -4,6 +4,8 @@ import 'package:cw_core/crypto_currency.dart';
class AmountConverter {
static const _moneroAmountLength = 12;
static const _moneroAmountDivider = 1000000000000;
static const _wowneroAmountLength = 11;
static const _wowneroAmountDivider = 100000000000;
static const _litecoinAmountDivider = 100000000;
static const _ethereumAmountDivider = 1000000000000000000;
static const _dashAmountDivider = 100000000;
@ -16,6 +18,9 @@ class AmountConverter {
static final _moneroAmountFormat = NumberFormat()
..maximumFractionDigits = _moneroAmountLength
..minimumFractionDigits = 1;
static final _wowneroAmountFormat = NumberFormat()
..maximumFractionDigits = _wowneroAmountLength
..minimumFractionDigits = 1;
static double amountIntToDouble(CryptoCurrency cryptoCurrency, int amount) {
switch (cryptoCurrency) {
@ -23,6 +28,8 @@ class AmountConverter {
return _moneroAmountToDouble(amount);
case CryptoCurrency.btc:
return _bitcoinAmountToDouble(amount);
case CryptoCurrency.wow:
return _wowneroAmountToDouble(amount);
case CryptoCurrency.bch:
return _bitcoinCashAmountToDouble(amount);
case CryptoCurrency.dash:
@ -55,6 +62,8 @@ class AmountConverter {
switch (cryptoCurrency) {
case CryptoCurrency.xmr:
return _moneroParseAmount(amount);
case CryptoCurrency.wow:
return _wowneroParseAmount(amount);
case CryptoCurrency.xhv:
case CryptoCurrency.xag:
case CryptoCurrency.xau:
@ -81,6 +90,8 @@ class AmountConverter {
return _moneroAmountToString(amount);
case CryptoCurrency.btc:
return _bitcoinAmountToString(amount);
case CryptoCurrency.wow:
return _wowneroAmountToString(amount);
case CryptoCurrency.xhv:
case CryptoCurrency.xag:
case CryptoCurrency.xau:
@ -113,6 +124,15 @@ class AmountConverter {
static int _moneroParseAmount(String amount) =>
_moneroAmountFormat.parse(amount).toInt();
static String _wowneroAmountToString(int amount) => _wowneroAmountFormat.format(
cryptoAmountToDouble(amount: amount, divider: _wowneroAmountDivider));
static double _wowneroAmountToDouble(int amount) =>
cryptoAmountToDouble(amount: amount, divider: _wowneroAmountDivider);
static int _wowneroParseAmount(String amount) =>
_wowneroAmountFormat.parse(amount).toInt();
static String _bitcoinAmountToString(int amount) =>
_bitcoinAmountFormat.format(
cryptoAmountToDouble(amount: amount, divider: _bitcoinAmountDivider));

@ -1,5 +1,6 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/entities/mnemonic_item.dart';
import 'package:cw_core/wallet_type.dart';
@ -22,6 +23,8 @@ class SeedValidator extends Validator<MnemonicItem> {
return getBitcoinWordList(language);
case WalletType.monero:
return monero.getMoneroWordList(language);
case WalletType.wownero:
return wownero.getWowneroWordList(language);
case WalletType.haven:
return haven.getMoneroWordList(language);
default:

@ -3,7 +3,7 @@ import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/entities/wake_lock.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart';
import 'package:cw_core/unspent_coins_info.dart';
@ -360,7 +360,7 @@ Future setup(
getIt.registerFactory(() {
final wallet = getIt.get<AppStore>().wallet;
if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) {
if (wallet.type == WalletType.monero || wallet.type == WalletType.haven || wallet.type == WalletType.wownero) {
return MoneroAccountListViewModel(wallet);
}
@ -391,6 +391,7 @@ Future setup(
(AccountListItem account, _) => MoneroAccountEditOrCreateViewModel(
monero.getAccountList(getIt.get<AppStore>().wallet),
haven?.getAccountList(getIt.get<AppStore>().wallet),
wownero.getAccountList(getIt.get<AppStore>().wallet),
wallet: getIt.get<AppStore>().wallet,
accountListItem: account));
@ -481,6 +482,8 @@ Future setup(
return haven.createHavenWalletService(_walletInfoSource);
case WalletType.monero:
return monero.createMoneroWalletService(_walletInfoSource);
case WalletType.wownero:
return wownero.createWowneroWalletService(_walletInfoSource);
case WalletType.bitcoin:
return bitcoin.createBitcoinWalletService(
_walletInfoSource, _unspentCoinsInfoSource);

@ -23,6 +23,7 @@ const newCakeWalletMoneroUri = 'xmr-node.cakewallet.com:18081';
const cakeWalletBitcoinElectrumUri = 'electrum.cakewallet.com:50002';
const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002';
const havenDefaultNodeUri = 'nodes.havenprotocol.org:443';
const wowneroDefaultNodeUri = 'http://eu-west-2.wow.xmr.pm:34568';
Future defaultSettingsMigration(
{@required int version,
@ -131,6 +132,12 @@ Future defaultSettingsMigration(
case 17:
await changeDefaultHavenNode(nodes);
break;
case 18:
await addWowneroNodeList(nodes: nodes);
await changeWowneroCurrentNodeToDefault(
sharedPreferences: sharedPreferences, nodes: nodes);
await checkCurrentNodes(nodes, sharedPreferences);
break;
default:
break;
@ -202,6 +209,14 @@ Node getHavenDefaultNode({@required Box<Node> nodes}) {
orElse: () => null);
}
Node getWowneroDefaultNode({@required Box<Node> nodes}) {
return nodes.values.firstWhere(
(Node node) => node.uriRaw == wowneroDefaultNodeUri,
orElse: () => null) ??
nodes.values.firstWhere((node) => node.type == WalletType.wownero,
orElse: () => null);
}
Node getMoneroDefaultNode({@required Box<Node> nodes}) {
final timeZone = DateTime.now().timeZoneOffset.inHours;
var nodeUri = '';
@ -246,6 +261,15 @@ Future<void> changeHavenCurrentNodeToDefault(
await sharedPreferences.setInt(PreferencesKey.currentHavenNodeIdKey, nodeId);
}
Future<void> changeWowneroCurrentNodeToDefault(
{@required SharedPreferences sharedPreferences,
@required Box<Node> nodes}) async {
final node = getWowneroDefaultNode(nodes: nodes);
final nodeId = node?.key as int ?? 0;
await sharedPreferences.setInt(PreferencesKey.currentWowneroNodeIdKey, nodeId);
}
Future<void> replaceDefaultNode(
{@required SharedPreferences sharedPreferences,
@required Box<Node> nodes}) async {
@ -292,6 +316,11 @@ Future<void> addHavenNodeList({@required Box<Node> nodes}) async {
await nodes.addAll(nodeList);
}
Future<void> addWowneroNodeList({@required Box<Node> nodes}) async {
final nodeList = await loadDefaultWowneroNodes();
await nodes.addAll(nodeList);
}
Future<void> addAddressesForMoneroWallets(
Box<WalletInfo> walletInfoSource) async {
final moneroWalletsInfo =
@ -383,6 +412,8 @@ Future<void> checkCurrentNodes(
.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
final currentHavenNodeId = sharedPreferences
.getInt(PreferencesKey.currentHavenNodeIdKey);
final currentWowneroNodeId = sharedPreferences
.getInt(PreferencesKey.currentWowneroNodeIdKey);
final currentMoneroNode = nodeSource.values.firstWhere(
(node) => node.key == currentMoneroNodeId,
orElse: () => null);
@ -395,6 +426,9 @@ Future<void> checkCurrentNodes(
final currentHavenNodeServer = nodeSource.values.firstWhere(
(node) => node.key == currentHavenNodeId,
orElse: () => null);
final currentWowneroNodeServer = nodeSource.values.firstWhere(
(node) => node.key == currentWowneroNodeId,
orElse: () => null);
if (currentMoneroNode == null) {
final newCakeWalletNode =
@ -429,6 +463,14 @@ Future<void> checkCurrentNodes(
await sharedPreferences.setInt(
PreferencesKey.currentHavenNodeIdKey, node.key as int);
}
if (currentWowneroNodeServer == null) {
final nodes = await loadDefaultWowneroNodes();
final node = nodes.first;
await nodeSource.add(node);
await sharedPreferences.setInt(
PreferencesKey.currentWowneroNodeIdKey, node.key as int);
}
}
Future<void> resetBitcoinElectrumServer(

@ -70,16 +70,34 @@ Future<List<Node>> loadDefaultHavenNodes() async {
}).toList();
}
Future<List<Node>> loadDefaultWowneroNodes() async {
final nodesRaw = await rootBundle.loadString('assets/wownero_node_list.yml');
final nodes = loadYaml(nodesRaw) as YamlList;
return nodes.map((dynamic raw) {
if (raw is Map) {
final node = Node.fromMap(raw);
node?.type = WalletType.wownero;
return node;
}
return null;
}).toList();
}
Future resetToDefault(Box<Node> nodeSource) async {
final moneroNodes = await loadDefaultNodes();
final bitcoinElectrumServerList = await loadBitcoinElectrumServerList();
final litecoinElectrumServerList = await loadLitecoinElectrumServerList();
final havenNodes = await loadDefaultHavenNodes();
final wowneroNodes = await loadDefaultWowneroNodes();
final nodes =
moneroNodes +
bitcoinElectrumServerList +
litecoinElectrumServerList +
havenNodes;
havenNodes +
wowneroNodes;
await nodeSource.clear();
await nodeSource.addAll(nodes);

@ -5,6 +5,7 @@ class PreferencesKey {
static const currentBitcoinElectrumSererIdKey = 'current_node_id_btc';
static const currentLitecoinElectrumSererIdKey = 'current_node_id_ltc';
static const currentHavenNodeIdKey = 'current_node_id_xhv';
static const currentWowneroNodeIdKey = 'current_node_id_wow';
static const currentFiatCurrencyKey = 'current_fiat_currency';
static const currentTransactionPriorityKeyLegacy = 'current_fee_priority';
static const currentBalanceDisplayModeKey = 'current_balance_display_mode';

@ -229,6 +229,9 @@ class ContactListPage extends BasePage {
case CryptoCurrency.xhv:
image = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
break;
case CryptoCurrency.wow:
image = Image.asset('assets/images/wownero_logo.png', height: 24, width: 24);
break;
default:
image = null;
}

@ -322,6 +322,7 @@ class DashboardPage extends BasePage {
switch (walletType) {
case WalletType.haven:
case WalletType.wownero:
await showPopUp<void>(
context: context,
builder: (BuildContext context) {

@ -23,6 +23,7 @@ class MenuWidgetState extends State<MenuWidget> {
Image bitcoinIcon;
Image litecoinIcon;
Image havenIcon;
Image wowneroIcon;
final largeScreen = 731;
double menuWidth;
@ -80,6 +81,7 @@ class MenuWidgetState extends State<MenuWidget> {
color: Theme.of(context).accentTextTheme.overline.decorationColor);
litecoinIcon = Image.asset('assets/images/litecoin_menu.png');
havenIcon = Image.asset('assets/images/haven_menu.png');
wowneroIcon = Image.asset('assets/images/wownero_menu.png');
return Row(
mainAxisSize: MainAxisSize.max,
@ -245,6 +247,8 @@ class MenuWidgetState extends State<MenuWidget> {
return litecoinIcon;
case WalletType.haven:
return havenIcon;
case WalletType.wownero:
return wowneroIcon;
default:
return null;
}

@ -67,7 +67,8 @@ class WalletTypeFormState extends State<WalletTypeForm> {
Image.asset('assets/images/wallet_type_light.png');
final havenIcon =
Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
final wowneroIcon =
Image.asset('assets/images/wownero_logo.png', height: 24, width: 24);
WalletType selected;
List<WalletType> types;
Flushbar<void> _progressBar;
@ -133,6 +134,8 @@ class WalletTypeFormState extends State<WalletTypeForm> {
return litecoinIcon;
case WalletType.haven:
return havenIcon;
case WalletType.wownero:
return wowneroIcon;
default:
return null;
}

@ -109,7 +109,7 @@ class ReceivePage extends BasePage {
@override
Widget body(BuildContext context) {
return (addressListViewModel.type == WalletType.monero || addressListViewModel.type == WalletType.haven)
return (addressListViewModel.type == WalletType.monero || addressListViewModel.type == WalletType.haven || addressListViewModel.type == WalletType.wownero)
? KeyboardActions(
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,

@ -131,7 +131,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
focusNode: widget.blockHeightFocusNode,
key: blockchainHeightKey,
onHeightOrDateEntered: widget.onHeightOrDateEntered,
hasDatePicker: widget.type == WalletType.monero)
hasDatePicker: widget.type == WalletType.monero || widget.type == WalletType.wownero)
]));
}

@ -215,9 +215,13 @@ class WalletRestorePage extends BasePage {
.text
.split(' ');
if ((walletRestoreViewModel.type == WalletType.monero || walletRestoreViewModel.type == WalletType.haven) &&
if ((walletRestoreViewModel.type == WalletType.monero ||
walletRestoreViewModel.type == WalletType.haven) &&
seedWords.length != WalletRestoreViewModelBase.moneroSeedMnemonicLength) {
return false;
} else if(walletRestoreViewModel.type == WalletType.wownero &&
seedWords.length != WalletRestoreViewModelBase.wowneroSeedMnemonicLength) {
return false;
}
if ((walletRestoreViewModel.type == WalletType.bitcoin ||

@ -10,15 +10,23 @@ import 'package:cake_wallet/src/screens/base_page.dart';
class PreSeedPage extends BasePage {
PreSeedPage(this.type)
: imageLight = Image.asset('assets/images/pre_seed_light.png'),
imageDark = Image.asset('assets/images/pre_seed_dark.png'),
wordsCount = type == WalletType.monero
? 25
: 24; // FIXME: Stupid fast implementation
imageDark = Image.asset('assets/images/pre_seed_dark.png');
final Image imageDark;
final Image imageLight;
final WalletType type;
final int wordsCount;
@override
int wordsCount() {
switch(this.type) {
case WalletType.monero:
return 25;
case WalletType.wownero:
return 14;
default:
return 24;
}
}
@override
Widget leading(BuildContext context) => null;
@ -51,7 +59,7 @@ class PreSeedPage extends BasePage {
child: Text(
S
.of(context)
.pre_seed_description(wordsCount.toString()),
.pre_seed_description(wordsCount().toString()),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,

@ -47,6 +47,8 @@ class WalletListBodyState extends State<WalletListBody> {
Image.asset('assets/images/close.png', height: 24, width: 24);
final havenIcon =
Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
final wowneroIcon =
Image.asset('assets/images/wownero_logo.png', height: 24, width: 24);
final scrollController = ScrollController();
final double tileHeight = 60;
Flushbar<void> _progressBar;
@ -220,6 +222,8 @@ class WalletListBodyState extends State<WalletListBody> {
return litecoinIcon;
case WalletType.haven:
return havenIcon;
case WalletType.wownero:
return wowneroIcon;
default:
return nonWalletTypeIcon;
}

@ -19,7 +19,11 @@ class WelcomePage extends BasePage {
if (isHaven) {
return S.of(context).haven_app;
}
if (isWownero) {
return S.of(context).wownero_app;
}
return S.of(context).cake_wallet;
}
@ -31,6 +35,10 @@ class WelcomePage extends BasePage {
if (isHaven) {
return S.of(context).haven_app_wallet_text;
}
if (isWownero) {
return S.of(context).wownero_app_wallet_text;
}
return S.of(context).first_wallet_text;
}

@ -38,6 +38,7 @@ abstract class SettingsStoreBase with Store {
@required Map<WalletType, Node> nodes,
@required TransactionPriority initialBitcoinTransactionPriority,
@required TransactionPriority initialMoneroTransactionPriority,
@required TransactionPriority initialWowneroTransactionPriority,
@required this.shouldShowYatPopup,
@required this.isBitcoinBuyEnabled,
this.actionlistDisplayMode}) {
@ -50,7 +51,8 @@ abstract class SettingsStoreBase with Store {
languageCode = initialLanguageCode;
priority = ObservableMap<WalletType, TransactionPriority>.of({
WalletType.monero: initialMoneroTransactionPriority,
WalletType.bitcoin: initialBitcoinTransactionPriority
WalletType.bitcoin: initialBitcoinTransactionPriority,
WalletType.wownero: initialWowneroTransactionPriority
});
this.nodes = ObservableMap<WalletType, Node>.of(nodes);
_sharedPreferences = sharedPreferences;
@ -229,10 +231,13 @@ abstract class SettingsStoreBase with Store {
.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
final havenNodeId = sharedPreferences
.getInt(PreferencesKey.currentHavenNodeIdKey);
final wowneroNodeId = sharedPreferences
.getInt(PreferencesKey.currentWowneroNodeIdKey);
final moneroNode = nodeSource.get(nodeId);
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
final havenNode = nodeSource.get(havenNodeId);
final wowneroNode = nodeSource.get(wowneroNodeId);
final packageInfo = await PackageInfo.fromPlatform();
final shouldShowYatPopup =
sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true;
@ -243,7 +248,8 @@ abstract class SettingsStoreBase with Store {
WalletType.monero: moneroNode,
WalletType.bitcoin: bitcoinElectrumServer,
WalletType.litecoin: litecoinElectrumServer,
WalletType.haven: havenNode
WalletType.haven: havenNode,
WalletType.wownero: wowneroNode
},
appVersion: packageInfo.version,
isBitcoinBuyEnabled: isBitcoinBuyEnabled,
@ -314,6 +320,10 @@ abstract class SettingsStoreBase with Store {
await _sharedPreferences.setInt(
PreferencesKey.currentNodeIdKey, node.key as int);
break;
case WalletType.wownero:
await _sharedPreferences.setInt(
PreferencesKey.currentWowneroNodeIdKey, node.key as int);
break;
default:
break;
}

@ -91,6 +91,7 @@ abstract class BalanceViewModelBase with Store {
switch(wallet.type) {
case WalletType.monero:
case WalletType.haven:
case WalletType.wownero:
return S.current.xmr_available_balance;
default:
return S.current.confirmed;
@ -102,6 +103,7 @@ abstract class BalanceViewModelBase with Store {
switch(wallet.type) {
case WalletType.monero:
case WalletType.haven:
case WalletType.wownero:
return S.current.xmr_full_balance;
default:
return S.current.unconfirmed;

@ -337,14 +337,16 @@ abstract class DashboardViewModelBase with Store {
}
void updateActions() {
isEnabledExchangeAction = wallet.type != WalletType.haven;
hasExchangeAction = !isHaven;
isEnabledExchangeAction = wallet.type != WalletType.haven && wallet.type != WalletType.wownero;
hasExchangeAction = !isHaven && !isWownero;
isEnabledBuyAction = wallet.type != WalletType.haven
&& wallet.type != WalletType.monero;
hasBuyAction = !isMoneroOnly && !isHaven;
&& wallet.type != WalletType.monero
&& wallet.type != WalletType.wownero;
hasBuyAction = !isMoneroOnly && !isHaven && !isWownero;
isEnabledSellAction = wallet.type != WalletType.haven
&& wallet.type != WalletType.monero
&& wallet.type != WalletType.wownero
&& wallet.type != WalletType.litecoin;
hasSellAction = !isMoneroOnly && !isHaven;
hasSellAction = !isMoneroOnly && !isHaven && !isWownero;
}
}

@ -5,6 +5,7 @@ import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
@ -56,6 +57,11 @@ class TransactionListItem extends ActionListItem with Keyable {
cryptoAmount: haven.formatterMoneroAmountToDouble(amount: transaction.amount),
price: price);
break;
case WalletType.wownero:
amount = calculateFiatAmountRaw(
cryptoAmount: wownero.formatterWowneroAmountToDouble(amount: transaction.amount),
price: price);
break;
default:
break;
}

@ -5,6 +5,7 @@ import 'package:mobx/mobx.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
part 'monero_account_edit_or_create_view_model.g.dart';
@ -13,7 +14,7 @@ class MoneroAccountEditOrCreateViewModel = MoneroAccountEditOrCreateViewModelBas
with _$MoneroAccountEditOrCreateViewModel;
abstract class MoneroAccountEditOrCreateViewModelBase with Store {
MoneroAccountEditOrCreateViewModelBase(this._moneroAccountList, this._havenAccountList,
MoneroAccountEditOrCreateViewModelBase(this._moneroAccountList, this._havenAccountList, this._wowneroAccountList,
{@required WalletBase wallet, AccountListItem accountListItem})
: state = InitialExecutionState(),
isEdit = accountListItem != null,
@ -31,6 +32,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store {
final MoneroAccountList _moneroAccountList;
final HavenAccountList _havenAccountList;
final WowneroAccountList _wowneroAccountList;
final AccountListItem _accountListItem;
final WalletBase _wallet;
@ -42,6 +44,10 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store {
if (_wallet.type == WalletType.haven) {
await saveHaven();
}
if (_wallet.type == WalletType.wownero) {
await saveWownero();
}
}
Future<void> saveMonero() async {
@ -91,4 +97,26 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store {
state = FailureState(e.toString());
}
}
Future<void> saveWownero() async {
try {
state = IsExecutingState();
if (_accountListItem != null) {
await _wowneroAccountList.setLabelAccount(
_wallet,
accountIndex: _accountListItem.id,
label: label);
} else {
await _wowneroAccountList.addAccount(
_wallet,
label: label);
}
await _wallet.save();
state = ExecutedSuccessfullyState();
} catch (e) {
state = FailureState(e.toString());
}
}
}

@ -4,6 +4,7 @@ import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/wownero/wownero.dart';
part 'monero_account_list_view_model.g.dart';

@ -42,7 +42,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
(address?.isNotEmpty ?? false) && (port?.isNotEmpty ?? false);
bool get hasAuthCredentials => _wallet.type == WalletType.monero ||
_wallet.type == WalletType.haven;
_wallet.type == WalletType.haven || _wallet.type == WalletType.wownero;
String get uri {
var uri = address;

@ -39,6 +39,9 @@ abstract class NodeListViewModelBase with Store {
case WalletType.monero:
node = getMoneroDefaultNode(nodes: _nodeSource);
break;
case WalletType.wownero:
node = getWowneroDefaultNode(nodes: _nodeSource);
break;
case WalletType.litecoin:
node = getLitecoinDefaultElectrumServer(nodes: _nodeSource);
break;

@ -3,6 +3,7 @@ import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
@ -82,6 +83,9 @@ abstract class OutputBase with Store {
case WalletType.haven:
_amount = haven.formatterMoneroParseAmount(amount: _cryptoAmount);
break;
case WalletType.wownero:
_amount = wownero.formatterWowneroParseAmount(amount: _cryptoAmount);
break;
default:
break;
}
@ -115,6 +119,10 @@ abstract class OutputBase with Store {
if (_wallet.type == WalletType.haven) {
return haven.formatterMoneroAmountToDouble(amount: fee);
}
if (_wallet.type == WalletType.wownero) {
return wownero.formatterWowneroAmountToDouble(amount: fee);
}
} catch (e) {
print(e.toString());
}
@ -220,6 +228,9 @@ abstract class OutputBase with Store {
case WalletType.haven:
maximumFractionDigits = 12;
break;
case WalletType.wownero:
maximumFractionDigits = 12;
break;
default:
break;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save