UNPKG

@ledgerhq/live-common

Version:
144 lines • 7.38 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const secp256k1_1 = require("@noble/curves/secp256k1"); const rxjs_1 = require("rxjs"); const errors_1 = require("@ledgerhq/errors"); const promise_1 = require("../../../promise"); const hw_app_exchange_1 = require("@ledgerhq/hw-app-exchange"); const account_1 = require("../../../account"); const bridge_1 = require("../../../bridge"); const errors_2 = require("../../../errors"); const deviceAccess_1 = require("../../../hw/deviceAccess"); const __1 = require("../.."); const providers_1 = require("../../providers"); const error_1 = require("../../error"); const withDevicePromise = (deviceId, fn) => (0, rxjs_1.firstValueFrom)((0, deviceAccess_1.withDevice)(deviceId)(transport => (0, rxjs_1.from)(fn(transport)))); const completeExchange = (input) => { let { transaction } = input; // TODO build a tx from the data const { deviceId, exchange, provider, binaryPayload, signature, exchangeType, rateType, // TODO Pass fixed/float for UI switch ? } = input; const { fromAccount, fromParentAccount } = exchange; return new rxjs_1.Observable(o => { let unsubscribed = false; let ignoreTransportError = false; let currentStep = "INIT"; const confirmExchange = async () => { await withDevicePromise(deviceId, async (transport) => { const providerNameAndSignature = await (0, providers_1.getProviderConfig)(exchangeType, provider); if (!providerNameAndSignature) throw new Error("Could not get provider infos"); const exchange = (0, hw_app_exchange_1.createExchange)(transport, exchangeType, rateType, providerNameAndSignature.version); const mainAccount = (0, account_1.getMainAccount)(fromAccount, fromParentAccount); const accountBridge = (0, bridge_1.getAccountBridge)(mainAccount); const mainPayoutCurrency = (0, account_1.getAccountCurrency)(mainAccount); const payoutCurrency = (0, account_1.getAccountCurrency)(fromAccount); if (mainPayoutCurrency.type !== "CryptoCurrency") throw new Error(`This should be a cryptocurrency, got ${mainPayoutCurrency.type}`); transaction = await accountBridge.prepareTransaction(mainAccount, transaction); if (unsubscribed) return; const { errors, estimatedFees } = await accountBridge.getTransactionStatus(mainAccount, transaction); if (unsubscribed) return; const errorsKeys = Object.keys(errors); if (errorsKeys.length > 0) throw errors[errorsKeys[0]]; // throw the first error currentStep = "SET_PARTNER_KEY"; await exchange.setPartnerKey((0, providers_1.convertToAppExchangePartnerKey)(providerNameAndSignature)); if (unsubscribed) return; currentStep = "CHECK_PARTNER"; await exchange.checkPartner(providerNameAndSignature.signature); if (unsubscribed) return; currentStep = "PROCESS_TRANSACTION"; const { payload, format } = (0, hw_app_exchange_1.isExchangeTypeNg)(exchange.transactionType) ? { payload: Buffer.from("." + binaryPayload), format: "jws" } : { payload: Buffer.from(binaryPayload, "hex"), format: "raw" }; await exchange.processTransaction(payload, estimatedFees, format); if (unsubscribed) return; const goodSign = convertSignature(signature, exchange.transactionType); currentStep = "CHECK_TRANSACTION_SIGNATURE"; await exchange.checkTransactionSignature(goodSign); if (unsubscribed) return; const payoutAddressParameters = accountBridge.getSerializedAddressParameters(mainAccount); if (unsubscribed) return; if (!payoutAddressParameters) { throw new Error(`Family not supported: ${mainPayoutCurrency.family}`); } const { config: payoutAddressConfig, signature: payoutAddressConfigSignature } = await (0, __1.getCurrencyExchangeConfig)(payoutCurrency); try { o.next({ type: "complete-exchange-requested", estimatedFees: estimatedFees.toString(), }); currentStep = "CHECK_PAYOUT_ADDRESS"; await exchange.validatePayoutOrAsset(payoutAddressConfig, payoutAddressConfigSignature, payoutAddressParameters); } catch (e) { if (e instanceof errors_1.TransportStatusError && e.statusCode === 0x6a83) { throw new errors_1.WrongDeviceForAccount(); } throw (0, error_1.convertTransportError)(currentStep, e); } if (unsubscribed) return; ignoreTransportError = true; currentStep = "SIGN_COIN_TRANSACTION"; await exchange.signCoinTransaction(); }).catch(e => { if (ignoreTransportError) return; if (e instanceof errors_1.TransportStatusError && e.statusCode === 0x6a84) { throw new errors_2.TransactionRefusedOnDevice(); } throw (0, error_1.convertTransportError)(currentStep, e); }); await (0, promise_1.delay)(3000); o.next({ type: "complete-exchange-result", completeExchangeResult: transaction, }); if (unsubscribed) return; }; confirmExchange().then(() => { o.complete(); unsubscribed = true; }, e => { o.next({ type: "complete-exchange-error", error: e, }); o.complete(); unsubscribed = true; }); return () => { unsubscribed = true; }; }); }; /** * For the Fund and Swap flow, the signature sent to the nano needs to * be in DER format, which is not the case for Sell flow. Hence the * ternary. * cf. https://github.com/LedgerHQ/app-exchange/blob/e67848f136dc7227521791b91f608f7cd32e7da7/src/check_tx_signature.c#L14-L32 * @param {Buffer} bufferSignature * @param {ExchangeTypes} exchangeType * @return {Buffer} The correct format Buffer for AppExchange call. */ function convertSignature(signature, exchangeType) { if ((0, hw_app_exchange_1.isExchangeTypeNg)(exchangeType)) { const base64Signature = signature.replace(/-/g, "+").replace(/_/g, "/"); return Buffer.from(base64Signature, "base64"); } if (exchangeType === 1 /* ExchangeTypes.Sell */) return Buffer.from(signature, "hex"); const sig = secp256k1_1.secp256k1.Signature.fromCompact(Buffer.from(signature, "hex")); return Buffer.from(sig.toDERRawBytes()); } exports.default = completeExchange; //# sourceMappingURL=completeExchange.js.map