@originals/sdk 1.2.0 → 1.4.3

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 (401) hide show
  1. package/package.json +4 -1
  2. package/.eslintrc.json +0 -33
  3. package/.turbo/turbo-build.log +0 -1
  4. package/dist/adapters/FeeOracleMock.d.ts +0 -6
  5. package/dist/adapters/FeeOracleMock.js +0 -8
  6. package/dist/adapters/index.d.ts +0 -4
  7. package/dist/adapters/index.js +0 -4
  8. package/dist/adapters/providers/OrdHttpProvider.d.ts +0 -56
  9. package/dist/adapters/providers/OrdHttpProvider.js +0 -110
  10. package/dist/adapters/providers/OrdMockProvider.d.ts +0 -70
  11. package/dist/adapters/providers/OrdMockProvider.js +0 -75
  12. package/dist/adapters/types.d.ts +0 -71
  13. package/dist/adapters/types.js +0 -1
  14. package/dist/bitcoin/BitcoinManager.d.ts +0 -15
  15. package/dist/bitcoin/BitcoinManager.js +0 -262
  16. package/dist/bitcoin/BroadcastClient.d.ts +0 -30
  17. package/dist/bitcoin/BroadcastClient.js +0 -35
  18. package/dist/bitcoin/OrdinalsClient.d.ts +0 -21
  19. package/dist/bitcoin/OrdinalsClient.js +0 -105
  20. package/dist/bitcoin/PSBTBuilder.d.ts +0 -24
  21. package/dist/bitcoin/PSBTBuilder.js +0 -80
  22. package/dist/bitcoin/fee-calculation.d.ts +0 -14
  23. package/dist/bitcoin/fee-calculation.js +0 -31
  24. package/dist/bitcoin/providers/OrdNodeProvider.d.ts +0 -38
  25. package/dist/bitcoin/providers/OrdNodeProvider.js +0 -67
  26. package/dist/bitcoin/providers/OrdinalsProvider.d.ts +0 -33
  27. package/dist/bitcoin/providers/OrdinalsProvider.js +0 -50
  28. package/dist/bitcoin/providers/types.d.ts +0 -63
  29. package/dist/bitcoin/providers/types.js +0 -1
  30. package/dist/bitcoin/transactions/commit.d.ts +0 -89
  31. package/dist/bitcoin/transactions/commit.js +0 -311
  32. package/dist/bitcoin/transactions/index.d.ts +0 -7
  33. package/dist/bitcoin/transactions/index.js +0 -8
  34. package/dist/bitcoin/transfer.d.ts +0 -9
  35. package/dist/bitcoin/transfer.js +0 -26
  36. package/dist/bitcoin/utxo-selection.d.ts +0 -78
  37. package/dist/bitcoin/utxo-selection.js +0 -237
  38. package/dist/bitcoin/utxo.d.ts +0 -26
  39. package/dist/bitcoin/utxo.js +0 -78
  40. package/dist/contexts/credentials-v1.json +0 -195
  41. package/dist/contexts/credentials-v2-examples.json +0 -5
  42. package/dist/contexts/credentials-v2.json +0 -301
  43. package/dist/contexts/credentials.json +0 -195
  44. package/dist/contexts/data-integrity-v2.json +0 -81
  45. package/dist/contexts/dids.json +0 -57
  46. package/dist/contexts/ed255192020.json +0 -93
  47. package/dist/contexts/ordinals-plus.json +0 -23
  48. package/dist/contexts/originals.json +0 -22
  49. package/dist/core/OriginalsSDK.d.ts +0 -158
  50. package/dist/core/OriginalsSDK.js +0 -274
  51. package/dist/crypto/Multikey.d.ts +0 -30
  52. package/dist/crypto/Multikey.js +0 -149
  53. package/dist/crypto/Signer.d.ts +0 -21
  54. package/dist/crypto/Signer.js +0 -196
  55. package/dist/crypto/noble-init.d.ts +0 -18
  56. package/dist/crypto/noble-init.js +0 -106
  57. package/dist/did/BtcoDidResolver.d.ts +0 -57
  58. package/dist/did/BtcoDidResolver.js +0 -166
  59. package/dist/did/DIDManager.d.ts +0 -101
  60. package/dist/did/DIDManager.js +0 -493
  61. package/dist/did/Ed25519Verifier.d.ts +0 -30
  62. package/dist/did/Ed25519Verifier.js +0 -59
  63. package/dist/did/KeyManager.d.ts +0 -17
  64. package/dist/did/KeyManager.js +0 -207
  65. package/dist/did/WebVHManager.d.ts +0 -100
  66. package/dist/did/WebVHManager.js +0 -304
  67. package/dist/did/createBtcoDidDocument.d.ts +0 -10
  68. package/dist/did/createBtcoDidDocument.js +0 -42
  69. package/dist/did/providers/OrdinalsClientProviderAdapter.d.ts +0 -23
  70. package/dist/did/providers/OrdinalsClientProviderAdapter.js +0 -51
  71. package/dist/events/EventEmitter.d.ts +0 -115
  72. package/dist/events/EventEmitter.js +0 -198
  73. package/dist/events/index.d.ts +0 -7
  74. package/dist/events/index.js +0 -6
  75. package/dist/events/types.d.ts +0 -286
  76. package/dist/events/types.js +0 -9
  77. package/dist/examples/basic-usage.d.ts +0 -3
  78. package/dist/examples/basic-usage.js +0 -62
  79. package/dist/examples/run.d.ts +0 -1
  80. package/dist/examples/run.js +0 -4
  81. package/dist/index.d.ts +0 -39
  82. package/dist/index.js +0 -47
  83. package/dist/lifecycle/BatchOperations.d.ts +0 -147
  84. package/dist/lifecycle/BatchOperations.js +0 -251
  85. package/dist/lifecycle/LifecycleManager.d.ts +0 -116
  86. package/dist/lifecycle/LifecycleManager.js +0 -971
  87. package/dist/lifecycle/OriginalsAsset.d.ts +0 -164
  88. package/dist/lifecycle/OriginalsAsset.js +0 -380
  89. package/dist/lifecycle/ProvenanceQuery.d.ts +0 -126
  90. package/dist/lifecycle/ProvenanceQuery.js +0 -220
  91. package/dist/lifecycle/ResourceVersioning.d.ts +0 -73
  92. package/dist/lifecycle/ResourceVersioning.js +0 -127
  93. package/dist/migration/MigrationManager.d.ts +0 -86
  94. package/dist/migration/MigrationManager.js +0 -412
  95. package/dist/migration/audit/AuditLogger.d.ts +0 -51
  96. package/dist/migration/audit/AuditLogger.js +0 -156
  97. package/dist/migration/checkpoint/CheckpointManager.d.ts +0 -31
  98. package/dist/migration/checkpoint/CheckpointManager.js +0 -96
  99. package/dist/migration/checkpoint/CheckpointStorage.d.ts +0 -26
  100. package/dist/migration/checkpoint/CheckpointStorage.js +0 -89
  101. package/dist/migration/index.d.ts +0 -22
  102. package/dist/migration/index.js +0 -27
  103. package/dist/migration/operations/BaseMigration.d.ts +0 -48
  104. package/dist/migration/operations/BaseMigration.js +0 -83
  105. package/dist/migration/operations/PeerToBtcoMigration.d.ts +0 -25
  106. package/dist/migration/operations/PeerToBtcoMigration.js +0 -67
  107. package/dist/migration/operations/PeerToWebvhMigration.d.ts +0 -19
  108. package/dist/migration/operations/PeerToWebvhMigration.js +0 -46
  109. package/dist/migration/operations/WebvhToBtcoMigration.d.ts +0 -25
  110. package/dist/migration/operations/WebvhToBtcoMigration.js +0 -67
  111. package/dist/migration/rollback/RollbackManager.d.ts +0 -29
  112. package/dist/migration/rollback/RollbackManager.js +0 -146
  113. package/dist/migration/state/StateMachine.d.ts +0 -25
  114. package/dist/migration/state/StateMachine.js +0 -76
  115. package/dist/migration/state/StateTracker.d.ts +0 -36
  116. package/dist/migration/state/StateTracker.js +0 -123
  117. package/dist/migration/types.d.ts +0 -306
  118. package/dist/migration/types.js +0 -33
  119. package/dist/migration/validation/BitcoinValidator.d.ts +0 -13
  120. package/dist/migration/validation/BitcoinValidator.js +0 -83
  121. package/dist/migration/validation/CredentialValidator.d.ts +0 -13
  122. package/dist/migration/validation/CredentialValidator.js +0 -46
  123. package/dist/migration/validation/DIDCompatibilityValidator.d.ts +0 -16
  124. package/dist/migration/validation/DIDCompatibilityValidator.js +0 -127
  125. package/dist/migration/validation/LifecycleValidator.d.ts +0 -10
  126. package/dist/migration/validation/LifecycleValidator.js +0 -52
  127. package/dist/migration/validation/StorageValidator.d.ts +0 -10
  128. package/dist/migration/validation/StorageValidator.js +0 -65
  129. package/dist/migration/validation/ValidationPipeline.d.ts +0 -29
  130. package/dist/migration/validation/ValidationPipeline.js +0 -180
  131. package/dist/storage/LocalStorageAdapter.d.ts +0 -11
  132. package/dist/storage/LocalStorageAdapter.js +0 -53
  133. package/dist/storage/MemoryStorageAdapter.d.ts +0 -6
  134. package/dist/storage/MemoryStorageAdapter.js +0 -21
  135. package/dist/storage/StorageAdapter.d.ts +0 -16
  136. package/dist/storage/StorageAdapter.js +0 -1
  137. package/dist/storage/index.d.ts +0 -2
  138. package/dist/storage/index.js +0 -2
  139. package/dist/types/bitcoin.d.ts +0 -84
  140. package/dist/types/bitcoin.js +0 -1
  141. package/dist/types/common.d.ts +0 -82
  142. package/dist/types/common.js +0 -1
  143. package/dist/types/credentials.d.ts +0 -75
  144. package/dist/types/credentials.js +0 -1
  145. package/dist/types/did.d.ts +0 -26
  146. package/dist/types/did.js +0 -1
  147. package/dist/types/index.d.ts +0 -5
  148. package/dist/types/index.js +0 -5
  149. package/dist/types/network.d.ts +0 -78
  150. package/dist/types/network.js +0 -145
  151. package/dist/utils/EventLogger.d.ts +0 -71
  152. package/dist/utils/EventLogger.js +0 -232
  153. package/dist/utils/Logger.d.ts +0 -106
  154. package/dist/utils/Logger.js +0 -257
  155. package/dist/utils/MetricsCollector.d.ts +0 -110
  156. package/dist/utils/MetricsCollector.js +0 -264
  157. package/dist/utils/bitcoin-address.d.ts +0 -38
  158. package/dist/utils/bitcoin-address.js +0 -113
  159. package/dist/utils/cbor.d.ts +0 -2
  160. package/dist/utils/cbor.js +0 -9
  161. package/dist/utils/encoding.d.ts +0 -37
  162. package/dist/utils/encoding.js +0 -120
  163. package/dist/utils/hash.d.ts +0 -1
  164. package/dist/utils/hash.js +0 -5
  165. package/dist/utils/retry.d.ts +0 -10
  166. package/dist/utils/retry.js +0 -35
  167. package/dist/utils/satoshi-validation.d.ts +0 -60
  168. package/dist/utils/satoshi-validation.js +0 -156
  169. package/dist/utils/serialization.d.ts +0 -14
  170. package/dist/utils/serialization.js +0 -76
  171. package/dist/utils/telemetry.d.ts +0 -17
  172. package/dist/utils/telemetry.js +0 -24
  173. package/dist/utils/validation.d.ts +0 -5
  174. package/dist/utils/validation.js +0 -98
  175. package/dist/vc/CredentialManager.d.ts +0 -22
  176. package/dist/vc/CredentialManager.js +0 -227
  177. package/dist/vc/Issuer.d.ts +0 -27
  178. package/dist/vc/Issuer.js +0 -70
  179. package/dist/vc/Verifier.d.ts +0 -16
  180. package/dist/vc/Verifier.js +0 -50
  181. package/dist/vc/cryptosuites/bbs.d.ts +0 -44
  182. package/dist/vc/cryptosuites/bbs.js +0 -213
  183. package/dist/vc/cryptosuites/bbsSimple.d.ts +0 -9
  184. package/dist/vc/cryptosuites/bbsSimple.js +0 -12
  185. package/dist/vc/cryptosuites/eddsa.d.ts +0 -30
  186. package/dist/vc/cryptosuites/eddsa.js +0 -81
  187. package/dist/vc/documentLoader.d.ts +0 -16
  188. package/dist/vc/documentLoader.js +0 -59
  189. package/dist/vc/proofs/data-integrity.d.ts +0 -21
  190. package/dist/vc/proofs/data-integrity.js +0 -15
  191. package/dist/vc/utils/jsonld.d.ts +0 -2
  192. package/dist/vc/utils/jsonld.js +0 -15
  193. package/src/adapters/FeeOracleMock.ts +0 -9
  194. package/src/adapters/index.ts +0 -5
  195. package/src/adapters/providers/OrdHttpProvider.ts +0 -126
  196. package/src/adapters/providers/OrdMockProvider.ts +0 -101
  197. package/src/adapters/types.ts +0 -66
  198. package/src/bitcoin/BitcoinManager.ts +0 -330
  199. package/src/bitcoin/BroadcastClient.ts +0 -54
  200. package/src/bitcoin/OrdinalsClient.ts +0 -119
  201. package/src/bitcoin/PSBTBuilder.ts +0 -106
  202. package/src/bitcoin/fee-calculation.ts +0 -38
  203. package/src/bitcoin/providers/OrdNodeProvider.ts +0 -92
  204. package/src/bitcoin/providers/OrdinalsProvider.ts +0 -56
  205. package/src/bitcoin/providers/types.ts +0 -59
  206. package/src/bitcoin/transactions/commit.ts +0 -465
  207. package/src/bitcoin/transactions/index.ts +0 -13
  208. package/src/bitcoin/transfer.ts +0 -43
  209. package/src/bitcoin/utxo-selection.ts +0 -322
  210. package/src/bitcoin/utxo.ts +0 -113
  211. package/src/contexts/credentials-v1.json +0 -237
  212. package/src/contexts/credentials-v2-examples.json +0 -5
  213. package/src/contexts/credentials-v2.json +0 -340
  214. package/src/contexts/credentials.json +0 -237
  215. package/src/contexts/data-integrity-v2.json +0 -81
  216. package/src/contexts/dids.json +0 -58
  217. package/src/contexts/ed255192020.json +0 -93
  218. package/src/contexts/ordinals-plus.json +0 -23
  219. package/src/contexts/originals.json +0 -22
  220. package/src/core/OriginalsSDK.ts +0 -416
  221. package/src/crypto/Multikey.ts +0 -194
  222. package/src/crypto/Signer.ts +0 -254
  223. package/src/crypto/noble-init.ts +0 -121
  224. package/src/did/BtcoDidResolver.ts +0 -227
  225. package/src/did/DIDManager.ts +0 -694
  226. package/src/did/Ed25519Verifier.ts +0 -68
  227. package/src/did/KeyManager.ts +0 -236
  228. package/src/did/WebVHManager.ts +0 -489
  229. package/src/did/createBtcoDidDocument.ts +0 -59
  230. package/src/did/providers/OrdinalsClientProviderAdapter.ts +0 -68
  231. package/src/events/EventEmitter.ts +0 -222
  232. package/src/events/index.ts +0 -19
  233. package/src/events/types.ts +0 -331
  234. package/src/examples/basic-usage.ts +0 -78
  235. package/src/examples/run.ts +0 -5
  236. package/src/index.ts +0 -84
  237. package/src/lifecycle/BatchOperations.ts +0 -373
  238. package/src/lifecycle/LifecycleManager.ts +0 -1218
  239. package/src/lifecycle/OriginalsAsset.ts +0 -524
  240. package/src/lifecycle/ProvenanceQuery.ts +0 -280
  241. package/src/lifecycle/ResourceVersioning.ts +0 -163
  242. package/src/migration/MigrationManager.ts +0 -527
  243. package/src/migration/audit/AuditLogger.ts +0 -176
  244. package/src/migration/checkpoint/CheckpointManager.ts +0 -112
  245. package/src/migration/checkpoint/CheckpointStorage.ts +0 -101
  246. package/src/migration/index.ts +0 -33
  247. package/src/migration/operations/BaseMigration.ts +0 -126
  248. package/src/migration/operations/PeerToBtcoMigration.ts +0 -105
  249. package/src/migration/operations/PeerToWebvhMigration.ts +0 -62
  250. package/src/migration/operations/WebvhToBtcoMigration.ts +0 -105
  251. package/src/migration/rollback/RollbackManager.ts +0 -170
  252. package/src/migration/state/StateMachine.ts +0 -92
  253. package/src/migration/state/StateTracker.ts +0 -156
  254. package/src/migration/types.ts +0 -344
  255. package/src/migration/validation/BitcoinValidator.ts +0 -107
  256. package/src/migration/validation/CredentialValidator.ts +0 -62
  257. package/src/migration/validation/DIDCompatibilityValidator.ts +0 -151
  258. package/src/migration/validation/LifecycleValidator.ts +0 -64
  259. package/src/migration/validation/StorageValidator.ts +0 -79
  260. package/src/migration/validation/ValidationPipeline.ts +0 -213
  261. package/src/storage/LocalStorageAdapter.ts +0 -61
  262. package/src/storage/MemoryStorageAdapter.ts +0 -29
  263. package/src/storage/StorageAdapter.ts +0 -25
  264. package/src/storage/index.ts +0 -3
  265. package/src/types/bitcoin.ts +0 -98
  266. package/src/types/common.ts +0 -92
  267. package/src/types/credentials.ts +0 -88
  268. package/src/types/did.ts +0 -31
  269. package/src/types/external-shims.d.ts +0 -53
  270. package/src/types/index.ts +0 -7
  271. package/src/types/network.ts +0 -175
  272. package/src/utils/EventLogger.ts +0 -298
  273. package/src/utils/Logger.ts +0 -322
  274. package/src/utils/MetricsCollector.ts +0 -358
  275. package/src/utils/bitcoin-address.ts +0 -130
  276. package/src/utils/cbor.ts +0 -12
  277. package/src/utils/encoding.ts +0 -127
  278. package/src/utils/hash.ts +0 -6
  279. package/src/utils/retry.ts +0 -46
  280. package/src/utils/satoshi-validation.ts +0 -196
  281. package/src/utils/serialization.ts +0 -96
  282. package/src/utils/telemetry.ts +0 -40
  283. package/src/utils/validation.ts +0 -119
  284. package/src/vc/CredentialManager.ts +0 -273
  285. package/src/vc/Issuer.ts +0 -100
  286. package/src/vc/Verifier.ts +0 -47
  287. package/src/vc/cryptosuites/bbs.ts +0 -253
  288. package/src/vc/cryptosuites/bbsSimple.ts +0 -21
  289. package/src/vc/cryptosuites/eddsa.ts +0 -99
  290. package/src/vc/documentLoader.ts +0 -67
  291. package/src/vc/proofs/data-integrity.ts +0 -33
  292. package/src/vc/utils/jsonld.ts +0 -18
  293. package/test/logs/did_webvh_QmNTn9Kkp8dQ75WrF9xqJ2kuDp9QhKc3aPiERRMj8XoTBN_example_com.jsonl +0 -1
  294. package/test/logs/did_webvh_QmNu4MNr8Lr5txx5gYNhuhZDchXsZEu3hJXKYuphpWTPDp_example_com_users_etc_passwd.jsonl +0 -1
  295. package/test/logs/did_webvh_QmR9MrGZACzjKETA8SBRNCKG11HxU85c4bVR2qN5eDCfsD_example_com.jsonl +0 -1
  296. package/test/logs/did_webvh_QmUc5suaqRM2P4nrXxZwqYMfqzhdMqjuL7oJaJbEpCQVCd_example_com_users_etc_passwd.jsonl +0 -1
  297. package/test/logs/did_webvh_QmUkiB2RCV2VZ1RTXsCebWN25Eiy9TLvpzDWAJNjhgvB4X_example_com_etc_passwd.jsonl +0 -1
  298. package/test/logs/did_webvh_QmUoRTe8UMwpAQXZSAW7pjAgZK1tq2X3C6Kfxq3UXGcaGy_example_com_secret.jsonl +0 -1
  299. package/test/logs/did_webvh_QmWWot3chx1t6KwTmcE5i2FeDZ5JMkQw3qXycsKDVmJ9Be_example_com_users_alice.jsonl +0 -1
  300. package/test/logs/did_webvh_QmWvVgALL5kjZdpgR7KZay7J8UiiUr834kkRmWeFAxjAuC_example_com_users_etc_passwd.jsonl +0 -1
  301. package/test/logs/did_webvh_QmWwaRQHUZAFcKihFC6xR6tRTTrQhHPTku6azf1egWbpy1_example_com_users_alice.jsonl +0 -1
  302. package/test/logs/did_webvh_QmXJLtkz23r7AozbtXsZMKWnVU6rd38CkVtjdWuATU3Yp6_example_com_users_alice123_profile.jsonl +0 -1
  303. package/test/logs/did_webvh_QmYsce448po14oDE1wXbyaP6wY9HQgHSKLwdezn1k577SF_example_com_my_org_user_name_test_123.jsonl +0 -1
  304. package/test/logs/did_webvh_QmZBeNzzqajxdfwcDUPZ4P8C5YSXyRztrAwmPiKuKUxmAK_example_com.jsonl +0 -1
  305. package/test/logs/did_webvh_QmZhJsqxizwVbRtqCUkmE6XQunSxtxMt3gbTYadVBNAaEq_example_com.jsonl +0 -1
  306. package/test/logs/did_webvh_QmZk7NHU2D57RzzbMq4tWW9gBa9AqtVTWfiRM6RFdwGVj2_example_com.jsonl +0 -1
  307. package/test/logs/did_webvh_QmZshSXp9w8ovH62zGGBS1b5pGGPsuYiu1VQ935sga2hWF_example_com_level1_level2.jsonl +0 -1
  308. package/test/logs/did_webvh_QmbWAmw7HQL7vKJyCsctZihXf1rmT4sGvggKCPKWcUWjw1_example_com.jsonl +0 -1
  309. package/test/logs/did_webvh_QmbdLUMbYs3juR39TLB6hhrFWLcNg45ybUzeBJCS1MhCh1_example_com_C_Windows_System32.jsonl +0 -1
  310. package/test/logs/did_webvh_QmcaQ1Ma4gkSbae85aCm8Mv4rvdT2Sb2RR3JzYwrm5XBq8_example_com_etc_passwd.jsonl +0 -1
  311. package/test/logs/did_webvh_QmcbA7WQhsBqZSoDpKJHjV8Q5o53h8vmgJhQfo6rqTY5ho_example_com.jsonl +0 -1
  312. package/test/logs/did_webvh_Qmdy8uWr2gkUJrXsThynAug3DASTWwb3onEj89LKmMGZYB_example_com.jsonl +0 -1
  313. package/tests/__mocks__/bbs-signatures.js +0 -17
  314. package/tests/__mocks__/mf-base58.js +0 -24
  315. package/tests/e2e/README.md +0 -97
  316. package/tests/e2e/example.spec.ts +0 -78
  317. package/tests/fixtures/did-documents.ts +0 -247
  318. package/tests/index.test.ts +0 -21
  319. package/tests/integration/BatchOperations.test.ts +0 -531
  320. package/tests/integration/CompleteLifecycle.e2e.test.ts +0 -735
  321. package/tests/integration/CredentialManager.test.ts +0 -42
  322. package/tests/integration/DIDManager.test.ts +0 -41
  323. package/tests/integration/DidPeerToWebVhFlow.test.ts +0 -351
  324. package/tests/integration/Events.test.ts +0 -435
  325. package/tests/integration/Lifecycle.transfer.btco.integration.test.ts +0 -25
  326. package/tests/integration/LifecycleManager.test.ts +0 -21
  327. package/tests/integration/MultikeyFlow.test.ts +0 -52
  328. package/tests/integration/TelemetryIntegration.test.ts +0 -395
  329. package/tests/integration/WebVhPublish.test.ts +0 -48
  330. package/tests/integration/migration/peer-to-webvh.test.ts +0 -172
  331. package/tests/manual/test-commit-creation.ts +0 -323
  332. package/tests/mocks/MockKeyStore.ts +0 -38
  333. package/tests/mocks/adapters/MemoryStorageAdapter.ts +0 -24
  334. package/tests/mocks/adapters/MockFeeOracle.ts +0 -11
  335. package/tests/mocks/adapters/MockOrdinalsProvider.ts +0 -76
  336. package/tests/mocks/adapters/OrdMockProvider.test.ts +0 -176
  337. package/tests/mocks/adapters/index.ts +0 -6
  338. package/tests/performance/BatchOperations.perf.test.ts +0 -403
  339. package/tests/performance/logging.perf.test.ts +0 -336
  340. package/tests/sdk.test.ts +0 -43
  341. package/tests/security/bitcoin-penetration-tests.test.ts +0 -622
  342. package/tests/setup.bun.ts +0 -69
  343. package/tests/setup.jest.ts +0 -23
  344. package/tests/stress/batch-operations-stress.test.ts +0 -571
  345. package/tests/unit/adapters/FeeOracleMock.test.ts +0 -40
  346. package/tests/unit/bitcoin/BitcoinManager.test.ts +0 -293
  347. package/tests/unit/bitcoin/BroadcastClient.test.ts +0 -52
  348. package/tests/unit/bitcoin/OrdNodeProvider.test.ts +0 -53
  349. package/tests/unit/bitcoin/OrdinalsClient.test.ts +0 -381
  350. package/tests/unit/bitcoin/OrdinalsClientProvider.test.ts +0 -102
  351. package/tests/unit/bitcoin/PSBTBuilder.test.ts +0 -84
  352. package/tests/unit/bitcoin/fee-calculation.test.ts +0 -261
  353. package/tests/unit/bitcoin/transactions/commit.test.ts +0 -649
  354. package/tests/unit/bitcoin/transfer.test.ts +0 -31
  355. package/tests/unit/bitcoin/utxo-selection-new.test.ts +0 -502
  356. package/tests/unit/bitcoin/utxo.more.test.ts +0 -39
  357. package/tests/unit/bitcoin/utxo.selection.test.ts +0 -38
  358. package/tests/unit/core/OriginalsSDK.test.ts +0 -152
  359. package/tests/unit/crypto/Multikey.test.ts +0 -206
  360. package/tests/unit/crypto/Signer.test.ts +0 -408
  361. package/tests/unit/did/BtcoDidResolver.test.ts +0 -611
  362. package/tests/unit/did/DIDManager.more.test.ts +0 -43
  363. package/tests/unit/did/DIDManager.test.ts +0 -185
  364. package/tests/unit/did/Ed25519Verifier.test.ts +0 -160
  365. package/tests/unit/did/KeyManager.test.ts +0 -452
  366. package/tests/unit/did/OrdinalsClientProviderAdapter.test.ts +0 -45
  367. package/tests/unit/did/WebVHManager.test.ts +0 -435
  368. package/tests/unit/did/createBtcoDidDocument.test.ts +0 -67
  369. package/tests/unit/did/providers/OrdinalsClientProviderAdapter.test.ts +0 -159
  370. package/tests/unit/events/EventEmitter.test.ts +0 -407
  371. package/tests/unit/lifecycle/BatchOperations.test.ts +0 -527
  372. package/tests/unit/lifecycle/LifecycleManager.keymanagement.test.ts +0 -312
  373. package/tests/unit/lifecycle/LifecycleManager.prov.test.ts +0 -18
  374. package/tests/unit/lifecycle/LifecycleManager.test.ts +0 -213
  375. package/tests/unit/lifecycle/LifecycleManager.transfer.unit.test.ts +0 -30
  376. package/tests/unit/lifecycle/OriginalsAsset.test.ts +0 -176
  377. package/tests/unit/lifecycle/ProvenanceQuery.test.ts +0 -577
  378. package/tests/unit/lifecycle/ResourceVersioning.test.ts +0 -651
  379. package/tests/unit/storage/MemoryStorageAdapter.test.ts +0 -93
  380. package/tests/unit/types/network.test.ts +0 -255
  381. package/tests/unit/utils/EventIntegration.test.ts +0 -384
  382. package/tests/unit/utils/Logger.test.ts +0 -473
  383. package/tests/unit/utils/MetricsCollector.test.ts +0 -358
  384. package/tests/unit/utils/bitcoin-address.test.ts +0 -250
  385. package/tests/unit/utils/cbor.test.ts +0 -35
  386. package/tests/unit/utils/encoding.test.ts +0 -318
  387. package/tests/unit/utils/hash.test.ts +0 -12
  388. package/tests/unit/utils/retry.test.ts +0 -100
  389. package/tests/unit/utils/satoshi-validation.test.ts +0 -354
  390. package/tests/unit/utils/serialization.test.ts +0 -124
  391. package/tests/unit/utils/telemetry.test.ts +0 -52
  392. package/tests/unit/utils/validation.test.ts +0 -141
  393. package/tests/unit/vc/CredentialManager.test.ts +0 -487
  394. package/tests/unit/vc/Issuer.test.ts +0 -107
  395. package/tests/unit/vc/Verifier.test.ts +0 -525
  396. package/tests/unit/vc/bbs.test.ts +0 -282
  397. package/tests/unit/vc/cryptosuites/eddsa.test.ts +0 -398
  398. package/tests/unit/vc/documentLoader.test.ts +0 -121
  399. package/tests/unit/vc/proofs/data-integrity.test.ts +0 -24
  400. package/tsconfig.json +0 -31
  401. package/tsconfig.test.json +0 -15
@@ -1,1218 +0,0 @@
1
- import {
2
- OriginalsConfig,
3
- AssetResource,
4
- BitcoinTransaction,
5
- KeyStore,
6
- ExternalSigner,
7
- VerifiableCredential
8
- } from '../types';
9
- import { BitcoinManager } from '../bitcoin/BitcoinManager';
10
- import { DIDManager } from '../did/DIDManager';
11
- import { CredentialManager } from '../vc/CredentialManager';
12
- import { OriginalsAsset } from './OriginalsAsset';
13
- import { MemoryStorageAdapter } from '../storage/MemoryStorageAdapter';
14
- import { encodeBase64UrlMultibase, hexToBytes } from '../utils/encoding';
15
- import { validateBitcoinAddress } from '../utils/bitcoin-address';
16
- import { multikey } from '../crypto/Multikey';
17
- import { EventEmitter } from '../events/EventEmitter';
18
- import type { EventHandler, EventTypeMap } from '../events/types';
19
- import { Logger } from '../utils/Logger';
20
- import { MetricsCollector } from '../utils/MetricsCollector';
21
- import {
22
- BatchOperationExecutor,
23
- BatchValidator,
24
- BatchError,
25
- type BatchResult,
26
- type BatchOperationOptions,
27
- type BatchInscriptionOptions,
28
- } from './BatchOperations';
29
-
30
- export class LifecycleManager {
31
- private eventEmitter: EventEmitter;
32
- private batchExecutor: BatchOperationExecutor;
33
- private batchValidator: BatchValidator;
34
- private logger: Logger;
35
- private metrics: MetricsCollector;
36
-
37
- constructor(
38
- private config: OriginalsConfig,
39
- private didManager: DIDManager,
40
- private credentialManager: CredentialManager,
41
- private deps?: { bitcoinManager?: BitcoinManager },
42
- private keyStore?: KeyStore
43
- ) {
44
- this.eventEmitter = new EventEmitter();
45
- this.batchExecutor = new BatchOperationExecutor();
46
- this.batchValidator = new BatchValidator();
47
- this.logger = new Logger('LifecycleManager', config);
48
- this.metrics = new MetricsCollector();
49
- }
50
-
51
- /**
52
- * Subscribe to a lifecycle event
53
- * @param eventType - The type of event to subscribe to
54
- * @param handler - The handler function to call when the event is emitted
55
- * @returns A function to unsubscribe from the event
56
- */
57
- on<K extends keyof EventTypeMap>(eventType: K, handler: EventHandler<EventTypeMap[K]>): () => void {
58
- return this.eventEmitter.on(eventType, handler);
59
- }
60
-
61
- /**
62
- * Subscribe to a lifecycle event once
63
- * @param eventType - The type of event to subscribe to
64
- * @param handler - The handler function to call when the event is emitted (will only fire once)
65
- * @returns A function to unsubscribe from the event
66
- */
67
- once<K extends keyof EventTypeMap>(eventType: K, handler: EventHandler<EventTypeMap[K]>): () => void {
68
- return this.eventEmitter.once(eventType, handler);
69
- }
70
-
71
- /**
72
- * Unsubscribe from a lifecycle event
73
- * @param eventType - The type of event to unsubscribe from
74
- * @param handler - The handler function to remove
75
- */
76
- off<K extends keyof EventTypeMap>(eventType: K, handler: EventHandler<EventTypeMap[K]>): void {
77
- this.eventEmitter.off(eventType, handler);
78
- }
79
-
80
- async registerKey(verificationMethodId: string, privateKey: string): Promise<void> {
81
- if (!this.keyStore) {
82
- throw new Error('KeyStore not configured. Provide keyStore to LifecycleManager constructor.');
83
- }
84
-
85
- // Validate verification method ID format
86
- if (!verificationMethodId || typeof verificationMethodId !== 'string') {
87
- throw new Error('Invalid verificationMethodId: must be a non-empty string');
88
- }
89
-
90
- // Validate private key format (should be multibase encoded)
91
- if (!privateKey || typeof privateKey !== 'string') {
92
- throw new Error('Invalid privateKey: must be a non-empty string');
93
- }
94
-
95
- // Validate that it's a valid multibase-encoded private key
96
- try {
97
- multikey.decodePrivateKey(privateKey);
98
- } catch (err) {
99
- throw new Error('Invalid privateKey format: must be a valid multibase-encoded private key');
100
- }
101
-
102
- await this.keyStore.setPrivateKey(verificationMethodId, privateKey);
103
- }
104
-
105
- async createAsset(resources: AssetResource[]): Promise<OriginalsAsset> {
106
- const stopTimer = this.logger.startTimer('createAsset');
107
- this.logger.info('Creating asset', { resourceCount: resources.length });
108
-
109
- try {
110
- // Input validation
111
- if (!Array.isArray(resources)) {
112
- throw new Error('Resources must be an array');
113
- }
114
- if (resources.length === 0) {
115
- throw new Error('At least one resource is required');
116
- }
117
-
118
- // Validate each resource
119
- for (const resource of resources) {
120
- if (!resource || typeof resource !== 'object') {
121
- throw new Error('Invalid resource: must be an object');
122
- }
123
- if (!resource.id || typeof resource.id !== 'string') {
124
- throw new Error('Invalid resource: missing or invalid id');
125
- }
126
- if (!resource.type || typeof resource.type !== 'string') {
127
- throw new Error('Invalid resource: missing or invalid type');
128
- }
129
- if (!resource.contentType || typeof resource.contentType !== 'string') {
130
- throw new Error('Invalid resource: missing or invalid contentType');
131
- }
132
- if (!resource.hash || typeof resource.hash !== 'string' || !/^[0-9a-fA-F]+$/.test(resource.hash)) {
133
- throw new Error('Invalid resource: missing or invalid hash (must be hex string)');
134
- }
135
- // Validate contentType is a valid MIME type
136
- if (!/^[a-zA-Z0-9][a-zA-Z0-9!#$&^_.+-]{0,126}\/[a-zA-Z0-9][a-zA-Z0-9!#$&^_.+-]{0,126}$/.test(resource.contentType)) {
137
- throw new Error(`Invalid resource: invalid contentType MIME format: ${resource.contentType}`);
138
- }
139
- }
140
-
141
- // Create a proper DID:peer document with verification methods
142
- // If keyStore is provided, request the key pair to be returned
143
- if (this.keyStore) {
144
- const result = await this.didManager.createDIDPeer(resources, true);
145
- const didDoc = result.didDocument;
146
- const keyPair = result.keyPair;
147
-
148
- // Register the private key in the keyStore
149
- if (didDoc.verificationMethod && didDoc.verificationMethod.length > 0) {
150
- let verificationMethodId = didDoc.verificationMethod[0].id;
151
-
152
- // Ensure VM ID is absolute (not just a fragment like #key-0)
153
- if (verificationMethodId.startsWith('#')) {
154
- verificationMethodId = `${didDoc.id}${verificationMethodId}`;
155
- }
156
-
157
- await this.keyStore.setPrivateKey(verificationMethodId, keyPair.privateKey);
158
- }
159
-
160
- const asset = new OriginalsAsset(resources, didDoc, []);
161
-
162
- // Defer asset:created event emission to next microtask so callers can subscribe first
163
- queueMicrotask(() => {
164
- const event = {
165
- type: 'asset:created' as const,
166
- timestamp: new Date().toISOString(),
167
- asset: {
168
- id: asset.id,
169
- layer: asset.currentLayer,
170
- resourceCount: resources.length,
171
- createdAt: asset.getProvenance().createdAt
172
- }
173
- };
174
-
175
- // Emit from both LifecycleManager and asset emitters
176
- this.eventEmitter.emit(event);
177
- (asset as any).eventEmitter.emit(event);
178
- });
179
-
180
- stopTimer();
181
- this.logger.info('Asset created successfully', { assetId: asset.id });
182
- this.metrics.recordAssetCreated();
183
-
184
- return asset;
185
- } else {
186
- // No keyStore, just create the DID document
187
- const didDoc = await this.didManager.createDIDPeer(resources);
188
- const asset = new OriginalsAsset(resources, didDoc, []);
189
-
190
- // Defer asset:created event emission to next microtask so callers can subscribe first
191
- queueMicrotask(() => {
192
- const event = {
193
- type: 'asset:created' as const,
194
- timestamp: new Date().toISOString(),
195
- asset: {
196
- id: asset.id,
197
- layer: asset.currentLayer,
198
- resourceCount: resources.length,
199
- createdAt: asset.getProvenance().createdAt
200
- }
201
- };
202
-
203
- // Emit from both LifecycleManager and asset emitters
204
- this.eventEmitter.emit(event);
205
- (asset as any).eventEmitter.emit(event);
206
- });
207
-
208
- stopTimer();
209
- this.logger.info('Asset created successfully', { assetId: asset.id });
210
- this.metrics.recordAssetCreated();
211
-
212
- return asset;
213
- }
214
- } catch (error) {
215
- stopTimer();
216
- this.logger.error('Asset creation failed', error as Error, { resourceCount: resources.length });
217
- this.metrics.recordError('ASSET_CREATION_FAILED', 'createAsset');
218
- throw error;
219
- }
220
- }
221
-
222
- async publishToWeb(
223
- asset: OriginalsAsset,
224
- publisherDidOrSigner: string | ExternalSigner
225
- ): Promise<OriginalsAsset> {
226
- const stopTimer = this.logger.startTimer('publishToWeb');
227
-
228
- try {
229
- if (asset.currentLayer !== 'did:peer') {
230
- throw new Error('Asset must be in did:peer layer to publish to web');
231
- }
232
-
233
- const { publisherDid, signer } = await this.extractPublisherInfo(publisherDidOrSigner);
234
- const { domain, userPath } = this.parseWebVHDid(publisherDid);
235
-
236
- this.logger.info('Publishing asset to web', { assetId: asset.id, publisherDid });
237
-
238
- // Publish resources to storage
239
- await this.publishResources(asset, publisherDid, domain, userPath);
240
-
241
- // Store the original did:peer ID before migration
242
- const originalPeerDid = asset.id;
243
-
244
- // Migrate asset to did:webvh layer
245
- await asset.migrate('did:webvh');
246
- asset.bindings = { ...(asset as any).bindings, 'did:peer': originalPeerDid, 'did:webvh': publisherDid };
247
-
248
- // Issue publication credential (best-effort)
249
- await this.issuePublicationCredential(asset, publisherDid, signer);
250
-
251
- stopTimer();
252
- this.logger.info('Asset published to web successfully', {
253
- assetId: asset.id,
254
- publisherDid,
255
- resourceCount: asset.resources.length
256
- });
257
- this.metrics.recordMigration('did:peer', 'did:webvh');
258
-
259
- return asset;
260
- } catch (error) {
261
- stopTimer();
262
- this.logger.error('Publish to web failed', error as Error, { assetId: asset.id });
263
- this.metrics.recordError('PUBLISH_FAILED', 'publishToWeb');
264
- throw error;
265
- }
266
- }
267
-
268
- private async extractPublisherInfo(publisherDidOrSigner: string | ExternalSigner): Promise<{
269
- publisherDid: string;
270
- signer?: ExternalSigner;
271
- }> {
272
- if (typeof publisherDidOrSigner === 'string') {
273
- // If it's already a did:webvh DID, use it as-is
274
- if (publisherDidOrSigner.startsWith('did:webvh:')) {
275
- return { publisherDid: publisherDidOrSigner };
276
- }
277
-
278
- // Otherwise, treat it as a domain and construct a did:webvh DID
279
- // Format: did:webvh:domain:user (use 'user' as default user path)
280
- // Encode the domain to handle ports (e.g., localhost:5000 -> localhost%3A5000)
281
- const domain = publisherDidOrSigner;
282
- const encodedDomain = encodeURIComponent(domain);
283
- const publisherDid = `did:webvh:${encodedDomain}:user`;
284
- return { publisherDid };
285
- }
286
-
287
- const signer = publisherDidOrSigner;
288
- const vmId = await signer.getVerificationMethodId();
289
- const publisherDid = vmId.includes('#') ? vmId.split('#')[0] : vmId;
290
-
291
- if (!publisherDid.startsWith('did:webvh:')) {
292
- throw new Error('Signer must be associated with a did:webvh identifier');
293
- }
294
-
295
- return { publisherDid, signer };
296
- }
297
-
298
- private parseWebVHDid(did: string): { domain: string; userPath: string } {
299
- const parts = did.split(':');
300
- if (parts.length < 4) {
301
- throw new Error('Invalid did:webvh format: must include domain and user path');
302
- }
303
-
304
- const domain = decodeURIComponent(parts[2]);
305
- const userPath = parts.slice(3).join('/');
306
-
307
- return { domain, userPath };
308
- }
309
-
310
- private async publishResources(
311
- asset: OriginalsAsset,
312
- publisherDid: string,
313
- domain: string,
314
- userPath: string
315
- ): Promise<void> {
316
- const storage = (this.config as any).storageAdapter || new MemoryStorageAdapter();
317
-
318
- for (const resource of asset.resources) {
319
- const hashBytes = hexToBytes(resource.hash);
320
- const multibase = encodeBase64UrlMultibase(hashBytes);
321
- const resourceUrl = `${publisherDid}/resources/${multibase}`;
322
- const relativePath = `${userPath}/resources/${multibase}`;
323
-
324
- // Store resource content
325
- const data = resource.content
326
- ? Buffer.from(resource.content)
327
- : Buffer.from(resource.hash);
328
-
329
- if (typeof storage.put === 'function') {
330
- await storage.put(`${domain}/${relativePath}`, data, { contentType: resource.contentType });
331
- } else {
332
- const encoded = new TextEncoder().encode(resource.content || resource.hash);
333
- await storage.putObject(domain, relativePath, encoded);
334
- }
335
-
336
- (resource as any).url = resourceUrl;
337
-
338
- await this.emitResourcePublishedEvent(asset, resource, resourceUrl, publisherDid, domain);
339
- }
340
- }
341
-
342
- private async emitResourcePublishedEvent(
343
- asset: OriginalsAsset,
344
- resource: AssetResource,
345
- resourceUrl: string,
346
- publisherDid: string,
347
- domain: string
348
- ): Promise<void> {
349
- const event = {
350
- type: 'resource:published' as const,
351
- timestamp: new Date().toISOString(),
352
- asset: { id: asset.id },
353
- resource: {
354
- id: resource.id,
355
- url: resourceUrl,
356
- contentType: resource.contentType,
357
- hash: resource.hash
358
- },
359
- publisherDid,
360
- domain
361
- };
362
-
363
- try {
364
- // Emit from both LifecycleManager and asset emitters
365
- await this.eventEmitter.emit(event);
366
- await (asset as any).eventEmitter.emit(event);
367
- } catch (err) {
368
- this.logger.error('Event handler error', err as Error, { event: event.type });
369
- }
370
- }
371
-
372
- private async issuePublicationCredential(
373
- asset: OriginalsAsset,
374
- publisherDid: string,
375
- signer?: ExternalSigner
376
- ): Promise<void> {
377
- try {
378
- const subject = {
379
- id: asset.id,
380
- publishedAs: publisherDid,
381
- resourceId: asset.resources[0]?.id,
382
- fromLayer: 'did:peer' as const,
383
- toLayer: 'did:webvh' as const,
384
- migratedAt: new Date().toISOString()
385
- };
386
-
387
- const unsigned = await this.credentialManager.createResourceCredential(
388
- 'ResourceMigrated',
389
- subject,
390
- publisherDid
391
- );
392
-
393
- const signed = signer
394
- ? await this.credentialManager.signCredentialWithExternalSigner(unsigned, signer)
395
- : await this.signWithKeyStore(unsigned, publisherDid);
396
-
397
- asset.credentials.push(signed);
398
-
399
- const event = {
400
- type: 'credential:issued' as const,
401
- timestamp: new Date().toISOString(),
402
- asset: { id: asset.id },
403
- credential: {
404
- type: signed.type,
405
- issuer: typeof signed.issuer === 'string' ? signed.issuer : signed.issuer.id
406
- }
407
- };
408
-
409
- // Emit from both LifecycleManager and asset emitters
410
- await this.eventEmitter.emit(event);
411
- await (asset as any).eventEmitter.emit(event);
412
- } catch (err) {
413
- this.logger.error('Failed to issue credential during publish', err as Error);
414
- }
415
- }
416
-
417
- private async signWithKeyStore(
418
- credential: VerifiableCredential,
419
- issuer: string
420
- ): Promise<VerifiableCredential> {
421
- if (!this.keyStore) {
422
- throw new Error('KeyStore required for signing. Provide keyStore or external signer.');
423
- }
424
-
425
- // Try to find a key in the keyStore for this DID
426
- // First try common verification method patterns: #key-0, #keys-1, etc.
427
- const commonVmIds = [
428
- `${issuer}#key-0`,
429
- `${issuer}#keys-1`,
430
- `${issuer}#authentication`,
431
- ];
432
-
433
- let privateKey: string | null = null;
434
- let vmId: string | null = null;
435
-
436
- for (const testVmId of commonVmIds) {
437
- const key = await this.keyStore.getPrivateKey(testVmId);
438
- if (key) {
439
- privateKey = key;
440
- vmId = testVmId;
441
- break;
442
- }
443
- }
444
-
445
- // If not found, try to find ANY key that starts with the issuer DID
446
- if (!privateKey && typeof (this.keyStore as any).getAllVerificationMethodIds === 'function') {
447
- const allVmIds = (this.keyStore as any).getAllVerificationMethodIds();
448
- for (const testVmId of allVmIds) {
449
- if (testVmId.startsWith(issuer)) {
450
- const key = await this.keyStore.getPrivateKey(testVmId);
451
- if (key) {
452
- privateKey = key;
453
- vmId = testVmId;
454
- break;
455
- }
456
- }
457
- }
458
- }
459
-
460
- // If no key found in common patterns, try resolving the DID
461
- if (!privateKey) {
462
- const didDoc = await this.didManager.resolveDID(issuer);
463
- if (!didDoc?.verificationMethod?.[0]) {
464
- throw new Error('No verification method found in publisher DID document');
465
- }
466
-
467
- vmId = didDoc.verificationMethod[0].id;
468
- if (vmId.startsWith('#')) {
469
- vmId = `${issuer}${vmId}`;
470
- }
471
-
472
- privateKey = await this.keyStore.getPrivateKey(vmId);
473
- if (!privateKey) {
474
- throw new Error('Private key not found in keyStore');
475
- }
476
- }
477
-
478
- return this.credentialManager.signCredential(credential, privateKey!, vmId!);
479
- }
480
-
481
- async inscribeOnBitcoin(
482
- asset: OriginalsAsset,
483
- feeRate?: number
484
- ): Promise<OriginalsAsset> {
485
- const stopTimer = this.logger.startTimer('inscribeOnBitcoin');
486
- this.logger.info('Inscribing asset on Bitcoin', { assetId: asset.id, feeRate });
487
-
488
- try {
489
- // Input validation
490
- if (!asset || typeof asset !== 'object') {
491
- throw new Error('Invalid asset: must be a valid OriginalsAsset');
492
- }
493
- if (feeRate !== undefined) {
494
- if (typeof feeRate !== 'number' || feeRate <= 0 || !Number.isFinite(feeRate)) {
495
- throw new Error('Invalid feeRate: must be a positive number');
496
- }
497
- if (feeRate < 1 || feeRate > 1000000) {
498
- throw new Error('Invalid feeRate: must be between 1 and 1000000 sat/vB');
499
- }
500
- }
501
-
502
- if (typeof (asset as any).migrate !== 'function') {
503
- throw new Error('Not implemented');
504
- }
505
- if (asset.currentLayer !== 'did:webvh' && asset.currentLayer !== 'did:peer') {
506
- throw new Error('Not implemented');
507
- }
508
- const bitcoinManager = this.deps?.bitcoinManager ?? new BitcoinManager(this.config);
509
- const manifest = {
510
- assetId: asset.id,
511
- resources: asset.resources.map(res => ({ id: res.id, hash: res.hash, contentType: res.contentType, url: res.url })),
512
- timestamp: new Date().toISOString()
513
- };
514
- const payload = Buffer.from(JSON.stringify(manifest));
515
- const inscription: any = await bitcoinManager.inscribeData(payload, 'application/json', feeRate);
516
- const revealTxId = inscription.revealTxId ?? inscription.txid;
517
- const commitTxId = inscription.commitTxId;
518
- const usedFeeRate = typeof inscription.feeRate === 'number' ? inscription.feeRate : feeRate;
519
-
520
- // Capture the layer before migration for accurate metrics
521
- const fromLayer = asset.currentLayer;
522
-
523
- await asset.migrate('did:btco', {
524
- transactionId: revealTxId,
525
- inscriptionId: inscription.inscriptionId,
526
- satoshi: inscription.satoshi,
527
- commitTxId,
528
- revealTxId,
529
- feeRate: usedFeeRate
530
- });
531
-
532
- const bindingValue = inscription.satoshi
533
- ? `did:btco:${inscription.satoshi}`
534
- : `did:btco:${inscription.inscriptionId}`;
535
- (asset as any).bindings = Object.assign({}, (asset as any).bindings, { 'did:btco': bindingValue });
536
-
537
- stopTimer();
538
- this.logger.info('Asset inscribed on Bitcoin successfully', {
539
- assetId: asset.id,
540
- inscriptionId: inscription.inscriptionId,
541
- transactionId: revealTxId
542
- });
543
- this.metrics.recordMigration(fromLayer, 'did:btco');
544
-
545
- return asset;
546
- } catch (error) {
547
- stopTimer();
548
- this.logger.error('Bitcoin inscription failed', error as Error, { assetId: asset.id, feeRate });
549
- this.metrics.recordError('INSCRIPTION_FAILED', 'inscribeOnBitcoin');
550
- throw error;
551
- }
552
- }
553
-
554
- async transferOwnership(
555
- asset: OriginalsAsset,
556
- newOwner: string
557
- ): Promise<BitcoinTransaction> {
558
- const stopTimer = this.logger.startTimer('transferOwnership');
559
- this.logger.info('Transferring asset ownership', { assetId: asset.id, newOwner });
560
-
561
- try {
562
- // Input validation
563
- if (!asset || typeof asset !== 'object') {
564
- throw new Error('Invalid asset: must be a valid OriginalsAsset');
565
- }
566
- if (!newOwner || typeof newOwner !== 'string') {
567
- throw new Error('Invalid newOwner: must be a non-empty string');
568
- }
569
-
570
- // Validate Bitcoin address format and checksum
571
- try {
572
- validateBitcoinAddress(newOwner, this.config.network);
573
- } catch (error) {
574
- const message = error instanceof Error ? error.message : 'Invalid Bitcoin address';
575
- throw new Error(`Invalid Bitcoin address for ownership transfer: ${message}`);
576
- }
577
-
578
- // Transfer Bitcoin-anchored asset ownership
579
- // Only works for assets in did:btco layer
580
- if (asset.currentLayer !== 'did:btco') {
581
- throw new Error('Asset must be inscribed on Bitcoin before transfer');
582
- }
583
- const bm = this.deps?.bitcoinManager ?? new BitcoinManager(this.config);
584
- const provenance = asset.getProvenance();
585
- const latestMigration = provenance.migrations[provenance.migrations.length - 1];
586
- const satoshi = latestMigration?.satoshi ?? (asset.id.startsWith('did:btco:') ? asset.id.split(':')[2] : '');
587
- const inscription = {
588
- satoshi,
589
- inscriptionId: latestMigration?.inscriptionId ?? `insc-${satoshi || 'unknown'}`,
590
- content: Buffer.alloc(0),
591
- contentType: 'application/octet-stream',
592
- txid: latestMigration?.transactionId ?? 'unknown-tx',
593
- vout: 0
594
- };
595
- const tx = await bm.transferInscription(inscription as any, newOwner);
596
- await asset.recordTransfer(asset.id, newOwner, tx.txid);
597
-
598
- stopTimer();
599
- this.logger.info('Asset ownership transferred successfully', {
600
- assetId: asset.id,
601
- newOwner,
602
- transactionId: tx.txid
603
- });
604
- this.metrics.recordTransfer();
605
-
606
- return tx;
607
- } catch (error) {
608
- stopTimer();
609
- this.logger.error('Ownership transfer failed', error as Error, { assetId: asset.id, newOwner });
610
- this.metrics.recordError('TRANSFER_FAILED', 'transferOwnership');
611
- throw error;
612
- }
613
- }
614
-
615
- /**
616
- * Create multiple assets in batch
617
- *
618
- * @param resourcesList - Array of resource arrays, one per asset to create
619
- * @param options - Batch operation options
620
- * @returns BatchResult with created assets
621
- */
622
- async batchCreateAssets(
623
- resourcesList: AssetResource[][],
624
- options?: BatchOperationOptions
625
- ): Promise<BatchResult<OriginalsAsset>> {
626
- const batchId = this.batchExecutor.generateBatchId();
627
-
628
- // Validate first if requested
629
- if (options?.validateFirst !== false) {
630
- const validationResults = this.batchValidator.validateBatchCreate(resourcesList);
631
- const invalid = validationResults.filter(r => !r.isValid);
632
- if (invalid.length > 0) {
633
- const errors = invalid.flatMap(r => r.errors).join('; ');
634
- throw new Error(`Batch validation failed: ${errors}`);
635
- }
636
- }
637
-
638
- // Emit batch:started event
639
- await this.eventEmitter.emit({
640
- type: 'batch:started',
641
- timestamp: new Date().toISOString(),
642
- operation: 'create',
643
- batchId,
644
- itemCount: resourcesList.length
645
- });
646
-
647
- try {
648
- // Use batch executor to process all asset creations
649
- const result = await this.batchExecutor.execute(
650
- resourcesList,
651
- async (resources, index) => {
652
- const asset = await this.createAsset(resources);
653
- return asset;
654
- },
655
- options,
656
- batchId // Pass the pre-generated batchId for event correlation
657
- );
658
-
659
- // Emit batch:completed event
660
- await this.eventEmitter.emit({
661
- type: 'batch:completed',
662
- timestamp: new Date().toISOString(),
663
- batchId,
664
- operation: 'create',
665
- results: {
666
- successful: result.successful.length,
667
- failed: result.failed.length,
668
- totalDuration: result.totalDuration
669
- }
670
- });
671
-
672
- return result;
673
- } catch (error) {
674
- // Emit batch:failed event
675
- await this.eventEmitter.emit({
676
- type: 'batch:failed',
677
- timestamp: new Date().toISOString(),
678
- batchId,
679
- operation: 'create',
680
- error: error instanceof Error ? error.message : String(error)
681
- });
682
-
683
- throw error;
684
- }
685
- }
686
-
687
- /**
688
- * Publish multiple assets to web storage in batch
689
- *
690
- * @param assets - Array of assets to publish
691
- * @param domain - Domain to publish to
692
- * @param options - Batch operation options
693
- * @returns BatchResult with published assets
694
- */
695
- async batchPublishToWeb(
696
- assets: OriginalsAsset[],
697
- domain: string,
698
- options?: BatchOperationOptions
699
- ): Promise<BatchResult<OriginalsAsset>> {
700
- const batchId = this.batchExecutor.generateBatchId();
701
-
702
- // Validate domain once
703
- if (!domain || typeof domain !== 'string') {
704
- throw new Error('Invalid domain: must be a non-empty string');
705
- }
706
-
707
- const normalized = domain.trim().toLowerCase();
708
-
709
- // Split domain and port if present
710
- const [domainPart, portPart] = normalized.split(':');
711
-
712
- // Validate port if present
713
- if (portPart && (!/^\d+$/.test(portPart) || parseInt(portPart) < 1 || parseInt(portPart) > 65535)) {
714
- throw new Error(`Invalid domain format: ${domain} - invalid port`);
715
- }
716
-
717
- // Allow localhost and IP addresses for development
718
- const isLocalhost = domainPart === 'localhost';
719
- const isIP = /^(\d{1,3}\.){3}\d{1,3}$/.test(domainPart);
720
-
721
- if (!isLocalhost && !isIP) {
722
- // For non-localhost domains, require proper domain format
723
- const label = '[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?';
724
- const domainRegex = new RegExp(`^(?=.{1,253}$)(?:${label})(?:\\.(?:${label}))+?$`, 'i');
725
- if (!domainRegex.test(domainPart)) {
726
- throw new Error(`Invalid domain format: ${domain}`);
727
- }
728
- }
729
-
730
- // Emit batch:started event
731
- await this.eventEmitter.emit({
732
- type: 'batch:started',
733
- timestamp: new Date().toISOString(),
734
- operation: 'publish',
735
- batchId,
736
- itemCount: assets.length
737
- });
738
-
739
- try {
740
- const result = await this.batchExecutor.execute(
741
- assets,
742
- async (asset, index) => {
743
- return await this.publishToWeb(asset, domain);
744
- },
745
- options,
746
- batchId // Pass the pre-generated batchId for event correlation
747
- );
748
-
749
- // Emit batch:completed event
750
- await this.eventEmitter.emit({
751
- type: 'batch:completed',
752
- timestamp: new Date().toISOString(),
753
- batchId,
754
- operation: 'publish',
755
- results: {
756
- successful: result.successful.length,
757
- failed: result.failed.length,
758
- totalDuration: result.totalDuration
759
- }
760
- });
761
-
762
- return result;
763
- } catch (error) {
764
- // Emit batch:failed event
765
- await this.eventEmitter.emit({
766
- type: 'batch:failed',
767
- timestamp: new Date().toISOString(),
768
- batchId,
769
- operation: 'publish',
770
- error: error instanceof Error ? error.message : String(error)
771
- });
772
-
773
- throw error;
774
- }
775
- }
776
-
777
- /**
778
- * Inscribe multiple assets on Bitcoin with cost optimization
779
- * KEY FEATURE: singleTransaction option for 30%+ cost savings
780
- *
781
- * @param assets - Array of assets to inscribe
782
- * @param options - Batch inscription options
783
- * @returns BatchResult with inscribed assets
784
- */
785
- async batchInscribeOnBitcoin(
786
- assets: OriginalsAsset[],
787
- options?: BatchInscriptionOptions
788
- ): Promise<BatchResult<OriginalsAsset>> {
789
- // Validate first if requested
790
- if (options?.validateFirst !== false) {
791
- const validationResults = this.batchValidator.validateBatchInscription(assets);
792
- const invalid = validationResults.filter(r => !r.isValid);
793
- if (invalid.length > 0) {
794
- const errors = invalid.flatMap(r => r.errors).join('; ');
795
- throw new Error(`Batch validation failed: ${errors}`);
796
- }
797
- }
798
-
799
- if (options?.singleTransaction) {
800
- return this.batchInscribeSingleTransaction(assets, options);
801
- } else {
802
- return this.batchInscribeIndividualTransactions(assets, options);
803
- }
804
- }
805
-
806
- /**
807
- * CORE INNOVATION: Single-transaction batch inscription
808
- * Combines multiple assets into one Bitcoin transaction for 30%+ cost savings
809
- *
810
- * @param assets - Array of assets to inscribe
811
- * @param options - Batch inscription options
812
- * @returns BatchResult with inscribed assets and cost savings data
813
- */
814
- private async batchInscribeSingleTransaction(
815
- assets: OriginalsAsset[],
816
- options?: BatchInscriptionOptions
817
- ): Promise<BatchResult<OriginalsAsset>> {
818
- const batchId = this.batchExecutor.generateBatchId();
819
- const startTime = Date.now();
820
- const startedAt = new Date().toISOString();
821
-
822
- // Emit batch:started event
823
- await this.eventEmitter.emit({
824
- type: 'batch:started',
825
- timestamp: startedAt,
826
- operation: 'inscribe',
827
- batchId,
828
- itemCount: assets.length
829
- });
830
-
831
- try {
832
- // Calculate total data size for all assets
833
- const totalDataSize = this.calculateTotalDataSize(assets);
834
-
835
- // Estimate savings from batch inscription
836
- const estimatedSavings = await this.estimateBatchSavings(assets, options?.feeRate);
837
-
838
- // Create manifests for all assets
839
- const manifests = assets.map(asset => ({
840
- assetId: asset.id,
841
- resources: asset.resources.map(res => ({
842
- id: res.id,
843
- hash: res.hash,
844
- contentType: res.contentType,
845
- url: res.url
846
- })),
847
- timestamp: new Date().toISOString()
848
- }));
849
-
850
- // Combine all manifests into a single batch payload
851
- const batchManifest = {
852
- batchId,
853
- assets: manifests,
854
- timestamp: new Date().toISOString()
855
- };
856
-
857
- const payload = Buffer.from(JSON.stringify(batchManifest));
858
-
859
- // Inscribe the batch manifest as a single transaction
860
- const bitcoinManager = this.deps?.bitcoinManager ?? new BitcoinManager(this.config);
861
- const inscription: any = await bitcoinManager.inscribeData(
862
- payload,
863
- 'application/json',
864
- options?.feeRate
865
- );
866
-
867
- const revealTxId = inscription.revealTxId ?? inscription.txid;
868
- const commitTxId = inscription.commitTxId;
869
- const usedFeeRate = typeof inscription.feeRate === 'number' ? inscription.feeRate : options?.feeRate;
870
-
871
- // Calculate fee per asset (split proportionally by data size)
872
- // Include both metadata and resource content size for accurate fee distribution
873
- const assetSizes = assets.map(asset => {
874
- // Calculate metadata size
875
- const metadataSize = JSON.stringify({
876
- assetId: asset.id,
877
- resources: asset.resources.map(r => ({
878
- id: r.id,
879
- hash: r.hash,
880
- contentType: r.contentType,
881
- url: r.url
882
- }))
883
- }).length;
884
-
885
- // Add resource content sizes
886
- const contentSize = asset.resources.reduce((sum, r) => {
887
- const content = (r as any).content;
888
- if (content) {
889
- return sum + (typeof content === 'string' ? Buffer.byteLength(content) : content.length || 0);
890
- }
891
- return sum;
892
- }, 0);
893
-
894
- return metadataSize + contentSize;
895
- });
896
- const totalSize = assetSizes.reduce((sum, size) => sum + size, 0);
897
-
898
- // Calculate total fee from batch transaction size and fee rate
899
- // Estimate transaction size: base overhead (200 bytes) + batch payload size
900
- const batchTxSize = 200 + totalDataSize;
901
- const effectiveFeeRate = usedFeeRate ?? 10;
902
- const totalFee = batchTxSize * effectiveFeeRate;
903
-
904
- // Split fees proportionally by asset data size
905
- const feePerAsset = assetSizes.map(size =>
906
- Math.floor(totalFee * (size / totalSize))
907
- );
908
-
909
- // Update all assets with batch inscription data
910
- const individualInscriptionIds: string[] = [];
911
- const successful: BatchResult<OriginalsAsset>['successful'] = [];
912
-
913
- for (let i = 0; i < assets.length; i++) {
914
- const asset = assets[i];
915
- // For batch inscriptions, use the base inscription ID for all assets
916
- // The batch index is stored as metadata, not in the ID
917
- const individualInscriptionId = inscription.inscriptionId;
918
- individualInscriptionIds.push(individualInscriptionId);
919
-
920
- await asset.migrate('did:btco', {
921
- transactionId: revealTxId,
922
- inscriptionId: individualInscriptionId,
923
- satoshi: inscription.satoshi,
924
- commitTxId,
925
- revealTxId,
926
- feeRate: usedFeeRate
927
- });
928
-
929
- // Add batch metadata to provenance
930
- const provenance = asset.getProvenance();
931
- const latestMigration = provenance.migrations[provenance.migrations.length - 1];
932
- (latestMigration as any).batchId = batchId;
933
- (latestMigration as any).batchInscription = true;
934
- (latestMigration as any).batchIndex = i; // Store index as metadata
935
- (latestMigration as any).feePaid = feePerAsset[i];
936
-
937
- const bindingValue = inscription.satoshi
938
- ? `did:btco:${inscription.satoshi}`
939
- : `did:btco:${individualInscriptionId}`;
940
- (asset as any).bindings = Object.assign({}, (asset as any).bindings, { 'did:btco': bindingValue });
941
-
942
- successful.push({
943
- index: i,
944
- result: asset,
945
- duration: Date.now() - startTime
946
- });
947
- }
948
-
949
- const totalDuration = Date.now() - startTime;
950
- const completedAt = new Date().toISOString();
951
-
952
- // Emit batch:completed event with cost savings
953
- await this.eventEmitter.emit({
954
- type: 'batch:completed',
955
- timestamp: completedAt,
956
- batchId,
957
- operation: 'inscribe',
958
- results: {
959
- successful: successful.length,
960
- failed: 0,
961
- totalDuration,
962
- costSavings: {
963
- amount: estimatedSavings.savings,
964
- percentage: estimatedSavings.savingsPercentage
965
- }
966
- }
967
- });
968
-
969
- return {
970
- successful,
971
- failed: [],
972
- totalProcessed: assets.length,
973
- totalDuration,
974
- batchId,
975
- startedAt,
976
- completedAt
977
- };
978
- } catch (error) {
979
- // Emit batch:failed event
980
- await this.eventEmitter.emit({
981
- type: 'batch:failed',
982
- timestamp: new Date().toISOString(),
983
- batchId,
984
- operation: 'inscribe',
985
- error: error instanceof Error ? error.message : String(error)
986
- });
987
-
988
- throw new BatchError(
989
- batchId,
990
- 'inscribe',
991
- { successful: 0, failed: assets.length },
992
- error instanceof Error ? error.message : String(error)
993
- );
994
- }
995
- }
996
-
997
- /**
998
- * Individual transaction batch inscription (fallback mode)
999
- * Each asset is inscribed in its own transaction
1000
- *
1001
- * @param assets - Array of assets to inscribe
1002
- * @param options - Batch inscription options
1003
- * @returns BatchResult with inscribed assets
1004
- */
1005
- private async batchInscribeIndividualTransactions(
1006
- assets: OriginalsAsset[],
1007
- options?: BatchInscriptionOptions
1008
- ): Promise<BatchResult<OriginalsAsset>> {
1009
- const batchId = this.batchExecutor.generateBatchId();
1010
-
1011
- // Emit batch:started event
1012
- await this.eventEmitter.emit({
1013
- type: 'batch:started',
1014
- timestamp: new Date().toISOString(),
1015
- operation: 'inscribe',
1016
- batchId,
1017
- itemCount: assets.length
1018
- });
1019
-
1020
- try {
1021
- const result = await this.batchExecutor.execute(
1022
- assets,
1023
- async (asset, index) => {
1024
- return await this.inscribeOnBitcoin(asset, options?.feeRate);
1025
- },
1026
- options,
1027
- batchId // Pass the pre-generated batchId for event correlation
1028
- );
1029
-
1030
- // Emit batch:completed event
1031
- await this.eventEmitter.emit({
1032
- type: 'batch:completed',
1033
- timestamp: new Date().toISOString(),
1034
- batchId,
1035
- operation: 'inscribe',
1036
- results: {
1037
- successful: result.successful.length,
1038
- failed: result.failed.length,
1039
- totalDuration: result.totalDuration
1040
- }
1041
- });
1042
-
1043
- return result;
1044
- } catch (error) {
1045
- // Emit batch:failed event
1046
- await this.eventEmitter.emit({
1047
- type: 'batch:failed',
1048
- timestamp: new Date().toISOString(),
1049
- batchId,
1050
- operation: 'inscribe',
1051
- error: error instanceof Error ? error.message : String(error)
1052
- });
1053
-
1054
- throw error;
1055
- }
1056
- }
1057
-
1058
- /**
1059
- * Transfer ownership of multiple assets in batch
1060
- *
1061
- * @param transfers - Array of transfer operations
1062
- * @param options - Batch operation options
1063
- * @returns BatchResult with transaction results
1064
- */
1065
- async batchTransferOwnership(
1066
- transfers: Array<{ asset: OriginalsAsset; to: string }>,
1067
- options?: BatchOperationOptions
1068
- ): Promise<BatchResult<BitcoinTransaction>> {
1069
- const batchId = this.batchExecutor.generateBatchId();
1070
-
1071
- // Validate first if requested
1072
- if (options?.validateFirst !== false) {
1073
- const validationResults = this.batchValidator.validateBatchTransfer(transfers);
1074
- const invalid = validationResults.filter(r => !r.isValid);
1075
- if (invalid.length > 0) {
1076
- const errors = invalid.flatMap(r => r.errors).join('; ');
1077
- throw new Error(`Batch validation failed: ${errors}`);
1078
- }
1079
-
1080
- // Validate all Bitcoin addresses
1081
- for (let i = 0; i < transfers.length; i++) {
1082
- try {
1083
- validateBitcoinAddress(transfers[i].to, this.config.network);
1084
- } catch (error) {
1085
- const message = error instanceof Error ? error.message : 'Invalid Bitcoin address';
1086
- throw new Error(`Transfer ${i}: Invalid Bitcoin address: ${message}`);
1087
- }
1088
- }
1089
- }
1090
-
1091
- // Emit batch:started event
1092
- await this.eventEmitter.emit({
1093
- type: 'batch:started',
1094
- timestamp: new Date().toISOString(),
1095
- operation: 'transfer',
1096
- batchId,
1097
- itemCount: transfers.length
1098
- });
1099
-
1100
- try {
1101
- const result = await this.batchExecutor.execute(
1102
- transfers,
1103
- async (transfer, index) => {
1104
- return await this.transferOwnership(transfer.asset, transfer.to);
1105
- },
1106
- options,
1107
- batchId // Pass the pre-generated batchId for event correlation
1108
- );
1109
-
1110
- // Emit batch:completed event
1111
- await this.eventEmitter.emit({
1112
- type: 'batch:completed',
1113
- timestamp: new Date().toISOString(),
1114
- batchId,
1115
- operation: 'transfer',
1116
- results: {
1117
- successful: result.successful.length,
1118
- failed: result.failed.length,
1119
- totalDuration: result.totalDuration
1120
- }
1121
- });
1122
-
1123
- return result;
1124
- } catch (error) {
1125
- // Emit batch:failed event
1126
- await this.eventEmitter.emit({
1127
- type: 'batch:failed',
1128
- timestamp: new Date().toISOString(),
1129
- batchId,
1130
- operation: 'transfer',
1131
- error: error instanceof Error ? error.message : String(error)
1132
- });
1133
-
1134
- throw error;
1135
- }
1136
- }
1137
-
1138
- /**
1139
- * Calculate total data size for all assets in a batch
1140
- */
1141
- private calculateTotalDataSize(assets: OriginalsAsset[]): number {
1142
- return assets.reduce((total, asset) => {
1143
- const manifest = {
1144
- assetId: asset.id,
1145
- resources: asset.resources.map(res => ({
1146
- id: res.id,
1147
- hash: res.hash,
1148
- contentType: res.contentType,
1149
- url: res.url
1150
- })),
1151
- timestamp: new Date().toISOString()
1152
- };
1153
- return total + JSON.stringify(manifest).length;
1154
- }, 0);
1155
- }
1156
-
1157
- /**
1158
- * Estimate cost savings from batch inscription vs individual inscriptions
1159
- */
1160
- private async estimateBatchSavings(
1161
- assets: OriginalsAsset[],
1162
- feeRate?: number
1163
- ): Promise<{
1164
- batchFee: number;
1165
- individualFees: number;
1166
- savings: number;
1167
- savingsPercentage: number;
1168
- }> {
1169
- // Calculate total size for batch
1170
- const batchSize = this.calculateTotalDataSize(assets);
1171
-
1172
- // Estimate individual sizes
1173
- const individualSizes = assets.map(asset =>
1174
- JSON.stringify({
1175
- assetId: asset.id,
1176
- resources: asset.resources.map(r => ({
1177
- id: r.id,
1178
- hash: r.hash,
1179
- contentType: r.contentType,
1180
- url: r.url
1181
- }))
1182
- }).length
1183
- );
1184
-
1185
- // Realistic fee estimation based on Bitcoin transaction structure
1186
- // Base transaction overhead: ~200 bytes (inputs, outputs, etc.)
1187
- // Per inscription witness overhead: ~120 bytes (script, envelope, etc.)
1188
- // In batch mode: shared transaction overhead + minimal per-asset overhead
1189
- const effectiveFeeRate = feeRate ?? 10; // default 10 sat/vB
1190
-
1191
- // Batch: one transaction overhead + batch data + minimal per-asset overhead
1192
- // The batch manifest is more efficient as it shares structure
1193
- const batchTxSize = 200 + batchSize + (assets.length * 5); // 5 bytes per asset for array/object overhead
1194
- const batchFee = batchTxSize * effectiveFeeRate;
1195
-
1196
- // Individual: each inscription needs full transaction overhead + witness overhead + data
1197
- const individualFees = individualSizes.reduce((total, size) => {
1198
- // Each individual inscription has:
1199
- // - Full transaction overhead: 200 bytes
1200
- // - Witness/inscription overhead: 122 bytes
1201
- // - Asset data: size bytes
1202
- const txSize = 200 + 122 + size;
1203
- return total + (txSize * effectiveFeeRate);
1204
- }, 0);
1205
-
1206
- const savings = individualFees - batchFee;
1207
- const savingsPercentage = (savings / individualFees) * 100;
1208
-
1209
- return {
1210
- batchFee,
1211
- individualFees,
1212
- savings,
1213
- savingsPercentage
1214
- };
1215
- }
1216
- }
1217
-
1218
-