feat: multimint sends using cdk

This commit is contained in:
Khushal
2026-06-01 06:06:31 +05:30
parent aa5c9d4ee7
commit 83ac9c0db3
20 changed files with 1975 additions and 107 deletions
+11
View File
@@ -105,6 +105,17 @@ RCT_EXTERN_METHOD(melt:(NSString *)mintUrl
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(transferExactReceive:(NSString *)sourceMint
targetMint:(NSString *)targetMint
amount:(nonnull NSNumber *)amount
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(transferFullBalance:(NSString *)sourceMint
targetMint:(NSString *)targetMint
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
// Token Operations
RCT_EXTERN_METHOD(prepareSend:(NSString *)mintUrl
amount:(nonnull NSNumber *)amount
+104 -2
View File
@@ -68,6 +68,32 @@ class CashuDevKitModule: RCTEventEmitter {
return .p2pk(pubkey: pubkey, conditions: conditions)
}
private func parseMeltOptions(from optionsJson: String?) -> MeltOptions? {
guard
let json = optionsJson,
let data = json.data(using: .utf8),
let parsed = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
else {
return nil
}
if let mpp = parsed["mpp"] as? [String: Any],
let amountNumber = mpp["amount"] as? NSNumber,
amountNumber.uint64Value > 0 {
return .mpp(amount: Amount(value: amountNumber.uint64Value))
}
if let amountless = parsed["amountless"] as? [String: Any],
let amountMsatNumber = amountless["amount_msat"] as? NSNumber,
amountMsatNumber.uint64Value > 0 {
return .amountless(
amountMsat: Amount(value: amountMsatNumber.uint64Value)
)
}
return nil
}
/// Returns the initialized wallet or rejects with NO_WALLET error
private func getInitializedWallet(reject: @escaping RCTPromiseRejectBlock) -> MultiMintWallet? {
guard isInitialized, let wallet = wallet else {
@@ -240,6 +266,16 @@ class CashuDevKitModule: RCTEventEmitter {
return result
}
private func encodeTransferResult(_ result: TransferResult) -> [String: Any] {
return [
"amount_sent": result.amountSent.value,
"amount_received": result.amountReceived.value,
"fees_paid": result.feesPaid.value,
"source_balance_after": result.sourceBalanceAfter.value,
"target_balance_after": result.targetBalanceAfter.value
]
}
private func encodeProof(_ proof: Proof) -> [String: Any] {
let result: [String: Any] = [
"amount": proof.amount.value,
@@ -761,7 +797,12 @@ class CashuDevKitModule: RCTEventEmitter {
Task {
do {
let url = MintUrl(url: mintUrl)
let quote = try await wallet.meltQuote(mintUrl: url, request: request, options: nil)
let options = parseMeltOptions(from: optionsJson)
let quote = try await wallet.meltQuote(
mintUrl: url,
request: request,
options: options
)
resolve(encodeToJson(encodeMeltQuote(quote)))
} catch let error as FfiError {
let (code, message) = mapFfiError(error)
@@ -812,6 +853,62 @@ class CashuDevKitModule: RCTEventEmitter {
}
}
@objc(transferExactReceive:targetMint:amount:resolver:rejecter:)
func transferExactReceive(_ sourceMint: String, targetMint: String, amount: NSNumber,
resolve: @escaping RCTPromiseResolveBlock,
reject: @escaping RCTPromiseRejectBlock) {
guard let wallet = getInitializedWallet(reject: reject) else { return }
Task {
do {
let sourceMintUrl = MintUrl(url: sourceMint)
let targetMintUrl = MintUrl(url: targetMint)
let transferMode = TransferMode.exactReceive(
amount: Amount(value: amount.uint64Value)
)
let result = try await wallet.transfer(
sourceMint: sourceMintUrl,
targetMint: targetMintUrl,
transferMode: transferMode
)
resolve(encodeToJson(encodeTransferResult(result)))
} catch let error as FfiError {
let (code, message) = mapFfiError(error)
reject(code, message, error)
} catch {
reject("TRANSFER_ERROR", error.localizedDescription, error)
}
}
}
@objc(transferFullBalance:targetMint:resolver:rejecter:)
func transferFullBalance(_ sourceMint: String, targetMint: String,
resolve: @escaping RCTPromiseResolveBlock,
reject: @escaping RCTPromiseRejectBlock) {
guard let wallet = getInitializedWallet(reject: reject) else { return }
Task {
do {
let sourceMintUrl = MintUrl(url: sourceMint)
let targetMintUrl = MintUrl(url: targetMint)
let result = try await wallet.transfer(
sourceMint: sourceMintUrl,
targetMint: targetMintUrl,
transferMode: .fullBalance
)
resolve(encodeToJson(encodeTransferResult(result)))
} catch let error as FfiError {
let (code, message) = mapFfiError(error)
reject(code, message, error)
} catch {
reject("TRANSFER_ERROR", error.localizedDescription, error)
}
}
}
// MARK: - Token Operations
@objc(prepareSend:amount:optionsJson:resolver:rejecter:)
@@ -1201,7 +1298,12 @@ class CashuDevKitModule: RCTEventEmitter {
Task {
do {
let url = MintUrl(url: mintUrl)
let quote = try await wallet.meltQuote(mintUrl: url, request: request, options: nil)
let options = parseMeltOptions(from: optionsJson)
let quote = try await wallet.meltQuote(
mintUrl: url,
request: request,
options: options
)
resolve(encodeToJson(encodeMeltQuote(quote)))
} catch let error as FfiError {
let (code, message) = mapFfiError(error)