diff --git a/src/CashuMint.ts b/src/CashuMint.ts index 32ded58a..0380545e 100644 --- a/src/CashuMint.ts +++ b/src/CashuMint.ts @@ -21,7 +21,7 @@ class CashuMint { /** * @param _mintUrl requires mint URL to create this object */ - constructor(private _mintUrl: string) {} + constructor(private _mintUrl: string) { } get mintUrl() { return this._mintUrl; @@ -143,7 +143,7 @@ class CashuMint { requestBody: splitPayload }); - if (!isObj(data) || !Array.isArray(data?.fst) || !Array.isArray(data?.snd)) { + if (!isObj(data) || !Array.isArray(data?.promises)) { throw new Error('bad response'); } diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index c0c0a09a..eb7770f5 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -179,28 +179,20 @@ class CashuWallet { let newKeys: MintKeys | undefined; try { const amount = tokenEntry.proofs.reduce((total, curr) => total + curr.amount, 0); - const { payload, amount1BlindedMessages, amount2BlindedMessages } = this.createSplitPayload( - 0, + const { payload, blindedMessages } = this.createSplitPayload( amount, tokenEntry.proofs ); - const { fst, snd } = await CashuMint.split(tokenEntry.mint, payload); - const proofs1 = dhke.constructProofs( - fst, - amount1BlindedMessages.rs, - amount1BlindedMessages.secrets, - await this.getKeys(fst, tokenEntry.mint) - ); - const proofs2 = dhke.constructProofs( - snd, - amount2BlindedMessages.rs, - amount2BlindedMessages.secrets, - await this.getKeys(snd, tokenEntry.mint) - ); - proofs.push(...proofs1, ...proofs2); + const { promises } = await CashuMint.split(tokenEntry.mint, payload); + proofs.push(...dhke.constructProofs( + promises, + blindedMessages.rs, + blindedMessages.secrets, + await this.getKeys(promises, tokenEntry.mint) + )); newKeys = tokenEntry.mint === this.mint.mintUrl - ? await this.changedKeys([...(fst || []), ...(snd || [])]) + ? await this.changedKeys([...(promises || [])]) : undefined; } catch (error) { console.error(error); @@ -222,10 +214,10 @@ class CashuWallet { async send(amount: number, proofs: Array): Promise { let amountAvailable = 0; const proofsToSend: Array = []; - const change: Array = []; + const proofsToKeep: Array = []; proofs.forEach((proof) => { if (amountAvailable >= amount) { - change.push(proof); + proofsToKeep.push(proof); return; } amountAvailable = amountAvailable + proof.amount; @@ -236,31 +228,36 @@ class CashuWallet { } if (amount < amountAvailable) { const { amount1, amount2 } = this.splitReceive(amount, amountAvailable); - const { payload, amount1BlindedMessages, amount2BlindedMessages } = this.createSplitPayload( + const { payload, blindedMessages } = this.createSplitPayload( amount1, - amount2, proofsToSend ); - const { fst, snd } = await this.mint.split(payload); - const proofs1 = dhke.constructProofs( - fst, - amount1BlindedMessages.rs, - amount1BlindedMessages.secrets, - await this.getKeys(fst) - ); - const proofs2 = dhke.constructProofs( - snd, - amount2BlindedMessages.rs, - amount2BlindedMessages.secrets, - await this.getKeys(snd) + const { promises } = await this.mint.split(payload); + const proofs = dhke.constructProofs( + promises, + blindedMessages.rs, + blindedMessages.secrets, + await this.getKeys(promises) ); + // sum up proofs until amount2 is reached + const splitProofsToKeep: Array = []; + const splitProofsToSend: Array = []; + let amount2Available = 0; + proofs.forEach((proof) => { + if (amount2Available >= amount2) { + splitProofsToKeep.push(proof); + return; + } + amount2Available = amount2Available + proof.amount; + splitProofsToSend.push(proof); + }); return { - returnChange: [...proofs1, ...change], - send: proofs2, - newKeys: await this.changedKeys([...(fst || []), ...(snd || [])]) + returnChange: [...splitProofsToKeep, ...proofsToKeep], + send: splitProofsToSend, + newKeys: await this.changedKeys([...(promises || [])]) }; } - return { returnChange: change, send: proofsToSend }; + return { returnChange: proofsToKeep, send: proofsToSend }; } async requestTokens( @@ -314,28 +311,38 @@ class CashuWallet { } private createSplitPayload( - amount1: number, - amount2: number, + amount: number, proofsToSend: Array ): { payload: SplitPayload; - amount1BlindedMessages: BlindedTransaction; - amount2BlindedMessages: BlindedTransaction; + blindedMessages: BlindedTransaction; } { - const amount1BlindedMessages = this.createRandomBlindedMessages(amount1); - const amount2BlindedMessages = this.createRandomBlindedMessages(amount2); - const allBlindedMessages: Array = []; + const totalAmount = proofsToSend.reduce((total, curr) => total + curr.amount, 0); + const amount1BlindedMessages = this.createRandomBlindedMessages(amount); + const amount2BlindedMessages = this.createRandomBlindedMessages(totalAmount - amount); + + // join amount1BlindedMessages and amount2BlindedMessages + const blindedMessages: BlindedTransaction = { + blindedMessages: [ + ...amount1BlindedMessages.blindedMessages, + ...amount2BlindedMessages.blindedMessages + ], + secrets: [...amount1BlindedMessages.secrets, ...amount2BlindedMessages.secrets], + rs: [...amount1BlindedMessages.rs, ...amount2BlindedMessages.rs], + amounts: [...amount1BlindedMessages.amounts, ...amount2BlindedMessages.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 - allBlindedMessages.push(...amount1BlindedMessages.blindedMessages); - allBlindedMessages.push(...amount2BlindedMessages.blindedMessages); + allSerializedBlindedMessages.push(...amount1BlindedMessages.blindedMessages); + allSerializedBlindedMessages.push(...amount2BlindedMessages.blindedMessages); const payload = { proofs: proofsToSend, - amount: amount2, - outputs: allBlindedMessages + outputs: allSerializedBlindedMessages }; - return { payload, amount1BlindedMessages, amount2BlindedMessages }; + return { payload, blindedMessages }; } //keep amount 1 send amount 2 private splitReceive( diff --git a/src/model/types/index.ts b/src/model/types/index.ts index 17fc5920..819ec28b 100644 --- a/src/model/types/index.ts +++ b/src/model/types/index.ts @@ -158,10 +158,6 @@ export type SplitPayload = { * Proofs to be split */ proofs: Array; - /** - * Amount that needs to be split from the total amount provided (in proofs) - */ - amount: number; /** * Fresh blinded messages to be signed by the mint to create the split proofs */ @@ -172,13 +168,9 @@ export type SplitPayload = { */ export type SplitResponse = { /** - * represents the left-over amount after the split - */ - fst: Array; - /** - * represents the specified amount when splitting + * represents the outputs after the split */ - snd: Array; + promises: Array; } & ApiError; /** diff --git a/test/wallet.test.ts b/test/wallet.test.ts index 562ff265..eec8aeb8 100644 --- a/test/wallet.test.ts +++ b/test/wallet.test.ts @@ -37,8 +37,7 @@ describe('receive', () => { nock(mintUrl) .post('/split') .reply(200, { - fst: [], - snd: [ + promises: [ { id: 'z32vUtKgNCm1', amount: 1, @@ -99,8 +98,7 @@ describe('receive', () => { nock(mintUrl) .post('/split') .reply(200, { - fst: [], - snd: [ + promises: [ { id: 'test', amount: 1, @@ -287,8 +285,7 @@ describe('send', () => { nock(mintUrl) .post('/split') .reply(200, { - fst: [], - snd: [ + promises: [ { id: '0NI3TUAs1Sfy', amount: 1, @@ -310,14 +307,12 @@ describe('send', () => { nock(mintUrl) .post('/split') .reply(200, { - fst: [ + promises: [ { id: 'z32vUtKgNCm1', amount: 1, C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' - } - ], - snd: [ + }, { id: 'z32vUtKgNCm1', amount: 1, @@ -350,14 +345,12 @@ describe('send', () => { nock(mintUrl) .post('/split') .reply(200, { - fst: [ + promises: [ { id: 'z32vUtKgNCm1', amount: 1, C_: '021179b095a67380ab3285424b563b7aab9818bd38068e1930641b3dceb364d422' - } - ], - snd: [ + }, { id: 'z32vUtKgNCm1', amount: 1, @@ -391,8 +384,7 @@ describe('send', () => { nock(mintUrl) .post('/split') .reply(200, { - fst: [], - snd: [ + promises: [ { id: 'z32vUtKgNCm1', amount: 1,