thesimplekid před 1 rokem
rodič
revize
066ee43158
46 změnil soubory, kde provedl 1699 přidání a 9 odebrání
  1. 5 2
      Cargo.toml
  2. 1 0
      bindings/cashu-ffi/.gitignore
  3. 19 0
      bindings/cashu-ffi/Cargo.toml
  4. 16 0
      bindings/cashu-ffi/bindings-python/.gitignore
  5. 2 0
      bindings/cashu-ffi/bindings-python/MANIFEST.in
  6. 1 0
      bindings/cashu-ffi/bindings-python/README.md
  7. 5 0
      bindings/cashu-ffi/bindings-python/examples/test.py
  8. 7 0
      bindings/cashu-ffi/bindings-python/pyproject.toml
  9. 4 0
      bindings/cashu-ffi/bindings-python/requirements.txt
  10. 26 0
      bindings/cashu-ffi/bindings-python/setup.py
  11. 2 0
      bindings/cashu-ffi/bindings-python/src/cashu/__init__.py
  12. 3 0
      bindings/cashu-ffi/build.rs
  13. 67 0
      bindings/cashu-ffi/justfile
  14. 217 0
      bindings/cashu-ffi/src/cashu.udl
  15. 50 0
      bindings/cashu-ffi/src/error.rs
  16. 35 0
      bindings/cashu-ffi/src/lib.rs
  17. 10 0
      bindings/cashu-ffi/src/nuts/mod.rs
  18. 49 0
      bindings/cashu-ffi/src/nuts/nut00/blinded_message.rs
  19. 61 0
      bindings/cashu-ffi/src/nuts/nut00/blinded_messages.rs
  20. 49 0
      bindings/cashu-ffi/src/nuts/nut00/blinded_signature.rs
  21. 56 0
      bindings/cashu-ffi/src/nuts/nut00/mint_proofs.rs
  22. 6 0
      bindings/cashu-ffi/src/nuts/nut00/mod.rs
  23. 129 0
      bindings/cashu-ffi/src/nuts/nut00/proof.rs
  24. 45 0
      bindings/cashu-ffi/src/nuts/nut00/token.rs
  25. 24 0
      bindings/cashu-ffi/src/nuts/nut01/key_pair.rs
  26. 68 0
      bindings/cashu-ffi/src/nuts/nut01/keys.rs
  27. 4 0
      bindings/cashu-ffi/src/nuts/nut01/mod.rs
  28. 40 0
      bindings/cashu-ffi/src/nuts/nut01/public_key.rs
  29. 40 0
      bindings/cashu-ffi/src/nuts/nut01/secret_key.rs
  30. 55 0
      bindings/cashu-ffi/src/nuts/nut02/key_set.rs
  31. 1 0
      bindings/cashu-ffi/src/nuts/nut02/mod.rs
  32. 27 0
      bindings/cashu-ffi/src/nuts/nut03/mod.rs
  33. 58 0
      bindings/cashu-ffi/src/nuts/nut04/mod.rs
  34. 96 0
      bindings/cashu-ffi/src/nuts/nut05/mod.rs
  35. 81 0
      bindings/cashu-ffi/src/nuts/nut06/mod.rs
  36. 51 0
      bindings/cashu-ffi/src/nuts/nut07/mod.rs
  37. 87 0
      bindings/cashu-ffi/src/nuts/nut08/mod.rs
  38. 76 0
      bindings/cashu-ffi/src/nuts/nut09/mod.rs
  39. 70 0
      bindings/cashu-ffi/src/types/amount.rs
  40. 1 0
      bindings/cashu-ffi/src/types/mod.rs
  41. 10 0
      bindings/cashu-ffi/uniffi.toml
  42. 11 0
      bindings/uniffi-bindgen/Cargo.toml
  43. 3 0
      bindings/uniffi-bindgen/src/main.rs
  44. 2 2
      crates/cashu-sdk/src/mint.rs
  45. 28 4
      crates/cashu/src/nuts/nut01.rs
  46. 1 1
      crates/cashu/src/nuts/nut02.rs

+ 5 - 2
Cargo.toml

@@ -2,7 +2,9 @@
 
 members = [
     "crates/cashu",
-    "crates/cashu-sdk"
+    "crates/cashu-sdk",
+    "bindings/uniffi-bindgen",
+    "bindings/cashu-ffi",
 ]
 
 [workspace.package]
@@ -25,4 +27,5 @@ serde_json = "1.0.96"
 url = "2.3.1"
 tokio = { version = "1", default-features = false }
 tracing = "0.1"
-tracing-subscriber = "0.3"
+tracing-subscriber = "0.3"
+uniffi = "0.24"

+ 1 - 0
bindings/cashu-ffi/.gitignore

@@ -0,0 +1 @@
+/ffi

+ 19 - 0
bindings/cashu-ffi/Cargo.toml

@@ -0,0 +1,19 @@
+[package]
+name = "cashu-ffi"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+
+[lib]
+name = "cashu_ffi"
+crate-type = ["lib", "cdylib", "staticlib"]
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+cashu  = { path = "../../crates/cashu", featues = ["wallet", "mint"] }
+url = { workspace = true }
+uniffi = { workspace = true }
+
+[build-dependencies]
+uniffi = { workspace = true, features = ["build"] }

+ 16 - 0
bindings/cashu-ffi/bindings-python/.gitignore

@@ -0,0 +1,16 @@
+.tox/
+dist/
+cashu_protocol.egg-info/
+__pycache__/
+libcashu_ffi.dylib
+.idea/
+.DS_Store
+
+*.swp
+
+src/cashu/cashu.py
+src/cashu/*.so
+*.whl
+build/
+
+testing-setup-py-simple-example.py

+ 2 - 0
bindings/cashu-ffi/bindings-python/MANIFEST.in

@@ -0,0 +1,2 @@
+include ./src/cashu/libcashu_ffi.dylib
+include ./src/cashu/libcashu_ffi.so

+ 1 - 0
bindings/cashu-ffi/bindings-python/README.md

@@ -0,0 +1 @@
+Hi

+ 5 - 0
bindings/cashu-ffi/bindings-python/examples/test.py

@@ -0,0 +1,5 @@
+from cashu_protocol import Amount
+
+amount = Amount().from_sat(10)
+
+print(amount.to_sat())

+ 7 - 0
bindings/cashu-ffi/bindings-python/pyproject.toml

@@ -0,0 +1,7 @@
+[build-system]
+requires = ["setuptools", "wheel"]
+
+[tool.pytest.ini_options]
+pythonpath = [
+  "."
+]

+ 4 - 0
bindings/cashu-ffi/bindings-python/requirements.txt

@@ -0,0 +1,4 @@
+semantic-version==2.9.0
+typing_extensions==4.0.1
+setuptools==67.4.0
+wheel==0.38.4

+ 26 - 0
bindings/cashu-ffi/bindings-python/setup.py

@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+from setuptools import setup
+
+from pathlib import Path
+this_directory = Path(__file__).parent
+long_description = (this_directory / "README.md").read_text()
+
+setup(
+    name='cashu-protocol',
+    version='0.0.1',
+    description="Rust implementation of cashu.",
+    long_description=long_description,
+    long_description_content_type='text/markdown',
+    include_package_data = True,
+    zip_safe=False,
+    packages=['cashu_protocol'],
+    package_dir={'cashu_protocol': './src/cashu'},
+    package_data={'cashu_protocol': ['*.so', 'lib*']},
+    url="https://github.com/thesimplekid/cashu-crab",
+    author="thesimplekid <tsk@thesimplekid.com>",
+    license="BSD-3-Clause",
+     # This is required to ensure the library name includes the python version, abi, and platform tags
+    # See issue #350 for more information
+    has_ext_modules=lambda: True,
+)

+ 2 - 0
bindings/cashu-ffi/bindings-python/src/cashu/__init__.py

@@ -0,0 +1,2 @@
+from cashu_protocol.cashu import *
+

+ 3 - 0
bindings/cashu-ffi/build.rs

@@ -0,0 +1,3 @@
+fn main() {
+    uniffi::generate_scaffolding("./src/cashu.udl").expect("Building the UDL file failed");
+}

+ 67 - 0
bindings/cashu-ffi/justfile

@@ -0,0 +1,67 @@
+
+init:
+	rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
+	rustup target add aarch64-apple-darwin x86_64-apple-darwin
+	rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
+	cargo install cbindgen
+	cargo install cargo-ndk
+
+
+kotlin:
+	$(JUST) ffi-kotlin-clean
+	$(JUST) ffi-kotlin-generate
+
+ffi-kotlin-clean:
+	find ./ffi/kotlin/jniLibs -name libcashu_sdk_ffi.so -type f -delete
+
+ffi-kotlin-generate:
+	cargo run -p uniffi-bindgen generate src/cashu.udl --language kotlin --no-format -o ffi/kotlin
+
+android: aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
+
+aarch64-linux-android:
+	cargo ndk -t aarch64-linux-android -o ffi/kotlin/jniLibs build --release
+
+armv7-linux-androideabi:
+	$(JUST) ffi-ndk-build TARGET=armv7-linux-androideabi
+
+i686-linux-android:
+	$(JUST) ffi-ndk-build TARGET=i686-linux-android
+
+x86_64-linux-android:
+	$(JUST) ffi-ndk-build TARGET=x86_64-linux-android
+
+ffi-ndk-build:
+	cargo ndk -t $(TARGET) -o ffi/kotlin/jniLibs build --release
+
+bindings-android:
+	$(JUST) bindings-android-clean
+	$(JUST) bindings-android-copy
+	cd bindings-android && ./gradlew assemble
+	$(JUST) bindings-android-package
+
+bindings-android-clean:
+	rm -rf bindings-android/lib/src/main/jniLibs
+	rm -rf bindings-android/lib/src/main/kotlin
+
+bindings-android-copy:
+	cp -r ffi/kotlin/jniLibs bindings-android/lib/src/main
+	cp -r ffi/kotlin/cashu bindings-android/lib/src/main/kotlin/
+
+bindings-android-package:
+	mkdir -p ffi/android
+	cp bindings-android/lib/build/outputs/aar/lib-release.aar ffi/android
+
+publish-android:
+	cd bindings-android && ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
+
+
+python:
+	rm -rf dist
+	pip install -r bindings-python/requirements.txt
+	cargo build --release
+	cargo run -p uniffi-bindgen generate src/cashu.udl --language python --no-format -o bindings-python/src/cashu/
+	cp ../../target/release/libcashu_ffi.so bindings-python/src/cashu/
+	cp ../../target/release/libcashu_ffi.dylib bindings-python/src/cashu/ | true
+	cd bindings-python && pip wheel --no-deps -w dist .
+	pip install ./bindings-python/dist/cashu_protocol*.whl --force-reinstall

+ 217 - 0
bindings/cashu-ffi/src/cashu.udl

@@ -0,0 +1,217 @@
+namespace cashu {};
+
+[Error]
+interface CashuError {
+    Generic(string err);
+};
+
+interface Amount {
+	u64 to_sat();	
+	u64 to_msat();
+	[Name = from_sat]
+	constructor(u64 sat);
+	[Name = from_msat]
+	constructor(u64 msat);
+ 	sequence<Amount> split();
+};
+
+
+interface PublicKey {
+    [Throws=CashuError, Name=from_hex]
+    constructor(string hex);
+    [Throws=CashuError]
+    string to_hex();
+};
+
+
+interface SecretKey {
+    [Throws=CashuError, Name=from_hex]
+    constructor(string hex);
+    [Throws=CashuError]
+    string to_hex();
+};
+
+interface BlindedMessage {
+	constructor(Amount amount, PublicKey b);
+	Amount amount();
+	PublicKey b();	
+};
+
+interface Proof {
+	constructor(Amount amount, string secret, PublicKey c, string? id);
+	Amount amount();
+	string secret();
+	PublicKey c();
+	string? id();
+};
+
+interface BlindedSignature {
+	constructor(string id, Amount amount, PublicKey c);
+	string id();
+	Amount amount();
+	PublicKey c();
+};
+
+interface MintProof {
+	constructor(Amount? amount, string secret, PublicKey? c, string? id);
+	Amount? amount();
+	string secret();
+	PublicKey? c();
+	string? id();
+	
+};
+
+interface MintProofs {
+    [Throws=CashuError]
+	constructor(string mint, sequence<Proof> proofs);
+	string url();
+	sequence<Proof> proofs();
+};
+
+interface Token {
+    [Throws=CashuError]
+	constructor(string mint, sequence<Proof> token, string? memo);
+	sequence<MintProofs> token();
+	string? memo();
+    [Throws=CashuError]
+	string as_string();
+    [Throws=CashuError, Name=from_string]
+	constructor(string token);
+	
+};
+
+interface BlindedMessages {
+    [Throws=CashuError, Name=random]
+	constructor(Amount amount);
+    [Throws=CashuError, Name=blank]
+	constructor(Amount fee_reserve);
+	sequence<BlindedMessage> blinded_messages();
+	sequence<string> secrets();
+	sequence<SecretKey> rs();
+	sequence<Amount> amounts();
+};
+
+interface KeyPair {
+	[Name=from_secret_key]
+	constructor(SecretKey secret_key);
+	SecretKey secret_key();
+	PublicKey public_key();	
+};
+
+interface Keys {
+	constructor(record<string, PublicKey> keys);
+	record<string, PublicKey> keys();
+	record<string, string> as_hashmap();
+	PublicKey? amount_key(Amount amount);
+};
+
+interface KeySet {
+	constructor(string id, Keys keys);
+	string id();
+	Keys keys();
+};
+
+interface KeySetResponse {
+	constructor(sequence<string> keyset_ids);
+	sequence<string> keyset_ids();
+};
+
+interface RequestMintResponse {
+    [Throws=CashuError]
+	constructor(string invoice, string hash);
+	string invoice();
+	string hash();
+};
+
+interface MintRequest {
+	constructor(sequence<BlindedMessage> outputs);
+	sequence<BlindedMessage> outputs();
+	Amount total_amount();
+};
+
+interface PostMintResponse {
+	constructor(sequence<BlindedSignature> promises);
+	sequence<BlindedSignature> promises();
+};
+
+interface CheckFeesRequest {
+    [Throws=CashuError]
+	constructor(string invoice);
+	string invoice();
+};
+
+interface CheckFeesResponse {
+	constructor(Amount amount);
+	Amount amount();
+};
+
+interface Nut05MeltRequest {
+    [Throws=CashuError]
+	constructor(sequence<Proof> proofs, string Invoice);
+	sequence<Proof> proofs();
+	string invoice();
+};
+
+interface Nut05MeltResponse {
+	constructor(boolean paid, string? preimage);
+	boolean paid();
+	string? preimage();
+};
+
+interface SplitRequest {
+	constructor(sequence<Proof> proofs, sequence<BlindedMessage> outputs);
+	sequence<Proof> proofs();
+	sequence<BlindedMessage> outputs();
+	Amount proofs_amount();
+	Amount output_amount();
+};
+
+interface SplitResponse {
+	constructor(sequence<BlindedSignature> promises);
+	sequence<BlindedSignature> promises();
+	Amount? promises_amount();
+
+};
+
+interface CheckSpendableRequest {
+	constructor(sequence<MintProof> proofs);
+	sequence<MintProof> proofs();
+};
+
+interface CheckSpendableResponse {
+	constructor(sequence<boolean> spendable, sequence<boolean> pending);
+	sequence<boolean> spendable();
+	sequence<boolean> pending();	
+};
+
+interface MeltRequest {
+    [Throws=CashuError]
+	constructor(sequence<Proof> proofs, string Invoice, sequence<BlindedMessage>? outputs);
+	sequence<Proof> proofs();
+	string invoice();
+	sequence<BlindedMessage>? outputs();
+};
+
+interface MeltResponse {
+	constructor(boolean paid, string? preimage, sequence<BlindedSignature>? change);
+	boolean paid();
+	string? preimage();
+	sequence<BlindedSignature>? change();
+};
+
+interface MintVersion {
+	constructor(string name, string version);
+	string name();
+	string version();
+};
+
+interface MintInfo {
+	constructor(string? name, PublicKey? pubkey, MintVersion? version, string? description, string? description_long, sequence<sequence<string>> contact, sequence<string> nuts, string? motd);
+};
+
+enum InvoiceStatus {
+	"Unpaid",
+	"Paid",
+	"Expired",
+	"InFlight"
+};

+ 50 - 0
bindings/cashu-ffi/src/error.rs

@@ -0,0 +1,50 @@
+use std::fmt;
+
+use cashu::lightning_invoice::ParseOrSemanticError;
+
+pub type Result<T, E = CashuError> = std::result::Result<T, E>;
+
+#[derive(Debug)]
+pub enum CashuError {
+    Generic { err: String },
+}
+
+impl fmt::Display for CashuError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::Generic { err } => write!(f, "{err}"),
+        }
+    }
+}
+
+impl From<cashu::error::Error> for CashuError {
+    fn from(err: cashu::error::Error) -> Self {
+        Self::Generic {
+            err: err.to_string(),
+        }
+    }
+}
+
+impl From<url::ParseError> for CashuError {
+    fn from(err: url::ParseError) -> Self {
+        Self::Generic {
+            err: err.to_string(),
+        }
+    }
+}
+
+impl From<cashu::error::wallet::Error> for CashuError {
+    fn from(err: cashu::error::wallet::Error) -> Self {
+        Self::Generic {
+            err: err.to_string(),
+        }
+    }
+}
+
+impl From<ParseOrSemanticError> for CashuError {
+    fn from(err: ParseOrSemanticError) -> Self {
+        Self::Generic {
+            err: err.to_string(),
+        }
+    }
+}

+ 35 - 0
bindings/cashu-ffi/src/lib.rs

@@ -0,0 +1,35 @@
+mod error;
+mod nuts;
+mod types;
+
+mod ffi {
+    pub use crate::error::CashuError;
+    pub use crate::nuts::nut00::blinded_message::BlindedMessage;
+    pub use crate::nuts::nut00::blinded_messages::BlindedMessages;
+    pub use crate::nuts::nut00::blinded_signature::BlindedSignature;
+    pub use crate::nuts::nut00::mint_proofs::MintProofs;
+    pub use crate::nuts::nut00::proof::{mint::Proof as MintProof, Proof};
+    pub use crate::nuts::nut00::token::Token;
+    pub use crate::nuts::nut01::key_pair::KeyPair;
+    pub use crate::nuts::nut01::keys::Keys;
+    pub use crate::nuts::nut01::public_key::PublicKey;
+    pub use crate::nuts::nut01::secret_key::SecretKey;
+    pub use crate::nuts::nut02::key_set::{KeySet, KeySetResponse};
+    pub use crate::nuts::nut03::RequestMintResponse;
+    pub use crate::nuts::nut04::{MintRequest, PostMintResponse};
+    pub use crate::nuts::nut05::{
+        CheckFeesRequest, CheckFeesResponse, MeltRequest as Nut05MeltRequest,
+        MeltResponse as Nut05MeltResponse,
+    };
+    pub use crate::nuts::nut06::{SplitRequest, SplitResponse};
+    pub use crate::nuts::nut07::{CheckSpendableRequest, CheckSpendableResponse};
+    pub use crate::nuts::nut08::{MeltRequest, MeltResponse};
+    pub use crate::nuts::nut09::{MintInfo, MintVersion};
+    pub use crate::types::amount::Amount;
+    pub use cashu::types::InvoiceStatus;
+
+    // UDL
+    uniffi::include_scaffolding!("cashu");
+}
+
+pub use ffi::*;

+ 10 - 0
bindings/cashu-ffi/src/nuts/mod.rs

@@ -0,0 +1,10 @@
+pub mod nut00;
+pub mod nut01;
+pub mod nut02;
+pub mod nut03;
+pub mod nut04;
+pub mod nut05;
+pub mod nut06;
+pub mod nut07;
+pub mod nut08;
+pub mod nut09;

+ 49 - 0
bindings/cashu-ffi/src/nuts/nut00/blinded_message.rs

@@ -0,0 +1,49 @@
+use std::ops::Deref;
+use std::sync::Arc;
+
+use cashu::nuts::nut00::BlindedMessage as BlindedMessageSdk;
+
+use crate::nuts::nut01::public_key::PublicKey;
+use crate::Amount;
+
+pub struct BlindedMessage {
+    inner: BlindedMessageSdk,
+}
+
+impl Deref for BlindedMessage {
+    type Target = BlindedMessageSdk;
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl BlindedMessage {
+    pub fn new(amount: Arc<Amount>, b: Arc<PublicKey>) -> Self {
+        Self {
+            inner: BlindedMessageSdk {
+                amount: *amount.as_ref().deref(),
+                b: b.as_ref().into(),
+            },
+        }
+    }
+
+    pub fn amount(&self) -> Arc<Amount> {
+        Arc::new(self.inner.amount.into())
+    }
+
+    pub fn b(&self) -> Arc<PublicKey> {
+        Arc::new(self.inner.b.clone().into())
+    }
+}
+
+impl From<&BlindedMessage> for BlindedMessageSdk {
+    fn from(blinded_message: &BlindedMessage) -> BlindedMessageSdk {
+        blinded_message.inner.clone()
+    }
+}
+
+impl From<BlindedMessageSdk> for BlindedMessage {
+    fn from(inner: BlindedMessageSdk) -> BlindedMessage {
+        BlindedMessage { inner }
+    }
+}

+ 61 - 0
bindings/cashu-ffi/src/nuts/nut00/blinded_messages.rs

@@ -0,0 +1,61 @@
+use std::{ops::Deref, sync::Arc};
+
+use cashu::nuts::nut00::wallet::BlindedMessages as BlindedMessagesSdk;
+
+use crate::{error::Result, Amount, BlindedMessage, SecretKey};
+
+pub struct BlindedMessages {
+    inner: BlindedMessagesSdk,
+}
+
+impl Deref for BlindedMessages {
+    type Target = BlindedMessagesSdk;
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl BlindedMessages {
+    pub fn random(amount: Arc<Amount>) -> Result<Self> {
+        Ok(Self {
+            inner: BlindedMessagesSdk::random(*amount.as_ref().deref())?,
+        })
+    }
+
+    pub fn blank(fee_reserve: Arc<Amount>) -> Result<Self> {
+        Ok(Self {
+            inner: BlindedMessagesSdk::blank(*fee_reserve.as_ref().deref())?,
+        })
+    }
+
+    pub fn blinded_messages(&self) -> Vec<Arc<BlindedMessage>> {
+        self.inner
+            .blinded_messages
+            .clone()
+            .into_iter()
+            .map(|b| Arc::new(b.into()))
+            .collect()
+    }
+
+    pub fn secrets(&self) -> Vec<String> {
+        self.inner.secrets.clone()
+    }
+
+    pub fn rs(&self) -> Vec<Arc<SecretKey>> {
+        self.inner
+            .rs
+            .clone()
+            .into_iter()
+            .map(|s| Arc::new(s.into()))
+            .collect()
+    }
+
+    pub fn amounts(&self) -> Vec<Arc<Amount>> {
+        self.inner
+            .amounts
+            .clone()
+            .into_iter()
+            .map(|a| Arc::new(a.into()))
+            .collect()
+    }
+}

+ 49 - 0
bindings/cashu-ffi/src/nuts/nut00/blinded_signature.rs

@@ -0,0 +1,49 @@
+use std::ops::Deref;
+use std::sync::Arc;
+
+use cashu::nuts::nut00::BlindedSignature as BlindedSignatureSdk;
+
+use crate::Amount;
+use crate::PublicKey;
+
+pub struct BlindedSignature {
+    inner: BlindedSignatureSdk,
+}
+
+impl BlindedSignature {
+    pub fn new(id: String, amount: Arc<Amount>, c: Arc<PublicKey>) -> Self {
+        Self {
+            inner: BlindedSignatureSdk {
+                id,
+                amount: *amount.as_ref().deref(),
+                c: c.as_ref().into(),
+            },
+        }
+    }
+
+    pub fn id(&self) -> String {
+        self.inner.id.clone()
+    }
+
+    pub fn amount(&self) -> Arc<Amount> {
+        Arc::new(self.inner.amount.into())
+    }
+
+    pub fn c(&self) -> Arc<PublicKey> {
+        Arc::new(self.inner.c.clone().into())
+    }
+}
+
+impl From<&BlindedSignature> for BlindedSignatureSdk {
+    fn from(blinded_signature: &BlindedSignature) -> BlindedSignatureSdk {
+        blinded_signature.inner.clone()
+    }
+}
+
+impl From<BlindedSignatureSdk> for BlindedSignature {
+    fn from(blinded_signature: BlindedSignatureSdk) -> BlindedSignature {
+        BlindedSignature {
+            inner: blinded_signature,
+        }
+    }
+}

+ 56 - 0
bindings/cashu-ffi/src/nuts/nut00/mint_proofs.rs

@@ -0,0 +1,56 @@
+use cashu::nuts::nut00::MintProofs as MintProofsSdk;
+use std::ops::Deref;
+use std::str::FromStr;
+use std::sync::Arc;
+
+use crate::error::Result;
+use crate::Proof;
+
+pub struct MintProofs {
+    inner: MintProofsSdk,
+}
+
+impl Deref for MintProofs {
+    type Target = MintProofsSdk;
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl MintProofs {
+    pub fn new(mint: String, proofs: Vec<Arc<Proof>>) -> Result<Self> {
+        let mint = url::Url::from_str(&mint)?;
+        let proofs = proofs.iter().map(|p| p.as_ref().deref().clone()).collect();
+
+        Ok(Self {
+            inner: MintProofsSdk { mint, proofs },
+        })
+    }
+
+    pub fn url(&self) -> String {
+        self.inner.mint.to_string()
+    }
+
+    pub fn proofs(&self) -> Vec<Arc<Proof>> {
+        let proofs = self
+            .inner
+            .proofs
+            .clone()
+            .into_iter()
+            .map(|p| Arc::new(p.into()))
+            .collect();
+        proofs
+    }
+}
+
+impl From<&MintProofs> for MintProofsSdk {
+    fn from(mint_proofs: &MintProofs) -> MintProofsSdk {
+        mint_proofs.inner.clone()
+    }
+}
+
+impl From<MintProofsSdk> for MintProofs {
+    fn from(inner: MintProofsSdk) -> MintProofs {
+        MintProofs { inner }
+    }
+}

+ 6 - 0
bindings/cashu-ffi/src/nuts/nut00/mod.rs

@@ -0,0 +1,6 @@
+pub mod blinded_message;
+pub mod blinded_messages;
+pub mod blinded_signature;
+pub mod mint_proofs;
+pub mod proof;
+pub mod token;

+ 129 - 0
bindings/cashu-ffi/src/nuts/nut00/proof.rs

@@ -0,0 +1,129 @@
+use std::{ops::Deref, sync::Arc};
+
+use cashu::nuts::nut00::Proof as ProofSdk;
+
+use crate::{Amount, PublicKey};
+
+pub struct Proof {
+    inner: ProofSdk,
+}
+
+impl Deref for Proof {
+    type Target = ProofSdk;
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl Proof {
+    pub fn new(amount: Arc<Amount>, secret: String, c: Arc<PublicKey>, id: Option<String>) -> Self {
+        Self {
+            inner: ProofSdk {
+                amount: *amount.as_ref().deref(),
+                secret,
+                c: c.as_ref().deref().clone(),
+                id,
+            },
+        }
+    }
+
+    pub fn amount(&self) -> Arc<Amount> {
+        Arc::new(self.inner.amount.into())
+    }
+
+    pub fn secret(&self) -> String {
+        self.inner.secret.clone()
+    }
+
+    pub fn c(&self) -> Arc<PublicKey> {
+        Arc::new(self.inner.c.clone().into())
+    }
+
+    pub fn id(&self) -> Option<String> {
+        self.inner.id.clone()
+    }
+}
+
+impl From<&Proof> for ProofSdk {
+    fn from(proof: &Proof) -> ProofSdk {
+        ProofSdk {
+            amount: *proof.amount().as_ref().deref(),
+            secret: proof.secret(),
+            c: proof.c().deref().into(),
+            id: proof.id(),
+        }
+    }
+}
+
+impl From<ProofSdk> for Proof {
+    fn from(inner: ProofSdk) -> Proof {
+        Proof { inner }
+    }
+}
+
+pub mod mint {
+    use std::ops::Deref;
+    use std::sync::Arc;
+
+    use cashu::nuts::nut00::mint::Proof as ProofSdk;
+
+    use crate::Amount;
+    use crate::PublicKey;
+
+    pub struct Proof {
+        inner: ProofSdk,
+    }
+
+    impl Deref for Proof {
+        type Target = ProofSdk;
+        fn deref(&self) -> &Self::Target {
+            &self.inner
+        }
+    }
+
+    impl Proof {
+        pub fn new(
+            amount: Option<Arc<Amount>>,
+            secret: String,
+            c: Option<Arc<PublicKey>>,
+            id: Option<String>,
+        ) -> Self {
+            Self {
+                inner: ProofSdk {
+                    amount: amount.map(|a| *a.as_ref().deref()),
+                    secret,
+                    c: c.map(|c| c.as_ref().into()),
+                    id,
+                },
+            }
+        }
+
+        pub fn amount(&self) -> Option<Arc<Amount>> {
+            self.inner.amount.map(|a| Arc::new(a.into()))
+        }
+
+        pub fn secret(&self) -> String {
+            self.inner.secret.clone()
+        }
+
+        pub fn c(&self) -> Option<Arc<PublicKey>> {
+            self.inner.c.clone().map(|c| Arc::new(c.into()))
+        }
+
+        pub fn id(&self) -> Option<String> {
+            self.inner.id.clone()
+        }
+    }
+
+    impl From<ProofSdk> for Proof {
+        fn from(proof: ProofSdk) -> Proof {
+            Proof { inner: proof }
+        }
+    }
+
+    impl From<&Proof> for ProofSdk {
+        fn from(proof: &Proof) -> ProofSdk {
+            proof.inner.clone()
+        }
+    }
+}

+ 45 - 0
bindings/cashu-ffi/src/nuts/nut00/token.rs

@@ -0,0 +1,45 @@
+use std::str::FromStr;
+use std::sync::Arc;
+
+use cashu::nuts::nut00::wallet::Token as TokenSdk;
+
+use crate::error::Result;
+use crate::MintProofs;
+use crate::Proof;
+
+pub struct Token {
+    token: TokenSdk,
+}
+
+impl Token {
+    pub fn new(mint: String, proofs: Vec<Arc<Proof>>, memo: Option<String>) -> Result<Self> {
+        let mint = url::Url::from_str(&mint)?;
+        let proofs = proofs.into_iter().map(|p| p.as_ref().into()).collect();
+        Ok(Self {
+            token: TokenSdk::new(mint, proofs, memo)?,
+        })
+    }
+
+    pub fn token(&self) -> Vec<Arc<MintProofs>> {
+        self.token
+            .token
+            .clone()
+            .into_iter()
+            .map(|p| Arc::new(p.into()))
+            .collect()
+    }
+
+    pub fn memo(&self) -> Option<String> {
+        self.token.memo.clone()
+    }
+
+    pub fn from_string(token: String) -> Result<Self> {
+        Ok(Self {
+            token: TokenSdk::from_str(&token)?,
+        })
+    }
+
+    pub fn as_string(&self) -> Result<String> {
+        Ok(self.token.convert_to_string()?)
+    }
+}

+ 24 - 0
bindings/cashu-ffi/src/nuts/nut01/key_pair.rs

@@ -0,0 +1,24 @@
+use std::{ops::Deref, sync::Arc};
+
+use crate::{PublicKey, SecretKey};
+use cashu::nuts::nut01::mint::KeyPair as KeyPairSdk;
+
+pub struct KeyPair {
+    inner: KeyPairSdk,
+}
+
+impl KeyPair {
+    pub fn from_secret_key(secret_key: Arc<SecretKey>) -> Self {
+        Self {
+            inner: KeyPairSdk::from_secret_key(secret_key.as_ref().deref().clone()),
+        }
+    }
+
+    pub fn secret_key(&self) -> Arc<SecretKey> {
+        Arc::new(self.inner.secret_key.clone().into())
+    }
+
+    pub fn public_key(&self) -> Arc<PublicKey> {
+        Arc::new(self.inner.public_key.clone().into())
+    }
+}

+ 68 - 0
bindings/cashu-ffi/src/nuts/nut01/keys.rs

@@ -0,0 +1,68 @@
+use std::{collections::HashMap, ops::Deref, sync::Arc};
+
+use crate::{Amount, PublicKey};
+use cashu::nuts::nut01::Keys as KeysSdk;
+
+pub struct Keys {
+    inner: KeysSdk,
+}
+
+impl Deref for Keys {
+    type Target = KeysSdk;
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl Keys {
+    pub fn new(keys: HashMap<String, Arc<PublicKey>>) -> Self {
+        let keys = keys
+            .into_iter()
+            .map(|(amount, pk)| (amount.parse::<u64>().unwrap(), pk.as_ref().into()))
+            .collect();
+
+        Self {
+            inner: KeysSdk::new(keys),
+        }
+    }
+
+    pub fn keys(&self) -> HashMap<String, Arc<PublicKey>> {
+        self.inner
+            .keys()
+            .into_iter()
+            .map(|(amount, pk)| (amount.to_string(), Arc::new(pk.into())))
+            .collect()
+    }
+
+    pub fn amount_key(&self, amount: Arc<Amount>) -> Option<Arc<PublicKey>> {
+        self.inner
+            .amount_key(*amount.as_ref().deref())
+            .map(|pk| Arc::new(pk.into()))
+    }
+
+    pub fn as_hashmap(&self) -> HashMap<String, String> {
+        self.inner
+            .as_hashmap()
+            .into_iter()
+            .map(|(amount, pk)| (amount.to_string(), pk))
+            .collect()
+    }
+}
+
+impl From<Keys> for KeysSdk {
+    fn from(keys: Keys) -> KeysSdk {
+        keys.inner
+    }
+}
+
+impl From<KeysSdk> for Keys {
+    fn from(keys: KeysSdk) -> Keys {
+        let keys = keys
+            .keys()
+            .into_iter()
+            .map(|(amount, pk)| (amount.to_string(), Arc::new(pk.into())))
+            .collect();
+
+        Keys::new(keys)
+    }
+}

+ 4 - 0
bindings/cashu-ffi/src/nuts/nut01/mod.rs

@@ -0,0 +1,4 @@
+pub mod key_pair;
+pub mod keys;
+pub mod public_key;
+pub mod secret_key;

+ 40 - 0
bindings/cashu-ffi/src/nuts/nut01/public_key.rs

@@ -0,0 +1,40 @@
+use std::ops::Deref;
+
+use cashu::nuts::nut01::PublicKey as PublicKeySdk;
+
+use crate::error::Result;
+
+pub struct PublicKey {
+    inner: PublicKeySdk,
+}
+
+impl From<PublicKeySdk> for PublicKey {
+    fn from(inner: PublicKeySdk) -> Self {
+        Self { inner }
+    }
+}
+
+impl From<&PublicKey> for PublicKeySdk {
+    fn from(pk: &PublicKey) -> PublicKeySdk {
+        pk.inner.clone()
+    }
+}
+
+impl Deref for PublicKey {
+    type Target = PublicKeySdk;
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl PublicKey {
+    pub fn from_hex(hex: String) -> Result<Self> {
+        Ok(Self {
+            inner: PublicKeySdk::from_hex(hex)?,
+        })
+    }
+
+    pub fn to_hex(&self) -> Result<String> {
+        Ok(self.inner.to_hex()?)
+    }
+}

+ 40 - 0
bindings/cashu-ffi/src/nuts/nut01/secret_key.rs

@@ -0,0 +1,40 @@
+use std::ops::Deref;
+
+use cashu::nuts::nut01::SecretKey as SecretKeySdk;
+
+use crate::error::Result;
+
+pub struct SecretKey {
+    inner: SecretKeySdk,
+}
+
+impl Deref for SecretKey {
+    type Target = SecretKeySdk;
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl From<SecretKeySdk> for SecretKey {
+    fn from(inner: SecretKeySdk) -> Self {
+        Self { inner }
+    }
+}
+
+impl From<SecretKey> for SecretKeySdk {
+    fn from(sk: SecretKey) -> SecretKeySdk {
+        sk.inner
+    }
+}
+
+impl SecretKey {
+    pub fn from_hex(hex: String) -> Result<Self> {
+        Ok(Self {
+            inner: SecretKeySdk::from_hex(hex)?,
+        })
+    }
+
+    pub fn to_hex(&self) -> Result<String> {
+        Ok(self.inner.to_hex()?)
+    }
+}

+ 55 - 0
bindings/cashu-ffi/src/nuts/nut02/key_set.rs

@@ -0,0 +1,55 @@
+use std::collections::HashSet;
+use std::ops::Deref;
+use std::sync::Arc;
+
+use cashu::nuts::nut02::KeySet as KeySetSdk;
+use cashu::nuts::nut02::Response;
+
+use crate::nuts::nut01::keys::Keys;
+
+pub struct KeySet {
+    inner: KeySetSdk,
+}
+
+impl Deref for KeySet {
+    type Target = KeySetSdk;
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl KeySet {
+    pub fn new(id: String, keys: Arc<Keys>) -> Self {
+        Self {
+            inner: KeySetSdk {
+                id,
+                keys: keys.as_ref().deref().clone(),
+            },
+        }
+    }
+
+    pub fn id(&self) -> String {
+        self.inner.id.clone()
+    }
+
+    pub fn keys(&self) -> Arc<Keys> {
+        Arc::new(self.inner.keys.clone().into())
+    }
+}
+
+pub struct KeySetResponse {
+    inner: Response,
+}
+
+impl KeySetResponse {
+    pub fn new(keyset_ids: Vec<String>) -> Self {
+        let keysets = HashSet::from_iter(keyset_ids);
+        Self {
+            inner: Response { keysets },
+        }
+    }
+
+    pub fn keyset_ids(&self) -> Vec<String> {
+        self.inner.clone().keysets.into_iter().collect()
+    }
+}

+ 1 - 0
bindings/cashu-ffi/src/nuts/nut02/mod.rs

@@ -0,0 +1 @@
+pub mod key_set;

+ 27 - 0
bindings/cashu-ffi/src/nuts/nut03/mod.rs

@@ -0,0 +1,27 @@
+use std::str::FromStr;
+
+use cashu::{nuts::nut03::RequestMintResponse as RequestMintResponseSdk, Bolt11Invoice};
+
+use crate::error::Result;
+
+pub struct RequestMintResponse {
+    inner: RequestMintResponseSdk,
+}
+
+impl RequestMintResponse {
+    pub fn new(invoice: String, hash: String) -> Result<Self> {
+        let pr = Bolt11Invoice::from_str(&invoice)?;
+
+        Ok(Self {
+            inner: RequestMintResponseSdk { pr, hash },
+        })
+    }
+
+    pub fn invoice(&self) -> String {
+        self.inner.pr.to_string()
+    }
+
+    pub fn hash(&self) -> String {
+        self.inner.hash.to_string()
+    }
+}

+ 58 - 0
bindings/cashu-ffi/src/nuts/nut04/mod.rs

@@ -0,0 +1,58 @@
+use std::{ops::Deref, sync::Arc};
+
+use cashu::nuts::nut04::{MintRequest as MintRequestSdk, PostMintResponse as PostMintResponseSdk};
+
+use crate::{Amount, BlindedMessage, BlindedSignature};
+
+pub struct MintRequest {
+    inner: MintRequestSdk,
+}
+
+impl MintRequest {
+    pub fn new(outputs: Vec<Arc<BlindedMessage>>) -> Self {
+        Self {
+            inner: MintRequestSdk {
+                outputs: outputs
+                    .into_iter()
+                    .map(|o| o.as_ref().deref().clone())
+                    .collect(),
+            },
+        }
+    }
+
+    pub fn outputs(&self) -> Vec<Arc<BlindedMessage>> {
+        self.inner
+            .outputs
+            .clone()
+            .into_iter()
+            .map(|o| Arc::new(o.into()))
+            .collect()
+    }
+
+    pub fn total_amount(&self) -> Arc<Amount> {
+        Arc::new(self.inner.total_amount().into())
+    }
+}
+
+pub struct PostMintResponse {
+    inner: PostMintResponseSdk,
+}
+
+impl PostMintResponse {
+    pub fn new(promises: Vec<Arc<BlindedSignature>>) -> Self {
+        Self {
+            inner: PostMintResponseSdk {
+                promises: promises.into_iter().map(|p| p.as_ref().into()).collect(),
+            },
+        }
+    }
+
+    pub fn promises(&self) -> Vec<Arc<BlindedSignature>> {
+        self.inner
+            .promises
+            .clone()
+            .into_iter()
+            .map(|p| Arc::new(p.into()))
+            .collect()
+    }
+}

+ 96 - 0
bindings/cashu-ffi/src/nuts/nut05/mod.rs

@@ -0,0 +1,96 @@
+use std::{ops::Deref, str::FromStr, sync::Arc};
+
+use cashu::{
+    nuts::nut05::{
+        CheckFeesRequest as CheckFeesRequestSdk, CheckFeesResponse as CheckFeesResponseSdk,
+        MeltRequest as MeltRequestSdk, MeltResponse as MeltResponseSdk,
+    },
+    Bolt11Invoice,
+};
+
+use crate::{error::Result, Amount, Proof};
+
+pub struct CheckFeesRequest {
+    inner: CheckFeesRequestSdk,
+}
+
+impl CheckFeesRequest {
+    pub fn new(invoice: String) -> Result<Self> {
+        Ok(Self {
+            inner: CheckFeesRequestSdk {
+                pr: Bolt11Invoice::from_str(&invoice)?,
+            },
+        })
+    }
+
+    pub fn invoice(&self) -> String {
+        self.inner.pr.to_string()
+    }
+}
+
+pub struct CheckFeesResponse {
+    inner: CheckFeesResponseSdk,
+}
+
+impl CheckFeesResponse {
+    pub fn new(amount: Arc<Amount>) -> Self {
+        Self {
+            inner: CheckFeesResponseSdk {
+                fee: *amount.as_ref().deref(),
+            },
+        }
+    }
+
+    pub fn amount(&self) -> Arc<Amount> {
+        Arc::new(self.inner.fee.into())
+    }
+}
+
+pub struct MeltRequest {
+    inner: MeltRequestSdk,
+}
+
+impl MeltRequest {
+    pub fn new(proofs: Vec<Arc<Proof>>, invoice: String) -> Result<Self> {
+        let pr = Bolt11Invoice::from_str(&invoice)?;
+        Ok(Self {
+            inner: MeltRequestSdk {
+                pr,
+                proofs: proofs.into_iter().map(|p| p.as_ref().into()).collect(),
+            },
+        })
+    }
+
+    pub fn proofs(&self) -> Vec<Arc<Proof>> {
+        self.inner
+            .proofs
+            .clone()
+            .into_iter()
+            .map(|p| Arc::new(p.into()))
+            .collect()
+    }
+
+    pub fn invoice(&self) -> String {
+        self.inner.pr.to_string()
+    }
+}
+
+pub struct MeltResponse {
+    inner: MeltResponseSdk,
+}
+
+impl MeltResponse {
+    pub fn new(paid: bool, preimage: Option<String>) -> Self {
+        Self {
+            inner: MeltResponseSdk { paid, preimage },
+        }
+    }
+
+    pub fn paid(&self) -> bool {
+        self.inner.paid
+    }
+
+    pub fn preimage(&self) -> Option<String> {
+        self.inner.preimage.clone()
+    }
+}

+ 81 - 0
bindings/cashu-ffi/src/nuts/nut06/mod.rs

@@ -0,0 +1,81 @@
+use std::sync::Arc;
+
+use cashu::nuts::nut06::{SplitRequest as SplitRequestSdk, SplitResponse as SplitResponseSdk};
+
+use crate::{Amount, BlindedMessage, BlindedMessages, BlindedSignature, Proof};
+
+pub struct SplitRequest {
+    inner: SplitRequestSdk,
+}
+
+impl SplitRequest {
+    pub fn new(proofs: Vec<Arc<Proof>>, outputs: Vec<Arc<BlindedMessage>>) -> Self {
+        let proofs = proofs.into_iter().map(|p| p.as_ref().into()).collect();
+        let outputs = outputs.into_iter().map(|o| o.as_ref().into()).collect();
+
+        Self {
+            inner: SplitRequestSdk {
+                amount: None,
+                proofs,
+                outputs,
+            },
+        }
+    }
+
+    pub fn proofs(&self) -> Vec<Arc<Proof>> {
+        self.inner
+            .proofs
+            .clone()
+            .into_iter()
+            .map(|p| Arc::new(p.into()))
+            .collect()
+    }
+
+    pub fn outputs(&self) -> Vec<Arc<BlindedMessage>> {
+        self.inner
+            .outputs
+            .clone()
+            .into_iter()
+            .map(|o| Arc::new(o.into()))
+            .collect()
+    }
+
+    pub fn proofs_amount(&self) -> Arc<Amount> {
+        Arc::new(self.inner.proofs_amount().into())
+    }
+
+    pub fn output_amount(&self) -> Arc<Amount> {
+        Arc::new(self.inner.output_amount().into())
+    }
+}
+
+pub struct SplitResponse {
+    inner: SplitResponseSdk,
+}
+
+impl SplitResponse {
+    pub fn new(promises: Vec<Arc<BlindedSignature>>) -> Self {
+        let promises = promises.into_iter().map(|p| p.as_ref().into()).collect();
+        Self {
+            inner: SplitResponseSdk {
+                fst: None,
+                snd: None,
+                promises: Some(promises),
+            },
+        }
+    }
+
+    pub fn promises(&self) -> Vec<Arc<BlindedSignature>> {
+        self.inner
+            .promises
+            .clone()
+            .unwrap_or_default()
+            .into_iter()
+            .map(|p| Arc::new(p.into()))
+            .collect()
+    }
+
+    pub fn promises_amount(&self) -> Option<Arc<Amount>> {
+        self.inner.promises_amount().map(|a| Arc::new(a.into()))
+    }
+}

+ 51 - 0
bindings/cashu-ffi/src/nuts/nut07/mod.rs

@@ -0,0 +1,51 @@
+use std::sync::Arc;
+
+use cashu::nuts::nut07::{
+    CheckSpendableRequest as CheckSpendableRequestSdk,
+    CheckSpendableResponse as CheckSpendableResponseSdk,
+};
+
+use crate::nuts::nut00::proof::mint::Proof;
+
+pub struct CheckSpendableRequest {
+    inner: CheckSpendableRequestSdk,
+}
+
+impl CheckSpendableRequest {
+    pub fn new(proofs: Vec<Arc<Proof>>) -> Self {
+        Self {
+            inner: CheckSpendableRequestSdk {
+                proofs: proofs.into_iter().map(|p| p.as_ref().into()).collect(),
+            },
+        }
+    }
+
+    pub fn proofs(&self) -> Vec<Arc<Proof>> {
+        self.inner
+            .proofs
+            .clone()
+            .into_iter()
+            .map(|p| Arc::new(p.into()))
+            .collect()
+    }
+}
+
+pub struct CheckSpendableResponse {
+    inner: CheckSpendableResponseSdk,
+}
+
+impl CheckSpendableResponse {
+    pub fn new(spendable: Vec<bool>, pending: Vec<bool>) -> Self {
+        Self {
+            inner: CheckSpendableResponseSdk { spendable, pending },
+        }
+    }
+
+    pub fn spendable(&self) -> Vec<bool> {
+        self.inner.spendable.clone()
+    }
+
+    pub fn pending(&self) -> Vec<bool> {
+        self.inner.pending.clone()
+    }
+}

+ 87 - 0
bindings/cashu-ffi/src/nuts/nut08/mod.rs

@@ -0,0 +1,87 @@
+use std::str::FromStr;
+use std::sync::Arc;
+
+use cashu::nuts::nut08::{MeltRequest as MeltRequestSdk, MeltResponse as MeltResponseSdk};
+use cashu::Bolt11Invoice;
+
+use crate::error::Result;
+use crate::{BlindedMessage, BlindedSignature, Proof};
+
+pub struct MeltRequest {
+    inner: MeltRequestSdk,
+}
+
+impl MeltRequest {
+    pub fn new(
+        proofs: Vec<Arc<Proof>>,
+        invoice: String,
+        outputs: Option<Vec<Arc<BlindedMessage>>>,
+    ) -> Result<Self> {
+        let pr = Bolt11Invoice::from_str(&invoice)?;
+
+        Ok(Self {
+            inner: MeltRequestSdk {
+                proofs: proofs.iter().map(|p| p.as_ref().into()).collect(),
+                pr,
+                outputs: outputs
+                    .map(|outputs| outputs.into_iter().map(|o| o.as_ref().into()).collect()),
+            },
+        })
+    }
+
+    pub fn proofs(&self) -> Vec<Arc<Proof>> {
+        self.inner
+            .proofs
+            .clone()
+            .into_iter()
+            .map(|o| Arc::new(o.into()))
+            .collect()
+    }
+
+    pub fn invoice(&self) -> String {
+        self.inner.pr.to_string()
+    }
+
+    pub fn outputs(&self) -> Option<Vec<Arc<BlindedMessage>>> {
+        self.inner
+            .outputs
+            .clone()
+            .map(|outputs| outputs.into_iter().map(|o| Arc::new(o.into())).collect())
+    }
+}
+
+pub struct MeltResponse {
+    inner: MeltResponseSdk,
+}
+
+impl MeltResponse {
+    pub fn new(
+        paid: bool,
+        preimage: Option<String>,
+        change: Option<Vec<Arc<BlindedSignature>>>,
+    ) -> Self {
+        Self {
+            inner: MeltResponseSdk {
+                paid,
+                preimage,
+                change: change
+                    .map(|change| change.into_iter().map(|bs| bs.as_ref().into()).collect()),
+            },
+        }
+    }
+
+    pub fn paid(&self) -> bool {
+        self.inner.paid
+    }
+
+    pub fn preimage(&self) -> Option<String> {
+        self.inner.preimage.clone()
+    }
+
+    pub fn change(&self) -> Option<Vec<Arc<BlindedSignature>>> {
+        self.inner
+            .change
+            .clone()
+            .map(|change| change.into_iter().map(|bs| Arc::new(bs.into())).collect())
+    }
+}

+ 76 - 0
bindings/cashu-ffi/src/nuts/nut09/mod.rs

@@ -0,0 +1,76 @@
+use std::{ops::Deref, sync::Arc};
+
+use cashu::nuts::nut09::{MintInfo as MintInfoSdk, MintVersion as MintVersionSdk};
+
+use crate::PublicKey;
+
+pub struct MintVersion {
+    inner: MintVersionSdk,
+}
+
+impl MintVersion {
+    pub fn new(name: String, version: String) -> Self {
+        Self {
+            inner: MintVersionSdk { name, version },
+        }
+    }
+
+    pub fn name(&self) -> String {
+        self.inner.name.clone()
+    }
+
+    pub fn version(&self) -> String {
+        self.inner.version.clone()
+    }
+}
+
+impl From<&MintVersion> for MintVersionSdk {
+    fn from(mint_version: &MintVersion) -> MintVersionSdk {
+        mint_version.inner.clone()
+    }
+}
+
+impl From<MintVersionSdk> for MintVersion {
+    fn from(inner: MintVersionSdk) -> MintVersion {
+        MintVersion { inner }
+    }
+}
+
+impl Deref for MintVersion {
+    type Target = MintVersionSdk;
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+pub struct MintInfo {
+    inner: MintInfoSdk,
+}
+
+impl MintInfo {
+    pub fn new(
+        name: Option<String>,
+        pubkey: Option<Arc<PublicKey>>,
+        version: Option<Arc<MintVersion>>,
+        description: Option<String>,
+        description_long: Option<String>,
+        contact: Vec<Vec<String>>,
+        nuts: Vec<String>,
+        motd: Option<String>,
+    ) -> Self {
+        let pubkey = pubkey.map(|p| p.as_ref().deref().clone());
+
+        Self {
+            inner: MintInfoSdk {
+                name,
+                pubkey,
+                version: version.map(|v| v.deref().into()),
+                description,
+                description_long,
+                contact,
+                nuts,
+                motd,
+            },
+        }
+    }
+}

+ 70 - 0
bindings/cashu-ffi/src/types/amount.rs

@@ -0,0 +1,70 @@
+use std::{ops::Deref, sync::Arc};
+
+use cashu::Amount as AmountSdk;
+
+pub struct Amount {
+    inner: AmountSdk,
+}
+
+impl Deref for Amount {
+    type Target = AmountSdk;
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl Amount {
+    pub fn new(sats: u64) -> Self {
+        Self {
+            inner: AmountSdk::from_sat(sats),
+        }
+    }
+
+    pub fn to_sat(&self) -> u64 {
+        self.inner.to_sat()
+    }
+
+    pub fn to_msat(&self) -> u64 {
+        self.inner.to_msat()
+    }
+
+    pub fn from_sat(sats: u64) -> Self {
+        Self {
+            inner: AmountSdk::from_sat(sats),
+        }
+    }
+
+    pub fn from_msat(msats: u64) -> Self {
+        Self {
+            inner: AmountSdk::from_msat(msats),
+        }
+    }
+
+    pub const ZERO: Amount = Amount {
+        inner: AmountSdk::ZERO,
+    };
+
+    /// Split into parts that are powers of two
+    pub fn split(&self) -> Vec<Arc<Self>> {
+        let sats = self.inner.to_sat();
+        (0_u64..64)
+            .rev()
+            .filter_map(|bit| {
+                let part = 1 << bit;
+                ((sats & part) == part).then_some(Arc::new(Self::from_sat(part)))
+            })
+            .collect()
+    }
+}
+
+impl From<AmountSdk> for Amount {
+    fn from(amount: AmountSdk) -> Amount {
+        Amount { inner: amount }
+    }
+}
+
+impl From<&Amount> for AmountSdk {
+    fn from(amount: &Amount) -> AmountSdk {
+        amount.inner
+    }
+}

+ 1 - 0
bindings/cashu-ffi/src/types/mod.rs

@@ -0,0 +1 @@
+pub mod amount;

+ 10 - 0
bindings/cashu-ffi/uniffi.toml

@@ -0,0 +1,10 @@
+[bindings.kotlin]
+package_name = "cashu"
+cdylib_name = "cashu_ffi"
+
+[bindings.swift]
+ffi_module_filename = "cashuFFI"
+cdylib_name = "cashu_ffi"
+
+[bindings.python]
+cdylib_name = "cashu_ffi"

+ 11 - 0
bindings/uniffi-bindgen/Cargo.toml

@@ -0,0 +1,11 @@
+[package]
+name = "uniffi-bindgen"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[dependencies]
+uniffi = { workspace = true, features = ["cli"] }
+
+[build-dependencies]
+uniffi = { workspace = true, features = ["build"] }

+ 3 - 0
bindings/uniffi-bindgen/src/main.rs

@@ -0,0 +1,3 @@
+fn main() {
+    uniffi::uniffi_bindgen_main()
+}

+ 2 - 2
crates/cashu-sdk/src/mint.rs

@@ -87,7 +87,7 @@ impl Mint {
             return Err(Error::AmountKey);
         };
 
-        let c = sign_message(key_pair.secret_key.clone(), b.clone().into())?;
+        let c = sign_message(key_pair.secret_key.clone().into(), b.clone().into())?;
 
         Ok(BlindedSignature {
             amount: *amount,
@@ -167,7 +167,7 @@ impl Mint {
         };
 
         verify_message(
-            keypair.secret_key.to_owned(),
+            keypair.secret_key.to_owned().into(),
             proof.c.clone().into(),
             &proof.secret,
         )?;

+ 28 - 4
crates/cashu/src/nuts/nut01.rs

@@ -6,6 +6,7 @@ use std::collections::HashMap;
 
 use serde::{Deserialize, Serialize};
 
+use crate::error::Error;
 use crate::Amount;
 
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -30,6 +31,16 @@ impl From<k256::PublicKey> for PublicKey {
     }
 }
 
+impl PublicKey {
+    pub fn from_hex(hex: String) -> Result<Self, Error> {
+        Ok(serde_json::from_str(&hex)?)
+    }
+
+    pub fn to_hex(&self) -> Result<String, Error> {
+        Ok(serde_json::to_string(&self)?)
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 #[serde(transparent)]
 pub struct SecretKey(#[serde(with = "crate::serde_utils::serde_secret_key")] k256::SecretKey);
@@ -46,6 +57,20 @@ impl From<k256::SecretKey> for SecretKey {
     }
 }
 
+impl SecretKey {
+    pub fn from_hex(hex: String) -> Result<Self, Error> {
+        Ok(serde_json::from_str(&hex)?)
+    }
+
+    pub fn to_hex(&self) -> Result<String, Error> {
+        Ok(serde_json::to_string(&self)?)
+    }
+
+    pub fn public_key(&self) -> PublicKey {
+        self.0.public_key().into()
+    }
+}
+
 /// Mint Keys [NUT-01]
 #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
 pub struct Keys(BTreeMap<u64, PublicKey>);
@@ -63,6 +88,7 @@ impl Keys {
         self.0.get(&amount.to_sat()).cloned()
     }
 
+    /// As seralized hashmap
     pub fn as_hashmap(&self) -> HashMap<u64, String> {
         self.0
             .iter()
@@ -85,12 +111,11 @@ impl From<mint::Keys> for Keys {
 pub mod mint {
     use std::collections::BTreeMap;
 
-    use k256::SecretKey;
-    use serde::Deserialize;
     use serde::Serialize;
 
     use super::PublicKey;
-    use crate::serde_utils;
+    use super::SecretKey;
+    use serde::Deserialize;
 
     #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
     pub struct Keys(pub BTreeMap<u64, KeyPair>);
@@ -98,7 +123,6 @@ pub mod mint {
     #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
     pub struct KeyPair {
         pub public_key: PublicKey,
-        #[serde(with = "serde_utils::serde_secret_key")]
         pub secret_key: SecretKey,
     }
 

+ 1 - 1
crates/cashu/src/nuts/nut02.rs

@@ -100,7 +100,7 @@ pub mod mint {
                 e.input(i.to_string().as_bytes());
                 let hash = Sha256::from_engine(e);
                 let secret_key = SecretKey::from_slice(&hash.to_byte_array()).unwrap();
-                let keypair = KeyPair::from_secret_key(secret_key);
+                let keypair = KeyPair::from_secret_key(secret_key.into());
                 map.insert(amount, keypair);
             }