@ledgerhq/coin-canton
Version:
64 lines (52 loc) • 2.16 kB
text/typescript
import type { OnboardingPrepareResponse, PrepareTransferResponse } from "../../types/gateway";
import { PrepareTransactionResponse } from "../../types/onboard";
import { CantonSignature, CantonSigner } from "../../types/signer";
import { splitTransaction } from "@ledgerhq/hw-app-canton";
/**
* Sign a Canton transaction - handles both prepared transactions and untyped versioned messages
*/
export async function signTransaction(
signer: CantonSigner,
derivationPath: string,
transactionData: PrepareTransferResponse | PrepareTransactionResponse | OnboardingPrepareResponse,
): Promise<CantonSignature> {
let signPayload;
// OnboardingPrepareResponse — sign topology transactions as untyped versioned messages
if ("transactions" in transactionData && transactionData.transactions) {
const challenge = getTransactionChallenge(transactionData);
const { transactions } = transactionData;
signPayload = {
transactions: [
transactions.namespace_transaction.serialized,
transactions.party_to_key_transaction.serialized,
transactions.party_to_participant_transaction.serialized,
],
...(challenge && { challenge }),
};
} else {
// PrepareTransferResponse / PrepareTransactionResponse (.json) or raw JSON from craftTransaction
const rawTransactionData = "json" in transactionData ? transactionData.json : transactionData;
signPayload = splitTransaction(rawTransactionData);
}
const signature = await signer.signTransaction(derivationPath, signPayload);
if (!signature?.signature) {
throw new Error("Device returned empty signature");
}
return signature;
}
const getTransactionChallenge = ({
challenge_nonce,
challenge_deadline,
}: OnboardingPrepareResponse) => {
if (!challenge_nonce || !challenge_deadline) {
return undefined;
}
try {
const deadlineHex = Buffer.alloc(8);
deadlineHex.writeBigInt64BE(BigInt(challenge_deadline), 0);
// Format: [length (1B) = 0x18][challenge (16 bytes)][deadline (8 bytes)]
return "18" + challenge_nonce.toLowerCase() + deadlineHex.toString("hex");
} catch {
return undefined;
}
};