@originals/sdk 1.2.0 → 1.4.2

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 (244) hide show
  1. package/package.json +1 -1
  2. package/src/did/DIDManager.ts +1 -1
  3. package/src/did/WebVHManager.ts +11 -2
  4. package/src/examples/create-module-original.ts +435 -0
  5. package/src/examples/full-lifecycle-flow.ts +514 -0
  6. package/src/examples/run.ts +59 -4
  7. package/src/index.ts +69 -3
  8. package/src/kinds/KindRegistry.ts +290 -0
  9. package/src/kinds/index.ts +74 -0
  10. package/src/kinds/types.ts +470 -0
  11. package/src/kinds/validators/AgentValidator.ts +257 -0
  12. package/src/kinds/validators/AppValidator.ts +211 -0
  13. package/src/kinds/validators/DatasetValidator.ts +242 -0
  14. package/src/kinds/validators/DocumentValidator.ts +311 -0
  15. package/src/kinds/validators/MediaValidator.ts +269 -0
  16. package/src/kinds/validators/ModuleValidator.ts +225 -0
  17. package/src/kinds/validators/base.ts +276 -0
  18. package/src/kinds/validators/index.ts +12 -0
  19. package/src/lifecycle/LifecycleManager.ts +909 -1
  20. package/src/resources/ResourceManager.ts +655 -0
  21. package/src/resources/index.ts +21 -0
  22. package/src/resources/types.ts +202 -0
  23. package/src/types/common.ts +1 -1
  24. package/src/vc/CredentialManager.ts +647 -2
  25. package/tests/integration/createTypedOriginal.test.ts +379 -0
  26. package/tests/performance/BatchOperations.perf.test.ts +2 -2
  27. package/tests/unit/kinds/KindRegistry.test.ts +329 -0
  28. package/tests/unit/kinds/types.test.ts +409 -0
  29. package/tests/unit/kinds/validators.test.ts +651 -0
  30. package/tests/unit/lifecycle/LifecycleManager.cleanapi.test.ts +441 -0
  31. package/tests/unit/resources/ResourceManager.test.ts +740 -0
  32. package/tests/unit/vc/CredentialManager.helpers.test.ts +527 -0
  33. package/.turbo/turbo-build.log +0 -1
  34. package/dist/adapters/FeeOracleMock.d.ts +0 -6
  35. package/dist/adapters/FeeOracleMock.js +0 -8
  36. package/dist/adapters/index.d.ts +0 -4
  37. package/dist/adapters/index.js +0 -4
  38. package/dist/adapters/providers/OrdHttpProvider.d.ts +0 -56
  39. package/dist/adapters/providers/OrdHttpProvider.js +0 -110
  40. package/dist/adapters/providers/OrdMockProvider.d.ts +0 -70
  41. package/dist/adapters/providers/OrdMockProvider.js +0 -75
  42. package/dist/adapters/types.d.ts +0 -71
  43. package/dist/adapters/types.js +0 -1
  44. package/dist/bitcoin/BitcoinManager.d.ts +0 -15
  45. package/dist/bitcoin/BitcoinManager.js +0 -262
  46. package/dist/bitcoin/BroadcastClient.d.ts +0 -30
  47. package/dist/bitcoin/BroadcastClient.js +0 -35
  48. package/dist/bitcoin/OrdinalsClient.d.ts +0 -21
  49. package/dist/bitcoin/OrdinalsClient.js +0 -105
  50. package/dist/bitcoin/PSBTBuilder.d.ts +0 -24
  51. package/dist/bitcoin/PSBTBuilder.js +0 -80
  52. package/dist/bitcoin/fee-calculation.d.ts +0 -14
  53. package/dist/bitcoin/fee-calculation.js +0 -31
  54. package/dist/bitcoin/providers/OrdNodeProvider.d.ts +0 -38
  55. package/dist/bitcoin/providers/OrdNodeProvider.js +0 -67
  56. package/dist/bitcoin/providers/OrdinalsProvider.d.ts +0 -33
  57. package/dist/bitcoin/providers/OrdinalsProvider.js +0 -50
  58. package/dist/bitcoin/providers/types.d.ts +0 -63
  59. package/dist/bitcoin/providers/types.js +0 -1
  60. package/dist/bitcoin/transactions/commit.d.ts +0 -89
  61. package/dist/bitcoin/transactions/commit.js +0 -311
  62. package/dist/bitcoin/transactions/index.d.ts +0 -7
  63. package/dist/bitcoin/transactions/index.js +0 -8
  64. package/dist/bitcoin/transfer.d.ts +0 -9
  65. package/dist/bitcoin/transfer.js +0 -26
  66. package/dist/bitcoin/utxo-selection.d.ts +0 -78
  67. package/dist/bitcoin/utxo-selection.js +0 -237
  68. package/dist/bitcoin/utxo.d.ts +0 -26
  69. package/dist/bitcoin/utxo.js +0 -78
  70. package/dist/contexts/credentials-v1.json +0 -195
  71. package/dist/contexts/credentials-v2-examples.json +0 -5
  72. package/dist/contexts/credentials-v2.json +0 -301
  73. package/dist/contexts/credentials.json +0 -195
  74. package/dist/contexts/data-integrity-v2.json +0 -81
  75. package/dist/contexts/dids.json +0 -57
  76. package/dist/contexts/ed255192020.json +0 -93
  77. package/dist/contexts/ordinals-plus.json +0 -23
  78. package/dist/contexts/originals.json +0 -22
  79. package/dist/core/OriginalsSDK.d.ts +0 -158
  80. package/dist/core/OriginalsSDK.js +0 -274
  81. package/dist/crypto/Multikey.d.ts +0 -30
  82. package/dist/crypto/Multikey.js +0 -149
  83. package/dist/crypto/Signer.d.ts +0 -21
  84. package/dist/crypto/Signer.js +0 -196
  85. package/dist/crypto/noble-init.d.ts +0 -18
  86. package/dist/crypto/noble-init.js +0 -106
  87. package/dist/did/BtcoDidResolver.d.ts +0 -57
  88. package/dist/did/BtcoDidResolver.js +0 -166
  89. package/dist/did/DIDManager.d.ts +0 -101
  90. package/dist/did/DIDManager.js +0 -493
  91. package/dist/did/Ed25519Verifier.d.ts +0 -30
  92. package/dist/did/Ed25519Verifier.js +0 -59
  93. package/dist/did/KeyManager.d.ts +0 -17
  94. package/dist/did/KeyManager.js +0 -207
  95. package/dist/did/WebVHManager.d.ts +0 -100
  96. package/dist/did/WebVHManager.js +0 -304
  97. package/dist/did/createBtcoDidDocument.d.ts +0 -10
  98. package/dist/did/createBtcoDidDocument.js +0 -42
  99. package/dist/did/providers/OrdinalsClientProviderAdapter.d.ts +0 -23
  100. package/dist/did/providers/OrdinalsClientProviderAdapter.js +0 -51
  101. package/dist/events/EventEmitter.d.ts +0 -115
  102. package/dist/events/EventEmitter.js +0 -198
  103. package/dist/events/index.d.ts +0 -7
  104. package/dist/events/index.js +0 -6
  105. package/dist/events/types.d.ts +0 -286
  106. package/dist/events/types.js +0 -9
  107. package/dist/examples/basic-usage.d.ts +0 -3
  108. package/dist/examples/basic-usage.js +0 -62
  109. package/dist/examples/run.d.ts +0 -1
  110. package/dist/examples/run.js +0 -4
  111. package/dist/index.d.ts +0 -39
  112. package/dist/index.js +0 -47
  113. package/dist/lifecycle/BatchOperations.d.ts +0 -147
  114. package/dist/lifecycle/BatchOperations.js +0 -251
  115. package/dist/lifecycle/LifecycleManager.d.ts +0 -116
  116. package/dist/lifecycle/LifecycleManager.js +0 -971
  117. package/dist/lifecycle/OriginalsAsset.d.ts +0 -164
  118. package/dist/lifecycle/OriginalsAsset.js +0 -380
  119. package/dist/lifecycle/ProvenanceQuery.d.ts +0 -126
  120. package/dist/lifecycle/ProvenanceQuery.js +0 -220
  121. package/dist/lifecycle/ResourceVersioning.d.ts +0 -73
  122. package/dist/lifecycle/ResourceVersioning.js +0 -127
  123. package/dist/migration/MigrationManager.d.ts +0 -86
  124. package/dist/migration/MigrationManager.js +0 -412
  125. package/dist/migration/audit/AuditLogger.d.ts +0 -51
  126. package/dist/migration/audit/AuditLogger.js +0 -156
  127. package/dist/migration/checkpoint/CheckpointManager.d.ts +0 -31
  128. package/dist/migration/checkpoint/CheckpointManager.js +0 -96
  129. package/dist/migration/checkpoint/CheckpointStorage.d.ts +0 -26
  130. package/dist/migration/checkpoint/CheckpointStorage.js +0 -89
  131. package/dist/migration/index.d.ts +0 -22
  132. package/dist/migration/index.js +0 -27
  133. package/dist/migration/operations/BaseMigration.d.ts +0 -48
  134. package/dist/migration/operations/BaseMigration.js +0 -83
  135. package/dist/migration/operations/PeerToBtcoMigration.d.ts +0 -25
  136. package/dist/migration/operations/PeerToBtcoMigration.js +0 -67
  137. package/dist/migration/operations/PeerToWebvhMigration.d.ts +0 -19
  138. package/dist/migration/operations/PeerToWebvhMigration.js +0 -46
  139. package/dist/migration/operations/WebvhToBtcoMigration.d.ts +0 -25
  140. package/dist/migration/operations/WebvhToBtcoMigration.js +0 -67
  141. package/dist/migration/rollback/RollbackManager.d.ts +0 -29
  142. package/dist/migration/rollback/RollbackManager.js +0 -146
  143. package/dist/migration/state/StateMachine.d.ts +0 -25
  144. package/dist/migration/state/StateMachine.js +0 -76
  145. package/dist/migration/state/StateTracker.d.ts +0 -36
  146. package/dist/migration/state/StateTracker.js +0 -123
  147. package/dist/migration/types.d.ts +0 -306
  148. package/dist/migration/types.js +0 -33
  149. package/dist/migration/validation/BitcoinValidator.d.ts +0 -13
  150. package/dist/migration/validation/BitcoinValidator.js +0 -83
  151. package/dist/migration/validation/CredentialValidator.d.ts +0 -13
  152. package/dist/migration/validation/CredentialValidator.js +0 -46
  153. package/dist/migration/validation/DIDCompatibilityValidator.d.ts +0 -16
  154. package/dist/migration/validation/DIDCompatibilityValidator.js +0 -127
  155. package/dist/migration/validation/LifecycleValidator.d.ts +0 -10
  156. package/dist/migration/validation/LifecycleValidator.js +0 -52
  157. package/dist/migration/validation/StorageValidator.d.ts +0 -10
  158. package/dist/migration/validation/StorageValidator.js +0 -65
  159. package/dist/migration/validation/ValidationPipeline.d.ts +0 -29
  160. package/dist/migration/validation/ValidationPipeline.js +0 -180
  161. package/dist/storage/LocalStorageAdapter.d.ts +0 -11
  162. package/dist/storage/LocalStorageAdapter.js +0 -53
  163. package/dist/storage/MemoryStorageAdapter.d.ts +0 -6
  164. package/dist/storage/MemoryStorageAdapter.js +0 -21
  165. package/dist/storage/StorageAdapter.d.ts +0 -16
  166. package/dist/storage/StorageAdapter.js +0 -1
  167. package/dist/storage/index.d.ts +0 -2
  168. package/dist/storage/index.js +0 -2
  169. package/dist/types/bitcoin.d.ts +0 -84
  170. package/dist/types/bitcoin.js +0 -1
  171. package/dist/types/common.d.ts +0 -82
  172. package/dist/types/common.js +0 -1
  173. package/dist/types/credentials.d.ts +0 -75
  174. package/dist/types/credentials.js +0 -1
  175. package/dist/types/did.d.ts +0 -26
  176. package/dist/types/did.js +0 -1
  177. package/dist/types/index.d.ts +0 -5
  178. package/dist/types/index.js +0 -5
  179. package/dist/types/network.d.ts +0 -78
  180. package/dist/types/network.js +0 -145
  181. package/dist/utils/EventLogger.d.ts +0 -71
  182. package/dist/utils/EventLogger.js +0 -232
  183. package/dist/utils/Logger.d.ts +0 -106
  184. package/dist/utils/Logger.js +0 -257
  185. package/dist/utils/MetricsCollector.d.ts +0 -110
  186. package/dist/utils/MetricsCollector.js +0 -264
  187. package/dist/utils/bitcoin-address.d.ts +0 -38
  188. package/dist/utils/bitcoin-address.js +0 -113
  189. package/dist/utils/cbor.d.ts +0 -2
  190. package/dist/utils/cbor.js +0 -9
  191. package/dist/utils/encoding.d.ts +0 -37
  192. package/dist/utils/encoding.js +0 -120
  193. package/dist/utils/hash.d.ts +0 -1
  194. package/dist/utils/hash.js +0 -5
  195. package/dist/utils/retry.d.ts +0 -10
  196. package/dist/utils/retry.js +0 -35
  197. package/dist/utils/satoshi-validation.d.ts +0 -60
  198. package/dist/utils/satoshi-validation.js +0 -156
  199. package/dist/utils/serialization.d.ts +0 -14
  200. package/dist/utils/serialization.js +0 -76
  201. package/dist/utils/telemetry.d.ts +0 -17
  202. package/dist/utils/telemetry.js +0 -24
  203. package/dist/utils/validation.d.ts +0 -5
  204. package/dist/utils/validation.js +0 -98
  205. package/dist/vc/CredentialManager.d.ts +0 -22
  206. package/dist/vc/CredentialManager.js +0 -227
  207. package/dist/vc/Issuer.d.ts +0 -27
  208. package/dist/vc/Issuer.js +0 -70
  209. package/dist/vc/Verifier.d.ts +0 -16
  210. package/dist/vc/Verifier.js +0 -50
  211. package/dist/vc/cryptosuites/bbs.d.ts +0 -44
  212. package/dist/vc/cryptosuites/bbs.js +0 -213
  213. package/dist/vc/cryptosuites/bbsSimple.d.ts +0 -9
  214. package/dist/vc/cryptosuites/bbsSimple.js +0 -12
  215. package/dist/vc/cryptosuites/eddsa.d.ts +0 -30
  216. package/dist/vc/cryptosuites/eddsa.js +0 -81
  217. package/dist/vc/documentLoader.d.ts +0 -16
  218. package/dist/vc/documentLoader.js +0 -59
  219. package/dist/vc/proofs/data-integrity.d.ts +0 -21
  220. package/dist/vc/proofs/data-integrity.js +0 -15
  221. package/dist/vc/utils/jsonld.d.ts +0 -2
  222. package/dist/vc/utils/jsonld.js +0 -15
  223. package/test/logs/did_webvh_QmNTn9Kkp8dQ75WrF9xqJ2kuDp9QhKc3aPiERRMj8XoTBN_example_com.jsonl +0 -1
  224. package/test/logs/did_webvh_QmNu4MNr8Lr5txx5gYNhuhZDchXsZEu3hJXKYuphpWTPDp_example_com_users_etc_passwd.jsonl +0 -1
  225. package/test/logs/did_webvh_QmR9MrGZACzjKETA8SBRNCKG11HxU85c4bVR2qN5eDCfsD_example_com.jsonl +0 -1
  226. package/test/logs/did_webvh_QmUc5suaqRM2P4nrXxZwqYMfqzhdMqjuL7oJaJbEpCQVCd_example_com_users_etc_passwd.jsonl +0 -1
  227. package/test/logs/did_webvh_QmUkiB2RCV2VZ1RTXsCebWN25Eiy9TLvpzDWAJNjhgvB4X_example_com_etc_passwd.jsonl +0 -1
  228. package/test/logs/did_webvh_QmUoRTe8UMwpAQXZSAW7pjAgZK1tq2X3C6Kfxq3UXGcaGy_example_com_secret.jsonl +0 -1
  229. package/test/logs/did_webvh_QmWWot3chx1t6KwTmcE5i2FeDZ5JMkQw3qXycsKDVmJ9Be_example_com_users_alice.jsonl +0 -1
  230. package/test/logs/did_webvh_QmWvVgALL5kjZdpgR7KZay7J8UiiUr834kkRmWeFAxjAuC_example_com_users_etc_passwd.jsonl +0 -1
  231. package/test/logs/did_webvh_QmWwaRQHUZAFcKihFC6xR6tRTTrQhHPTku6azf1egWbpy1_example_com_users_alice.jsonl +0 -1
  232. package/test/logs/did_webvh_QmXJLtkz23r7AozbtXsZMKWnVU6rd38CkVtjdWuATU3Yp6_example_com_users_alice123_profile.jsonl +0 -1
  233. package/test/logs/did_webvh_QmYsce448po14oDE1wXbyaP6wY9HQgHSKLwdezn1k577SF_example_com_my_org_user_name_test_123.jsonl +0 -1
  234. package/test/logs/did_webvh_QmZBeNzzqajxdfwcDUPZ4P8C5YSXyRztrAwmPiKuKUxmAK_example_com.jsonl +0 -1
  235. package/test/logs/did_webvh_QmZhJsqxizwVbRtqCUkmE6XQunSxtxMt3gbTYadVBNAaEq_example_com.jsonl +0 -1
  236. package/test/logs/did_webvh_QmZk7NHU2D57RzzbMq4tWW9gBa9AqtVTWfiRM6RFdwGVj2_example_com.jsonl +0 -1
  237. package/test/logs/did_webvh_QmZshSXp9w8ovH62zGGBS1b5pGGPsuYiu1VQ935sga2hWF_example_com_level1_level2.jsonl +0 -1
  238. package/test/logs/did_webvh_QmbWAmw7HQL7vKJyCsctZihXf1rmT4sGvggKCPKWcUWjw1_example_com.jsonl +0 -1
  239. package/test/logs/did_webvh_QmbdLUMbYs3juR39TLB6hhrFWLcNg45ybUzeBJCS1MhCh1_example_com_C_Windows_System32.jsonl +0 -1
  240. package/test/logs/did_webvh_QmcaQ1Ma4gkSbae85aCm8Mv4rvdT2Sb2RR3JzYwrm5XBq8_example_com_etc_passwd.jsonl +0 -1
  241. package/test/logs/did_webvh_QmcbA7WQhsBqZSoDpKJHjV8Q5o53h8vmgJhQfo6rqTY5ho_example_com.jsonl +0 -1
  242. package/test/logs/did_webvh_Qmdy8uWr2gkUJrXsThynAug3DASTWwb3onEj89LKmMGZYB_example_com.jsonl +0 -1
  243. package/tests/e2e/README.md +0 -97
  244. package/tests/e2e/example.spec.ts +0 -78
@@ -1,311 +0,0 @@
1
- /**
2
- * Commit Transaction Processing for Ordinals
3
- *
4
- * This module implements the commit transaction process for ordinals inscriptions.
5
- * It handles the generation of the commit address and preparation of the commit transaction.
6
- *
7
- * Ported from legacy ordinalsplus transaction infrastructure.
8
- */
9
- import * as btc from '@scure/btc-signer';
10
- import * as ordinals from 'micro-ordinals';
11
- import { schnorr } from '@noble/curves/secp256k1';
12
- import { calculateFee } from '../fee-calculation.js';
13
- import { selectUtxos } from '../utxo-selection.js';
14
- // Define minimum dust limit (satoshis)
15
- const MIN_DUST_LIMIT = 546;
16
- // Maximum iterations for UTXO reselection to prevent infinite loops
17
- const MAX_SELECTION_ITERATIONS = 5;
18
- /**
19
- * Get @scure/btc-signer network configuration
20
- */
21
- function getScureNetwork(network) {
22
- switch (network) {
23
- case 'mainnet':
24
- return btc.NETWORK;
25
- case 'testnet':
26
- case 'signet':
27
- case 'regtest':
28
- return btc.TEST_NETWORK;
29
- default:
30
- return btc.NETWORK;
31
- }
32
- }
33
- /**
34
- * Validates that a UTXO has all required fields for spending
35
- *
36
- * @param utxo - The UTXO to validate
37
- * @returns true if the UTXO is valid and spendable, false otherwise
38
- */
39
- function isValidSpendableUtxo(utxo) {
40
- return !!(utxo.txid &&
41
- typeof utxo.vout === 'number' &&
42
- utxo.value > 0 &&
43
- utxo.scriptPubKey &&
44
- utxo.scriptPubKey.length > 0);
45
- }
46
- /**
47
- * Estimates the size of a commit transaction
48
- *
49
- * @param inputCount - Number of transaction inputs
50
- * @param outputCount - Number of transaction outputs (including commit and change)
51
- * @returns Estimated transaction size in virtual bytes
52
- */
53
- function estimateCommitTxSize(inputCount, outputCount) {
54
- // Transaction overhead
55
- const overhead = 10.5;
56
- // P2WPKH inputs (assuming most common case)
57
- const inputSize = 68 * inputCount;
58
- // P2TR output for commit and P2WPKH for change
59
- const commitOutputSize = 43; // P2TR output
60
- const changeOutputSize = outputCount > 1 ? 31 * (outputCount - 1) : 0; // P2WPKH outputs for change
61
- return Math.ceil(overhead + inputSize + commitOutputSize + changeOutputSize);
62
- }
63
- /**
64
- * Creates a commit transaction for an ordinals inscription
65
- *
66
- * This function:
67
- * 1. Validates and filters UTXOs to ensure they are spendable
68
- * 2. Creates an inscription with the provided content
69
- * 3. Generates a reveal keypair and script
70
- * 4. Creates a P2TR commit address
71
- * 5. Selects UTXOs to fund the transaction (with iterative reselection if needed)
72
- * 6. Builds a PSBT with commit output and change
73
- *
74
- * The function ensures that:
75
- * - All selected UTXOs have valid scriptPubKey fields
76
- * - Total input value always covers output value + fees
77
- * - UTXO selection is re-run if fee increases after accurate calculation
78
- *
79
- * @param params - Parameters for the commit transaction
80
- * @returns Complete information for the prepared commit transaction
81
- * @throws Error if no valid UTXOs are available or insufficient funds
82
- */
83
- export async function createCommitTransaction(params) {
84
- const { content, contentType, utxos, changeAddress, feeRate, network, minimumCommitAmount = MIN_DUST_LIMIT, metadata, pointer } = params;
85
- // Validate inputs
86
- if (!utxos || utxos.length === 0) {
87
- throw new Error('No UTXOs provided to fund the transaction.');
88
- }
89
- if (!content || content.length === 0) {
90
- throw new Error('Invalid inscription: missing content.');
91
- }
92
- if (!contentType) {
93
- throw new Error('Invalid inscription: missing content type.');
94
- }
95
- if (!changeAddress) {
96
- throw new Error('Change address is required.');
97
- }
98
- if (feeRate <= 0) {
99
- throw new Error(`Invalid fee rate: ${feeRate}`);
100
- }
101
- // CRITICAL: Pre-filter UTXOs to ensure all have valid scriptPubKey
102
- // This prevents silent failures where UTXOs are selected but can't be spent
103
- const validUtxos = utxos.filter(isValidSpendableUtxo);
104
- if (validUtxos.length === 0) {
105
- const invalidCount = utxos.length;
106
- const invalidReasons = [];
107
- utxos.forEach((utxo, idx) => {
108
- if (!utxo.scriptPubKey || utxo.scriptPubKey.length === 0) {
109
- invalidReasons.push(`UTXO ${idx} (${utxo.txid}:${utxo.vout}): missing scriptPubKey`);
110
- }
111
- else if (!utxo.txid) {
112
- invalidReasons.push(`UTXO ${idx}: missing txid`);
113
- }
114
- else if (typeof utxo.vout !== 'number') {
115
- invalidReasons.push(`UTXO ${idx} (${utxo.txid}): missing or invalid vout`);
116
- }
117
- else if (utxo.value <= 0) {
118
- invalidReasons.push(`UTXO ${idx} (${utxo.txid}:${utxo.vout}): invalid value (${utxo.value})`);
119
- }
120
- });
121
- throw new Error(`No valid spendable UTXOs available. ${invalidCount} UTXO(s) provided but all are invalid:\n` +
122
- invalidReasons.slice(0, 5).join('\n') +
123
- (invalidReasons.length > 5 ? `\n... and ${invalidReasons.length - 5} more` : ''));
124
- }
125
- // Log filtered UTXOs for debugging
126
- if (validUtxos.length < utxos.length) {
127
- const filteredCount = utxos.length - validUtxos.length;
128
- console.warn(`Filtered out ${filteredCount} invalid UTXO(s). ${validUtxos.length} valid UTXO(s) remain.`);
129
- }
130
- // Step 1: Create the inscription object
131
- const tags = {
132
- contentType
133
- };
134
- // Add metadata if provided
135
- if (metadata && Object.keys(metadata).length > 0) {
136
- tags.metadata = metadata;
137
- }
138
- // Add pointer if provided
139
- if (typeof pointer !== 'undefined') {
140
- tags.pointer = pointer;
141
- }
142
- const inscription = {
143
- tags,
144
- body: new Uint8Array(content)
145
- };
146
- // Step 2: Generate a reveal keypair
147
- // Use random private key for reveal transaction
148
- const revealPrivateKey = schnorr.utils.randomPrivateKey();
149
- const revealPublicKey = schnorr.getPublicKey(revealPrivateKey);
150
- // Step 3: Create the inscription script tree using micro-ordinals
151
- const scriptTree = ordinals.p2tr_ord_reveal(revealPublicKey, [inscription]);
152
- // Step 4: Create P2TR address for the commit output
153
- const scureNetwork = getScureNetwork(network);
154
- // Create taproot output using the inscription script tree
155
- // Use the reveal public key as the internal key
156
- const taprootPayment = btc.p2tr(revealPublicKey, // internal key
157
- scriptTree, // script tree
158
- scureNetwork, false, // allowUnknownOutputs
159
- [ordinals.OutOrdinalReveal] // customScripts
160
- );
161
- if (!taprootPayment.address) {
162
- throw new Error('Failed to generate P2TR commit address');
163
- }
164
- const commitAddress = taprootPayment.address;
165
- // Extract script information from the taproot payment
166
- if (!taprootPayment.leaves || taprootPayment.leaves.length === 0) {
167
- throw new Error('Failed to extract taproot leaves from P2TR payment');
168
- }
169
- const leaf = taprootPayment.leaves[0];
170
- const leafVersion = leaf.version ?? 0xc0;
171
- // Compute control block from leaf data
172
- // The control block is: version byte | internal key (32 bytes) | merkle path
173
- const controlBlock = btc.TaprootControlBlock.encode({
174
- version: leafVersion,
175
- internalKey: revealPublicKey,
176
- merklePath: leaf.path
177
- });
178
- // Step 5: Calculate minimum amount needed for the commit output
179
- const commitOutputValue = Math.max(minimumCommitAmount, MIN_DUST_LIMIT);
180
- // Step 6: Iterative UTXO selection with fee recalculation
181
- // This ensures that after we know the actual input count, we have enough funds
182
- let selectedUtxos = [];
183
- let totalInputValue = 0;
184
- let estimatedFee = 0;
185
- let iteration = 0;
186
- // Start with initial estimate (1 input, 2 outputs)
187
- let targetAmount = commitOutputValue + Number(calculateFee(estimateCommitTxSize(1, 2), feeRate));
188
- while (iteration < MAX_SELECTION_ITERATIONS) {
189
- iteration++;
190
- // Select UTXOs based on current target amount
191
- const options = {
192
- targetAmount
193
- };
194
- try {
195
- const selectionResult = selectUtxos(validUtxos, options);
196
- selectedUtxos = selectionResult.selectedUtxos;
197
- totalInputValue = selectionResult.totalInputValue;
198
- }
199
- catch (error) {
200
- throw new Error(`Insufficient funds. Need ${targetAmount} sats for commit output (${commitOutputValue} sats) and estimated fees. ` +
201
- `Available: ${validUtxos.reduce((sum, u) => sum + u.value, 0)} sats from ${validUtxos.length} valid UTXO(s). ` +
202
- `${error instanceof Error ? error.message : 'Unknown error'}`);
203
- }
204
- // Calculate accurate fee based on actual selected input count
205
- // Assume 2 outputs (commit + change) for now - we'll adjust later if no change
206
- const actualInputCount = selectedUtxos.length;
207
- const estimatedVBytes = estimateCommitTxSize(actualInputCount, 2);
208
- estimatedFee = Number(calculateFee(estimatedVBytes, feeRate));
209
- // Check if we need to account for no change output
210
- const potentialChange = totalInputValue - commitOutputValue - estimatedFee;
211
- let finalOutputCount = 2;
212
- if (potentialChange < MIN_DUST_LIMIT) {
213
- // No change output, recalculate fee with 1 output
214
- finalOutputCount = 1;
215
- const adjustedVBytes = estimateCommitTxSize(actualInputCount, finalOutputCount);
216
- estimatedFee = Number(calculateFee(adjustedVBytes, feeRate));
217
- }
218
- // Check if we have enough funds with the accurate fee calculation
219
- const requiredTotal = commitOutputValue + estimatedFee;
220
- if (totalInputValue >= requiredTotal) {
221
- // We have enough funds, break out of loop
222
- break;
223
- }
224
- // Not enough funds, need to reselect with higher target
225
- // Add a small buffer (5%) to account for potential fee variations
226
- targetAmount = Math.ceil(requiredTotal * 1.05);
227
- if (iteration >= MAX_SELECTION_ITERATIONS) {
228
- throw new Error(`Unable to select sufficient UTXOs after ${MAX_SELECTION_ITERATIONS} iterations. ` +
229
- `Required: ${requiredTotal} sats (commit: ${commitOutputValue}, fee: ${estimatedFee}), ` +
230
- `Selected: ${totalInputValue} sats from ${selectedUtxos.length} UTXO(s). ` +
231
- `Total available: ${validUtxos.reduce((sum, u) => sum + u.value, 0)} sats from ${validUtxos.length} valid UTXO(s).`);
232
- }
233
- }
234
- // Final validation: ensure we have selected UTXOs
235
- if (!selectedUtxos || selectedUtxos.length === 0) {
236
- throw new Error('No UTXOs selected for the transaction after selection process.');
237
- }
238
- // Step 7: Create transaction using @scure/btc-signer
239
- const tx = new btc.Transaction();
240
- // Add inputs - all selected UTXOs are already validated to have scriptPubKey
241
- for (const utxo of selectedUtxos) {
242
- // This check is now redundant due to pre-filtering, but kept as defense-in-depth
243
- if (!utxo.scriptPubKey) {
244
- throw new Error(`CRITICAL ERROR: Selected UTXO ${utxo.txid}:${utxo.vout} is missing scriptPubKey. ` +
245
- `This should never happen due to pre-filtering. Please report this bug.`);
246
- }
247
- tx.addInput({
248
- txid: utxo.txid,
249
- index: utxo.vout,
250
- witnessUtxo: {
251
- script: Buffer.from(utxo.scriptPubKey, 'hex'),
252
- amount: BigInt(utxo.value)
253
- }
254
- });
255
- }
256
- // Verify input count matches selected UTXOs
257
- if (tx.inputsLength !== selectedUtxos.length) {
258
- throw new Error(`Input count mismatch: expected ${selectedUtxos.length} inputs but transaction has ${tx.inputsLength}. ` +
259
- `This indicates a critical error in transaction construction.`);
260
- }
261
- // Step 8: Calculate final fee based on actual transaction structure
262
- const actualInputCount = tx.inputsLength;
263
- // Determine if we'll have a change output
264
- const preliminaryChange = totalInputValue - commitOutputValue - estimatedFee;
265
- const willHaveChange = preliminaryChange >= MIN_DUST_LIMIT;
266
- const finalOutputCount = willHaveChange ? 2 : 1;
267
- // Calculate final fee with correct output count
268
- const finalVBytes = estimateCommitTxSize(actualInputCount, finalOutputCount);
269
- const finalFee = Number(calculateFee(finalVBytes, feeRate));
270
- // CRITICAL: Final validation that inputs cover outputs + fees
271
- const finalChange = totalInputValue - commitOutputValue - finalFee;
272
- if (finalChange < 0) {
273
- throw new Error(`CRITICAL ERROR: Outputs exceed inputs! ` +
274
- `Inputs: ${totalInputValue} sats, ` +
275
- `Outputs: ${commitOutputValue} sats (commit) + ${finalFee} sats (fee) = ${commitOutputValue + finalFee} sats. ` +
276
- `Deficit: ${Math.abs(finalChange)} sats. ` +
277
- `This should never happen due to iterative selection. Please report this bug.`);
278
- }
279
- // Step 9: Add the commit output using the P2TR address
280
- tx.addOutputAddress(commitAddress, BigInt(commitOutputValue), scureNetwork);
281
- // Step 10: Add change output if above dust limit
282
- if (finalChange >= MIN_DUST_LIMIT) {
283
- tx.addOutputAddress(changeAddress, BigInt(finalChange), scureNetwork);
284
- }
285
- else if (finalChange > 0) {
286
- // If change is below dust limit, it's effectively added to the fee
287
- console.log(`Change amount ${finalChange} sats is below dust limit (${MIN_DUST_LIMIT} sats), adding to fee. ` +
288
- `Final fee: ${finalFee + finalChange} sats.`);
289
- }
290
- // Step 11: Get the PSBT as base64
291
- const txPsbt = tx.toPSBT();
292
- const commitPsbtBase64 = typeof txPsbt === 'string' ? txPsbt : Buffer.from(txPsbt).toString('base64');
293
- return {
294
- commitAddress,
295
- commitPsbtBase64,
296
- commitPsbt: tx,
297
- commitAmount: commitOutputValue,
298
- selectedUtxos,
299
- fees: {
300
- // Include dust in final fee if no change output
301
- commit: finalChange >= MIN_DUST_LIMIT ? finalFee : finalFee + finalChange
302
- },
303
- revealPrivateKey: Buffer.from(revealPrivateKey).toString('hex'),
304
- revealPublicKey: Buffer.from(revealPublicKey).toString('hex'),
305
- inscriptionScript: {
306
- script: leaf.script,
307
- controlBlock,
308
- leafVersion
309
- }
310
- };
311
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * Bitcoin Transactions
3
- *
4
- * This directory contains modules for creating and managing Bitcoin transactions,
5
- * particularly for Ordinals inscriptions.
6
- */
7
- export { createCommitTransaction, type CommitTransactionParams, type CommitTransactionResult } from './commit.js';
@@ -1,8 +0,0 @@
1
- /**
2
- * Bitcoin Transactions
3
- *
4
- * This directory contains modules for creating and managing Bitcoin transactions,
5
- * particularly for Ordinals inscriptions.
6
- */
7
- // Export commit transaction functionality
8
- export { createCommitTransaction } from './commit.js';
@@ -1,9 +0,0 @@
1
- import { BitcoinTransaction, Utxo } from '../types';
2
- import { SelectionOptions, SelectionResult } from './utxo';
3
- export interface BuildTransferOptions extends Omit<SelectionOptions, 'targetAmountSats' | 'feeRateSatsPerVb'> {
4
- changeAddress?: string;
5
- }
6
- export declare function buildTransferTransaction(availableUtxos: Utxo[], recipientAddress: string, amountSats: number, feeRateSatsPerVb: number, options?: BuildTransferOptions): {
7
- tx: BitcoinTransaction;
8
- selection: SelectionResult;
9
- };
@@ -1,26 +0,0 @@
1
- import { DUST_LIMIT_SATS } from '../types';
2
- import { selectUtxos } from './utxo';
3
- export function buildTransferTransaction(availableUtxos, recipientAddress, amountSats, feeRateSatsPerVb, options = {}) {
4
- const selection = selectUtxos(availableUtxos, {
5
- targetAmountSats: amountSats,
6
- feeRateSatsPerVb,
7
- allowLocked: options.allowLocked,
8
- forbidInscriptionBearingInputs: options.forbidInscriptionBearingInputs,
9
- changeAddress: options.changeAddress,
10
- feeEstimate: options.feeEstimate
11
- });
12
- const vin = selection.selected.map(u => ({ txid: u.txid, vout: u.vout }));
13
- const outputs = [];
14
- outputs.push({ value: amountSats, scriptPubKey: 'script', address: recipientAddress });
15
- if (selection.changeSats >= DUST_LIMIT_SATS) {
16
- const changeAddress = options.changeAddress || (selection.selected.find(u => !!u.address)?.address ?? 'change');
17
- outputs.push({ value: selection.changeSats, scriptPubKey: 'script', address: changeAddress });
18
- }
19
- const tx = {
20
- txid: '',
21
- vin,
22
- vout: outputs,
23
- fee: selection.feeSats
24
- };
25
- return { tx, selection };
26
- }
@@ -1,78 +0,0 @@
1
- /**
2
- * UTXO Selection for Ordinals Transactions
3
- *
4
- * This module implements functions for selecting UTXOs for ordinals transactions.
5
- * It provides a simple coin selection algorithm optimized for ordinals inscriptions.
6
- *
7
- * Ported from legacy ordinalsplus transaction infrastructure.
8
- */
9
- import { Utxo, ResourceUtxo, ResourceUtxoSelectionOptions, ResourceUtxoSelectionResult } from '../types/bitcoin.js';
10
- /**
11
- * Estimates transaction size in vbytes based on input and output counts
12
- * This is a simplified calculation, and actual size may vary based on script types
13
- *
14
- * @param inputCount Number of inputs in the transaction
15
- * @param outputCount Number of outputs in the transaction
16
- * @returns Estimated transaction size in vbytes
17
- */
18
- export declare function estimateTransactionSize(inputCount: number, outputCount: number): number;
19
- /**
20
- * Tags UTXOs as resource-containing or regular based on provided data
21
- *
22
- * @param utxos List of UTXOs to tag
23
- * @param resourceData Optional data about which UTXOs contain resources
24
- * @returns Tagged ResourceUtxo[] list with hasResource flags set appropriately
25
- */
26
- export declare function tagResourceUtxos(utxos: ResourceUtxo[], resourceData?: {
27
- [utxoId: string]: boolean;
28
- }): ResourceUtxo[];
29
- /**
30
- * Options for simple UTXO selection
31
- */
32
- export interface SimpleUtxoSelectionOptions {
33
- /** Target amount to reach (in satoshis) */
34
- targetAmount: number;
35
- /** Optional maximum amount of UTXOs to use */
36
- maxNumUtxos?: number;
37
- /** Optional preference for UTXO selection strategy */
38
- strategy?: 'minimize_change' | 'minimize_inputs' | 'optimize_size';
39
- }
40
- /**
41
- * Result of simple UTXO selection
42
- */
43
- export interface SimpleUtxoSelectionResult {
44
- /** Selected UTXOs for the transaction */
45
- selectedUtxos: Utxo[];
46
- /** Total value of selected UTXOs */
47
- totalInputValue: number;
48
- /** Estimated change amount (if any) */
49
- changeAmount: number;
50
- }
51
- /**
52
- * Selects UTXOs to cover a target amount using a simplified approach.
53
- * This version is used specifically for commit transactions where we
54
- * don't need the more complex resource-aware selection.
55
- *
56
- * @param utxos - Available UTXOs
57
- * @param options - Target amount or detailed options
58
- * @returns Selected UTXOs and related information
59
- */
60
- export declare function selectUtxos(utxos: Utxo[], options: number | SimpleUtxoSelectionOptions): SimpleUtxoSelectionResult;
61
- /**
62
- * Selects UTXOs for a transaction, excluding UTXOs with resources unless explicitly allowed
63
- *
64
- * @param availableUtxos List of available UTXOs to select from
65
- * @param options Configuration options for the selection process
66
- * @returns Selection result with chosen UTXOs and fee information
67
- * @throws Error if insufficient funds or if all available UTXOs contain resources
68
- */
69
- export declare function selectResourceUtxos(availableUtxos: ResourceUtxo[], options: ResourceUtxoSelectionOptions): ResourceUtxoSelectionResult;
70
- /**
71
- * Convenience function to select UTXOs for a payment, explicitly avoiding resource UTXOs
72
- *
73
- * @param availableUtxos List of available UTXOs
74
- * @param requiredAmount Amount needed for the payment in satoshis
75
- * @param feeRate Fee rate in satoshis per vbyte
76
- * @returns Selection result with UTXOs, fee and change information
77
- */
78
- export declare function selectUtxosForPayment(availableUtxos: ResourceUtxo[], requiredAmount: number, feeRate: number): ResourceUtxoSelectionResult;
@@ -1,237 +0,0 @@
1
- /**
2
- * UTXO Selection for Ordinals Transactions
3
- *
4
- * This module implements functions for selecting UTXOs for ordinals transactions.
5
- * It provides a simple coin selection algorithm optimized for ordinals inscriptions.
6
- *
7
- * Ported from legacy ordinalsplus transaction infrastructure.
8
- */
9
- import { DUST_LIMIT_SATS } from '../types/bitcoin.js';
10
- import { calculateFee } from './fee-calculation.js';
11
- // Minimum dust limit for Bitcoin outputs (546 satoshis)
12
- const MIN_DUST_LIMIT = DUST_LIMIT_SATS;
13
- /**
14
- * Estimates transaction size in vbytes based on input and output counts
15
- * This is a simplified calculation, and actual size may vary based on script types
16
- *
17
- * @param inputCount Number of inputs in the transaction
18
- * @param outputCount Number of outputs in the transaction
19
- * @returns Estimated transaction size in vbytes
20
- */
21
- export function estimateTransactionSize(inputCount, outputCount) {
22
- // Rough estimation based on segwit transaction format
23
- // Transaction overhead: ~10 vbytes
24
- // Each input: ~68 vbytes (P2WPKH)
25
- // Each output: ~31 vbytes
26
- return 10 + (inputCount * 68) + (outputCount * 31);
27
- }
28
- /**
29
- * Tags UTXOs as resource-containing or regular based on provided data
30
- *
31
- * @param utxos List of UTXOs to tag
32
- * @param resourceData Optional data about which UTXOs contain resources
33
- * @returns Tagged ResourceUtxo[] list with hasResource flags set appropriately
34
- */
35
- export function tagResourceUtxos(utxos, resourceData) {
36
- return utxos.map(utxo => {
37
- const utxoId = `${utxo.txid}:${utxo.vout}`;
38
- const hasResource = resourceData ? !!resourceData[utxoId] : utxo.hasResource || false;
39
- return {
40
- ...utxo,
41
- hasResource
42
- };
43
- });
44
- }
45
- /**
46
- * Selects UTXOs to cover a target amount using a simplified approach.
47
- * This version is used specifically for commit transactions where we
48
- * don't need the more complex resource-aware selection.
49
- *
50
- * @param utxos - Available UTXOs
51
- * @param options - Target amount or detailed options
52
- * @returns Selected UTXOs and related information
53
- */
54
- export function selectUtxos(utxos, options) {
55
- // Handle simple number input
56
- const targetAmount = typeof options === 'number'
57
- ? options
58
- : options.targetAmount;
59
- const maxNumUtxos = typeof options === 'number'
60
- ? undefined
61
- : options.maxNumUtxos;
62
- const strategy = typeof options === 'number'
63
- ? 'minimize_inputs'
64
- : options.strategy || 'minimize_inputs';
65
- // Validate inputs
66
- if (!utxos || utxos.length === 0) {
67
- throw new Error('No UTXOs provided for selection.');
68
- }
69
- if (targetAmount <= 0) {
70
- throw new Error(`Invalid target amount: ${targetAmount}`);
71
- }
72
- // Sort UTXOs based on selected strategy
73
- let sortedUtxos = [...utxos];
74
- if (strategy === 'minimize_inputs') {
75
- // Sort by value descending to use fewest inputs
76
- sortedUtxos.sort((a, b) => b.value - a.value);
77
- }
78
- else if (strategy === 'minimize_change') {
79
- // Sort by value ascending to minimize change
80
- sortedUtxos.sort((a, b) => a.value - b.value);
81
- }
82
- else if (strategy === 'optimize_size') {
83
- // Sort by value/size ratio (value density) for optimal fee efficiency
84
- // For now, just sort by value as a reasonable approximation
85
- sortedUtxos.sort((a, b) => b.value - a.value);
86
- }
87
- const selected = [];
88
- let totalValue = 0;
89
- // Add UTXOs until we reach the target amount
90
- for (const utxo of sortedUtxos) {
91
- // Skip invalid UTXOs
92
- if (!utxo.txid || utxo.vout === undefined || !utxo.value) {
93
- console.warn(`Skipping invalid UTXO: ${utxo.txid}:${utxo.vout}`);
94
- continue;
95
- }
96
- selected.push(utxo);
97
- totalValue += utxo.value;
98
- // Check if we've reached the target
99
- if (totalValue >= targetAmount) {
100
- break;
101
- }
102
- // Check if we've reached the maximum allowed number of UTXOs
103
- if (maxNumUtxos && selected.length >= maxNumUtxos) {
104
- break;
105
- }
106
- }
107
- // Check if we have enough funds
108
- if (totalValue < targetAmount) {
109
- throw new Error(`Insufficient funds. Required: ${targetAmount}, Available: ${totalValue} from ${utxos.length} UTXOs.`);
110
- }
111
- // Calculate change amount
112
- const changeAmount = totalValue - targetAmount;
113
- return {
114
- selectedUtxos: selected,
115
- totalInputValue: totalValue,
116
- changeAmount
117
- };
118
- }
119
- /**
120
- * Selects UTXOs for a transaction, excluding UTXOs with resources unless explicitly allowed
121
- *
122
- * @param availableUtxos List of available UTXOs to select from
123
- * @param options Configuration options for the selection process
124
- * @returns Selection result with chosen UTXOs and fee information
125
- * @throws Error if insufficient funds or if all available UTXOs contain resources
126
- */
127
- export function selectResourceUtxos(availableUtxos, options) {
128
- const { requiredAmount, feeRate, allowResourceUtxos = false, preferOlder = false, preferCloserAmount = false, avoidUtxoIds = [] } = options;
129
- // Convert requiredAmount to bigint for compatibility with fee calculations
130
- const requiredAmountBigInt = BigInt(requiredAmount);
131
- // Filter out UTXOs to avoid and those with resources if not allowed
132
- let eligibleUtxos = availableUtxos.filter(utxo => {
133
- const utxoId = `${utxo.txid}:${utxo.vout}`;
134
- const shouldAvoid = avoidUtxoIds.includes(utxoId);
135
- const containsResource = utxo.hasResource === true;
136
- // Skip this UTXO if it's in the avoid list
137
- if (shouldAvoid)
138
- return false;
139
- // Skip this UTXO if it contains a resource and we're not allowed to use resource UTXOs
140
- if (containsResource && !allowResourceUtxos)
141
- return false;
142
- return true;
143
- });
144
- if (eligibleUtxos.length === 0) {
145
- // Special error message if we have UTXOs but they all contain resources
146
- if (availableUtxos.length > 0 && availableUtxos.every(u => u.hasResource)) {
147
- throw new Error('All available UTXOs contain resources and cannot be used for fees/payments. Please add non-resource UTXOs to your wallet.');
148
- }
149
- throw new Error('No eligible UTXOs available for selection');
150
- }
151
- // Apply sorting strategy
152
- if (preferCloserAmount) {
153
- // Sort by closest to required amount (but still above it)
154
- eligibleUtxos.sort((a, b) => {
155
- const aDiff = a.value - requiredAmount;
156
- const bDiff = b.value - requiredAmount;
157
- // Prioritize UTXOs that cover the amount
158
- if (aDiff >= 0 && bDiff < 0)
159
- return -1;
160
- if (aDiff < 0 && bDiff >= 0)
161
- return 1;
162
- // If both cover or both don't cover, prefer the one closer to required amount
163
- return Math.abs(aDiff) - Math.abs(bDiff);
164
- });
165
- }
166
- else if (preferOlder) {
167
- // Prefer older UTXOs (by txid as a proxy for age - not perfect but simple)
168
- eligibleUtxos.sort((a, b) => a.txid.localeCompare(b.txid));
169
- }
170
- else {
171
- // Default: sort by value descending (largest first)
172
- eligibleUtxos.sort((a, b) => b.value - a.value);
173
- }
174
- // Initial fee estimation (1 input, 2 outputs - payment and change)
175
- let estimatedVbytes = estimateTransactionSize(1, 2);
176
- let estimatedFee = calculateFee(estimatedVbytes, feeRate);
177
- // Target amount including estimated fee
178
- let targetAmount = requiredAmountBigInt + estimatedFee;
179
- // Select UTXOs
180
- const selectedUtxos = [];
181
- let totalSelectedValue = 0n;
182
- // First pass: try to find a single UTXO that covers the amount
183
- const singleUtxo = eligibleUtxos.find(utxo => BigInt(utxo.value) >= targetAmount);
184
- if (singleUtxo) {
185
- selectedUtxos.push(singleUtxo);
186
- totalSelectedValue = BigInt(singleUtxo.value);
187
- }
188
- else {
189
- // Second pass: accumulate UTXOs until we reach the target amount
190
- for (const utxo of eligibleUtxos) {
191
- selectedUtxos.push(utxo);
192
- totalSelectedValue += BigInt(utxo.value);
193
- // Recalculate fee as we add more inputs
194
- estimatedVbytes = estimateTransactionSize(selectedUtxos.length, 2);
195
- estimatedFee = calculateFee(estimatedVbytes, feeRate);
196
- targetAmount = requiredAmountBigInt + estimatedFee;
197
- if (totalSelectedValue >= targetAmount) {
198
- break;
199
- }
200
- }
201
- }
202
- // Final fee calculation based on actual number of inputs
203
- estimatedVbytes = estimateTransactionSize(selectedUtxos.length, 2);
204
- estimatedFee = calculateFee(estimatedVbytes, feeRate);
205
- // Check if we have enough funds
206
- if (totalSelectedValue < requiredAmountBigInt + estimatedFee) {
207
- throw new Error(`Insufficient funds. Required: ${requiredAmountBigInt + estimatedFee}, Available: ${totalSelectedValue}`);
208
- }
209
- // Calculate change
210
- let changeAmount = totalSelectedValue - requiredAmountBigInt - estimatedFee;
211
- // If change is less than dust limit, add it to the fee
212
- if (changeAmount > 0n && changeAmount < BigInt(MIN_DUST_LIMIT)) {
213
- estimatedFee += changeAmount;
214
- changeAmount = 0n;
215
- }
216
- return {
217
- selectedUtxos,
218
- totalSelectedValue: Number(totalSelectedValue),
219
- estimatedFee: Number(estimatedFee),
220
- changeAmount: Number(changeAmount)
221
- };
222
- }
223
- /**
224
- * Convenience function to select UTXOs for a payment, explicitly avoiding resource UTXOs
225
- *
226
- * @param availableUtxos List of available UTXOs
227
- * @param requiredAmount Amount needed for the payment in satoshis
228
- * @param feeRate Fee rate in satoshis per vbyte
229
- * @returns Selection result with UTXOs, fee and change information
230
- */
231
- export function selectUtxosForPayment(availableUtxos, requiredAmount, feeRate) {
232
- return selectResourceUtxos(availableUtxos, {
233
- requiredAmount,
234
- feeRate,
235
- allowResourceUtxos: false // Never use resource UTXOs for payments
236
- });
237
- }