2020117-agent 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cashu.d.ts +16 -0
- package/dist/cashu.js +25 -0
- package/dist/session.js +61 -24
- package/package.json +1 -1
package/dist/cashu.d.ts
CHANGED
|
@@ -45,6 +45,22 @@ export declare function decodeCashuToken(tokenStr: string): {
|
|
|
45
45
|
* Encode proofs back into a portable token string.
|
|
46
46
|
*/
|
|
47
47
|
export declare function encodeCashuToken(mintUrl: string, proofs: Proof[]): string;
|
|
48
|
+
/**
|
|
49
|
+
* Melt proofs back into a Lightning invoice — converts Cashu back to Lightning.
|
|
50
|
+
* Returns the payment preimage and any change proofs.
|
|
51
|
+
*/
|
|
52
|
+
export declare function meltProofs(mintUrl: string, proofs: Proof[], invoice: string): Promise<{
|
|
53
|
+
preimage: string;
|
|
54
|
+
change: Proof[];
|
|
55
|
+
}>;
|
|
56
|
+
/**
|
|
57
|
+
* Estimate total cost to melt (invoice amount + fee_reserve) for a given invoice.
|
|
58
|
+
*/
|
|
59
|
+
export declare function estimateMeltFee(mintUrl: string, invoice: string): Promise<{
|
|
60
|
+
amount: number;
|
|
61
|
+
fee: number;
|
|
62
|
+
total: number;
|
|
63
|
+
}>;
|
|
48
64
|
/**
|
|
49
65
|
* Request a mint quote — returns a Lightning invoice to pay for minting tokens.
|
|
50
66
|
*/
|
package/dist/cashu.js
CHANGED
|
@@ -60,6 +60,31 @@ export function decodeCashuToken(tokenStr) {
|
|
|
60
60
|
export function encodeCashuToken(mintUrl, proofs) {
|
|
61
61
|
return getEncodedTokenV4({ mint: mintUrl, proofs });
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Melt proofs back into a Lightning invoice — converts Cashu back to Lightning.
|
|
65
|
+
* Returns the payment preimage and any change proofs.
|
|
66
|
+
*/
|
|
67
|
+
export async function meltProofs(mintUrl, proofs, invoice) {
|
|
68
|
+
const wallet = await getWallet(mintUrl);
|
|
69
|
+
const meltQuote = await wallet.createMeltQuote(invoice);
|
|
70
|
+
const amountNeeded = meltQuote.amount + meltQuote.fee_reserve;
|
|
71
|
+
const total = proofs.reduce((s, p) => s + p.amount, 0);
|
|
72
|
+
if (total < amountNeeded) {
|
|
73
|
+
throw new Error(`Need ${amountNeeded} sats (invoice ${meltQuote.amount} + fee ${meltQuote.fee_reserve}) but only have ${total}`);
|
|
74
|
+
}
|
|
75
|
+
const { send, keep } = await wallet.send(amountNeeded, proofs, { includeFees: true });
|
|
76
|
+
const result = await wallet.meltProofs(meltQuote, send);
|
|
77
|
+
const change = [...keep, ...(result.change || [])];
|
|
78
|
+
return { preimage: result.quote.payment_preimage || '', change };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Estimate total cost to melt (invoice amount + fee_reserve) for a given invoice.
|
|
82
|
+
*/
|
|
83
|
+
export async function estimateMeltFee(mintUrl, invoice) {
|
|
84
|
+
const wallet = await getWallet(mintUrl);
|
|
85
|
+
const quote = await wallet.createMeltQuote(invoice);
|
|
86
|
+
return { amount: quote.amount, fee: quote.fee_reserve, total: quote.amount + quote.fee_reserve };
|
|
87
|
+
}
|
|
63
88
|
/**
|
|
64
89
|
* Request a mint quote — returns a Lightning invoice to pay for minting tokens.
|
|
65
90
|
*/
|
package/dist/session.js
CHANGED
|
@@ -51,8 +51,8 @@ for (const arg of process.argv.slice(2)) {
|
|
|
51
51
|
import { SwarmNode, topicFromKind } from './swarm.js';
|
|
52
52
|
import { queryProviderSkill } from './p2p-customer.js';
|
|
53
53
|
import { walletPayInvoice, walletGetBalance, hasApiKey } from './api.js';
|
|
54
|
-
import { decodeCashuToken, sendCashuToken, createMintQuote, claimMintQuote } from './cashu.js';
|
|
55
|
-
import { parseNwcUri, nwcGetBalance, nwcPayInvoice } from './nwc.js';
|
|
54
|
+
import { decodeCashuToken, sendCashuToken, createMintQuote, claimMintQuote, meltProofs, estimateMeltFee } from './cashu.js';
|
|
55
|
+
import { parseNwcUri, nwcGetBalance, nwcPayInvoice, nwcMakeInvoice } from './nwc.js';
|
|
56
56
|
import { loadSovereignKeys } from './nostr.js';
|
|
57
57
|
import { randomBytes } from 'crypto';
|
|
58
58
|
import { createServer } from 'http';
|
|
@@ -694,6 +694,32 @@ async function endSession() {
|
|
|
694
694
|
}
|
|
695
695
|
log(`Session ended. Total: ${state.totalSpent} sats for ${duration}s.`);
|
|
696
696
|
}
|
|
697
|
+
// Refund remaining Cashu proofs back to NWC wallet
|
|
698
|
+
if (cashuState && cashuState.proofs.length > 0 && nwcParsed) {
|
|
699
|
+
const remaining = cashuState.proofs.reduce((s, p) => s + p.amount, 0);
|
|
700
|
+
if (remaining >= 3) {
|
|
701
|
+
log(`Refunding ${remaining} sats Cashu → NWC wallet...`);
|
|
702
|
+
try {
|
|
703
|
+
// Probe: create 1-sat invoice to discover melt fee_reserve + swap fee
|
|
704
|
+
const probe = await nwcMakeInvoice(nwcParsed, 1000, 'fee-probe');
|
|
705
|
+
const { fee } = await estimateMeltFee(cashuState.mintUrl, probe.bolt11);
|
|
706
|
+
// fee = melt fee_reserve; +1 for Cashu internal swap fee
|
|
707
|
+
const refundAmount = remaining - fee - 1;
|
|
708
|
+
if (refundAmount < 1) {
|
|
709
|
+
warn(`Remaining ${remaining} sats < melt fee (${fee} sats), cannot refund`);
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
const { bolt11 } = await nwcMakeInvoice(nwcParsed, refundAmount * 1000, '2020117-session refund');
|
|
713
|
+
const { preimage } = await meltProofs(cashuState.mintUrl, cashuState.proofs, bolt11);
|
|
714
|
+
cashuState.proofs = [];
|
|
715
|
+
log(`Refunded ${refundAmount} sats (fee: ${fee}, preimage: ${preimage?.slice(0, 16)}...)`);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
catch (e) {
|
|
719
|
+
warn(`Refund failed: ${e.message} — ${remaining} sats lost`);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
697
723
|
// Close HTTP proxy
|
|
698
724
|
if (state.httpServer) {
|
|
699
725
|
state.httpServer.close();
|
|
@@ -774,36 +800,20 @@ async function main() {
|
|
|
774
800
|
}
|
|
775
801
|
}
|
|
776
802
|
else if (nwcUri) {
|
|
777
|
-
// NWC direct:
|
|
803
|
+
// NWC direct: pay provider invoices via Lightning — no Cashu needed
|
|
778
804
|
nwcParsed = parseNwcUri(nwcUri);
|
|
779
805
|
const { balance_msats } = await nwcGetBalance(nwcParsed);
|
|
780
806
|
const balance = Math.floor(balance_msats / 1000);
|
|
781
807
|
if (balance <= 0) {
|
|
782
|
-
warn('NWC wallet balance is 0.
|
|
808
|
+
warn('NWC wallet balance is 0.');
|
|
783
809
|
await node.destroy();
|
|
784
810
|
process.exit(1);
|
|
785
811
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
try {
|
|
789
|
-
const { quote, invoice } = await createMintQuote(MINT_URL, mintAmount);
|
|
790
|
-
log(`Mint quote: ${quote} (invoice: ${invoice.slice(0, 30)}...)`);
|
|
791
|
-
log('Paying mint invoice via NWC...');
|
|
792
|
-
const { preimage } = await nwcPayInvoice(nwcParsed, invoice);
|
|
793
|
-
log(`Invoice paid (preimage: ${preimage?.slice(0, 16)}...)`);
|
|
794
|
-
log('Claiming minted tokens...');
|
|
795
|
-
const token = await claimMintQuote(MINT_URL, mintAmount, quote);
|
|
796
|
-
const { mint, proofs } = decodeCashuToken(token);
|
|
797
|
-
cashuState = { mintUrl: mint, proofs };
|
|
798
|
-
paymentMethod = 'cashu';
|
|
799
|
-
const totalMinted = proofs.reduce((s, p) => s + p.amount, 0);
|
|
800
|
-
log(`Minted ${totalMinted} sats Cashu token — using Cashu payment mode`);
|
|
801
|
-
}
|
|
802
|
-
catch (e) {
|
|
803
|
-
warn(`NWC auto-mint failed: ${e.message}`);
|
|
804
|
-
await node.destroy();
|
|
805
|
-
process.exit(1);
|
|
812
|
+
if (balance < BUDGET) {
|
|
813
|
+
warn(`NWC wallet balance (${balance} sats) < budget (${BUDGET} sats)`);
|
|
806
814
|
}
|
|
815
|
+
paymentMethod = 'invoice';
|
|
816
|
+
log(`Payment: NWC direct (balance: ${balance} sats, pay-per-tick via Lightning)`);
|
|
807
817
|
}
|
|
808
818
|
else if (hasApiKey()) {
|
|
809
819
|
// Platform API fallback: auto-mint Cashu tokens via platform wallet proxy
|
|
@@ -867,6 +877,33 @@ async function main() {
|
|
|
867
877
|
state.satsPerMinute = ackResp.sats_per_minute;
|
|
868
878
|
log(`Provider adjusted rate: ${ackResp.sats_per_minute} sats/min`);
|
|
869
879
|
}
|
|
880
|
+
// Provider may override payment method (e.g. no Lightning Address → force cashu)
|
|
881
|
+
if (ackResp.payment_method && ackResp.payment_method !== paymentMethod) {
|
|
882
|
+
if (ackResp.payment_method === 'cashu' && paymentMethod === 'invoice' && nwcParsed) {
|
|
883
|
+
// Provider doesn't support invoice — fall back to NWC-minted Cashu
|
|
884
|
+
log(`Provider requires Cashu — minting via NWC...`);
|
|
885
|
+
try {
|
|
886
|
+
const { balance_msats } = await nwcGetBalance(nwcParsed);
|
|
887
|
+
const balance = Math.floor(balance_msats / 1000);
|
|
888
|
+
const mintAmount = Math.min(balance, BUDGET);
|
|
889
|
+
const { quote, invoice } = await createMintQuote(MINT_URL, mintAmount);
|
|
890
|
+
const { preimage } = await nwcPayInvoice(nwcParsed, invoice);
|
|
891
|
+
const token = await claimMintQuote(MINT_URL, mintAmount, quote);
|
|
892
|
+
const { mint, proofs } = decodeCashuToken(token);
|
|
893
|
+
cashuState = { mintUrl: mint, proofs };
|
|
894
|
+
paymentMethod = 'cashu';
|
|
895
|
+
log(`Minted ${proofs.reduce((s, p) => s + p.amount, 0)} sats Cashu — fallback ready`);
|
|
896
|
+
}
|
|
897
|
+
catch (e) {
|
|
898
|
+
warn(`Cashu fallback mint failed: ${e.message}`);
|
|
899
|
+
await node.destroy();
|
|
900
|
+
process.exit(1);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
paymentMethod = ackResp.payment_method;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
870
907
|
log(`Session started: ${state.sessionId}`);
|
|
871
908
|
log(`Billing: ${state.satsPerMinute} sats/min via ${paymentMethod}`);
|
|
872
909
|
// 6. Start HTTP proxy
|