@@ -58,6 +58,7 @@ import {
5858 prepareTrezorCertificate,
5959 prepareTrezorWithdrawal,
6060 prepareTrezorAuxiliaryData,
61+ TrezorTransactionSigningMode,
6162} from '../utils/shelleyTrezor';
6263import {
6364 DeviceModels,
@@ -93,6 +94,7 @@ import type {
9394 HardwareWalletExtendedPublicKeyResponse,
9495 HardwareWalletConnectionRequest,
9596 Witness,
97+ TrezorWitness,
9698} from '../../../common/types/hardware-wallets.types';
9799
98100import { logger } from '../utils/logging';
@@ -1645,27 +1647,95 @@ export default class HardwareWalletsStore extends Store {
16451647 throw new Error(`Missing Coins Selection for wallet: ${walletId}`);
16461648 }
16471649
1648- const { inputs, outputs, fee, certificates, withdrawals } = coinSelection;
1650+ const {
1651+ inputs,
1652+ outputs,
1653+ fee: flatFee,
1654+ certificates,
1655+ withdrawals,
1656+ } = coinSelection;
1657+
1658+ logger.debug('[HW-DEBUG] HWStore - sign transaction Trezor: ', {
1659+ walletId,
1660+ });
1661+
1662+ const hardwareWalletConnectionData = get(
1663+ this.hardwareWalletsConnectionData,
1664+ walletId
1665+ );
1666+
1667+ // Guard against potential null value
1668+ if (!hardwareWalletConnectionData)
1669+ throw new Error('Wallet not paired or Device not connected');
16491670
1671+ const publicKeyHex = get(hardwareWalletConnectionData, [
1672+ 'extendedPublicKey',
1673+ 'publicKeyHex',
1674+ ]);
1675+ const chainCodeHex = get(hardwareWalletConnectionData, [
1676+ 'extendedPublicKey',
1677+ 'chainCodeHex',
1678+ ]);
1679+ const xpubHex = `${publicKeyHex}${chainCodeHex}`;
1680+
1681+ const unsignedTxInputs = [];
16501682 const inputsData = map(inputs, (input) => {
1683+ const shelleyTxInput = ShelleyTxInputFromUtxo(input);
1684+ unsignedTxInputs.push(shelleyTxInput);
16511685 return prepareTrezorInput(input);
16521686 });
16531687
1654- const outputsData = map(outputs, (output) => {
1655- return prepareTrezorOutput(output);
1656- });
1688+ const unsignedTxOutputs = [];
1689+ const outputsData = [];
1690+ for (const output of outputs) {
1691+ const {
1692+ address_style: addressStyle,
1693+ } = await this.stores.addresses._inspectAddress({
1694+ addressId: output.address,
1695+ });
1696+ const shelleyTxOutput = ShelleyTxOutput(output, addressStyle);
1697+ unsignedTxOutputs.push(shelleyTxOutput);
1698+ const ledgerOutput = prepareTrezorOutput(output);
1699+ outputsData.push(ledgerOutput);
1700+ }
16571701
1658- const withdrawalsData = map(withdrawals, (withdrawal) => {
1659- return prepareTrezorWithdrawal(withdrawal);
1702+ // Construct certificates
1703+ const unsignedTxCerts = [];
1704+ const _certificatesData = map(certificates, async (certificate) => {
1705+ const accountAddress = await this._getRewardAccountAddress(
1706+ walletId,
1707+ certificate.rewardAccountPath
1708+ );
1709+ const shelleyTxCert = ShelleyTxCert({
1710+ accountAddress,
1711+ pool: certificate.pool,
1712+ type: CERTIFICATE_TYPE[certificate.certificateType],
1713+ });
1714+ unsignedTxCerts.push(shelleyTxCert);
1715+ return prepareTrezorCertificate(certificate);
16601716 });
1717+ const certificatesData = await Promise.all(_certificatesData);
16611718
1662- const certificatesData = map(certificates, (certificate) =>
1663- prepareTrezorCertificate(certificate)
1719+ // Construct Withdrawals
1720+ const _withdrawalsData = map(withdrawals, async (withdrawal) =>
1721+ prepareTrezorWithdrawal(withdrawal)
16641722 );
1723+ const withdrawalsData = await Promise.all(_withdrawalsData);
16651724
1725+ let unsignedTxAuxiliaryData = null;
16661726 let auxiliaryData = null;
16671727 if (this.votingData) {
1668- const { votingKey, nonce } = this.votingData;
1728+ const { stakeAddress, stakeKey, votingKey, nonce } = this.votingData;
1729+ unsignedTxAuxiliaryData = {
1730+ nonce, // unique increaseable number e.g. current epoch number or absolute slot number ( identifies unique tx / vote registration )
1731+ rewardDestinationAddress: {
1732+ address: stakeAddress,
1733+ stakingPath: [2147485500, 2147485463, 2147483648, 2, 0],
1734+ },
1735+ stakePubKey: stakeKey,
1736+ type: CATALYST_VOTING_REGISTRATION_TYPE,
1737+ votingPubKey: votingKey,
1738+ };
16691739 auxiliaryData = prepareTrezorAuxiliaryData({
16701740 votingKey,
16711741 nonce: nonce.toString(),
@@ -1715,15 +1785,16 @@ export default class HardwareWalletsStore extends Store {
17151785 });
17161786 }
17171787
1718- const { isMainnet } = this.environment ;
1788+ const fee = formattedAmountToLovelace(flatFee.toString()) ;
17191789 const ttl = this._getTtl();
17201790 const absoluteSlotNumber = this._getAbsoluteSlotNumber();
1791+ const { isMainnet } = this.environment;
17211792
17221793 try {
17231794 const signedTransaction = await signTransactionTrezorChannel.request({
17241795 inputs: inputsData,
17251796 outputs: outputsData,
1726- fee: formattedAmountToLovelace( fee.toString()) .toString(),
1797+ fee: fee.toString(),
17271798 ttl: ttl.toString(),
17281799 validityIntervalStartStr: absoluteSlotNumber.toString(),
17291800 networkId: isMainnet
@@ -1735,21 +1806,78 @@ export default class HardwareWalletsStore extends Store {
17351806 certificates: certificatesData,
17361807 withdrawals: withdrawalsData,
17371808 devicePath: recognizedDevicePath,
1809+ signingMode: TrezorTransactionSigningMode.ORDINARY_TRANSACTION,
17381810 auxiliaryData,
17391811 });
17401812
1741- if (!signedTransaction.success) {
1813+ if (signedTransaction && !signedTransaction.success) {
17421814 throw signedTransaction.payload;
17431815 }
1816+ // Compatible with old firmwares
1817+ const serializedTx = get(signedTransaction, ['payload', 'serializedTx']);
17441818
1745- runInAction(
1746- 'HardwareWalletsStore:: transaction successfully signed',
1747- () => {
1748- this.txBody = signedTransaction.payload.serializedTx;
1749- this.hwDeviceStatus =
1750- HwDeviceStatuses.VERIFYING_TRANSACTION_SUCCEEDED;
1751- }
1819+ if (serializedTx) {
1820+ runInAction(
1821+ 'HardwareWalletsStore:: transaction successfully signed',
1822+ () => {
1823+ this.txBody = serializedTx;
1824+ this.hwDeviceStatus =
1825+ HwDeviceStatuses.VERIFYING_TRANSACTION_SUCCEEDED;
1826+ }
1827+ );
1828+ return;
1829+ }
1830+
1831+ const unsignedTxWithdrawals =
1832+ withdrawals.length > 0 ? ShelleyTxWithdrawal(withdrawals) : null;
1833+
1834+ // Prepare unsigned transaction structure for serialzation
1835+ let txAuxData = {
1836+ txInputs: unsignedTxInputs,
1837+ txOutputs: unsignedTxOutputs,
1838+ fee,
1839+ ttl,
1840+ certificates: unsignedTxCerts,
1841+ withdrawals: unsignedTxWithdrawals,
1842+ };
1843+
1844+ let txAuxiliaryData = null;
1845+ const auxiliaryDataSupplement = get(signedTransaction, [
1846+ 'payload',
1847+ 'auxiliaryDataSupplement',
1848+ ]);
1849+ if (unsignedTxAuxiliaryData && auxiliaryDataSupplement) {
1850+ txAuxData = {
1851+ ...txAuxData,
1852+ txAuxiliaryData: unsignedTxAuxiliaryData,
1853+ txAuxiliaryDataHash: auxiliaryDataSupplement.auxiliaryDataHash,
1854+ };
1855+ txAuxiliaryData = cborizeTxAuxiliaryVotingData(
1856+ unsignedTxAuxiliaryData,
1857+ auxiliaryDataSupplement.catalystSignature
1858+ );
1859+ }
1860+
1861+ const unsignedTx = prepareTxAux(txAuxData);
1862+ const witnesses = get(signedTransaction, ['payload', 'witnesses'], []);
1863+ const signedWitnesses = await this._signWitnesses(witnesses, xpubHex);
1864+ const txWitnesses = new Map();
1865+ if (signedWitnesses.length > 0) {
1866+ txWitnesses.set(0, signedWitnesses);
1867+ }
1868+
1869+ // Prepare serialized transaction with unsigned data and signed witnesses
1870+ const txBody = await prepareBody(
1871+ unsignedTx,
1872+ txWitnesses,
1873+ txAuxiliaryData
17521874 );
1875+
1876+ runInAction('HardwareWalletsStore:: set Transaction verified', () => {
1877+ this.hwDeviceStatus = HwDeviceStatuses.VERIFYING_TRANSACTION_SUCCEEDED;
1878+ this.txBody = txBody;
1879+ this.activeDevicePath = null;
1880+ });
17531881 } catch (error) {
17541882 runInAction(
17551883 'HardwareWalletsStore:: set Transaction verifying failed',
@@ -1758,15 +1886,17 @@ export default class HardwareWalletsStore extends Store {
17581886 this.isTransactionInitiated = false;
17591887 }
17601888 );
1761- // @TODO - Maybe we should handle this case as separated message in tx dialog
17621889 if (error.code === 'Device_CallInProgress') {
17631890 throw new Error('Device is busy - reconnect device and try again');
17641891 }
17651892 throw error;
17661893 }
17671894 };
17681895
1769- _signWitnesses = async (witnesses: Array<Witness>, xpubHex: string) => {
1896+ _signWitnesses = async (
1897+ witnesses: Array<TrezorWitness | Witness>,
1898+ xpubHex: string
1899+ ) => {
17701900 const signedWitnesses = [];
17711901 for (const witness of witnesses) {
17721902 const signedWitness = await this.ShelleyWitness(witness, xpubHex);
@@ -1775,11 +1905,25 @@ export default class HardwareWalletsStore extends Store {
17751905 return signedWitnesses;
17761906 };
17771907
1778- ShelleyWitness = async (witness: Witness, xpubHex: string) => {
1779- const xpub = await this._deriveXpub(witness.path, xpubHex);
1780- const publicKey = xpub.slice(0, 32);
1781- const signature = Buffer.from(witness.witnessSignatureHex, 'hex');
1782- return ShelleyTxWitnessShelley(publicKey, signature);
1908+ ShelleyWitness = async (
1909+ witness: TrezorWitness | Witness,
1910+ xpubHex: string
1911+ ) => {
1912+ let publicKey;
1913+ let witnessSignatureHex;
1914+ if (witness.pubKey && witness.signature) {
1915+ publicKey = Buffer.from(witness.pubKey, 'hex');
1916+ witnessSignatureHex = witness.signature;
1917+ } else if (witness.path && witness.witnessSignatureHex) {
1918+ const xpub = await this._deriveXpub(witness.path, xpubHex);
1919+ publicKey = xpub.slice(0, 32);
1920+ witnessSignatureHex = witness.witnessSignatureHex;
1921+ }
1922+ if (witnessSignatureHex && publicKey) {
1923+ const signature = Buffer.from(witnessSignatureHex, 'hex');
1924+ return ShelleyTxWitnessShelley(publicKey, signature);
1925+ }
1926+ return null;
17831927 };
17841928
17851929 _deriveXpub = CachedDeriveXpubFactory(async (xpubHex) => {
@@ -1944,6 +2088,7 @@ export default class HardwareWalletsStore extends Store {
19442088 let txAuxiliaryData = null;
19452089 if (
19462090 unsignedTxAuxiliaryData &&
2091+ signedTransaction &&
19472092 signedTransaction.auxiliaryDataSupplement
19482093 ) {
19492094 txAuxData = {
@@ -1961,10 +2106,8 @@ export default class HardwareWalletsStore extends Store {
19612106
19622107 const unsignedTx = prepareTxAux(txAuxData);
19632108
1964- const signedWitnesses = await this . _signWitnesses (
1965- signedTransaction . witnesses ,
1966- xpubHex
1967- ) ;
2109+ const witnesses = get(signedTransaction, 'witnesses', []);
2110+ const signedWitnesses = await this._signWitnesses(witnesses, xpubHex);
19682111 const txWitnesses = new Map();
19692112 if (signedWitnesses.length > 0) {
19702113 txWitnesses.set(0, signedWitnesses);
0 commit comments