From daf8d9c65460d5f9cda7568470f49b4ef2427c5f Mon Sep 17 00:00:00 2001 From: onmax Date: Wed, 7 Aug 2024 16:32:44 +0200 Subject: [PATCH] feat(sinpe): Finished UI for sinpe swap --- client/src/PublicRequest.ts | 2 +- src/assets/icons/sinpe-movil.svg | 1 + src/request/sign-swap/SignSwap.js | 12 +-- src/request/sign-swap/SignSwapApi.js | 135 ++++++++++++++++----------- types/Keyguard.d.ts | 6 +- 5 files changed, 92 insertions(+), 64 deletions(-) create mode 100644 src/assets/icons/sinpe-movil.svg diff --git a/client/src/PublicRequest.ts b/client/src/PublicRequest.ts index dd1951eb6..c21a23609 100644 --- a/client/src/PublicRequest.ts +++ b/client/src/PublicRequest.ts @@ -311,7 +311,7 @@ export type SignSwapRequestCommon = SimpleRequest & { & { amount: number, fee: number, - recipientLabel?: string, + senderLabel?: string, } ), redeem: ( diff --git a/src/assets/icons/sinpe-movil.svg b/src/assets/icons/sinpe-movil.svg new file mode 100644 index 000000000..3b300a2af --- /dev/null +++ b/src/assets/icons/sinpe-movil.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/request/sign-swap/SignSwap.js b/src/request/sign-swap/SignSwap.js index 375930d97..610a58580 100644 --- a/src/request/sign-swap/SignSwap.js +++ b/src/request/sign-swap/SignSwap.js @@ -210,9 +210,8 @@ class SignSwap { $leftIdenticon.innerHTML = TemplateTags.hasVars(0)``; $leftLabel.textContent = request.fund.bankLabel || I18n.translatePhrase('sign-swap-your-bank'); } else if (request.fund.type === 'CRC') { - $leftIdenticon.innerHTML = 'TODO'; - // TODO Translation - $leftLabel.textContent = request.fund.sinpeLabel || I18n.translatePhrase('sign-swap-your-bank'); + $leftIdenticon.innerHTML = TemplateTags.hasVars(0)``; + $leftLabel.textContent = request.fund.senderLabel || 'Sinpe Móvil'; } if (request.redeem.type === 'NIM') { @@ -237,11 +236,8 @@ class SignSwap { $rightLabel.textContent = label; } else if (request.redeem.type === 'CRC') { - $rightIdenticon.innerHTML = 'TODO'; - - // TODO Translation - const label = request.redeem.recipientLabel || I18n.translatePhrase('sign-swap-your-bank'); - $rightLabel.textContent = label; + $rightIdenticon.innerHTML = TemplateTags.hasVars(0)``; + $rightLabel.textContent = request.redeem.recipientlabel || 'Sinpe Móvil'; } } diff --git a/src/request/sign-swap/SignSwapApi.js b/src/request/sign-swap/SignSwapApi.js index 64deea518..026057819 100644 --- a/src/request/sign-swap/SignSwapApi.js +++ b/src/request/sign-swap/SignSwapApi.js @@ -100,7 +100,7 @@ class SignSwapApi extends PolygonRequestParserMixin(BitcoinRequestParserMixin(To type: 'CRC', amount: this.parsePositiveInteger(request.fund.amount, false, 'fund.amount'), fee: this.parsePositiveInteger(request.fund.fee, true, 'fund.fee'), - sinpeLabel: this.parseLabel(request.fund.recipientLabel, true, 'fund.recipientLabel'), + recipientLabel: this.parseLabel(request.fund.recipientLabel, true, 'fund.recipientLabel'), }; } else { throw new Errors.InvalidRequestError('Invalid funding type'); @@ -152,19 +152,32 @@ class SignSwapApi extends PolygonRequestParserMixin(BitcoinRequestParserMixin(To amount: this.parsePositiveInteger(request.redeem.amount, false, 'redeem.amount'), }; } else if (request.redeem.type === 'EUR') { + /** @type MockSettlementInstruction | SepaSettlementInstruction | null */ + const settlement = this.parseOasisSepaSettlementInstruction(request.redeem.settlement, 'redeem.settlement') + || this.parseOasisMockSettlementInstruction(request.redeem.settlement); + if (!settlement) { + throw new Errors.InvalidRequestError('Invalid redeeming settlement'); + } parsedRequest.redeem = { type: 'EUR', keyPath: this.parsePath(request.redeem.keyPath, 'redeem.keyPath'), - settlement: this.parseOasisSettlementInstruction(request.redeem.settlement, 'redeem.settlement'), + settlement, amount: this.parsePositiveInteger(request.redeem.amount, false, 'redeem.amount'), fee: this.parsePositiveInteger(request.redeem.fee, true, 'redeem.fee'), bankLabel: this.parseLabel(request.redeem.bankLabel, true, 'redeem.bankLabel'), }; } else if (request.redeem.type === 'CRC') { + /** @type MockSettlementInstruction | SinpeMovilSettlementInstruction | null */ + const settlement = this.parseOasisSinpeMovilSettlementInstruction(request.redeem.settlement) + || this.parseOasisMockSettlementInstruction(request.redeem.settlement); + if (!settlement) { + throw new Errors.InvalidRequestError('Invalid redeeming settlement'); + } + parsedRequest.redeem = { type: 'CRC', keyPath: this.parsePath(request.redeem.keyPath, 'redeem.keyPath'), - settlement: this.parseOasisCrcSettlementInstruction(request.redeem.settlement, 'redeem.settlement'), + settlement, amount: this.parsePositiveInteger(request.redeem.amount, false, 'redeem.amount'), fee: this.parsePositiveInteger(request.redeem.fee, true, 'redeem.fee'), recipientLabel: this.parseLabel(request.redeem.recipientLabel, true, 'redeem.recipientLabel'), @@ -386,82 +399,100 @@ class SignSwapApi extends PolygonRequestParserMixin(BitcoinRequestParserMixin(To } /** - * Checks that the given instruction is a valid OASIS SettlementInstruction + * @typedef {Omit} MockSettlementInstruction + * @typedef {Omit} SepaSettlementInstruction + * @typedef {Omit} SinpeMovilSettlementInstruction + */ + + + /** + * Checks that the given instruction is a valid OASIS MockSettlementInstruction * @param {unknown} obj - * @param {string} parameterName - * @returns {Omit | - * Omit} + * @returns {MockSettlementInstruction | null} + * @throws {Errors.InvalidRequestError} */ - parseOasisSettlementInstruction(obj, parameterName) { + parseOasisMockSettlementInstruction(obj) { if (typeof obj !== 'object' || obj === null) { throw new Errors.InvalidRequestError('Invalid settlement'); } - switch (/** @type {{type: unknown}} */ (obj).type) { - case 'mock': { - /** @type {Omit} */ - const settlement = { - type: 'mock', - }; - return settlement; - } - case 'sepa': { - const recipient = /** @type {{recipient: unknown}} */ (obj).recipient; - if (typeof recipient !== 'object' || recipient === null) { - throw new Errors.InvalidRequestError('Invalid settlement recipient'); - } - - /** @type {Omit} */ - const settlement = { - type: 'sepa', - recipient: { - name: /** @type {string} */ ( - this.parseLabel( - /** @type {{name: unknown}} */ (recipient).name, - false, - `${parameterName}.recipient.name`, - ) - ), - iban: this.parseIban( - /** @type {{iban: unknown}} */ (recipient).iban, - `${parameterName}.recipient.iban`, - ), - bic: this.parseBic( - /** @type {{bic: unknown}} */ (recipient).bic, - `${parameterName}.recipient.bic`, - ), - }, - }; - return settlement; - } - default: throw new Errors.InvalidRequestError('Invalid settlement type'); + if (/** @type {{type: unknown}} */ (obj).type !== 'mock') { + return null; } + + /** @type {MockSettlementInstruction} */ + const settlement = { type: 'mock' }; + return settlement; } /** - * Checks that the given instruction is a valid OASIS SettlementInstruction + * Checks that the given instruction is a valid OASIS SepaSettlementInstruction * @param {unknown} obj * @param {string} parameterName - * @returns {Omit} + * @returns {SepaSettlementInstruction | null} + * @throws {Errors.InvalidRequestError} */ - parseOasisCrcSettlementInstruction(obj, parameterName) { + parseOasisSepaSettlementInstruction(obj, parameterName) { if (typeof obj !== 'object' || obj === null) { throw new Errors.InvalidRequestError('Invalid settlement'); } + if (/** @type {{type: unknown}} */ (obj).type !== 'sepa') { + return null; + } + const recipient = /** @type {{recipient: unknown}} */ (obj).recipient; if (typeof recipient !== 'object' || recipient === null) { throw new Errors.InvalidRequestError('Invalid settlement recipient'); } - /** @type {Omit} */ + /** @type {SepaSettlementInstruction} */ + const settlement = { + type: 'sepa', + recipient: { + name: /** @type {string} */ ( + this.parseLabel( + /** @type {{name: unknown}} */(recipient).name, + false, + `${parameterName}.recipient.name`, + ) + ), + iban: this.parseIban( + /** @type {{iban: unknown}} */(recipient).iban, + `${parameterName}.recipient.iban`, + ), + bic: this.parseBic( + /** @type {{bic: unknown}} */(recipient).bic, + `${parameterName}.recipient.bic`, + ), + }, + }; + return settlement; + } + + /** + * Checks that the given instruction is a valid OASIS SinpeMovilSettlementInstruction + * @param {unknown} obj + * @returns {SinpeMovilSettlementInstruction | null} + * @throws {Errors.InvalidRequestError} + */ + parseOasisSinpeMovilSettlementInstruction(obj) { + if (typeof obj !== 'object' || obj === null) { + throw new Errors.InvalidRequestError('Invalid settlement'); + } + + if (/** @type {{type: unknown}} */ (obj).type !== 'sinpemovil') { + return null; + } + + /** @type {SinpeMovilSettlementInstruction} */ const settlement = { type: 'sinpemovil', phoneNumber: /** @type {string} */ ( this.parseLabel( - /** @type {{phoneNumber: unknown}} */ (recipient).phoneNumber, + /** @type {{phoneNumber: unknown}} */(obj).phoneNumber, false, - `${parameterName}.phoneNumber`, + 'phoneNumber', ) ), }; diff --git a/types/Keyguard.d.ts b/types/Keyguard.d.ts index cc64b1bc5..c3e3345de 100644 --- a/types/Keyguard.d.ts +++ b/types/Keyguard.d.ts @@ -228,7 +228,7 @@ type ConstructSwap = Transform< type: 'CRC', amount: number, fee: number, - sinpeLabel?: string, + senderLabel?: string, }, redeem: { type: 'NIM', @@ -264,10 +264,10 @@ type ConstructSwap = Transform< keyPath: string, // A SettlementInstruction contains a `type`, so cannot be in the // root of the object (it conflicts with the 'CRC' type). - settlement: Omit, + settlement: Omit | Omit, amount: number, fee: number, - recipientLabel?: string, + recipientlabel?: string, }, }>