parent
b58b0002b0
commit
13816cf3b5
@ -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(""));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 49 KiB |
@ -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
|
@ -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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue