@sidhujag/sysweb3-keyring 1.0.545 → 1.0.547

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/coverage/clover.xml +2875 -0
  2. package/coverage/coverage-final.json +29468 -0
  3. package/coverage/lcov-report/base.css +354 -0
  4. package/coverage/lcov-report/block-navigation.js +85 -0
  5. package/coverage/lcov-report/favicon.png +0 -0
  6. package/coverage/lcov-report/index.html +320 -0
  7. package/coverage/lcov-report/prettify.css +101 -0
  8. package/coverage/lcov-report/prettify.js +1008 -0
  9. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  10. package/coverage/lcov-report/sorter.js +191 -0
  11. package/coverage/lcov-report/src/index.html +276 -0
  12. package/coverage/lcov-report/src/index.ts.html +114 -0
  13. package/coverage/lcov-report/src/initial-state.ts.html +558 -0
  14. package/coverage/lcov-report/src/keyring-manager.ts.html +6279 -0
  15. package/coverage/lcov-report/src/ledger/bitcoin_client/index.html +178 -0
  16. package/coverage/lcov-report/src/ledger/bitcoin_client/index.ts.html +144 -0
  17. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/appClient.ts.html +1560 -0
  18. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/bip32.ts.html +276 -0
  19. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/buffertools.ts.html +495 -0
  20. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/clientCommands.ts.html +1138 -0
  21. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/index.html +363 -0
  22. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts.html +289 -0
  23. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkle.ts.html +486 -0
  24. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/merkleMap.ts.html +240 -0
  25. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/policy.ts.html +342 -0
  26. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/psbtv2.ts.html +2388 -0
  27. package/coverage/lcov-report/src/ledger/bitcoin_client/lib/varint.ts.html +453 -0
  28. package/coverage/lcov-report/src/ledger/consts.ts.html +177 -0
  29. package/coverage/lcov-report/src/ledger/index.html +216 -0
  30. package/coverage/lcov-report/src/ledger/index.ts.html +1371 -0
  31. package/coverage/lcov-report/src/ledger/utils.ts.html +102 -0
  32. package/coverage/lcov-report/src/signers.ts.html +591 -0
  33. package/coverage/lcov-report/src/storage.ts.html +198 -0
  34. package/coverage/lcov-report/src/transactions/ethereum.ts.html +5826 -0
  35. package/coverage/lcov-report/src/transactions/index.html +216 -0
  36. package/coverage/lcov-report/src/transactions/index.ts.html +93 -0
  37. package/coverage/lcov-report/src/transactions/syscoin.ts.html +1521 -0
  38. package/coverage/lcov-report/src/trezor/index.html +176 -0
  39. package/coverage/lcov-report/src/trezor/index.ts.html +2655 -0
  40. package/coverage/lcov-report/src/types.ts.html +1443 -0
  41. package/coverage/lcov-report/src/utils/derivation-paths.ts.html +486 -0
  42. package/coverage/lcov-report/src/utils/index.html +196 -0
  43. package/coverage/lcov-report/src/utils/psbt.ts.html +159 -0
  44. package/coverage/lcov-report/test/helpers/constants.ts.html +627 -0
  45. package/coverage/lcov-report/test/helpers/index.html +176 -0
  46. package/coverage/lcov.info +4832 -0
  47. package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/appClient.js +1 -124
  48. package/dist/cjs/ledger/bitcoin_client/lib/appClient.js.map +1 -0
  49. package/{cjs → dist/cjs}/transactions/ethereum.js +6 -2
  50. package/dist/cjs/transactions/ethereum.js.map +1 -0
  51. package/dist/package.json +50 -0
  52. package/{types → dist/types}/ledger/bitcoin_client/lib/appClient.d.ts +0 -6
  53. package/examples/basic-usage.js +140 -0
  54. package/jest.config.js +32 -0
  55. package/package.json +31 -13
  56. package/readme.md +201 -0
  57. package/src/declare.d.ts +7 -0
  58. package/src/errorUtils.ts +83 -0
  59. package/src/hardware-wallet-manager.ts +655 -0
  60. package/src/index.ts +12 -0
  61. package/src/initial-state.ts +108 -0
  62. package/src/keyring-manager.ts +2698 -0
  63. package/src/ledger/bitcoin_client/index.ts +19 -0
  64. package/src/ledger/bitcoin_client/lib/appClient.ts +405 -0
  65. package/src/ledger/bitcoin_client/lib/bip32.ts +61 -0
  66. package/src/ledger/bitcoin_client/lib/buffertools.ts +134 -0
  67. package/src/ledger/bitcoin_client/lib/clientCommands.ts +356 -0
  68. package/src/ledger/bitcoin_client/lib/constants.ts +12 -0
  69. package/src/ledger/bitcoin_client/lib/merkelizedPsbt.ts +65 -0
  70. package/src/ledger/bitcoin_client/lib/merkle.ts +136 -0
  71. package/src/ledger/bitcoin_client/lib/merkleMap.ts +49 -0
  72. package/src/ledger/bitcoin_client/lib/policy.ts +91 -0
  73. package/src/ledger/bitcoin_client/lib/psbtv2.ts +768 -0
  74. package/src/ledger/bitcoin_client/lib/varint.ts +120 -0
  75. package/src/ledger/consts.ts +3 -0
  76. package/src/ledger/index.ts +685 -0
  77. package/src/ledger/types.ts +74 -0
  78. package/src/network-utils.ts +99 -0
  79. package/src/providers.ts +345 -0
  80. package/src/signers.ts +158 -0
  81. package/src/storage.ts +63 -0
  82. package/src/transactions/__tests__/integration.test.ts +303 -0
  83. package/src/transactions/__tests__/syscoin.test.ts +409 -0
  84. package/src/transactions/ethereum.ts +2503 -0
  85. package/src/transactions/index.ts +2 -0
  86. package/src/transactions/syscoin.ts +542 -0
  87. package/src/trezor/index.ts +1050 -0
  88. package/src/types.ts +366 -0
  89. package/src/utils/derivation-paths.ts +133 -0
  90. package/src/utils/psbt.ts +24 -0
  91. package/src/utils.ts +191 -0
  92. package/test/README.md +158 -0
  93. package/test/__mocks__/ledger-mock.js +20 -0
  94. package/test/__mocks__/trezor-mock.js +75 -0
  95. package/test/cleanup-summary.md +167 -0
  96. package/test/helpers/README.md +78 -0
  97. package/test/helpers/constants.ts +79 -0
  98. package/test/helpers/setup.ts +714 -0
  99. package/test/integration/import-validation.spec.ts +588 -0
  100. package/test/unit/hardware/ledger.spec.ts +869 -0
  101. package/test/unit/hardware/trezor.spec.ts +828 -0
  102. package/test/unit/keyring-manager/account-management.spec.ts +970 -0
  103. package/test/unit/keyring-manager/import-watchonly.spec.ts +181 -0
  104. package/test/unit/keyring-manager/import-wif.spec.ts +126 -0
  105. package/test/unit/keyring-manager/initialization.spec.ts +782 -0
  106. package/test/unit/keyring-manager/key-derivation.spec.ts +996 -0
  107. package/test/unit/keyring-manager/security.spec.ts +505 -0
  108. package/test/unit/keyring-manager/state-management.spec.ts +375 -0
  109. package/test/unit/network/network-management.spec.ts +372 -0
  110. package/test/unit/transactions/ethereum-transactions.spec.ts +382 -0
  111. package/test/unit/transactions/syscoin-transactions.spec.ts +615 -0
  112. package/tsconfig.json +14 -0
  113. package/cjs/ledger/bitcoin_client/lib/appClient.js.map +0 -1
  114. package/cjs/transactions/ethereum.js.map +0 -1
  115. /package/{README.md → dist/README.md} +0 -0
  116. /package/{cjs → dist/cjs}/errorUtils.js +0 -0
  117. /package/{cjs → dist/cjs}/errorUtils.js.map +0 -0
  118. /package/{cjs → dist/cjs}/hardware-wallet-manager.js +0 -0
  119. /package/{cjs → dist/cjs}/hardware-wallet-manager.js.map +0 -0
  120. /package/{cjs → dist/cjs}/index.js +0 -0
  121. /package/{cjs → dist/cjs}/index.js.map +0 -0
  122. /package/{cjs → dist/cjs}/initial-state.js +0 -0
  123. /package/{cjs → dist/cjs}/initial-state.js.map +0 -0
  124. /package/{cjs → dist/cjs}/keyring-manager.js +0 -0
  125. /package/{cjs → dist/cjs}/keyring-manager.js.map +0 -0
  126. /package/{cjs → dist/cjs}/ledger/bitcoin_client/index.js +0 -0
  127. /package/{cjs → dist/cjs}/ledger/bitcoin_client/index.js.map +0 -0
  128. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/bip32.js +0 -0
  129. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/bip32.js.map +0 -0
  130. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/buffertools.js +0 -0
  131. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/buffertools.js.map +0 -0
  132. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/clientCommands.js +0 -0
  133. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/clientCommands.js.map +0 -0
  134. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/constants.js +0 -0
  135. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/constants.js.map +0 -0
  136. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js +0 -0
  137. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkelizedPsbt.js.map +0 -0
  138. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkle.js +0 -0
  139. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkle.js.map +0 -0
  140. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkleMap.js +0 -0
  141. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/merkleMap.js.map +0 -0
  142. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/policy.js +0 -0
  143. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/policy.js.map +0 -0
  144. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/psbtv2.js +0 -0
  145. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/psbtv2.js.map +0 -0
  146. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/varint.js +0 -0
  147. /package/{cjs → dist/cjs}/ledger/bitcoin_client/lib/varint.js.map +0 -0
  148. /package/{cjs → dist/cjs}/ledger/consts.js +0 -0
  149. /package/{cjs → dist/cjs}/ledger/consts.js.map +0 -0
  150. /package/{cjs → dist/cjs}/ledger/index.js +0 -0
  151. /package/{cjs → dist/cjs}/ledger/index.js.map +0 -0
  152. /package/{cjs → dist/cjs}/ledger/types.js +0 -0
  153. /package/{cjs → dist/cjs}/ledger/types.js.map +0 -0
  154. /package/{cjs → dist/cjs}/network-utils.js +0 -0
  155. /package/{cjs → dist/cjs}/network-utils.js.map +0 -0
  156. /package/{cjs → dist/cjs}/providers.js +0 -0
  157. /package/{cjs → dist/cjs}/providers.js.map +0 -0
  158. /package/{cjs → dist/cjs}/signers.js +0 -0
  159. /package/{cjs → dist/cjs}/signers.js.map +0 -0
  160. /package/{cjs → dist/cjs}/storage.js +0 -0
  161. /package/{cjs → dist/cjs}/storage.js.map +0 -0
  162. /package/{cjs → dist/cjs}/transactions/__tests__/integration.test.js +0 -0
  163. /package/{cjs → dist/cjs}/transactions/__tests__/integration.test.js.map +0 -0
  164. /package/{cjs → dist/cjs}/transactions/__tests__/syscoin.test.js +0 -0
  165. /package/{cjs → dist/cjs}/transactions/__tests__/syscoin.test.js.map +0 -0
  166. /package/{cjs → dist/cjs}/transactions/index.js +0 -0
  167. /package/{cjs → dist/cjs}/transactions/index.js.map +0 -0
  168. /package/{cjs → dist/cjs}/transactions/syscoin.js +0 -0
  169. /package/{cjs → dist/cjs}/transactions/syscoin.js.map +0 -0
  170. /package/{cjs → dist/cjs}/trezor/index.js +0 -0
  171. /package/{cjs → dist/cjs}/trezor/index.js.map +0 -0
  172. /package/{cjs → dist/cjs}/types.js +0 -0
  173. /package/{cjs → dist/cjs}/types.js.map +0 -0
  174. /package/{cjs → dist/cjs}/utils/derivation-paths.js +0 -0
  175. /package/{cjs → dist/cjs}/utils/derivation-paths.js.map +0 -0
  176. /package/{cjs → dist/cjs}/utils/psbt.js +0 -0
  177. /package/{cjs → dist/cjs}/utils/psbt.js.map +0 -0
  178. /package/{cjs → dist/cjs}/utils.js +0 -0
  179. /package/{cjs → dist/cjs}/utils.js.map +0 -0
  180. /package/{types → dist/types}/errorUtils.d.ts +0 -0
  181. /package/{types → dist/types}/hardware-wallet-manager.d.ts +0 -0
  182. /package/{types → dist/types}/index.d.ts +0 -0
  183. /package/{types → dist/types}/initial-state.d.ts +0 -0
  184. /package/{types → dist/types}/keyring-manager.d.ts +0 -0
  185. /package/{types → dist/types}/ledger/bitcoin_client/index.d.ts +0 -0
  186. /package/{types → dist/types}/ledger/bitcoin_client/lib/bip32.d.ts +0 -0
  187. /package/{types → dist/types}/ledger/bitcoin_client/lib/buffertools.d.ts +0 -0
  188. /package/{types → dist/types}/ledger/bitcoin_client/lib/clientCommands.d.ts +0 -0
  189. /package/{types → dist/types}/ledger/bitcoin_client/lib/constants.d.ts +0 -0
  190. /package/{types → dist/types}/ledger/bitcoin_client/lib/merkelizedPsbt.d.ts +0 -0
  191. /package/{types → dist/types}/ledger/bitcoin_client/lib/merkle.d.ts +0 -0
  192. /package/{types → dist/types}/ledger/bitcoin_client/lib/merkleMap.d.ts +0 -0
  193. /package/{types → dist/types}/ledger/bitcoin_client/lib/policy.d.ts +0 -0
  194. /package/{types → dist/types}/ledger/bitcoin_client/lib/psbtv2.d.ts +0 -0
  195. /package/{types → dist/types}/ledger/bitcoin_client/lib/varint.d.ts +0 -0
  196. /package/{types → dist/types}/ledger/consts.d.ts +0 -0
  197. /package/{types → dist/types}/ledger/index.d.ts +0 -0
  198. /package/{types → dist/types}/ledger/types.d.ts +0 -0
  199. /package/{types → dist/types}/network-utils.d.ts +0 -0
  200. /package/{types → dist/types}/providers.d.ts +0 -0
  201. /package/{types → dist/types}/signers.d.ts +0 -0
  202. /package/{types → dist/types}/storage.d.ts +0 -0
  203. /package/{types → dist/types}/transactions/__tests__/integration.test.d.ts +0 -0
  204. /package/{types → dist/types}/transactions/__tests__/syscoin.test.d.ts +0 -0
  205. /package/{types → dist/types}/transactions/ethereum.d.ts +0 -0
  206. /package/{types → dist/types}/transactions/index.d.ts +0 -0
  207. /package/{types → dist/types}/transactions/syscoin.d.ts +0 -0
  208. /package/{types → dist/types}/trezor/index.d.ts +0 -0
  209. /package/{types → dist/types}/types.d.ts +0 -0
  210. /package/{types → dist/types}/utils/derivation-paths.d.ts +0 -0
  211. /package/{types → dist/types}/utils/psbt.d.ts +0 -0
  212. /package/{types → dist/types}/utils.d.ts +0 -0
@@ -0,0 +1,2 @@
1
+ export * from './ethereum';
2
+ export * from './syscoin';
@@ -0,0 +1,542 @@
1
+ import { INetwork, getNetworkConfig } from '@sidhujag/sysweb3-network';
2
+ import { ITxid, txUtils } from '@sidhujag/sysweb3-utils';
3
+ import * as syscoinjs from 'syscoinjs-lib';
4
+ // import { BIP_84, ONE_HUNDRED_MILLION, SYSCOIN_BASIC_FEE } from 'utils';
5
+
6
+ import { LedgerKeyring } from '../ledger';
7
+ import { DefaultWalletPolicy } from '../ledger/bitcoin_client';
8
+ import { SyscoinHDSigner } from '../signers';
9
+ import { TrezorKeyring } from '../trezor';
10
+ import {
11
+ ISyscoinTransactions,
12
+ KeyringAccountType,
13
+ accountType,
14
+ } from '../types';
15
+ import {
16
+ getAccountDerivationPath,
17
+ convertExtendedKeyVersion,
18
+ } from '../utils/derivation-paths';
19
+ import { PsbtUtils } from '../utils/psbt';
20
+
21
+ type EstimateFeeParams = {
22
+ changeAddress: string;
23
+ feeRateBN: any;
24
+ outputs: { address: string; value: any; subtractFeeFrom?: boolean }[];
25
+ txOptions: any;
26
+ xpub: string;
27
+ };
28
+
29
+ export class SyscoinTransactions implements ISyscoinTransactions {
30
+ // New separated transaction flow for better UX:
31
+ // 1. Call getEstimateSysTransactionFee() - creates UNSIGNED PSBT and calculates fee
32
+ // 2. Call signPSBT() - signs the PSBT with appropriate wallet (HD/Trezor/Ledger)
33
+ // 3. Call sendTransaction() - broadcasts the signed PSBT
34
+ //
35
+ // This separation allows:
36
+ // - Independent error handling for each step
37
+ // - Better UX feedback (fee estimation, signing, broadcasting)
38
+ // - Hardware wallet compatibility with proper PSBT enhancement
39
+
40
+ private getSigner: () => {
41
+ hd: SyscoinHDSigner;
42
+ main: any;
43
+ };
44
+ private getReadOnlySigner: () => {
45
+ main: any;
46
+ };
47
+ private trezor: TrezorKeyring;
48
+ private ledger: LedgerKeyring;
49
+ private getState: () => {
50
+ accounts: {
51
+ HDAccount: accountType;
52
+ Imported: accountType;
53
+ Ledger: accountType;
54
+ Trezor: accountType;
55
+ };
56
+ activeAccountId: number;
57
+ activeAccountType: KeyringAccountType;
58
+ activeNetwork: INetwork;
59
+ };
60
+ private getAddress: (
61
+ xpub: string,
62
+ isChangeAddress: boolean
63
+ ) => Promise<string>;
64
+
65
+ constructor(
66
+ getSyscoinSigner: () => {
67
+ hd: SyscoinHDSigner;
68
+ main: any;
69
+ },
70
+ getReadOnlySigner: () => {
71
+ main: any;
72
+ },
73
+ getState: () => {
74
+ accounts: {
75
+ HDAccount: accountType;
76
+ Imported: accountType;
77
+ Ledger: accountType;
78
+ Trezor: accountType;
79
+ };
80
+ activeAccountId: number;
81
+ activeAccountType: KeyringAccountType;
82
+ activeNetwork: INetwork;
83
+ },
84
+ getAddress: (xpub: string, isChangeAddress: boolean) => Promise<string>,
85
+ ledgerSigner: LedgerKeyring,
86
+ trezorSigner: TrezorKeyring
87
+ ) {
88
+ this.getSigner = getSyscoinSigner;
89
+ this.getReadOnlySigner = getReadOnlySigner;
90
+ this.getState = getState;
91
+ this.getAddress = getAddress;
92
+ this.trezor = trezorSigner;
93
+ this.ledger = ledgerSigner;
94
+ }
95
+
96
+ private getTransactionPSBT = async (
97
+ { txOptions, outputs, changeAddress, feeRateBN, xpub }: EstimateFeeParams,
98
+ main: any
99
+ ) => {
100
+ try {
101
+ // Use syscoinjs-lib directly for transaction creation
102
+ const result = await main.createTransaction(
103
+ txOptions,
104
+ changeAddress,
105
+ outputs,
106
+ feeRateBN,
107
+ xpub // sysFromXpubOrAddress
108
+ );
109
+
110
+ if (result && result.psbt) {
111
+ return { psbt: result.psbt, fee: result.fee };
112
+ }
113
+ throw new Error('psbt not found');
114
+ } catch (error) {
115
+ // Propagate structured error from syscoinjs-lib
116
+ if (error.error && error.code) {
117
+ throw error;
118
+ }
119
+ // Wrap non-structured errors
120
+ throw {
121
+ error: true,
122
+ code: 'TRANSACTION_CREATION_FAILED',
123
+ message: error.message || 'Failed to create transaction',
124
+ details: error,
125
+ };
126
+ }
127
+ };
128
+
129
+ public decodeRawTransaction = (psbtOrHex: any, isRawHex = false) => {
130
+ const { main } = this.getReadOnlySigner();
131
+
132
+ if (isRawHex) {
133
+ // Handle raw transaction hex
134
+ const bitcoinTx =
135
+ syscoinjs.utils.bitcoinjs.Transaction.fromHex(psbtOrHex);
136
+ return main.decodeRawTransaction(bitcoinTx);
137
+ } else {
138
+ // Handle PSBT format (existing behavior)
139
+ const psbtObj = PsbtUtils.fromPali(psbtOrHex);
140
+ return main.decodeRawTransaction(psbtObj);
141
+ }
142
+ };
143
+
144
+ public getRecommendedFee = async (explorerUrl: string): Promise<number> =>
145
+ (await syscoinjs.utils.fetchEstimateFee(explorerUrl, 1)) / 1024;
146
+
147
+ public txUtilsFunctions = () => {
148
+ const { getRawTransaction } = txUtils();
149
+ return {
150
+ getRawTransaction,
151
+ };
152
+ };
153
+
154
+ // Internal method for signing with the HD signer
155
+ private signPSBTWithSigner = async ({
156
+ psbt,
157
+ signer,
158
+ }: {
159
+ psbt: any;
160
+ signer: any;
161
+ }): Promise<any> => await signer.sign(psbt);
162
+
163
+ // Create unsigned PSBT for any transaction type
164
+ private createUnsignedPSBT = async ({
165
+ txOptions = {},
166
+ isMax = false,
167
+ amount,
168
+ receivingAddress,
169
+ feeRateBN,
170
+ token = null,
171
+ }: {
172
+ amount: number | string; // Accept both for safer precision handling
173
+ feeRateBN: any; // BigNumber in satoshis/byte
174
+ receivingAddress: string;
175
+ token?: { guid: string; symbol?: string } | null;
176
+ txOptions?: any;
177
+ isMax?: boolean | false;
178
+ }): Promise<{ psbt: any; fee: number }> => {
179
+ // Ensure RBF is enabled by default if not explicitly set
180
+ const finalTxOptions = { rbf: true, ...txOptions };
181
+ const { activeAccountId, accounts, activeAccountType } = this.getState();
182
+ // Use read-only signer since we're just creating an unsigned PSBT
183
+ const { main } = this.getReadOnlySigner();
184
+ const account = accounts[activeAccountType]?.[activeAccountId];
185
+ if (!account) {
186
+ throw new Error('Active account not found');
187
+ }
188
+ const xpub = account.xpub;
189
+ const isSingleAddressImported =
190
+ activeAccountType === KeyringAccountType.Imported &&
191
+ xpub === account.address;
192
+ // Convert amount to satoshis (1 SYS = 1e8 satoshis)
193
+ // Using BigNumber to prevent precision loss
194
+ const amountStr = amount.toString();
195
+
196
+ // Safe conversion without parseFloat to avoid precision loss
197
+ // Split the string to handle decimal values properly
198
+ const parts = amountStr.split('.');
199
+ const integerPart = parts[0] || '0';
200
+ const decimalPart = parts[1] || '';
201
+
202
+ // Pad or truncate decimal part to 8 places (satoshi precision)
203
+ const paddedDecimal = decimalPart.padEnd(8, '0').substring(0, 8);
204
+
205
+ // Combine to get satoshis (integer + decimal as one number)
206
+ const satoshiStr = integerPart + paddedDecimal;
207
+
208
+ // Remove leading zeros but keep at least one digit
209
+ const trimmedSatoshis = satoshiStr.replace(/^0+/, '') || '0';
210
+
211
+ const value = new syscoinjs.utils.BN(trimmedSatoshis);
212
+ // If this is a single-address imported account (WIF import), use the account.address for both from and change
213
+ const changeAddress = isSingleAddressImported
214
+ ? account.address
215
+ : await this.getAddress(xpub, true);
216
+
217
+ try {
218
+ if (token && token.guid) {
219
+ // Token transaction: use assetAllocationSend
220
+ // Create a Map for the asset allocation
221
+ const assetMap = new Map();
222
+ assetMap.set(token.guid, {
223
+ changeAddress,
224
+ outputs: [
225
+ {
226
+ value: value as any,
227
+ address: receivingAddress,
228
+ },
229
+ ],
230
+ });
231
+
232
+ // Pass xpub to get back just the PSBT without signing and sending
233
+ // syscoinjs-lib will validate asset existence and sufficient balance
234
+ const result = await main.assetAllocationSend(
235
+ finalTxOptions,
236
+ assetMap,
237
+ changeAddress,
238
+ feeRateBN,
239
+ isSingleAddressImported ? account.address : xpub // Pass source
240
+ );
241
+
242
+ // Return PSBT and fee
243
+ return { psbt: result.psbt, fee: result.fee };
244
+ } else {
245
+ // Native transaction: use getTransactionPSBT to create unsigned PSBT
246
+ const outputs = [
247
+ {
248
+ address: receivingAddress,
249
+ value: value, // Pass BN object directly
250
+ ...(isMax && { subtractFeeFrom: true }), // Only add subtractFeeFrom if isMax is true
251
+ },
252
+ ];
253
+
254
+ const result = await this.getTransactionPSBT(
255
+ {
256
+ txOptions: finalTxOptions,
257
+ outputs,
258
+ changeAddress,
259
+ feeRateBN,
260
+ xpub: isSingleAddressImported ? account.address : xpub,
261
+ },
262
+ main
263
+ );
264
+
265
+ return result;
266
+ }
267
+ } catch (error) {
268
+ // Re-throw structured errors from syscoinjs-lib
269
+ if (error.error && error.code) {
270
+ throw error;
271
+ }
272
+ // Wrap other errors in structured format
273
+ throw {
274
+ error: true,
275
+ code: 'TRANSACTION_CREATION_FAILED',
276
+ message: error.message || 'Failed to create unsigned PSBT',
277
+ details: error,
278
+ };
279
+ }
280
+ };
281
+
282
+ // Sign PSBT with appropriate method - separated for better error handling
283
+ private signPSBTWithMethod = async (
284
+ psbt: any,
285
+ isTrezor: boolean,
286
+ isLedger = false
287
+ ): Promise<any> => {
288
+ const { activeNetwork, activeAccountId, activeAccountType, accounts } =
289
+ this.getState();
290
+
291
+ if (isLedger) {
292
+ // CRITICAL: Enhance PSBT with required Ledger fields
293
+ const account = accounts[activeAccountType]?.[activeAccountId];
294
+ if (!account) {
295
+ throw new Error('Active account not found');
296
+ }
297
+ const accountXpub = account.xpub;
298
+ const accountId = account.id;
299
+ const enhancedPsbt = await this.ledger.convertToLedgerFormat(
300
+ psbt,
301
+ accountXpub,
302
+ accountId,
303
+ activeNetwork.currency,
304
+ activeNetwork.slip44
305
+ );
306
+
307
+ // Get wallet policy for Ledger
308
+ const fingerprint =
309
+ await this.ledger.ledgerUtxoClient.getMasterFingerprint();
310
+
311
+ const hdPath = getAccountDerivationPath(
312
+ activeNetwork.currency,
313
+ activeNetwork.slip44,
314
+ accountId
315
+ );
316
+
317
+ // Convert stored/display zpub/vpub to device-friendly xpub/tpub for policy descriptor using network macros
318
+ const { types: deviceTypes } = getNetworkConfig(
319
+ activeNetwork.slip44,
320
+ activeNetwork.currency
321
+ );
322
+ const devicePubMagicDec =
323
+ activeNetwork.slip44 === 1
324
+ ? (deviceTypes.xPubType as any).testnet.vpub
325
+ : deviceTypes.xPubType.mainnet.zpub;
326
+ const devicePubMagicHex = Number(devicePubMagicDec)
327
+ .toString(16)
328
+ .padStart(8, '0');
329
+ const deviceXpub = convertExtendedKeyVersion(
330
+ accountXpub,
331
+ devicePubMagicHex
332
+ );
333
+ const xpubWithDescriptor = `[${hdPath}]${deviceXpub}`.replace(
334
+ 'm',
335
+ fingerprint
336
+ );
337
+ const walletPolicy = new DefaultWalletPolicy(
338
+ 'wpkh(@0/**)',
339
+ xpubWithDescriptor
340
+ );
341
+
342
+ // Register lazily and retrieve HMAC for silent operations thereafter
343
+ let hmac: Buffer | null = null;
344
+ if (typeof (this.ledger as any).getOrRegisterHmac === 'function') {
345
+ hmac = await (this.ledger as any).getOrRegisterHmac(
346
+ walletPolicy,
347
+ fingerprint
348
+ );
349
+ }
350
+
351
+ // Sign the enhanced PSBT with Ledger using the HMAC (silent after first approval)
352
+ const signatureEntries = await this.ledger.ledgerUtxoClient.signPsbt(
353
+ enhancedPsbt.toBase64(),
354
+ walletPolicy,
355
+ hmac
356
+ );
357
+
358
+ signatureEntries.forEach(([inputIndex, partialSig]) => {
359
+ enhancedPsbt.updateInput(inputIndex, {
360
+ partialSig: [partialSig],
361
+ });
362
+ });
363
+
364
+ // Finalize all inputs
365
+ enhancedPsbt.finalizeAllInputs();
366
+
367
+ return enhancedPsbt;
368
+ } else if (isTrezor) {
369
+ // Handle Trezor signing for UTXO
370
+ // Get network configuration for Trezor
371
+ const networkConfig = getNetworkConfig(
372
+ activeNetwork.slip44,
373
+ activeNetwork.currency
374
+ );
375
+ const isTestnet = activeNetwork.slip44 === 1;
376
+ const bitcoinjsNetwork = isTestnet
377
+ ? networkConfig?.networks?.testnet
378
+ : networkConfig?.networks?.mainnet;
379
+
380
+ const trezorTx = this.trezor.convertToTrezorFormat({
381
+ psbt,
382
+ coin: activeNetwork.currency.toLowerCase(),
383
+ network: bitcoinjsNetwork || undefined, // Pass network config for isScriptHash check
384
+ });
385
+ const signedPsbt = await this.trezor.signUtxoTransaction(trezorTx, psbt);
386
+ return signedPsbt;
387
+ } else {
388
+ const { hd } = this.getSigner();
389
+ const signedPsbt = await this.signPSBTWithSigner({
390
+ psbt,
391
+ signer: hd,
392
+ });
393
+ return signedPsbt;
394
+ }
395
+ };
396
+
397
+ // Create unsigned PSBT and estimate fee - NO SIGNING
398
+ public getEstimateSysTransactionFee = async ({
399
+ txOptions = {},
400
+ isMax = false,
401
+ amount,
402
+ receivingAddress,
403
+ feeRate,
404
+ token = null,
405
+ }: {
406
+ amount: number | string; // Accept both for safer precision handling
407
+ feeRate?: number;
408
+ receivingAddress: string;
409
+ // Optional fee rate in SYS/byte
410
+ token?: { guid: string; symbol?: string } | null;
411
+ txOptions?: any;
412
+ isMax?: boolean | false;
413
+ }) => {
414
+ try {
415
+ // Ensure RBF is enabled by default if not explicitly set
416
+ const finalTxOptions = { rbf: true, ...txOptions };
417
+ // Use read-only signer since we're just estimating fees and creating unsigned PSBT
418
+ const { main } = this.getReadOnlySigner();
419
+ // Step 1: Determine fee rate
420
+ let actualFeeRate;
421
+ if (feeRate !== undefined) {
422
+ actualFeeRate = feeRate;
423
+ } else {
424
+ actualFeeRate = await this.getRecommendedFee(main.blockbookURL);
425
+ }
426
+
427
+ // Convert fee rate to satoshis/byte and ensure minimum relay of 1 sat/vB
428
+ // Blockbook returns coins per kB; after division by 1024, some testnets
429
+ // can yield < 1 sat/vB (e.g., 0.9765625). Round up and clamp to 1 to
430
+ // avoid zero-fee transactions due to truncation when converting to BN.
431
+ const satPerByte = Math.max(1, Math.ceil(actualFeeRate * 1e8));
432
+ const feeRateBN = new syscoinjs.utils.BN(satPerByte);
433
+
434
+ // Step 2: Create unsigned PSBT
435
+ const result = await this.createUnsignedPSBT({
436
+ txOptions: finalTxOptions,
437
+ isMax,
438
+ amount,
439
+ receivingAddress,
440
+ feeRateBN,
441
+ token,
442
+ });
443
+
444
+ return {
445
+ fee: result.fee / 1e8,
446
+ psbt: PsbtUtils.toPali(result.psbt), // Return UNSIGNED PSBT as JSON
447
+ };
448
+ } catch (error) {
449
+ // Pass through structured errors from syscoinjs-lib
450
+ if (error.error && error.code) {
451
+ // Convert fee from satoshis to SYS if available
452
+ if (error.fee !== undefined) {
453
+ // If fee is a BN object, convert to number first
454
+ if (typeof error.fee === 'object' && error.fee.toNumber) {
455
+ error.fee = error.fee.toNumber() / 1e8;
456
+ } else {
457
+ error.fee = error.fee / 1e8;
458
+ }
459
+ }
460
+ if (error.remainingFee !== undefined) {
461
+ // If remainingFee is a BN object, convert to number first
462
+ if (
463
+ typeof error.remainingFee === 'object' &&
464
+ error.remainingFee.toNumber
465
+ ) {
466
+ error.remainingFee = error.remainingFee.toNumber() / 1e8;
467
+ } else {
468
+ error.remainingFee = error.remainingFee / 1e8;
469
+ }
470
+ }
471
+ if (error.shortfall !== undefined) {
472
+ // If shortfall is a BN object, convert to number first
473
+ if (typeof error.shortfall === 'object' && error.shortfall.toNumber) {
474
+ error.shortfall = error.shortfall.toNumber() / 1e8;
475
+ } else {
476
+ error.shortfall = error.shortfall / 1e8;
477
+ }
478
+ }
479
+ throw error;
480
+ }
481
+ // Wrap other errors
482
+ throw {
483
+ error: true,
484
+ code: 'TRANSACTION_CREATION_FAILED',
485
+ message: error.message || 'Failed to estimate transaction fee',
486
+ details: error,
487
+ };
488
+ }
489
+ };
490
+
491
+ // Removed createAndSignSysTransaction - functionality merged into getEstimateSysTransactionFee
492
+
493
+ private sendSignedTransaction = async (psbt): Promise<ITxid> => {
494
+ try {
495
+ // Use read-only signer since we're just broadcasting an already-signed transaction
496
+ const { main } = this.getReadOnlySigner();
497
+ // Send the transaction
498
+ const result = await main.send(psbt);
499
+
500
+ // Extract the transaction ID
501
+ const txid = result.extractTransaction().getId();
502
+ return { txid };
503
+ } catch (error) {
504
+ // Pass through structured errors
505
+ if (error.error && error.code) {
506
+ throw error;
507
+ }
508
+ // Wrap other errors
509
+ throw {
510
+ error: true,
511
+ code: 'TRANSACTION_SEND_FAILED',
512
+ message: error.message || 'Failed to send transaction',
513
+ details: error,
514
+ };
515
+ }
516
+ };
517
+
518
+ public signPSBT = async ({
519
+ psbt,
520
+ isTrezor = false,
521
+ isLedger = false,
522
+ }: {
523
+ psbt: any;
524
+ isTrezor?: boolean;
525
+ isLedger?: boolean;
526
+ }): Promise<any> => {
527
+ const psbtObj = PsbtUtils.fromPali(psbt);
528
+ const signedPsbt = await this.signPSBTWithMethod(
529
+ psbtObj,
530
+ isTrezor,
531
+ isLedger
532
+ );
533
+ return PsbtUtils.toPali(signedPsbt);
534
+ };
535
+
536
+ public sendTransaction = async (psbt: any): Promise<ITxid> => {
537
+ if (!psbt) {
538
+ throw new Error('Signed PSBT is required for broadcasting.');
539
+ }
540
+ return await this.sendSignedTransaction(PsbtUtils.fromPali(psbt));
541
+ };
542
+ }