@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
104 lines • 5.52 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.genericSignOperation = void 0;
const rxjs_1 = require("rxjs");
const alpaca_1 = require("./alpaca");
const bridge_1 = require("./bridge");
const utils_1 = require("./utils");
const errors_1 = require("@ledgerhq/errors");
const logs_1 = require("@ledgerhq/logs");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
/**
* Enriches transaction intent with memo and asset information
*/
function enrichTransactionIntent(transactionIntent, transaction, publicKey) {
// Set sender public key
transactionIntent.senderPublicKey = publicKey;
// Apply memo information
transactionIntent = (0, utils_1.applyMemoToIntent)(transactionIntent, transaction);
return transactionIntent;
}
/**
* Sign Transaction with Ledger hardware
*/
const genericSignOperation = (network, kind) => (signerContext) => ({ account, transaction, deviceId, }) => new rxjs_1.Observable(o => {
async function main() {
const alpacaApi = (0, alpaca_1.getAlpacaApi)(account.currency.id, kind);
const bridgeApi = (0, bridge_1.getBridgeApi)(account.currency, network);
if (!transaction.fees)
throw new errors_1.FeeNotLoaded();
const customFees = (0, utils_1.bigNumberToBigIntDeep)({
value: transaction.fees ?? new bignumber_js_1.default(0),
parameters: {
gasLimit: transaction.customGasLimit ?? transaction.gasLimit,
gasPrice: transaction.gasPrice,
maxFeePerGas: transaction.maxFeePerGas,
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas,
additionalFees: transaction.additionalFees,
},
});
if (transaction.useAllAmount) {
const draftTransaction = {
mode: transaction.mode,
recipient: transaction.recipient,
amount: transaction.amount ?? 0,
useAllAmount: !!transaction.useAllAmount,
assetReference: transaction?.assetReference || "",
assetOwner: transaction?.assetOwner || "",
subAccountId: transaction.subAccountId || "",
family: transaction.family,
feesStrategy: transaction.feesStrategy,
data: transaction.data,
type: transaction.type,
};
// TODO Remove the call to `validateIntent` https://ledgerhq.atlassian.net/browse/LIVE-22227
const { amount } = await alpacaApi.validateIntent((0, utils_1.transactionToIntent)(account, draftTransaction, alpacaApi.computeIntentType), (0, utils_1.extractBalances)(account, bridgeApi.getAssetFromToken), customFees);
transaction.amount = new bignumber_js_1.default(amount.toString());
}
const signedInfo = await signerContext(deviceId, async (signer) => {
const derivationPath = account.freshAddressPath;
const { publicKey } = (await signer.getAddress(derivationPath));
let transactionIntent = (0, utils_1.transactionToIntent)(account, { ...transaction }, alpacaApi.computeIntentType);
transactionIntent.senderPublicKey = publicKey;
// Enrich with memo and asset information
transactionIntent = enrichTransactionIntent(transactionIntent, transaction, publicKey);
if (typeof transactionIntent.sequence !== "bigint" || transactionIntent.sequence < 0n) {
// TODO: should compute it and pass it down to craftTransaction (duplicate call right now)
const sequenceNumber = await alpacaApi.getNextSequence(transactionIntent.sender);
transactionIntent.sequence = sequenceNumber;
}
/* Craft unsigned blob via Alpaca */
const { transaction: unsigned } = await alpacaApi.craftTransaction(transactionIntent, customFees);
/* Notify UI that the device is now showing the tx */
o.next({ type: "device-signature-requested" });
/* Sign on Ledger device */
const txnSig = await signer.signTransaction(derivationPath, unsigned, transaction.recipientDomain);
return { unsigned, txnSig, publicKey, sequence: transactionIntent.sequence };
});
/* If the user cancelled inside signerContext */
if (!signedInfo)
return;
o.next({ type: "device-signature-granted" });
/* Combine payload + signature for broadcast */
const combined = await alpacaApi.combine(signedInfo.unsigned, signedInfo.txnSig, signedInfo.publicKey);
const operation = (0, utils_1.buildOptimisticOperation)(account, transaction, signedInfo.sequence);
if (!operation.id) {
(0, logs_1.log)("Generic alpaca", "buildOptimisticOperation", operation);
}
// NOTE: we set the transactionSequenceNumber before on the operation
// now that we create it in craftTransaction, we might need to return it back from craftTransaction also
o.next({
type: "signed",
signedOperation: {
operation,
signature: combined,
},
});
}
main().then(() => o.complete(), e => o.error(e));
});
exports.genericSignOperation = genericSignOperation;
//# sourceMappingURL=signOperation.js.map