From 5881369ca7f5ab346cce10b02f3b510c423fe43b Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Thu, 21 Sep 2023 11:03:00 +0200 Subject: [PATCH 01/17] manually merge custom splits and nut-6 --- README.md | 2 +- package.json | 2 +- src/CashuWallet.ts | 84 ++++++++++++++++++++++++++++------------ src/model/types/index.ts | 5 +++ src/utils.ts | 69 +++++++++++++++++++++++++++++---- test/utils.test.ts | 31 +++++++++++++++ test/wallet.test.ts | 60 ++++++++++++++++++++++++++-- 7 files changed, 216 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 8d207362..a987ffa9 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ![npm type definitions](https://img.shields.io/npm/types/@cashu/cashu-ts) ![npm bundle size](https://img.shields.io/bundlephobia/min/@cashu/cashu-ts) -⚠️ **Don't be reckless:** This project is in early development, it does however work with real sats! Always use amounts you don't mind loosing. +⚠️ **Don't be reckless:** This project is in early development, it does however work with real sats! Always use amounts you don't mind losing. Cashu TS is a JavaScript library for [Cashu](https://github.com/cashubtc) wallets written in Typescript. diff --git a/package.json b/package.json index d7603f89..a00db2ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.0-rc.4", + "version": "0.8.0-rc.6", "description": "cashu library for communicating with a cashu mint", "main": "dist/lib/es5/index.js", "module": "dist/lib/es6/index.js", diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index c400693f..c7d3c02e 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -3,6 +3,7 @@ import { CashuMint } from './CashuMint.js'; import * as dhke from './DHKE.js'; import { BlindedMessage } from './model/BlindedMessage.js'; import { + AmountPreference, BlindedMessageData, BlindedTransaction, MintKeys, @@ -17,7 +18,13 @@ import { SplitPayload, TokenEntry } from './model/types/index.js'; -import { cleanToken, deriveKeysetId, getDecodedToken, splitAmount } from './utils.js'; +import { + cleanToken, + deriveKeysetId, + getDecodedToken, + getDefaultAmountPreference, + splitAmount +} from './utils.js'; /** * Class that represents a Cashu wallet. @@ -29,7 +36,6 @@ class CashuWallet { mint: CashuMint; /** - * * @param keys public keys from the mint * @param mint Cashu mint instance is used to make api calls */ @@ -90,7 +96,10 @@ class CashuWallet { feeReserve = await this.getFee(invoice); } const { blindedMessages, secrets, rs } = this.createBlankOutputs(feeReserve); - const payData = await this.mint.melt({ ...paymentPayload, outputs: blindedMessages }); + const payData = await this.mint.melt({ + ...paymentPayload, + outputs: blindedMessages + }); return { isPaid: payData.paid ?? false, preimage: payData.preimage, @@ -179,16 +188,20 @@ class CashuWallet { let newKeys: MintKeys | undefined; try { const amount = tokenEntry.proofs.reduce((total, curr) => total + curr.amount, 0); - const { payload, blindedMessages } = this.createSplitPayload(amount, tokenEntry.proofs); - const { promises } = await CashuMint.split(tokenEntry.mint, payload); - proofs.push( - ...dhke.constructProofs( - promises, - blindedMessages.rs, - blindedMessages.secrets, - await this.getKeys(promises, tokenEntry.mint) - ) + + const { payload, blindedMessages } = this.createSplitPayload( + amount, + tokenEntry.proofs, + getDefaultAmountPreference(amount) + ); + const { promises, error } = await CashuMint.split(tokenEntry.mint, payload); + const newProofs = dhke.constructProofs( + promises, + blindedMessages.rs, + blindedMessages.secrets, + await this.getKeys(promises, tokenEntry.mint) ); + proofs.push(...newProofs); newKeys = tokenEntry.mint === this.mint.mintUrl ? await this.changedKeys([...(promises || [])]) @@ -206,28 +219,43 @@ class CashuWallet { /** * Splits and creates sendable tokens - * @param amount amount to send + * if no amount is specified, the amount is implied by the cumulative amount of all proofs + * if both amount and preference are set, but the preference cannot fulfill the amount, then we use the default split + * @param amount amount to send while performing the optimal split (least proofs possible). can be set to undefined if preference is set * @param proofs proofs matching that amount + * @param preference optional preference for splitting proofs into specific amounts. overrides amount param * @returns promise of the change- and send-proofs */ - async send(amount: number, proofs: Array): Promise { + async send( + amount: number, + proofs: Array, + preference?: Array + ): Promise { + if (preference) { + amount = preference?.reduce((acc, curr) => acc + curr.amount * curr.count, 0); + } + let amountAvailable = 0; const proofsToSend: Array = []; const proofsToKeep: Array = []; proofs.forEach((proof) => { if (amountAvailable >= amount) { proofsToKeep.push(proof); - return; } amountAvailable = amountAvailable + proof.amount; proofsToSend.push(proof); }); + if (amount > amountAvailable) { throw new Error('Not enough funds available'); } - if (amount < amountAvailable) { + if (amount < amountAvailable || preference) { const { amount1, amount2 } = this.splitReceive(amount, amountAvailable); - const { payload, blindedMessages } = this.createSplitPayload(amount1, proofsToSend); + const { payload, blindedMessages } = this.createSplitPayload( + amount1, + proofsToSend, + preference + ); const { promises } = await this.mint.split(payload); const proofs = dhke.constructProofs( promises, @@ -264,9 +292,13 @@ class CashuWallet { */ async requestTokens( amount: number, - hash: string + hash: string, + AmountPreference?: Array ): Promise<{ proofs: Array; newKeys?: MintKeys }> { - const { blindedMessages, secrets, rs } = this.createRandomBlindedMessages(amount); + const { blindedMessages, secrets, rs } = this.createRandomBlindedMessages( + amount, + AmountPreference + ); const payloads = { outputs: blindedMessages }; const { promises } = await this.mint.mint(payloads, hash); return { @@ -337,14 +369,18 @@ class CashuWallet { */ private createSplitPayload( amount: number, - proofsToSend: Array + proofsToSend: Array, + preference?: Array ): { payload: SplitPayload; blindedMessages: BlindedTransaction; } { const totalAmount = proofsToSend.reduce((total, curr) => total + curr.amount, 0); const amount1BlindedMessages = this.createRandomBlindedMessages(amount); - const amount2BlindedMessages = this.createRandomBlindedMessages(totalAmount - amount); + const amount2BlindedMessages = this.createRandomBlindedMessages( + totalAmount - amount, + preference + ); // join amount1BlindedMessages and amount2BlindedMessages const blindedMessages: BlindedTransaction = { @@ -369,7 +405,6 @@ class CashuWallet { }; return { payload, blindedMessages }; } - //keep amount 1 send amount 2 private splitReceive( amount: number, amountAvailable: number @@ -385,12 +420,13 @@ class CashuWallet { * @returns blinded messages, secrets, rs, and amounts */ private createRandomBlindedMessages( - amount: number + amount: number, + amountPreference?: Array ): BlindedMessageData & { amounts: Array } { const blindedMessages: Array = []; const secrets: Array = []; const rs: Array = []; - const amounts = splitAmount(amount); + const amounts = splitAmount(amount, amountPreference); for (let i = 0; i < amounts.length; i++) { const secret = randomBytes(32); secrets.push(secret); diff --git a/src/model/types/index.ts b/src/model/types/index.ts index 819ec28b..1fd13cdc 100644 --- a/src/model/types/index.ts +++ b/src/model/types/index.ts @@ -336,3 +336,8 @@ export type GetInfoResponse = { motd?: string; parameter: { peg_out_only: boolean }; }; + +export type AmountPreference = { + amount: number; + count: number; +}; diff --git a/src/utils.ts b/src/utils.ts index c9e0f381..541a94f6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,12 +1,27 @@ import { encodeBase64ToJson, encodeJsonToBase64 } from './base64.js'; -import { MintKeys, Proof, Token, TokenEntry, TokenV2 } from './model/types/index.js'; +import { + AmountPreference, + MintKeys, + Proof, + Token, + TokenEntry, + TokenV2 +} from './model/types/index.js'; import { TOKEN_PREFIX, TOKEN_VERSION } from './utils/Constants.js'; import { bytesToHex } from '@noble/curves/abstract/utils'; import { sha256 } from '@noble/hashes/sha256'; import { Buffer } from 'buffer/'; -function splitAmount(value: number): Array { +function splitAmount(value: number, amountPreference?: Array): Array { const chunks: Array = []; + if (amountPreference) { + chunks.push(...getPreference(value, amountPreference)); + value = + value - + chunks.reduce((curr, acc) => { + return curr + acc; + }, 0); + } for (let i = 0; i < 32; i++) { const mask: number = 1 << i; if ((value & mask) !== 0) { @@ -16,6 +31,37 @@ function splitAmount(value: number): Array { return chunks; } +function isPowerOfTwo(number: number) { + return number && !(number & (number - 1)); +} + +function getPreference(amount: number, preferredAmounts: Array): Array { + const chunks: Array = []; + let accumulator = 0; + preferredAmounts.forEach((pa) => { + if (!isPowerOfTwo(pa.amount)) { + throw new Error( + 'Provided amount preferences contain non-power-of-2 numbers. Use only ^2 numbers' + ); + } + for (let i = 1; i <= pa.count; i++) { + accumulator += pa.amount; + if (accumulator > amount) { + return; + } + chunks.push(pa.amount); + } + }); + return chunks; +} + +function getDefaultAmountPreference(amount: number): Array { + const amounts = splitAmount(amount); + return amounts.map((a) => { + return { amount: a, count: 1 }; + }); +} + function bytesToNumber(bytes: Uint8Array): bigint { return hexToNumber(bytesToHex(bytes)); } @@ -107,11 +153,17 @@ export function cleanToken(token: Token): Token { tokenEntryMap[tokenEntry.mint].proofs.push(...[...tokenEntry.proofs]); continue; } - tokenEntryMap[tokenEntry.mint] = { mint: tokenEntry.mint, proofs: [...tokenEntry.proofs] }; + tokenEntryMap[tokenEntry.mint] = { + mint: tokenEntry.mint, + proofs: [...tokenEntry.proofs] + }; } return { memo: token?.memo, - token: Object.values(tokenEntryMap).map((x) => ({ ...x, proofs: sortProofsById(x.proofs) })) + token: Object.values(tokenEntryMap).map((x) => ({ + ...x, + proofs: sortProofsById(x.proofs) + })) }; } export function sortProofsById(proofs: Array) { @@ -137,10 +189,11 @@ export function joinUrls(...parts: string[]): string { } export { - hexToNumber, - splitAmount, - bytesToNumber, bigIntStringify, + bytesToNumber, getDecodedToken, - getEncodedToken + getEncodedToken, + hexToNumber, + splitAmount, + getDefaultAmountPreference }; diff --git a/test/utils.test.ts b/test/utils.test.ts index 2fc5bc52..3795a91a 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -1,3 +1,4 @@ +import { AmountPreference } from '../src/model/types/index.js'; import * as utils from '../src/utils.js'; describe('test split amounts ', () => { @@ -11,6 +12,36 @@ describe('test split amounts ', () => { }); }); +describe('test split custom amounts ', () => { + const fiveToOne: AmountPreference = { amount: 1, count: 5 }; + test('testing amount 5', async () => { + const chunks = utils.splitAmount(5, [fiveToOne]); + expect(chunks).toStrictEqual([1, 1, 1, 1, 1]); + }); + const tenToOneAndTwo: Array = [ + { amount: 1, count: 2 }, + { amount: 2, count: 4 } + ]; + test('testing amount 10', async () => { + const chunks = utils.splitAmount(10, tenToOneAndTwo); + expect(chunks).toStrictEqual([1, 1, 2, 2, 2, 2]); + }); + const fiveTwelve: Array = [{ amount: 512, count: 2 }]; + test('testing amount 516', async () => { + const chunks = utils.splitAmount(518, fiveTwelve); + expect(chunks).toStrictEqual([512, 2, 4]); + }); + const illegal: Array = [{ amount: 3, count: 2 }]; + test('testing non pow2', async () => { + expect(() => utils.splitAmount(6, illegal)).toThrowError(); + }); + const empty: Array = []; + test('testing empty', async () => { + const chunks = utils.splitAmount(5, empty); + expect(chunks).toStrictEqual([1, 4]); + }); +}); + describe('test decode token', () => { test('testing v1 Token', () => { const token = diff --git a/test/wallet.test.ts b/test/wallet.test.ts index eec8aeb8..1c86a295 100644 --- a/test/wallet.test.ts +++ b/test/wallet.test.ts @@ -361,7 +361,6 @@ describe('send', () => { const wallet = new CashuWallet(mint); const overpayProofs = [ - ...proofs, { id: 'z32vUtKgNCm1', amount: 2, @@ -372,14 +371,69 @@ describe('send', () => { const result = await wallet.send(1, overpayProofs); expect(result.send).toHaveLength(1); - expect(result.send[0]).toMatchObject({ amount: 1, id: '0NI3TUAs1Sfy' }); + expect(result.send[0]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); expect(/[0-9a-f]{64}/.test(result.send[0].C)).toBe(true); expect(/[A-Za-z0-9+/]{43}=/.test(result.send[0].secret)).toBe(true); expect(result.returnChange).toHaveLength(1); - expect(result.returnChange[0]).toMatchObject({ amount: 2, id: 'z32vUtKgNCm1' }); + expect(result.returnChange[0]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); expect(/[0-9a-f]{64}/.test(result.returnChange[0].C)).toBe(true); expect(/[A-Za-z0-9+/]{43}=/.test(result.returnChange[0].secret)).toBe(true); }); + test('test send preference', async () => { + nock(mintUrl) + .post('/split') + .reply(200, { + promises: [ + { + id: 'z32vUtKgNCm1', + amount: 1, + C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' + }, + { + id: 'z32vUtKgNCm1', + amount: 1, + C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' + }, + { + id: 'z32vUtKgNCm1', + amount: 1, + C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' + }, + { + id: 'z32vUtKgNCm1', + amount: 1, + C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' + } + ] + }); + const wallet = new CashuWallet(mint); + + const overpayProofs = [ + { + id: 'z32vUtKgNCm1', + amount: 2, + secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' + }, + { + id: 'z32vUtKgNCm1', + amount: 2, + secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' + } + ]; + const result = await wallet.send(4, overpayProofs, [{ amount: 1, count: 4 }]); + + expect(result.send).toHaveLength(4); + expect(result.send[0]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); + expect(result.send[1]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); + expect(result.send[2]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); + expect(result.send[3]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); + expect(/[0-9a-f]{64}/.test(result.send[0].C)).toBe(true); + expect(/[A-Za-z0-9+/]{43}=/.test(result.send[0].secret)).toBe(true); + expect(result.returnChange).toHaveLength(0); + }); + test('test send not enough funds', async () => { nock(mintUrl) .post('/split') From 0f4fd09bd0fbda7c5c2c722793cad2c0019a888e Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Thu, 21 Sep 2023 11:05:00 +0200 Subject: [PATCH 02/17] bump version --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 81cc24c8..066f83ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "@gandlaf21/cashu-ts", - "version": "0.8.0-rc.8", + "name": "@cashu/cashu-ts", + "version": "0.8.2.rc-1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "@gandlaf21/cashu-ts", - "version": "0.8.0-rc.8", + "name": "@cashu/cashu-ts", + "version": "0.8.2.rc-1", "license": "MIT", "dependencies": { "@gandlaf21/bolt11-decode": "^3.0.6", diff --git a/package.json b/package.json index a00db2ec..76efcc58 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.0-rc.6", + "version": "0.8.2.rc-1", "description": "cashu library for communicating with a cashu mint", "main": "dist/lib/es5/index.js", "module": "dist/lib/es6/index.js", From 63c35dc0ad52afffe400acc108a10f24536b2c8a Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Thu, 21 Sep 2023 11:53:04 +0200 Subject: [PATCH 03/17] testcase for overpaying preference --- test/wallet.test.ts | 55 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/test/wallet.test.ts b/test/wallet.test.ts index 1c86a295..f20abd1d 100644 --- a/test/wallet.test.ts +++ b/test/wallet.test.ts @@ -434,6 +434,61 @@ describe('send', () => { expect(result.returnChange).toHaveLength(0); }); + test('test send preference overpay', async () => { + nock(mintUrl) + .post('/split') + .reply(200, { + promises: [ + { + id: 'z32vUtKgNCm1', + amount: 1, + C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' + }, + { + id: 'z32vUtKgNCm1', + amount: 1, + C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' + }, + { + id: 'z32vUtKgNCm1', + amount: 1, + C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' + }, + { + id: 'z32vUtKgNCm1', + amount: 1, + C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' + } + ] + }); + const wallet = new CashuWallet(mint); + + const overpayProofs = [ + { + id: 'z32vUtKgNCm1', + amount: 2, + secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' + }, + { + id: 'z32vUtKgNCm1', + amount: 2, + secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' + } + ]; + const result = await wallet.send(4, overpayProofs, [{ amount: 1, count: 3 }]); + + expect(result.send).toHaveLength(3); + expect(result.send[0]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); + expect(result.send[1]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); + expect(result.send[2]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); + expect(/[0-9a-f]{64}/.test(result.send[0].C)).toBe(true); + expect(/[A-Za-z0-9+/]{43}=/.test(result.send[0].secret)).toBe(true); + expect(result.returnChange).toHaveLength(1); + expect(result.returnChange[0]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); + }); + test('test send not enough funds', async () => { nock(mintUrl) .post('/split') From e178e80b9abdca3b9a83e56923603f4fdd6b1b63 Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Thu, 21 Sep 2023 19:22:36 +0700 Subject: [PATCH 04/17] fix version typo and update deps --- package-lock.json | 112 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/package-lock.json b/package-lock.json index 066f83ba..9b1de4ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.2.rc-1", + "version": "0.8.2-rc.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@cashu/cashu-ts", - "version": "0.8.2.rc-1", + "version": "0.8.2-rc.1", "license": "MIT", "dependencies": { "@gandlaf21/bolt11-decode": "^3.0.6", @@ -1435,9 +1435,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1579,9 +1579,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1638,9 +1638,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -2112,9 +2112,9 @@ } }, "node_modules/builtins/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -2819,9 +2819,9 @@ } }, "node_modules/eslint-plugin-n/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4492,9 +4492,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5506,9 +5506,9 @@ } }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -5858,9 +5858,9 @@ } }, "node_modules/ts-jest/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6211,9 +6211,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -7424,9 +7424,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -7505,9 +7505,9 @@ } }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -7547,9 +7547,9 @@ } }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -7882,9 +7882,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -8486,9 +8486,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -9607,9 +9607,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -10328,9 +10328,9 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true }, "shebang-command": { @@ -10575,9 +10575,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -10840,9 +10840,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "wrap-ansi": { diff --git a/package.json b/package.json index 76efcc58..96aa70b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.2.rc-1", + "version": "0.8.2-rc.1", "description": "cashu library for communicating with a cashu mint", "main": "dist/lib/es5/index.js", "module": "dist/lib/es6/index.js", From 32859548d46f324c83915c30a800283a80b5e012 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Thu, 21 Sep 2023 16:49:44 +0200 Subject: [PATCH 05/17] merge --- src/CashuWallet.ts | 49 +++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index c7d3c02e..d0cd4954 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -250,12 +250,8 @@ class CashuWallet { throw new Error('Not enough funds available'); } if (amount < amountAvailable || preference) { - const { amount1, amount2 } = this.splitReceive(amount, amountAvailable); - const { payload, blindedMessages } = this.createSplitPayload( - amount1, - proofsToSend, - preference - ); + const { amountKeep, amountSend } = this.splitReceive(amount, amountAvailable); + const { payload, blindedMessages } = this.createSplitPayload(amountSend, proofsToSend, preference); const { promises } = await this.mint.split(payload); const proofs = dhke.constructProofs( promises, @@ -266,13 +262,13 @@ class CashuWallet { // sum up proofs until amount2 is reached const splitProofsToKeep: Array = []; const splitProofsToSend: Array = []; - let amount2Available = 0; + let amountSendCounter = 0; proofs.forEach((proof) => { - if (amount2Available >= amount2) { + if (amountSendCounter >= amountSend) { splitProofsToKeep.push(proof); return; } - amount2Available = amount2Available + proof.amount; + amountSendCounter = amountSendCounter + proof.amount; splitProofsToSend.push(proof); }); return { @@ -376,42 +372,33 @@ class CashuWallet { blindedMessages: BlindedTransaction; } { const totalAmount = proofsToSend.reduce((total, curr) => total + curr.amount, 0); - const amount1BlindedMessages = this.createRandomBlindedMessages(amount); - const amount2BlindedMessages = this.createRandomBlindedMessages( - totalAmount - amount, - preference - ); + const keepBlindedMessages = this.createRandomBlindedMessages(totalAmount - amount); + const sendBlindedMessages = this.createRandomBlindedMessages(amount, preference); - // join amount1BlindedMessages and amount2BlindedMessages + // join keepBlindedMessages and sendBlindedMessages const blindedMessages: BlindedTransaction = { blindedMessages: [ - ...amount1BlindedMessages.blindedMessages, - ...amount2BlindedMessages.blindedMessages + ...keepBlindedMessages.blindedMessages, + ...sendBlindedMessages.blindedMessages ], - secrets: [...amount1BlindedMessages.secrets, ...amount2BlindedMessages.secrets], - rs: [...amount1BlindedMessages.rs, ...amount2BlindedMessages.rs], - amounts: [...amount1BlindedMessages.amounts, ...amount2BlindedMessages.amounts] + secrets: [...keepBlindedMessages.secrets, ...sendBlindedMessages.secrets], + rs: [...keepBlindedMessages.rs, ...sendBlindedMessages.rs], + amounts: [...keepBlindedMessages.amounts, ...sendBlindedMessages.amounts] }; - const allSerializedBlindedMessages: Array = []; - // the order of this array apparently matters if it's the other way around, - // the mint complains that the split is not as expected - allSerializedBlindedMessages.push(...amount1BlindedMessages.blindedMessages); - allSerializedBlindedMessages.push(...amount2BlindedMessages.blindedMessages); - const payload = { proofs: proofsToSend, - outputs: allSerializedBlindedMessages + outputs: [...blindedMessages.blindedMessages] }; return { payload, blindedMessages }; } private splitReceive( amount: number, amountAvailable: number - ): { amount1: number; amount2: number } { - const amount1: number = amountAvailable - amount; - const amount2: number = amount; - return { amount1, amount2 }; + ): { amountKeep: number; amountSend: number } { + const amountKeep: number = amountAvailable - amount; + const amountSend: number = amount; + return { amountKeep, amountSend }; } /** From a69bdc6b1bc14177fa92811a04d0580eed464dd1 Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Thu, 21 Sep 2023 21:58:18 +0700 Subject: [PATCH 06/17] 0.8.2-rc.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9b1de4ba..a2f67161 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.1", + "version": "0.8.2-rc.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.1", + "version": "0.8.2-rc.2", "license": "MIT", "dependencies": { "@gandlaf21/bolt11-decode": "^3.0.6", diff --git a/package.json b/package.json index 96aa70b1..27fcd689 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.1", + "version": "0.8.2-rc.2", "description": "cashu library for communicating with a cashu mint", "main": "dist/lib/es5/index.js", "module": "dist/lib/es6/index.js", From c93306c7b655a929d678507d97e6909b0d30844b Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Fri, 22 Sep 2023 15:56:00 +0700 Subject: [PATCH 07/17] fix split wrong proofs --- src/CashuWallet.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index d0cd4954..02e63b17 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -241,6 +241,7 @@ class CashuWallet { proofs.forEach((proof) => { if (amountAvailable >= amount) { proofsToKeep.push(proof); + return } amountAvailable = amountAvailable + proof.amount; proofsToSend.push(proof); From 257475830ca101b276d48fd02ee484a77f8438da Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Fri, 22 Sep 2023 15:56:57 +0700 Subject: [PATCH 08/17] 0.8.2-rc.3 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a2f67161..a726533d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.2", + "version": "0.8.2-rc.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.2", + "version": "0.8.2-rc.3", "license": "MIT", "dependencies": { "@gandlaf21/bolt11-decode": "^3.0.6", diff --git a/package.json b/package.json index 27fcd689..30250833 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.2", + "version": "0.8.2-rc.3", "description": "cashu library for communicating with a cashu mint", "main": "dist/lib/es5/index.js", "module": "dist/lib/es6/index.js", From 81d309611df774eaf1625c8c01462a8a7f33360a Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Fri, 22 Sep 2023 16:55:29 +0700 Subject: [PATCH 09/17] fix sequence of proofs --- src/CashuWallet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index 02e63b17..8007e45e 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -264,7 +264,7 @@ class CashuWallet { const splitProofsToKeep: Array = []; const splitProofsToSend: Array = []; let amountSendCounter = 0; - proofs.forEach((proof) => { + proofs.reverse().forEach((proof) => { if (amountSendCounter >= amountSend) { splitProofsToKeep.push(proof); return; From f999da045a20e348793b4d77c3b74a3ce10d5e60 Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Fri, 22 Sep 2023 16:55:48 +0700 Subject: [PATCH 10/17] 0.8.2-rc.4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a726533d..3bac3fff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.3", + "version": "0.8.2-rc.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.3", + "version": "0.8.2-rc.4", "license": "MIT", "dependencies": { "@gandlaf21/bolt11-decode": "^3.0.6", diff --git a/package.json b/package.json index 30250833..5b689fef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.3", + "version": "0.8.2-rc.4", "description": "cashu library for communicating with a cashu mint", "main": "dist/lib/es5/index.js", "module": "dist/lib/es6/index.js", From 89dd6632dc282e342043b4a79d50c2a4bb3d7842 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 22 Sep 2023 14:00:19 +0200 Subject: [PATCH 11/17] reverse loop --- src/CashuWallet.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index 8007e45e..9dce2a7a 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -264,13 +264,14 @@ class CashuWallet { const splitProofsToKeep: Array = []; const splitProofsToSend: Array = []; let amountSendCounter = 0; - proofs.reverse().forEach((proof) => { - if (amountSendCounter >= amountSend) { - splitProofsToKeep.push(proof); + proofs.forEach((proof) => { + if (amountSendCounter < amountSend) { + amountSendCounter = amountSendCounter + proof.amount; + splitProofsToSend.push(proof); return; + } - amountSendCounter = amountSendCounter + proof.amount; - splitProofsToSend.push(proof); + splitProofsToKeep.push(proof); }); return { returnChange: [...splitProofsToKeep, ...proofsToKeep], From ed5010ad072bd9325474ba294e4004460516c65b Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 22 Sep 2023 14:14:18 +0200 Subject: [PATCH 12/17] revert the reverse --- src/CashuWallet.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index 9dce2a7a..9fd407f4 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -263,15 +263,15 @@ class CashuWallet { // sum up proofs until amount2 is reached const splitProofsToKeep: Array = []; const splitProofsToSend: Array = []; - let amountSendCounter = 0; + let amountKeepCounter = 0; proofs.forEach((proof) => { - if (amountSendCounter < amountSend) { - amountSendCounter = amountSendCounter + proof.amount; - splitProofsToSend.push(proof); + if (amountKeepCounter < amount - amountSend) { + amountKeepCounter += proof.amount; + splitProofsToKeep.push(proof); return; } - splitProofsToKeep.push(proof); + splitProofsToSend.push(proof); }); return { returnChange: [...splitProofsToKeep, ...proofsToKeep], From 7d07413bac7763e75438c85943f9f9674f003ce8 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 22 Sep 2023 14:30:31 +0200 Subject: [PATCH 13/17] amountKeep --- src/CashuWallet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index 9fd407f4..d6531362 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -265,7 +265,7 @@ class CashuWallet { const splitProofsToSend: Array = []; let amountKeepCounter = 0; proofs.forEach((proof) => { - if (amountKeepCounter < amount - amountSend) { + if (amountKeepCounter < amountKeep) { amountKeepCounter += proof.amount; splitProofsToKeep.push(proof); return; From 214b75bb5f83778ba45d5101571e85603968126e Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Sat, 23 Sep 2023 14:28:10 +0700 Subject: [PATCH 14/17] add preference to receive --- src/CashuWallet.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index d6531362..55c45ba3 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -140,9 +140,10 @@ class CashuWallet { /** * Receive an encoded Cashu token * @param encodedToken Cashu token + * @param preference optional preference for splitting proofs into specific amounts * @returns New token with newly created proofs, token entries that had errors, and newKeys if they have changed */ - async receive(encodedToken: string): Promise { + async receive(encodedToken: string, preference?: Array): Promise { const { token } = cleanToken(getDecodedToken(encodedToken)); const tokenEntries: Array = []; const tokenEntriesWithError: Array = []; @@ -156,7 +157,7 @@ class CashuWallet { proofsWithError, proofs, newKeys: newKeysFromReceive - } = await this.receiveTokenEntry(tokenEntry); + } = await this.receiveTokenEntry(tokenEntry, preference); if (proofsWithError?.length) { tokenEntriesWithError.push(tokenEntry); continue; @@ -180,19 +181,22 @@ class CashuWallet { /** * Receive a single cashu token entry * @param tokenEntry a single entry of a cashu token + * @param preference optional preference for splitting proofs into specific amounts. * @returns New token entry with newly created proofs, proofs that had errors, and newKeys if they have changed */ - async receiveTokenEntry(tokenEntry: TokenEntry): Promise { + async receiveTokenEntry(tokenEntry: TokenEntry, preference?: Array): Promise { const proofsWithError: Array = []; const proofs: Array = []; let newKeys: MintKeys | undefined; try { const amount = tokenEntry.proofs.reduce((total, curr) => total + curr.amount, 0); - + if (!preference) { + preference = getDefaultAmountPreference(amount) + } const { payload, blindedMessages } = this.createSplitPayload( amount, tokenEntry.proofs, - getDefaultAmountPreference(amount) + preference ); const { promises, error } = await CashuMint.split(tokenEntry.mint, payload); const newProofs = dhke.constructProofs( From 423e4df474e423c502a266b06dcda760782a6432 Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Sat, 23 Sep 2023 14:38:17 +0700 Subject: [PATCH 15/17] add receive custom split test --- test/wallet.test.ts | 83 ++++++++++++++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 24 deletions(-) diff --git a/test/wallet.test.ts b/test/wallet.test.ts index f20abd1d..b375661c 100644 --- a/test/wallet.test.ts +++ b/test/wallet.test.ts @@ -33,32 +33,67 @@ describe('test fees', () => { describe('receive', () => { const tokenInput = 'eyJwcm9vZnMiOlt7ImlkIjoiL3VZQi82d1duWWtVIiwiYW1vdW50IjoxLCJzZWNyZXQiOiJBZmtRYlJYQUc1UU1tT3ArbG9vRzQ2OXBZWTdiaStqbEcxRXRDT2tIa2hZPSIsIkMiOiIwMmY4NWRkODRiMGY4NDE4NDM2NmNiNjkxNDYxMDZhZjdjMGYyNmYyZWUwYWQyODdhM2U1ZmE4NTI1MjhiYjI5ZGYifV0sIm1pbnRzIjpbeyJ1cmwiOiJodHRwczovL2xlZ2VuZC5sbmJpdHMuY29tL2Nhc2h1L2FwaS92MS80Z3I5WGNtejNYRWtVTndpQmlRR29DIiwiaWRzIjpbIi91WUIvNndXbllrVSJdfV19'; - test('test receive', async () => { - nock(mintUrl) - .post('/split') - .reply(200, { - promises: [ - { - id: 'z32vUtKgNCm1', - amount: 1, - C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' - } - ] + test('test receive', async () => { + nock(mintUrl) + .post('/split') + .reply(200, { + promises: [ + { + id: 'z32vUtKgNCm1', + amount: 1, + C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' + } + ] + }); + const wallet = new CashuWallet(mint); + + const { token: t, tokensWithErrors } = await wallet.receive(tokenInput); + + expect(t.token).toHaveLength(1); + expect(t.token[0].proofs).toHaveLength(1); + expect(t.token[0]).toMatchObject({ + proofs: [{ amount: 1, id: 'z32vUtKgNCm1' }], + mint: 'https://legend.lnbits.com/cashu/api/v1/4gr9Xcmz3XEkUNwiBiQGoC' }); - const wallet = new CashuWallet(mint); - - const { token: t, tokensWithErrors } = await wallet.receive(tokenInput); - - expect(t.token).toHaveLength(1); - expect(t.token[0].proofs).toHaveLength(1); - expect(t.token[0]).toMatchObject({ - proofs: [{ amount: 1, id: 'z32vUtKgNCm1' }], - mint: 'https://legend.lnbits.com/cashu/api/v1/4gr9Xcmz3XEkUNwiBiQGoC' + expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].C)).toBe(true); + expect(/[A-Za-z0-9+/]{43}=/.test(t.token[0].proofs[0].secret)).toBe(true); + expect(tokensWithErrors).toBe(undefined); + }); + test('test receive custom split', async () => { + nock(mintUrl) + .post('/split') + .reply(200, { + promises: [ + { + id: 'z32vUtKgNCm1', + amount: 1, + C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' + }, + { + id: 'z32vUtKgNCm1', + amount: 1, + C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' + }, + { + id: 'z32vUtKgNCm1', + amount: 1, + C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' + } + ] + }); + const wallet = new CashuWallet(mint); + const token3sat = 'eyJwcm9vZnMiOlt7ImlkIjoiL3VZQi82d1duWWtVIiwiYW1vdW50IjoxLCJzZWNyZXQiOiJBZmtRYlJYQUc1UU1tT3ArbG9vRzQ2OXBZWTdiaStqbEcxRXRDT2tIa2hZPSIsIkMiOiIwMmY4NWRkODRiMGY4NDE4NDM2NmNiNjkxNDYxMDZhZjdjMGYyNmYyZWUwYWQyODdhM2U1ZmE4NTI1MjhiYjI5ZGYifSx7ImlkIjoiL3VZQi82d1duWWtVIiwiYW1vdW50IjoxLCJzZWNyZXQiOiJBZmtRYlJYQUc1UU1tT3ArbG9vRzQ2OXBZWTdiaStqbEcxRXRDT2tIa2hZPSIsIkMiOiIwMmY4NWRkODRiMGY4NDE4NDM2NmNiNjkxNDYxMDZhZjdjMGYyNmYyZWUwYWQyODdhM2U1ZmE4NTI1MjhiYjI5ZGYifSx7ImlkIjoiL3VZQi82d1duWWtVIiwiYW1vdW50IjoxLCJzZWNyZXQiOiJBZmtRYlJYQUc1UU1tT3ArbG9vRzQ2OXBZWTdiaStqbEcxRXRDT2tIa2hZPSIsIkMiOiIwMmY4NWRkODRiMGY4NDE4NDM2NmNiNjkxNDYxMDZhZjdjMGYyNmYyZWUwYWQyODdhM2U1ZmE4NTI1MjhiYjI5ZGYifV0sIm1pbnRzIjpbeyJ1cmwiOiJodHRwczovL2xlZ2VuZC5sbmJpdHMuY29tL2Nhc2h1L2FwaS92MS80Z3I5WGNtejNYRWtVTndpQmlRR29DIiwiaWRzIjpbIi91WUIvNndXbllrVSJdfV19' + const { token: t, tokensWithErrors } = await wallet.receive(token3sat, [{amount:1, count:3}]); + + expect(t.token).toHaveLength(1); + expect(t.token[0].proofs).toHaveLength(3); + expect(t.token[0]).toMatchObject({ + proofs: [{ amount: 1, id: 'z32vUtKgNCm1' },{ amount: 1, id: 'z32vUtKgNCm1' },{ amount: 1, id: 'z32vUtKgNCm1' }], + }); + expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].C)).toBe(true); + expect(/[A-Za-z0-9+/]{43}=/.test(t.token[0].proofs[0].secret)).toBe(true); + expect(tokensWithErrors).toBe(undefined); }); - expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(t.token[0].proofs[0].secret)).toBe(true); - expect(tokensWithErrors).toBe(undefined); - }); test('test receive tokens already spent', async () => { const msg = 'tokens already spent. Secret: oEpEuViVHUV2vQH81INUbq++Yv2w3u5H0LhaqXJKeR0='; nock(mintUrl).post('/split').reply(200, { detail: msg }); From 83cf880b66a3b079c0e84572be3023b005970c42 Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Sat, 23 Sep 2023 14:38:31 +0700 Subject: [PATCH 16/17] 0.8.2-rc.5 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3bac3fff..fa3bb442 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.4", + "version": "0.8.2-rc.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.4", + "version": "0.8.2-rc.5", "license": "MIT", "dependencies": { "@gandlaf21/bolt11-decode": "^3.0.6", diff --git a/package.json b/package.json index 5b689fef..cf2a6287 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.4", + "version": "0.8.2-rc.5", "description": "cashu library for communicating with a cashu mint", "main": "dist/lib/es5/index.js", "module": "dist/lib/es6/index.js", From 0ddcbe35017e14d12d48b22d631a7a9f6985777f Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Sat, 23 Sep 2023 14:48:37 +0700 Subject: [PATCH 17/17] 0.8.2-rc.6 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index fa3bb442..8e022bd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.5", + "version": "0.8.2-rc.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.5", + "version": "0.8.2-rc.6", "license": "MIT", "dependencies": { "@gandlaf21/bolt11-decode": "^3.0.6", diff --git a/package.json b/package.json index cf2a6287..3af73c6f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cashu/cashu-ts", - "version": "0.8.2-rc.5", + "version": "0.8.2-rc.6", "description": "cashu library for communicating with a cashu mint", "main": "dist/lib/es5/index.js", "module": "dist/lib/es6/index.js",