summaryrefslogtreecommitdiff
path: root/impls
diff options
context:
space:
mode:
Diffstat (limited to 'impls')
-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
14 files changed, 1271 insertions, 0 deletions
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);
+ }
+}