@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
144 lines • 7.38 kB
JavaScript
;
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