feat: deno ffi bindings (#40)
* initial ts commit * monero.ts improvements * test on latest, build on debian:bookworm * feat: upstream changes to bindings * chore: update checksums * feat: allow manually loading dylib * chore: add readme * fix: free strings after being read to prevent potential memory leaks * fix: load dylib * fix: checksum checks segfaulting because of freeing const variables --------- Co-authored-by: Mateusz Franik <47059999+Im-Beast@users.noreply.github.com> Co-authored-by: Im-Beast <franik.mateusz@gmail.com>
This commit is contained in:
@@ -137,7 +137,7 @@ jobs:
|
|||||||
coin: [monero, wownero]
|
coin: [monero, wownero]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: debian:bookworm
|
image: debian:bullseye
|
||||||
steps:
|
steps:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
@@ -698,6 +698,123 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: ${{ matrix.coin }} xmruw apk
|
name: ${{ matrix.coin }} xmruw apk
|
||||||
path: unnamed_monero_wallet/build/app/outputs/flutter-apk/*.apk
|
path: unnamed_monero_wallet/build/app/outputs/flutter-apk/*.apk
|
||||||
|
bulk_lib_release:
|
||||||
|
name: create single release file
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [
|
||||||
|
lib_mingw, lib_android, lib_linux, lib_sailfishos_aarch64, lib_sailfishos_i486, lib_darwin, lib_macos, lib_ios
|
||||||
|
]
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: android monero
|
||||||
|
path: release/monero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: android wownero
|
||||||
|
path: release/wownero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: darwin monero
|
||||||
|
path: release/monero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: darwin wownero
|
||||||
|
path: release/wownero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ios monero
|
||||||
|
path: release/monero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ios wownero
|
||||||
|
path: release/wownero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: linux monero
|
||||||
|
path: release/monero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: linux wownero
|
||||||
|
path: release/wownero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: macos monero
|
||||||
|
path: release/monero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: macos wownero
|
||||||
|
path: release/wownero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: mingw monero
|
||||||
|
path: release/monero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: mingw wownero
|
||||||
|
path: release/wownero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sfos aarch64 monero
|
||||||
|
path: release/monero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sfos aarch64 wownero
|
||||||
|
path: release/wownero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sfos_i486 monero
|
||||||
|
path: release/monero
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sfos_i486 wownero
|
||||||
|
path: release/wownero
|
||||||
|
- name: zip release dir
|
||||||
|
run: zip -r release-bundle.zip release
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
with:
|
||||||
|
files: release-bundle.zip
|
||||||
|
token: ${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||||
|
- name: Upload lib
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-bulk
|
||||||
|
path: release
|
||||||
|
deno_monerots_test_linux:
|
||||||
|
name: test ts library
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
needs: [
|
||||||
|
lib_linux
|
||||||
|
]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: recursive
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: linux monero
|
||||||
|
path: release/monero
|
||||||
|
- name: unpack and move monero_c
|
||||||
|
run: |
|
||||||
|
unxz -f -k release/*/*.xz
|
||||||
|
- uses: denoland/setup-deno@v1
|
||||||
|
with:
|
||||||
|
deno-version: vx.x.x
|
||||||
|
- name: Create symlink
|
||||||
|
run: |
|
||||||
|
cd impls/monero.ts
|
||||||
|
mkdir lib
|
||||||
|
cd lib
|
||||||
|
ln -s ../../../release/monero/x86_64-linux-gnu_libwallet2_api_c.so
|
||||||
|
mv x86_64-linux-gnu_libwallet2_api_c.so monero_libwallet2_api_c.so
|
||||||
|
cd ..
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
cd impls/monero.ts
|
||||||
|
deno run --unstable-ffi --allow-ffi checksum.ts
|
||||||
|
|
||||||
comment_pr:
|
comment_pr:
|
||||||
name: comment on pr
|
name: comment on pr
|
||||||
@@ -752,4 +869,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
issue-number: ${{steps.get_issue_number.outputs.result}}
|
issue-number: ${{steps.get_issue_number.outputs.result}}
|
||||||
body: |
|
body: |
|
||||||
[download artifacts #${{github.run_id}}](https://nightly.link/MrCyjaneK/monero_c/actions/runs/${{github.run_id}}) (this comment will update whenever you push)
|
[download artifacts #${{github.run_id}}](https://nightly.link/MrCyjaneK/monero_c/actions/runs/${{github.run_id}}) (this comment will update whenever you push)
|
||||||
|
|||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"deno.enable": true
|
||||||
|
}
|
||||||
+13
-1
@@ -1,6 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
cd "$(realpath $(dirname $0))"
|
cd "$(realpath $(dirname $0))"
|
||||||
function sha256sum() { shasum -a 256 "$@" ; } && export -f sha256sum
|
|
||||||
|
if [[ "$(uname)" == "Darwin" ]];
|
||||||
|
then
|
||||||
|
function sha256sum() { shasum -a 256 "$@" ; } && export -f sha256sum
|
||||||
|
fi
|
||||||
|
|
||||||
for coin in monero wownero;
|
for coin in monero wownero;
|
||||||
do
|
do
|
||||||
submodule_hash=$(git ls-tree HEAD ${coin} | xargs | awk '{ print $3 }')
|
submodule_hash=$(git ls-tree HEAD ${coin} | xargs | awk '{ print $3 }')
|
||||||
@@ -22,6 +27,13 @@ EOF
|
|||||||
const String wallet2_api_c_h_sha256 = "${COIN_wallet2_api_c_h_sha256}";
|
const String wallet2_api_c_h_sha256 = "${COIN_wallet2_api_c_h_sha256}";
|
||||||
const String wallet2_api_c_cpp_sha256 = "${COIN_wallet2_api_c_cpp_sha256}";
|
const String wallet2_api_c_cpp_sha256 = "${COIN_wallet2_api_c_cpp_sha256}";
|
||||||
const String wallet2_api_c_exp_sha256 = "${COIN_wallet2_api_c_exp_sha256}";
|
const String wallet2_api_c_exp_sha256 = "${COIN_wallet2_api_c_exp_sha256}";
|
||||||
|
EOF
|
||||||
|
cat > impls/monero.ts/checksum_${coin}.ts << EOF
|
||||||
|
export const ${coin}Checksum = {
|
||||||
|
wallet2_api_c_h_sha256: "${COIN_wallet2_api_c_h_sha256}",
|
||||||
|
wallet2_api_c_cpp_sha256: "${COIN_wallet2_api_c_cpp_sha256}",
|
||||||
|
wallet2_api_c_exp_sha256: "${COIN_wallet2_api_c_exp_sha256}",
|
||||||
|
}
|
||||||
EOF
|
EOF
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
*_libwallet2_api_c.*
|
||||||
|
lib
|
||||||
@@ -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();
|
||||||
|
```
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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",
|
||||||
|
}
|
||||||
@@ -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",
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"fmt": {
|
||||||
|
"lineWidth": 120
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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";
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) || "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user