summaryrefslogtreecommitdiff
path: root/impls
diff options
context:
space:
mode:
authorKonstantin Ullrich <konstantinullrich12@gmail.com>2024-10-07 12:37:30 +0200
committerKonstantin Ullrich <konstantinullrich12@gmail.com>2024-10-07 12:37:30 +0200
commit98272ee381bd07081502dd426226f58c879300a6 (patch)
tree672f6f06727dbc1c84270973ce13e9403913e481 /impls
parent04b29d84a2c368c677cf5ec946269203622ca170 (diff)
parent67f4baa015a4407d096e35b6e5a81d72932fb55f (diff)
Merge branch 'master' into ledger
# Conflicts: # .github/workflows/full_check.yaml # impls/monero.dart/lib/monero.dart # impls/monero.dart/pubspec.yaml # patches/monero/0016-add-dummy-device-for-ledger.patch
Diffstat (limited to 'impls')
-rw-r--r--impls/monero.c#/README.md7
-rw-r--r--impls/monero.c#/monero_wrapper.cs184
-rw-r--r--impls/monero.dart/lib/monero.dart55
-rw-r--r--impls/monero.dart/lib/src/checksum_monero.dart2
-rw-r--r--impls/monero.dart/lib/src/checksum_wownero.dart2
-rw-r--r--impls/monero.dart/lib/wownero.dart46
-rw-r--r--impls/monero.dart/pubspec.yaml4
-rw-r--r--impls/monero.ts/.gitignore2
-rw-r--r--impls/monero.ts/README.md42
-rw-r--r--impls/monero.ts/checksum.ts65
-rw-r--r--impls/monero.ts/checksum_monero.ts5
-rw-r--r--impls/monero.ts/checksum_wownero.ts5
-rw-r--r--impls/monero.ts/deno.jsonc5
-rw-r--r--impls/monero.ts/mod.ts6
-rw-r--r--impls/monero.ts/src/bindings.ts562
-rw-r--r--impls/monero.ts/src/pending_transaction.ts81
-rw-r--r--impls/monero.ts/src/transaction_history.ts38
-rw-r--r--impls/monero.ts/src/transaction_info.ts104
-rw-r--r--impls/monero.ts/src/utils.ts25
-rw-r--r--impls/monero.ts/src/wallet.ts304
-rw-r--r--impls/monero.ts/src/wallet_manager.ts27
21 files changed, 1541 insertions, 30 deletions
diff --git a/impls/monero.c#/README.md b/impls/monero.c#/README.md
new file mode 100644
index 0000000..bfbb3b7
--- /dev/null
+++ b/impls/monero.c#/README.md
@@ -0,0 +1,7 @@
+# monero.c#
+
+> C# port for monero
+
+This is not a ready-to-use package (as opposed to monero.dart and monero.ts), but it works. You can check the `monero_wrapper.cs`.
+
+Currently C# code is optimized to be used in Godot.., if you are not planning to use it there - simply do not extend Node class and remove Godot import, if you are planning to use it in production, please let me know - we can work on a better approach to using monero with proper package code in here. \ No newline at end of file
diff --git a/impls/monero.c#/monero_wrapper.cs b/impls/monero.c#/monero_wrapper.cs
new file mode 100644
index 0000000..ce7749d
--- /dev/null
+++ b/impls/monero.c#/monero_wrapper.cs
@@ -0,0 +1,184 @@
+using Godot;
+using System;
+using System.Runtime.InteropServices;
+
+public partial class monero_wrapper : Node
+{
+ public static IntPtr wmPtr = MONERO_WalletManagerFactory_getWalletManager();
+ public static IntPtr wPtr;
+ public static IntPtr pendingTx;
+ public static IntPtr txHistory;
+ public string path = "";
+
+ // Called when the node enters the scene tree for the first time.
+ public override void _Ready()
+ {
+ }
+
+ // Called every frame. 'delta' is the elapsed time since the previous frame.
+ public override void _Process(double delta)
+ {
+ }
+
+ public static bool openWallet(string path, string password) {
+ MONERO_WalletManager_createWallet(wmPtr, path, password, "English", 0);
+ wPtr = MONERO_WalletManager_openWallet(wmPtr, path, password, 0);
+ return MONERO_Wallet_status(wPtr) == 0;
+ }
+
+ public static void initWallet(string daemonAddress, bool useSsl, String proxyString) {
+ //MONERO_Wallet_init(string daemon_password, bool use_ssl, bool lightWallet, string proxy_address);
+ MONERO_Wallet_init(wPtr, daemonAddress, 0, "", "", useSsl, false, proxyString);
+ GD.Print(lastError());
+ MONERO_Wallet_init3(wPtr, "", "", "/dev/shm/godot_moneroc.log", false);
+ GD.Print(lastError());
+ MONERO_Wallet_setTrustedDaemon(wPtr, true);
+ GD.Print(lastError());
+ MONERO_Wallet_refreshAsync(wPtr);
+ GD.Print(lastError());
+ MONERO_Wallet_startRefresh(wPtr);
+ GD.Print(lastError());
+ }
+
+ public static void storeWallet() {
+ MONERO_Wallet_store(wPtr);
+ }
+
+ public static string lastError() {
+ IntPtr resultPtr = MONERO_Wallet_errorString(wPtr);
+ string result = Marshal.PtrToStringAnsi(resultPtr);
+ return result;
+ }
+
+ public static int lastErrorCode() {
+ return MONERO_Wallet_status(wPtr);
+ }
+
+ public static string lastTxError() {
+ IntPtr resultPtr = MONERO_PendingTransaction_errorString(pendingTx);
+ string result = Marshal.PtrToStringAnsi(resultPtr);
+ return result;
+ }
+
+ public static int lastTxErrorCode() {
+ return MONERO_PendingTransaction_status(pendingTx);
+ }
+
+ public static string getAddress(ulong accountIndex, ulong addressIndex) {
+ IntPtr resultPtr = MONERO_Wallet_address(wPtr, accountIndex, addressIndex);
+ string result = Marshal.PtrToStringAnsi(resultPtr);
+ return result;
+ }
+
+ public static ulong getBalance(uint accountIndex) {
+ return MONERO_Wallet_balance(wPtr, accountIndex);
+ }
+
+ public static void createTransaction(string address, ulong amount) {
+ pendingTx = MONERO_Wallet_createTransaction(wPtr, address, "", amount, 0, 0, 0, "", "");
+ MONERO_PendingTransaction_commit(pendingTx, "", false);
+ }
+
+ public static int getTransactionCount() {
+ txHistory = MONERO_Wallet_history(wPtr);
+ MONERO_TransactionHistory_refresh(txHistory);
+ return MONERO_TransactionHistory_count(txHistory);
+ }
+
+ public static int getTransactionDirection(int index) {
+ txHistory = MONERO_Wallet_history(wPtr);
+ IntPtr txPtr = MONERO_TransactionHistory_transaction(txHistory, index);
+ return MONERO_TransactionInfo_direction(txPtr);
+ }
+
+ public static ulong getTransactionAmount(int index) {
+ txHistory = MONERO_Wallet_history(wPtr);
+ IntPtr txPtr = MONERO_TransactionHistory_transaction(txHistory, index);
+ return MONERO_TransactionInfo_amount(txPtr);
+ }
+
+ public static ulong getTransactionTimestamp(int index) {
+ txHistory = MONERO_Wallet_history(wPtr);
+ IntPtr txPtr = MONERO_TransactionHistory_transaction(txHistory, index);
+ return MONERO_TransactionInfo_timestamp(txPtr);
+ }
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern ulong MONERO_TransactionInfo_timestamp(IntPtr txPtr);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern IntPtr MONERO_TransactionHistory_transaction(IntPtr txHistory, int index);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern int MONERO_TransactionInfo_direction(IntPtr txPtr);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern int MONERO_TransactionHistory_count(IntPtr txHistory);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern ulong MONERO_TransactionInfo_amount(IntPtr txPtr);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern IntPtr MONERO_Wallet_history(IntPtr wm_ptr);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern IntPtr MONERO_TransactionHistory_refresh(IntPtr txHistory);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern IntPtr MONERO_WalletManager_createWallet(IntPtr wm_ptr, string path, string password, string language, int networkType);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern IntPtr MONERO_WalletManager_openWallet(IntPtr wm_ptr, string path, string password, int networkType);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern IntPtr MONERO_WalletManagerFactory_getWalletManager();
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern int MONERO_Wallet_status(IntPtr wPtr);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern int MONERO_PendingTransaction_status(IntPtr wPtr);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern bool MONERO_WalletManager_walletExists(IntPtr wmPtr, string path);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern IntPtr MONERO_Wallet_errorString(IntPtr wPtr);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern IntPtr MONERO_PendingTransaction_errorString(IntPtr pendingTx);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern ulong MONERO_Wallet_balance(IntPtr wPtr, uint accountIndex);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern IntPtr MONERO_Wallet_address(IntPtr wPtr, ulong accountIndex, ulong addressIndex);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern IntPtr MONERO_Wallet_createTransaction(IntPtr wallet_ptr, string dst_addr, string payment_id,
+ ulong amount, uint mixin_count,
+ int pendingTransactionPriority,
+ uint subaddr_account,
+ string preferredInputs, string separator);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern bool MONERO_PendingTransaction_commit(IntPtr pendingTx_ptr, string filename, bool overwrite);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern bool MONERO_Wallet_init(IntPtr wallet_ptr, string daemon_address, ulong upper_transaction_size_limit, string daemon_username, string daemon_password, bool use_ssl, bool lightWallet, string proxy_address);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern bool MONERO_Wallet_init3(IntPtr wallet_ptr, string argv0, string default_log_base_name, string log_path, bool console);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern void MONERO_Wallet_setTrustedDaemon(IntPtr wallet_ptr, bool arg);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern void MONERO_Wallet_startRefresh(IntPtr wallet_ptr);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern void MONERO_Wallet_refreshAsync(IntPtr wallet_ptr);
+
+ [DllImport("/usr/lib/monero_libwallet2_api_c.so")]
+ public static extern bool MONERO_Wallet_store(IntPtr wallet_ptr);
+}
diff --git a/impls/monero.dart/lib/monero.dart b/impls/monero.dart/lib/monero.dart
index 8a0eea4..7b6eac3 100644
--- a/impls/monero.dart/lib/monero.dart
+++ b/impls/monero.dart/lib/monero.dart
@@ -101,14 +101,36 @@ final Stopwatch sw = Stopwatch()..start();
bool printStarts = false;
void Function(String call)? debugStart = (call) {
- if (printStarts) print("MONERO: $call");
- debugCallLength[call] ??= <int>[];
- debugCallLength[call]!.add(sw.elapsedMicroseconds);
+ try {
+ if (printStarts) print("MONERO: $call");
+ debugCallLength[call] ??= <int>[];
+ debugCallLength[call]!.add(sw.elapsedMicroseconds);
+ } catch (e) {}
};
+void debugChores() {
+ for (var key in debugCallLength.keys) {
+ if (debugCallLength[key]!.length > 1000000) {
+ final elm =
+ debugCallLength[key]!.reduce((value, element) => value + element);
+ debugCallLength[key]!.clear();
+ debugCallLength["${key}_1M"] ??= <int>[];
+ debugCallLength["${key}_1M"]!.add(elm);
+ }
+ }
+}
+
+int debugCount = 0;
+
void Function(String call)? debugEnd = (call) {
- final id = debugCallLength[call]!.length - 1;
- debugCallLength[call]![id] =
- sw.elapsedMicroseconds - debugCallLength[call]![id];
+ try {
+ final id = debugCallLength[call]!.length - 1;
+ if (++debugCount > 1000000) {
+ debugCount = 0;
+ debugChores();
+ }
+ debugCallLength[call]![id] =
+ sw.elapsedMicroseconds - debugCallLength[call]![id];
+ } catch (e) {}
};
void Function(String call, dynamic error)? errorHandler = (call, error) {
print("$call: $error");
@@ -3586,11 +3608,11 @@ bool WalletManager_verifyWalletPassword(
}
int WalletManager_queryWalletDevice(
- WalletManager wm_ptr, {
- required String keysFileName,
- required String password,
- required int kdfRounds,
-}) {
+ WalletManager wm_ptr, {
+ required String keysFileName,
+ required String password,
+ required int kdfRounds,
+ }) {
debugStart?.call('MONERO_WalletManager_queryWalletDevice');
lib ??= MoneroC(DynamicLibrary.open(libPath));
final keysFileName_ = keysFileName.toNativeUtf8().cast<Char>();
@@ -3603,7 +3625,8 @@ int WalletManager_queryWalletDevice(
return s;
}
-String WalletManager_findWallets(WalletManager wm_ptr, {required String path}) {
+List<String> WalletManager_findWallets(WalletManager wm_ptr,
+ {required String path}) {
debugStart?.call('MONERO_WalletManager_findWallets');
lib ??= MoneroC(DynamicLibrary.open(libPath));
try {
@@ -3613,13 +3636,15 @@ String WalletManager_findWallets(WalletManager wm_ptr, {required String path}) {
.cast<Utf8>();
final str = strPtr.toDartString();
calloc.free(path_);
- MONERO_free(strPtr.cast());
+ if (str.isNotEmpty) {
+ MONERO_free(strPtr.cast());
+ }
debugEnd?.call('MONERO_WalletManager_findWallets');
- return str;
+ return str.split(";");
} catch (e) {
errorHandler?.call('MONERO_WalletManager_findWallets', e);
debugEnd?.call('MONERO_WalletManager_findWallets');
- return "";
+ return [];
}
}
diff --git a/impls/monero.dart/lib/src/checksum_monero.dart b/impls/monero.dart/lib/src/checksum_monero.dart
index 9e553f9..4ab72a3 100644
--- a/impls/monero.dart/lib/src/checksum_monero.dart
+++ b/impls/monero.dart/lib/src/checksum_monero.dart
@@ -1,4 +1,4 @@
// ignore_for_file: constant_identifier_names
const String wallet2_api_c_h_sha256 = "e8db0ef0324a153f5e3ecca4c0db23c54f4576e84988f04bd4f11c1142f9d7ad";
-const String wallet2_api_c_cpp_sha256 = "d1842cded0040c16b8886878681c8938005f69ec1378fa9be68a430311cc3666";
+const String wallet2_api_c_cpp_sha256 = "dca52ac9ee009fda9fb5726543a454885e61d8eb74fb33112288029ed625bec5-b089f9ee69924882c5d14dd1a6991deb05d9d1cd";
const String wallet2_api_c_exp_sha256 = "c8913ac41068f67b57c9b0a3c7dd8973e3c1273b66c2ff0aadb0003931da748c";
diff --git a/impls/monero.dart/lib/src/checksum_wownero.dart b/impls/monero.dart/lib/src/checksum_wownero.dart
index f4ed1e7..7d501c6 100644
--- a/impls/monero.dart/lib/src/checksum_wownero.dart
+++ b/impls/monero.dart/lib/src/checksum_wownero.dart
@@ -1,4 +1,4 @@
// ignore_for_file: constant_identifier_names
const String wallet2_api_c_h_sha256 = "8a8d386dd5d996c89a0586c55b295ef95ca584bf1ffa26255152b291910a0a77";
-const String wallet2_api_c_cpp_sha256 = "ed400bd9c4709383ffd42a9fbe68be37a2a47a42f92eacaf3a2dbd248c422739";
+const String wallet2_api_c_cpp_sha256 = "07d67f34a07869aaa4af6ca04e142dbad2fb1fba0e2ebdefd22bc333fd982e25-e25963cbc11ca0a0fe5eb34b9bd7c72e4f51b795";
const String wallet2_api_c_exp_sha256 = "3673e40e1a7115552276d1d541f6e4d5a0fef47c40fff7b988f49923af84c8a4";
diff --git a/impls/monero.dart/lib/wownero.dart b/impls/monero.dart/lib/wownero.dart
index 5a0a288..d355005 100644
--- a/impls/monero.dart/lib/wownero.dart
+++ b/impls/monero.dart/lib/wownero.dart
@@ -101,14 +101,36 @@ final Stopwatch sw = Stopwatch()..start();
bool printStarts = false;
void Function(String call)? debugStart = (call) {
- if (printStarts) print("MONERO: $call");
- debugCallLength[call] ??= <int>[];
- debugCallLength[call]!.add(sw.elapsedMicroseconds);
+ try {
+ if (printStarts) print("MONERO: $call");
+ debugCallLength[call] ??= <int>[];
+ debugCallLength[call]!.add(sw.elapsedMicroseconds);
+ } catch (e) {}
};
+void debugChores() {
+ for (var key in debugCallLength.keys) {
+ if (debugCallLength[key]!.length > 1000000) {
+ final elm =
+ debugCallLength[key]!.reduce((value, element) => value + element);
+ debugCallLength[key]!.clear();
+ debugCallLength["${key}_1M"] ??= <int>[];
+ debugCallLength["${key}_1M"]!.add(elm);
+ }
+ }
+}
+
+int debugCount = 0;
+
void Function(String call)? debugEnd = (call) {
- final id = debugCallLength[call]!.length - 1;
- debugCallLength[call]![id] =
- sw.elapsedMicroseconds - debugCallLength[call]![id];
+ try {
+ final id = debugCallLength[call]!.length - 1;
+ if (++debugCount > 1000000) {
+ debugCount = 0;
+ debugChores();
+ }
+ debugCallLength[call]![id] =
+ sw.elapsedMicroseconds - debugCallLength[call]![id];
+ } catch (e) {}
};
void Function(String call, dynamic error)? errorHandler = (call, error) {
print("$call: $error");
@@ -3220,7 +3242,8 @@ bool WalletManager_verifyWalletPassword(
return s;
}
-String WalletManager_findWallets(WalletManager wm_ptr, {required String path}) {
+List<String> WalletManager_findWallets(WalletManager wm_ptr,
+ {required String path}) {
debugStart?.call('WOWNERO_WalletManager_findWallets');
lib ??= WowneroC(DynamicLibrary.open(libPath));
try {
@@ -3230,13 +3253,15 @@ String WalletManager_findWallets(WalletManager wm_ptr, {required String path}) {
.cast<Utf8>();
final str = strPtr.toDartString();
calloc.free(path_);
- WOWNERO_free(strPtr.cast());
+ if (str.isNotEmpty) {
+ WOWNERO_free(strPtr.cast());
+ }
debugEnd?.call('WOWNERO_WalletManager_findWallets');
- return str;
+ return str.split(";");
} catch (e) {
errorHandler?.call('WOWNERO_WalletManager_findWallets', e);
debugEnd?.call('WOWNERO_WalletManager_findWallets');
- return "";
+ return [];
}
}
@@ -3606,7 +3631,6 @@ int WOWNERO_deprecated_14WordSeedHeight({
return s;
}
-
String WOWNERO_checksum_wallet2_api_c_h() {
debugStart?.call('WOWNERO_checksum_wallet2_api_c_h');
lib ??= WowneroC(DynamicLibrary.open(libPath));
diff --git a/impls/monero.dart/pubspec.yaml b/impls/monero.dart/pubspec.yaml
index 67dc577..577dcbe 100644
--- a/impls/monero.dart/pubspec.yaml
+++ b/impls/monero.dart/pubspec.yaml
@@ -11,6 +11,6 @@ dependencies:
ledger_flutter_plus: ^1.2.5
dev_dependencies:
- lints: ^4.0.0
+ lints: ^5.0.0
test: ^1.24.0
- ffigen: ^13.0.0
+ ffigen: ^14.0.0
diff --git a/impls/monero.ts/.gitignore b/impls/monero.ts/.gitignore
new file mode 100644
index 0000000..8b61d3b
--- /dev/null
+++ b/impls/monero.ts/.gitignore
@@ -0,0 +1,2 @@
+*_libwallet2_api_c.*
+lib
diff --git a/impls/monero.ts/README.md b/impls/monero.ts/README.md
new file mode 100644
index 0000000..e3b20f6
--- /dev/null
+++ b/impls/monero.ts/README.md
@@ -0,0 +1,42 @@
+# monero.ts
+
+`monero_c` bindings for Deno.
+
+## Usage
+
+This library does not ship with `monero_c` libraries.\
+To use these bindings you have to bring your own `monero_c` libraries.\
+There are at least two ways to do so:
+- Ahead-of-time, during builds where you only ship necessary library for a given platform.\
+ See [monero-tui](https://github.com/Im-Beast/monero-tui/blob/main/.github/workflows/dev-build.yml) build workflow as an example of doing so.
+ ```ts
+ import { loadDylib, Wallet, WalletManager } from "https://raw.githubusercontent.com/MrCyjaneK/monero_c/master/impls/monero.ts/mod.ts";
+
+ // Try to load dylib from the default lib/* path
+ loadDylib();
+
+ const wm = await WalletManager.new();
+ const wallet = await Wallet.create(wm, "./my_wallet", "password");
+
+ console.log(await wallet.address());
+
+ await wallet.store();
+ ```
+- Just-in-time, where you download and cache the library at runtime.\
+ You can use something like [plug](https://jsr.io/@denosaurs/plug) to achieve the result.
+ ```ts
+ import { dlopen } from "jsr:@denosaurs/plug";
+ // It's recommened to put the monero.ts github link into your import_map to reduce the url clutter
+ import { loadDylib, symbols, Wallet, WalletManager } from "https://raw.githubusercontent.com/MrCyjaneK/monero_c/master/impls/monero.ts/mod.ts";
+
+ // Load dylib loaded by plug
+ const lib = await dlopen(..., symbols);
+ loadDylib(lib);
+
+ const wm = await WalletManager.new();
+ const wallet = await Wallet.create(wm, "./my_wallet", "password");
+
+ console.log(await wallet.address());
+
+ await wallet.store();
+ ```
diff --git a/impls/monero.ts/checksum.ts b/impls/monero.ts/checksum.ts
new file mode 100644
index 0000000..22d3038
--- /dev/null
+++ b/impls/monero.ts/checksum.ts
@@ -0,0 +1,65 @@
+import { moneroChecksum } from "./checksum_monero.ts";
+import { readCString } from "./src/utils.ts";
+import { dylib, loadDylib } from "./src/bindings.ts";
+
+loadDylib();
+
+export class ChecksumError extends Error {
+ readonly code: number;
+ readonly errors: string[];
+
+ constructor(code: number, errors: string[]) {
+ super("MoneroC binding checksum failed:\n" + errors.join("\n"));
+ this.code = code;
+ this.errors = errors;
+ }
+}
+
+/**
+ * Validates MoneroC checksums
+ * @returns {null} if checksums are correct
+ * @returns {ChecksumError} which contains information about why checksum failed
+ */
+export async function validateChecksum(): Promise<ChecksumError | null> {
+ const cppHeaderHash = await readCString(await dylib.symbols.MONERO_checksum_wallet2_api_c_h(), false);
+ const tsHeaderHash = moneroChecksum.wallet2_api_c_h_sha256;
+
+ const errors: string[] = [];
+
+ let errorCode = 0;
+ if (cppHeaderHash !== tsHeaderHash) {
+ errors.push("ERR: Header file check mismatch");
+ errorCode++;
+ }
+
+ const cppSourceHash = await readCString(await dylib.symbols.MONERO_checksum_wallet2_api_c_cpp(), false);
+ const tsSourceHash = moneroChecksum.wallet2_api_c_cpp_sha256;
+ if (cppSourceHash !== tsSourceHash) {
+ errors.push(`ERR: CPP source file check mismatch ${cppSourceHash} == ${tsSourceHash}`);
+ errorCode++;
+ }
+
+ const cppExportHash = await readCString(await dylib.symbols.MONERO_checksum_wallet2_api_c_exp(), false);
+ const tsExportHash = moneroChecksum.wallet2_api_c_exp_sha256;
+ if (cppExportHash !== tsExportHash) {
+ if (Deno.build.os !== "darwin") {
+ errors.push("WARN: EXP source file check mismatch");
+ } else {
+ errors.push(`ERR: EXP source file check mismatch ${cppExportHash} == ${tsExportHash}`);
+ }
+ errorCode++;
+ }
+
+ if (errorCode) {
+ return new ChecksumError(errorCode, errors);
+ }
+
+ return null;
+}
+
+if (import.meta.main) {
+ const maybeError = await validateChecksum();
+ if (maybeError) {
+ throw maybeError;
+ }
+}
diff --git a/impls/monero.ts/checksum_monero.ts b/impls/monero.ts/checksum_monero.ts
new file mode 100644
index 0000000..88406a0
--- /dev/null
+++ b/impls/monero.ts/checksum_monero.ts
@@ -0,0 +1,5 @@
+export const moneroChecksum = {
+ wallet2_api_c_h_sha256: "e8db0ef0324a153f5e3ecca4c0db23c54f4576e84988f04bd4f11c1142f9d7ad",
+ wallet2_api_c_cpp_sha256: "dca52ac9ee009fda9fb5726543a454885e61d8eb74fb33112288029ed625bec5-b089f9ee69924882c5d14dd1a6991deb05d9d1cd",
+ wallet2_api_c_exp_sha256: "c8913ac41068f67b57c9b0a3c7dd8973e3c1273b66c2ff0aadb0003931da748c",
+}
diff --git a/impls/monero.ts/checksum_wownero.ts b/impls/monero.ts/checksum_wownero.ts
new file mode 100644
index 0000000..8b2899c
--- /dev/null
+++ b/impls/monero.ts/checksum_wownero.ts
@@ -0,0 +1,5 @@
+export const wowneroChecksum = {
+ wallet2_api_c_h_sha256: "8a8d386dd5d996c89a0586c55b295ef95ca584bf1ffa26255152b291910a0a77",
+ wallet2_api_c_cpp_sha256: "07d67f34a07869aaa4af6ca04e142dbad2fb1fba0e2ebdefd22bc333fd982e25-e25963cbc11ca0a0fe5eb34b9bd7c72e4f51b795",
+ wallet2_api_c_exp_sha256: "3673e40e1a7115552276d1d541f6e4d5a0fef47c40fff7b988f49923af84c8a4",
+}
diff --git a/impls/monero.ts/deno.jsonc b/impls/monero.ts/deno.jsonc
new file mode 100644
index 0000000..a7b75ec
--- /dev/null
+++ b/impls/monero.ts/deno.jsonc
@@ -0,0 +1,5 @@
+{
+ "fmt": {
+ "lineWidth": 120
+ }
+}
diff --git a/impls/monero.ts/mod.ts b/impls/monero.ts/mod.ts
new file mode 100644
index 0000000..1eca773
--- /dev/null
+++ b/impls/monero.ts/mod.ts
@@ -0,0 +1,6 @@
+export * from "./src/bindings.ts";
+export * from "./src/pending_transaction.ts";
+export * from "./src/transaction_history.ts";
+export * from "./src/transaction_info.ts";
+export * from "./src/wallet.ts";
+export * from "./src/wallet_manager.ts";
diff --git a/impls/monero.ts/src/bindings.ts b/impls/monero.ts/src/bindings.ts
new file mode 100644
index 0000000..b854f7d
--- /dev/null
+++ b/impls/monero.ts/src/bindings.ts
@@ -0,0 +1,562 @@
+export const symbols = {
+ "MONERO_WalletManagerFactory_getWalletManager": {
+ nonblocking: true,
+ parameters: [],
+ // void*
+ result: "pointer",
+ },
+
+ //#region WalletManager
+ "MONERO_WalletManager_createWallet": {
+ nonblocking: true,
+ // void* wm_ptr, const char* path, const char* password, const char* language, int networkType
+ parameters: ["pointer", "pointer", "pointer", "pointer", "i32"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_WalletManager_openWallet": {
+ nonblocking: true,
+ // void* wm_ptr, const char* path, const char* password, int networkType
+ "parameters": ["pointer", "pointer", "pointer", "i32"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_WalletManager_recoveryWallet": {
+ nonblocking: true,
+ // void* wm_ptr, const char* path, const char* password, const char* mnemonic,
+ // int networkType, uint64_t restoreHeight, uint64_t kdfRounds, const char* seedOffset
+ parameters: ["pointer", "pointer", "pointer", "pointer", "i32", "u64", "u64", "pointer"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_WalletManager_blockchainHeight": {
+ nonblocking: true,
+ // void* wm_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_WalletManager_blockchainTargetHeight": {
+ nonblocking: true,
+ // void* wm_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_WalletManager_setDaemonAddress": {
+ nonblocking: true,
+ // void* wm_ptr, const char* address
+ parameters: ["pointer", "pointer"],
+ // void
+ result: "void",
+ },
+ //#endregion
+
+ //#region Wallet
+ "MONERO_Wallet_init": {
+ nonblocking: true,
+ // void* wallet_ptr, const char* daemon_address, uint64_t upper_transaction_size_limit,
+ // const char* daemon_username, const char* daemon_password, bool use_ssl, bool lightWallet,
+ // const char* proxy_address
+ parameters: ["pointer", "pointer", "u64", "pointer", "pointer", "bool", "bool", "pointer"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_Wallet_init3": {
+ nonblocking: true,
+ // void* wallet_ptr, const char* argv0, const char* default_log_base_name,
+ // const char* log_path, bool console
+ parameters: ["pointer", "pointer", "pointer", "pointer", "bool"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_setTrustedDaemon": {
+ nonblocking: true,
+ // void* wallet_ptr, bool arg
+ parameters: ["pointer", "bool"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_startRefresh": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_refreshAsync": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_blockChainHeight": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_Wallet_daemonBlockChainHeight": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_Wallet_synchronized": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_Wallet_store": {
+ nonblocking: true,
+ // void* wallet_ptr, const char* path
+ parameters: ["pointer", "pointer"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_Wallet_address": {
+ nonblocking: true,
+ // void* wallet_ptr, uint64_t accountIndex, uint64_t addressIndex
+ parameters: ["pointer", "u64", "u64"],
+ // char*
+ result: "pointer",
+ },
+ "MONERO_Wallet_balance": {
+ nonblocking: true,
+ // void* wallet_ptr, uint32_t accountIndex
+ parameters: ["pointer", "u32"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_Wallet_unlockedBalance": {
+ nonblocking: true,
+ // void* wallet_ptr, uint32_t accountIndex
+ parameters: ["pointer", "u32"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_Wallet_addSubaddressAccount": {
+ nonblocking: true,
+ // void* wallet_ptr, const char* label
+ parameters: ["pointer", "pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_numSubaddressAccounts": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // size_t
+ result: "usize",
+ },
+ "MONERO_Wallet_addSubaddress": {
+ nonblocking: true,
+ // void* wallet_ptr, uint32_t accountIndex, const char* label
+ parameters: ["pointer", "u32", "pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_numSubaddresses": {
+ nonblocking: true,
+ // void* wallet_ptr, uint32_t accountIndex
+ parameters: ["pointer", "u32"],
+ // size_t
+ result: "usize",
+ },
+ "MONERO_Wallet_getSubaddressLabel": {
+ nonblocking: true,
+ // void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex
+ parameters: ["pointer", "u32", "u32"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_Wallet_setSubaddressLabel": {
+ nonblocking: true,
+ // void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex, const char* label
+ parameters: ["pointer", "u32", "u32", "pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_Wallet_status": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // int
+ result: "i32",
+ },
+ "MONERO_Wallet_errorString": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // char*
+ result: "pointer",
+ },
+ "MONERO_Wallet_history": {
+ nonblocking: true,
+ // void* wallet_ptr
+ parameters: ["pointer"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_Wallet_createTransaction": {
+ nonblocking: true,
+ // void* wallet_ptr, const char* dst_addr, const char* payment_id
+ // uint64_t amount, uint32_t mixin_count, int pendingTransactionPriority,
+ // uint32_t subaddr_account, const char* preferredInputs, const char* separator
+ parameters: ["pointer", "pointer", "pointer", "u64", "u32", "i32", "u32", "pointer", "pointer"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_Wallet_amountFromString": {
+ nonblocking: true,
+ // const char* amount
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ //#endregion
+
+ //#region TransactionHistory
+ "MONERO_TransactionHistory_count": {
+ nonblocking: true,
+ // void* txHistory_ptr
+ parameters: ["pointer"],
+ // int
+ result: "i32",
+ },
+ "MONERO_TransactionHistory_transaction": {
+ nonblocking: true,
+ // void* txHistory_ptr, int index
+ parameters: ["pointer", "i32"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_TransactionHistory_transactionById": {
+ nonblocking: true,
+ // void* txHistory_ptr, const char* id
+ parameters: ["pointer", "pointer"],
+ // void*
+ result: "pointer",
+ },
+ "MONERO_TransactionHistory_refresh": {
+ nonblocking: true,
+ // void* txHistory_ptr
+ parameters: ["pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_TransactionHistory_setTxNote": {
+ nonblocking: true,
+ // void* txHistory_ptr, const char* txid, const char* note
+ parameters: ["pointer", "pointer", "pointer"],
+ // void
+ result: "void",
+ },
+ //#endregion
+
+ //#region TransactionInfo
+ "MONERO_TransactionInfo_direction": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // int
+ result: "i32",
+ },
+ "MONERO_TransactionInfo_isPending": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_TransactionInfo_isFailed": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_TransactionInfo_isCoinbase": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_TransactionInfo_amount": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_fee": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_blockHeight": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_description": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_TransactionInfo_subaddrIndex": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_TransactionInfo_subaddrAccount": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint32_t
+ result: "u32",
+ },
+ "MONERO_TransactionInfo_label": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_TransactionInfo_confirmations": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_unlockTime": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_hash": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_TransactionInfo_timestamp": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_paymentId": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_TransactionInfo_transfers_count": {
+ nonblocking: true,
+ // void* txInfo_ptr
+ parameters: ["pointer"],
+ // int
+ result: "i32",
+ },
+ "MONERO_TransactionInfo_transfers_amount": {
+ nonblocking: true,
+ // void* txInfo_ptr, int index
+ parameters: ["pointer", "i32"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_TransactionInfo_transfers_address": {
+ nonblocking: true,
+ // void* txInfo_ptr, int index
+ parameters: ["pointer", "i32"],
+ // const char*
+ result: "pointer",
+ },
+ //#endregion
+
+ //#region PendingTransaction
+ "MONERO_PendingTransaction_status": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // int
+ result: "i32",
+ },
+ "MONERO_PendingTransaction_errorString": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_commit": {
+ nonblocking: true,
+ // void* pendingTx_ptr, const char* filename, bool overwrite
+ parameters: ["pointer", "pointer", "bool"],
+ // bool
+ result: "bool",
+ },
+ "MONERO_PendingTransaction_commitUR": {
+ nonblocking: true,
+ // void* pendingTx_ptr, int max_fragment_length
+ parameters: ["pointer", "i32"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_amount": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_PendingTransaction_dust": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_PendingTransaction_fee": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_PendingTransaction_txid": {
+ nonblocking: true,
+ // void* pendingTx_ptr, const char* separator
+ parameters: ["pointer", "pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_txCount": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // uint64_t
+ result: "u64",
+ },
+ "MONERO_PendingTransaction_subaddrAccount": {
+ nonblocking: true,
+ // void* pendingTx_ptr, const char* separator
+ parameters: ["pointer", "pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_subaddrIndices": {
+ nonblocking: true,
+ // void* pendingTx_ptr, const char* separator
+ parameters: ["pointer", "pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_multisigSignData": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_signMultisigTx": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // void
+ result: "void",
+ },
+ "MONERO_PendingTransaction_signersKeys": {
+ nonblocking: true,
+ // void* pendingTx_ptr
+ parameters: ["pointer"],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_PendingTransaction_hex": {
+ nonblocking: true,
+ // void* pendingTx_ptr, const char* separator
+ parameters: ["pointer", "pointer"],
+ // const char*
+ result: "pointer",
+ },
+ //#endregion
+
+ //#region Checksum
+ "MONERO_checksum_wallet2_api_c_h": {
+ nonblocking: true,
+ parameters: [],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_checksum_wallet2_api_c_cpp": {
+ nonblocking: true,
+ parameters: [],
+ // const char*
+ result: "pointer",
+ },
+ "MONERO_checksum_wallet2_api_c_exp": {
+ nonblocking: true,
+ parameters: [],
+ // const char*
+ result: "pointer",
+ },
+ //#endregion
+
+ "MONERO_free": {
+ nonblocking: true,
+ // void* ptr
+ parameters: ["pointer"],
+ // void
+ result: "void",
+ },
+} as const;
+
+type MoneroTsDylib = Deno.DynamicLibrary<typeof symbols>;
+
+export let dylib: MoneroTsDylib;
+export function loadDylib(newDylib?: MoneroTsDylib) {
+ if (newDylib) {
+ dylib = newDylib;
+ return;
+ }
+
+ let libPath: string;
+ switch (Deno.build.os) {
+ case "darwin":
+ libPath = "./lib/monero_libwallet2_api_c.dylib";
+ break;
+ case "android":
+ libPath = "./lib/libmonero_libwallet2_api_c.so";
+ break;
+ case "windows":
+ libPath = "./lib/monero_libwallet2_api_c.dll";
+ break;
+ default:
+ libPath = "./lib/monero_libwallet2_api_c.so";
+ break;
+ }
+
+ dylib = Deno.dlopen(libPath, symbols);
+}
diff --git a/impls/monero.ts/src/pending_transaction.ts b/impls/monero.ts/src/pending_transaction.ts
new file mode 100644
index 0000000..cf48721
--- /dev/null
+++ b/impls/monero.ts/src/pending_transaction.ts
@@ -0,0 +1,81 @@
+import { dylib } from "./bindings.ts";
+import { CString, readCString, type Sanitizer } from "./utils.ts";
+
+export type PendingTransactionPtr = Deno.PointerObject<"transactionInfo">;
+
+export class PendingTransaction {
+ #pendingTxPtr: PendingTransactionPtr;
+ sanitizer?: Sanitizer;
+
+ constructor(pendingTxPtr: PendingTransactionPtr, sanitizer?: Sanitizer) {
+ this.sanitizer = sanitizer;
+ this.#pendingTxPtr = pendingTxPtr;
+ }
+
+ async status(): Promise<number> {
+ return await dylib.symbols.MONERO_PendingTransaction_status(this.#pendingTxPtr);
+ }
+
+ async errorString(): Promise<string | null> {
+ if (!await this.status()) return null;
+
+ const error = await dylib.symbols.MONERO_PendingTransaction_errorString(this.#pendingTxPtr);
+ if (!error) return null;
+
+ return await readCString(error) || null;
+ }
+
+ async throwIfError(sanitize = true): Promise<void> {
+ const maybeError = await this.errorString();
+ if (maybeError) {
+ if (sanitize) this.sanitizer?.();
+ throw new Error(maybeError);
+ }
+ }
+
+ async commit(fileName: string, overwrite: boolean, sanitize = true): Promise<boolean> {
+ const bool = await dylib.symbols.MONERO_PendingTransaction_commit(
+ this.#pendingTxPtr,
+ CString(fileName),
+ overwrite,
+ );
+ await this.throwIfError(sanitize);
+ return bool;
+ }
+
+ async commitUR(maxFragmentLength: number): Promise<string | null> {
+ const result = await dylib.symbols.MONERO_PendingTransaction_commitUR(
+ this.#pendingTxPtr,
+ maxFragmentLength,
+ );
+ if (!result) return null;
+ await this.throwIfError();
+ return await readCString(result) || null;
+ }
+
+ async amount(): Promise<bigint> {
+ return await dylib.symbols.MONERO_PendingTransaction_amount(this.#pendingTxPtr);
+ }
+
+ async dust(): Promise<bigint> {
+ return await dylib.symbols.MONERO_PendingTransaction_dust(this.#pendingTxPtr);
+ }
+
+ async fee(): Promise<bigint> {
+ return await dylib.symbols.MONERO_PendingTransaction_fee(this.#pendingTxPtr);
+ }
+
+ async txid(separator: string, sanitize = true): Promise<string | null> {
+ const result = await dylib.symbols.MONERO_PendingTransaction_txid(
+ this.#pendingTxPtr,
+ CString(separator),
+ );
+ if (!result) return null;
+ await this.throwIfError(sanitize);
+ return await readCString(result) || null;
+ }
+
+ async txCount(): Promise<bigint> {
+ return await dylib.symbols.MONERO_PendingTransaction_txCount(this.#pendingTxPtr);
+ }
+}
diff --git a/impls/monero.ts/src/transaction_history.ts b/impls/monero.ts/src/transaction_history.ts
new file mode 100644
index 0000000..cc76fc2
--- /dev/null
+++ b/impls/monero.ts/src/transaction_history.ts
@@ -0,0 +1,38 @@
+import { dylib } from "./bindings.ts";
+import { TransactionInfo, TransactionInfoPtr } from "./transaction_info.ts";
+import { CString } from "./utils.ts";
+
+export type TransactionHistoryPtr = Deno.PointerObject<"transactionHistory">;
+
+export class TransactionHistory {
+ #txHistoryPtr: TransactionHistoryPtr;
+
+ constructor(txHistoryPtr: TransactionHistoryPtr) {
+ this.#txHistoryPtr = txHistoryPtr;
+ }
+
+ async count(): Promise<number> {
+ return await dylib.symbols.MONERO_TransactionHistory_count(this.#txHistoryPtr);
+ }
+
+ async transaction(index: number): Promise<TransactionInfo> {
+ return new TransactionInfo(
+ (await dylib.symbols.MONERO_TransactionHistory_transaction(
+ this.#txHistoryPtr,
+ index,
+ )) as TransactionInfoPtr,
+ );
+ }
+
+ async refresh(): Promise<void> {
+ await dylib.symbols.MONERO_TransactionHistory_refresh(this.#txHistoryPtr);
+ }
+
+ async setTxNote(transactionId: string, note: string): Promise<void> {
+ await dylib.symbols.MONERO_TransactionHistory_setTxNote(
+ this.#txHistoryPtr,
+ CString(transactionId),
+ CString(note),
+ );
+ }
+}
diff --git a/impls/monero.ts/src/transaction_info.ts b/impls/monero.ts/src/transaction_info.ts
new file mode 100644
index 0000000..7db45f5
--- /dev/null
+++ b/impls/monero.ts/src/transaction_info.ts
@@ -0,0 +1,104 @@
+import { dylib } from "./bindings.ts";
+import { readCString, Sanitizer } from "./utils.ts";
+
+export type TransactionInfoPtr = Deno.PointerObject<"transactionInfo">;
+
+export class TransactionInfo {
+ #txInfoPtr: TransactionInfoPtr;
+ sanitizer?: Sanitizer;
+
+ constructor(txInfoPtr: TransactionInfoPtr, sanitizer?: Sanitizer) {
+ this.#txInfoPtr = txInfoPtr;
+ this.sanitizer = sanitizer;
+ }
+
+ async direction(): Promise<"in" | "out"> {
+ switch (await dylib.symbols.MONERO_TransactionInfo_direction(this.#txInfoPtr)) {
+ case 0:
+ return "in";
+ case 1:
+ return "out";
+ default:
+ await this.sanitizer?.();
+ throw new Error("Invalid TransactionInfo direction");
+ }
+ }
+
+ async isPending(): Promise<boolean> {
+ return await dylib.symbols.MONERO_TransactionInfo_isPending(this.#txInfoPtr);
+ }
+
+ async isFailed(): Promise<boolean> {
+ return await dylib.symbols.MONERO_TransactionInfo_isFailed(this.#txInfoPtr);
+ }
+
+ async isCoinbase(): Promise<boolean> {
+ return await dylib.symbols.MONERO_TransactionInfo_isCoinbase(this.#txInfoPtr);
+ }
+
+ async amount(): Promise<bigint> {
+ return await dylib.symbols.MONERO_TransactionInfo_amount(this.#txInfoPtr);
+ }
+
+ async fee(): Promise<bigint> {
+ return await dylib.symbols.MONERO_TransactionInfo_fee(this.#txInfoPtr);
+ }
+
+ async blockHeight(): Promise<bigint> {
+ return await dylib.symbols.MONERO_TransactionInfo_blockHeight(this.#txInfoPtr);
+ }
+
+ async description(): Promise<string> {
+ const description = await dylib.symbols.MONERO_TransactionInfo_description(this.#txInfoPtr);
+ return await readCString(description) || "";
+ }
+
+ async subaddrIndex(): Promise<string> {
+ const subaddrIndex = await dylib.symbols.MONERO_TransactionInfo_subaddrIndex(this.#txInfoPtr);
+ return await readCString(subaddrIndex) || "";
+ }
+
+ async subaddrAccount(): Promise<number> {
+ return await dylib.symbols.MONERO_TransactionInfo_subaddrAccount(this.#txInfoPtr);
+ }
+
+ async label(): Promise<string> {
+ const label = await dylib.symbols.MONERO_TransactionInfo_label(this.#txInfoPtr);
+ return await readCString(label) || "";
+ }
+
+ async confirmations(): Promise<bigint> {
+ return await dylib.symbols.MONERO_TransactionInfo_confirmations(this.#txInfoPtr);
+ }
+
+ async unlockTime(): Promise<bigint> {
+ return await dylib.symbols.MONERO_TransactionInfo_unlockTime(this.#txInfoPtr);
+ }
+
+ async hash(): Promise<string> {
+ const hash = await dylib.symbols.MONERO_TransactionInfo_hash(this.#txInfoPtr);
+ return await readCString(hash) || "";
+ }
+
+ async timestamp(): Promise<bigint> {
+ return await dylib.symbols.MONERO_TransactionInfo_timestamp(this.#txInfoPtr);
+ }
+
+ async paymentId(): Promise<string> {
+ const paymentId = await dylib.symbols.MONERO_TransactionInfo_paymentId(this.#txInfoPtr);
+ return await readCString(paymentId) || "";
+ }
+
+ async transfersCount(): Promise<number> {
+ return await dylib.symbols.MONERO_TransactionInfo_transfers_count(this.#txInfoPtr);
+ }
+
+ async transfersAmount(index: number): Promise<bigint> {
+ return await dylib.symbols.MONERO_TransactionInfo_transfers_amount(this.#txInfoPtr, index);
+ }
+
+ async transfersAddress(index: number): Promise<string> {
+ const transfersAddress = await dylib.symbols.MONERO_TransactionInfo_transfers_address(this.#txInfoPtr, index);
+ return await readCString(transfersAddress) || "";
+ }
+}
diff --git a/impls/monero.ts/src/utils.ts b/impls/monero.ts/src/utils.ts
new file mode 100644
index 0000000..6fa640f
--- /dev/null
+++ b/impls/monero.ts/src/utils.ts
@@ -0,0 +1,25 @@
+import { dylib } from "../mod.ts";
+
+export type Sanitizer = () => void | PromiseLike<void>;
+
+const textEncoder = new TextEncoder();
+export function CString(string: string): Deno.PointerValue<string> {
+ return Deno.UnsafePointer.of(textEncoder.encode(`${string}\x00`));
+}
+
+/**
+ * This method reads string from the given pointer and frees the string.
+ *
+ * SAFETY: Do not use readCString twice on the same pointer as it will cause double free\
+ * If you want to read CString without freeing it set the {@linkcode free} parameter to false
+ */
+export async function readCString(pointer: Deno.PointerObject, free?: boolean): Promise<string>;
+export async function readCString(pointer: Deno.PointerValue, free?: boolean): Promise<string | null>;
+export async function readCString(pointer: Deno.PointerValue, free = true): Promise<string | null> {
+ if (!pointer) return null;
+ const string = new Deno.UnsafePointerView(pointer).getCString();
+ if (free) {
+ await dylib.symbols.MONERO_free(pointer);
+ }
+ return string;
+}
diff --git a/impls/monero.ts/src/wallet.ts b/impls/monero.ts/src/wallet.ts
new file mode 100644
index 0000000..07c40ce
--- /dev/null
+++ b/impls/monero.ts/src/wallet.ts
@@ -0,0 +1,304 @@
+import { dylib } from "./bindings.ts";
+import { CString, readCString, Sanitizer } from "./utils.ts";
+import { WalletManager, type WalletManagerPtr } from "./wallet_manager.ts";
+import { TransactionHistory, TransactionHistoryPtr } from "./transaction_history.ts";
+
+import { PendingTransaction } from "./pending_transaction.ts";
+import { PendingTransactionPtr } from "./pending_transaction.ts";
+
+export type WalletPtr = Deno.PointerObject<"walletManager">;
+
+export class Wallet {
+ #walletManagerPtr: WalletManagerPtr;
+ #walletPtr: WalletPtr;
+ sanitizer?: Sanitizer;
+
+ constructor(walletManagerPtr: WalletManager, walletPtr: WalletPtr, sanitizer?: Sanitizer) {
+ this.#walletPtr = walletPtr;
+ this.#walletManagerPtr = walletManagerPtr.getPointer();
+ this.sanitizer = sanitizer;
+ }
+
+ async store(path = ""): Promise<boolean> {
+ const bool = await dylib.symbols.MONERO_Wallet_store(this.#walletPtr, CString(path));
+ await this.throwIfError();
+ return bool;
+ }
+
+ async initWallet(): Promise<void> {
+ await this.init();
+ await this.setTrustedDaemon(true);
+ await this.setDaemonAddress("http://nodex.monerujo.io:18081");
+ await this.startRefresh();
+ await this.refreshAsync();
+ await this.throwIfError();
+ }
+
+ async setDaemonAddress(address: string): Promise<void> {
+ await dylib.symbols.MONERO_WalletManager_setDaemonAddress(
+ this.#walletManagerPtr,
+ CString(address),
+ );
+ }
+
+ async startRefresh(): Promise<void> {
+ await dylib.symbols.MONERO_Wallet_startRefresh(this.#walletPtr);
+ await this.throwIfError();
+ }
+
+ async refreshAsync(): Promise<void> {
+ await dylib.symbols.MONERO_Wallet_refreshAsync(this.#walletPtr);
+ await this.throwIfError();
+ }
+
+ async init(): Promise<boolean> {
+ const bool = await dylib.symbols.MONERO_Wallet_init(
+ this.#walletPtr,
+ CString("http://nodex.monerujo.io:18081"),
+ 0n,
+ CString(""),
+ CString(""),
+ false,
+ false,
+ CString(""),
+ );
+ await this.throwIfError();
+ return bool;
+ }
+
+ async setTrustedDaemon(value: boolean): Promise<void> {
+ await dylib.symbols.MONERO_Wallet_setTrustedDaemon(this.#walletPtr, value);
+ }
+
+ static async create(
+ walletManager: WalletManager,
+ path: string,
+ password: string,
+ sanitizeError = true,
+ ): Promise<Wallet> {
+ // We assign holder of the pointer in Wallet constructor
+ const walletManagerPtr = walletManager.getPointer();
+
+ const walletPtr = await dylib.symbols.MONERO_WalletManager_createWallet(
+ walletManagerPtr,
+ CString(path),
+ CString(password),
+ CString("English"),
+ 0,
+ );
+
+ const wallet = new Wallet(walletManager, walletPtr as WalletPtr, walletManager.sanitizer);
+ await wallet.throwIfError(sanitizeError);
+ await wallet.initWallet();
+
+ return wallet;
+ }
+
+ static async open(
+ walletManager: WalletManager,
+ path: string,
+ password: string,
+ sanitizeError = true,
+ ): Promise<Wallet> {
+ // We assign holder of the pointer in Wallet constructor
+ const walletManagerPtr = walletManager.getPointer();
+
+ const walletPtr = await dylib.symbols.MONERO_WalletManager_openWallet(
+ walletManagerPtr,
+ CString(path),
+ CString(password),
+ 0,
+ );
+
+ const wallet = new Wallet(walletManager, walletPtr as WalletPtr, walletManager.sanitizer);
+ await wallet.throwIfError(sanitizeError);
+ await wallet.initWallet();
+
+ return wallet;
+ }
+
+ static async recover(
+ walletManager: WalletManager,
+ path: string,
+ password: string,
+ mnemonic: string,
+ restoreHeight: bigint,
+ seedOffset: string = "",
+ sanitizeError = true,
+ ): Promise<Wallet> {
+ // We assign holder of the pointer in Wallet constructor
+ const walletManagerPtr = walletManager.getPointer();
+
+ const walletPtr = await dylib.symbols.MONERO_WalletManager_recoveryWallet(
+ walletManagerPtr,
+ CString(path),
+ CString(password),
+ CString(mnemonic),
+ 0,
+ restoreHeight,
+ 1n,
+ CString(seedOffset),
+ );
+
+ const wallet = new Wallet(walletManager, walletPtr as WalletPtr, walletManager.sanitizer);
+ await wallet.throwIfError(sanitizeError);
+ await wallet.initWallet();
+
+ return wallet;
+ }
+
+ async address(accountIndex = 0n, addressIndex = 0n): Promise<string> {
+ const address = await dylib.symbols.MONERO_Wallet_address(this.#walletPtr, accountIndex, addressIndex);
+ if (!address) {
+ const error = await this.errorString();
+ throw new Error(`Failed getting address from a wallet: ${error ?? "<Error unknown>"}`);
+ }
+ return await readCString(address);
+ }
+
+ async balance(accountIndex = 0): Promise<bigint> {
+ return await dylib.symbols.MONERO_Wallet_balance(this.#walletPtr, accountIndex);
+ }
+
+ async unlockedBalance(accountIndex = 0): Promise<bigint> {
+ return await dylib.symbols.MONERO_Wallet_unlockedBalance(this.#walletPtr, accountIndex);
+ }
+
+ status(): Promise<number> {
+ return dylib.symbols.MONERO_Wallet_status(this.#walletPtr);
+ }
+
+ async errorString(): Promise<string | null> {
+ if (!await this.status()) return null;
+
+ const error = await dylib.symbols.MONERO_Wallet_errorString(this.#walletPtr);
+ if (!error) return null;
+
+ return await readCString(error) || null;
+ }
+
+ async throwIfError(sanitize = true): Promise<void> {
+ const maybeError = await this.errorString();
+ if (maybeError) {
+ if (sanitize) this.sanitizer?.();
+ throw new Error(maybeError);
+ }
+ }
+
+ async synchronized(): Promise<boolean> {
+ const synchronized = await dylib.symbols.MONERO_Wallet_synchronized(this.#walletPtr);
+ await this.throwIfError();
+ return synchronized;
+ }
+
+ async blockChainHeight(): Promise<bigint> {
+ const height = await dylib.symbols.MONERO_Wallet_blockChainHeight(this.#walletPtr);
+ await this.throwIfError();
+ return height;
+ }
+
+ async daemonBlockChainHeight(): Promise<bigint> {
+ const height = await dylib.symbols.MONERO_Wallet_daemonBlockChainHeight(this.#walletPtr);
+ await this.throwIfError();
+ return height;
+ }
+
+ async managerBlockChainHeight(): Promise<bigint> {
+ const height = await dylib.symbols.MONERO_WalletManager_blockchainHeight(this.#walletManagerPtr);
+ await this.throwIfError();
+ return height;
+ }
+
+ async managerTargetBlockChainHeight(): Promise<bigint> {
+ const height = await dylib.symbols.MONERO_WalletManager_blockchainTargetHeight(this.#walletManagerPtr);
+ await this.throwIfError();
+ return height;
+ }
+
+ async addSubaddressAccount(label: string): Promise<void> {
+ await dylib.symbols.MONERO_Wallet_addSubaddressAccount(
+ this.#walletPtr,
+ CString(label),
+ );
+ await this.throwIfError();
+ }
+
+ async numSubaddressAccounts(): Promise<bigint> {
+ const accountsLen = await dylib.symbols.MONERO_Wallet_numSubaddressAccounts(this.#walletPtr);
+ await this.throwIfError();
+ return accountsLen;
+ }
+
+ async addSubaddress(accountIndex: number, label: string): Promise<void> {
+ await dylib.symbols.MONERO_Wallet_addSubaddress(
+ this.#walletPtr,
+ accountIndex,
+ CString(label),
+ );
+ await this.throwIfError();
+ }
+
+ async numSubaddresses(accountIndex: number): Promise<bigint> {
+ const address = await dylib.symbols.MONERO_Wallet_numSubaddresses(
+ this.#walletPtr,
+ accountIndex,
+ );
+ await this.throwIfError();
+ return address;
+ }
+
+ async getSubaddressLabel(accountIndex: number, addressIndex: number): Promise<string> {
+ const label = await dylib.symbols.MONERO_Wallet_getSubaddressLabel(this.#walletPtr, accountIndex, addressIndex);
+ if (!label) {
+ const error = await this.errorString();
+ throw new Error(`Failed getting subaddress label from a wallet: ${error ?? "<Error unknown>"}`);
+ }
+ return await readCString(label);
+ }
+
+ async setSubaddressLabel(accountIndex: number, addressIndex: number, label: string): Promise<void> {
+ await dylib.symbols.MONERO_Wallet_setSubaddressLabel(
+ this.#walletPtr,
+ accountIndex,
+ addressIndex,
+ CString(label),
+ );
+ await this.throwIfError();
+ }
+
+ async getHistory(): Promise<TransactionHistory> {
+ const transactionHistoryPointer = await dylib.symbols.MONERO_Wallet_history(this.#walletPtr);
+ await this.throwIfError();
+ return new TransactionHistory(transactionHistoryPointer as TransactionHistoryPtr);
+ }
+
+ async createTransaction(
+ destinationAddress: string,
+ amount: bigint,
+ pendingTransactionPriority = 0 | 1 | 2 | 3,
+ subaddressAccount: number,
+ sanitize = true,
+ prefferedInputs = "",
+ mixinCount = 0,
+ paymentId = "",
+ separator = ",",
+ ): Promise<PendingTransaction> {
+ const pendingTxPtr = await dylib.symbols.MONERO_Wallet_createTransaction(
+ this.#walletPtr,
+ CString(destinationAddress),
+ CString(paymentId),
+ amount,
+ mixinCount,
+ pendingTransactionPriority,
+ subaddressAccount,
+ CString(prefferedInputs),
+ CString(separator),
+ );
+ await this.throwIfError(sanitize);
+ return new PendingTransaction(pendingTxPtr as PendingTransactionPtr);
+ }
+
+ async amountFromString(amount: string): Promise<bigint> {
+ return await dylib.symbols.MONERO_Wallet_amountFromString(CString(amount));
+ }
+}
diff --git a/impls/monero.ts/src/wallet_manager.ts b/impls/monero.ts/src/wallet_manager.ts
new file mode 100644
index 0000000..ad9cf31
--- /dev/null
+++ b/impls/monero.ts/src/wallet_manager.ts
@@ -0,0 +1,27 @@
+import { dylib } from "./bindings.ts";
+import { Sanitizer } from "./utils.ts";
+
+export type WalletManagerPtr = Deno.PointerObject<"walletManager">;
+
+export class WalletManager {
+ #ptr: WalletManagerPtr;
+ sanitizer?: Sanitizer;
+
+ constructor(walletManagerPtr: WalletManagerPtr, sanitizer?: Sanitizer) {
+ this.#ptr = walletManagerPtr;
+ this.sanitizer = sanitizer;
+ }
+
+ getPointer(): WalletManagerPtr {
+ return this.#ptr;
+ }
+
+ static async new(sanitizer?: Sanitizer) {
+ const ptr = await dylib.symbols.MONERO_WalletManagerFactory_getWalletManager();
+ if (!ptr) {
+ sanitizer?.();
+ throw new Error("Failed retrieving wallet manager");
+ }
+ return new WalletManager(ptr as WalletManagerPtr, sanitizer);
+ }
+}