@arkade-os/sdk 0.3.12 → 0.4.0-next.0

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 (250) hide show
  1. package/README.md +483 -54
  2. package/dist/cjs/adapters/expo-db.js +35 -0
  3. package/dist/cjs/asset/assetGroup.js +141 -0
  4. package/dist/cjs/asset/assetId.js +88 -0
  5. package/dist/cjs/asset/assetInput.js +204 -0
  6. package/dist/cjs/asset/assetOutput.js +159 -0
  7. package/dist/cjs/asset/assetRef.js +82 -0
  8. package/dist/cjs/asset/index.js +24 -0
  9. package/dist/cjs/asset/metadata.js +172 -0
  10. package/dist/cjs/asset/packet.js +164 -0
  11. package/dist/cjs/asset/types.js +25 -0
  12. package/dist/cjs/asset/utils.js +105 -0
  13. package/dist/cjs/contracts/arkcontract.js +148 -0
  14. package/dist/cjs/contracts/contractManager.js +436 -0
  15. package/dist/cjs/contracts/contractWatcher.js +567 -0
  16. package/dist/cjs/contracts/handlers/default.js +85 -0
  17. package/dist/cjs/contracts/handlers/delegate.js +89 -0
  18. package/dist/cjs/contracts/handlers/helpers.js +105 -0
  19. package/dist/cjs/contracts/handlers/index.js +19 -0
  20. package/dist/cjs/contracts/handlers/registry.js +89 -0
  21. package/dist/cjs/contracts/handlers/vhtlc.js +193 -0
  22. package/dist/cjs/contracts/index.js +41 -0
  23. package/dist/cjs/contracts/types.js +2 -0
  24. package/dist/cjs/db/manager.js +97 -0
  25. package/dist/cjs/forfeit.js +12 -8
  26. package/dist/cjs/identity/index.js +1 -0
  27. package/dist/cjs/identity/seedIdentity.js +255 -0
  28. package/dist/cjs/index.js +70 -14
  29. package/dist/cjs/intent/index.js +28 -2
  30. package/dist/cjs/providers/ark.js +7 -0
  31. package/dist/cjs/providers/delegator.js +66 -0
  32. package/dist/cjs/providers/expoIndexer.js +5 -0
  33. package/dist/cjs/providers/indexer.js +68 -1
  34. package/dist/cjs/providers/onchain.js +2 -2
  35. package/dist/cjs/providers/utils.js +1 -0
  36. package/dist/cjs/repositories/contractRepository.js +0 -103
  37. package/dist/cjs/repositories/inMemory/contractRepository.js +55 -0
  38. package/dist/cjs/repositories/inMemory/walletRepository.js +80 -0
  39. package/dist/cjs/repositories/index.js +16 -0
  40. package/dist/cjs/repositories/indexedDB/contractRepository.js +187 -0
  41. package/dist/cjs/repositories/indexedDB/db.js +57 -0
  42. package/dist/cjs/repositories/indexedDB/schema.js +159 -0
  43. package/dist/cjs/repositories/indexedDB/walletRepository.js +338 -0
  44. package/dist/cjs/repositories/indexedDB/websqlAdapter.js +144 -0
  45. package/dist/cjs/repositories/migrations/contractRepositoryImpl.js +127 -0
  46. package/dist/cjs/repositories/migrations/fromStorageAdapter.js +66 -0
  47. package/dist/cjs/repositories/migrations/walletRepositoryImpl.js +180 -0
  48. package/dist/cjs/repositories/walletRepository.js +0 -169
  49. package/dist/cjs/script/base.js +54 -0
  50. package/dist/cjs/script/delegate.js +49 -0
  51. package/dist/cjs/storage/asyncStorage.js +4 -1
  52. package/dist/cjs/storage/fileSystem.js +3 -0
  53. package/dist/cjs/storage/inMemory.js +3 -0
  54. package/dist/cjs/storage/indexedDB.js +5 -1
  55. package/dist/cjs/storage/localStorage.js +3 -0
  56. package/dist/cjs/utils/arkTransaction.js +16 -0
  57. package/dist/cjs/utils/transactionHistory.js +50 -0
  58. package/dist/cjs/utils/txSizeEstimator.js +39 -14
  59. package/dist/cjs/wallet/asset-manager.js +338 -0
  60. package/dist/cjs/wallet/asset.js +117 -0
  61. package/dist/cjs/wallet/batch.js +1 -1
  62. package/dist/cjs/wallet/delegator.js +235 -0
  63. package/dist/cjs/wallet/expo/background.js +133 -0
  64. package/dist/cjs/wallet/expo/index.js +9 -0
  65. package/dist/cjs/wallet/expo/wallet.js +231 -0
  66. package/dist/cjs/wallet/onchain.js +57 -12
  67. package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +568 -0
  68. package/dist/cjs/wallet/serviceWorker/wallet.js +383 -102
  69. package/dist/cjs/wallet/unroll.js +7 -2
  70. package/dist/cjs/wallet/utils.js +60 -0
  71. package/dist/cjs/wallet/validation.js +151 -0
  72. package/dist/cjs/wallet/vtxo-manager.js +1 -1
  73. package/dist/cjs/wallet/wallet.js +702 -260
  74. package/dist/cjs/worker/browser/service-worker-manager.js +82 -0
  75. package/dist/cjs/{wallet/serviceWorker → worker/browser}/utils.js +2 -1
  76. package/dist/cjs/worker/expo/asyncStorageTaskQueue.js +78 -0
  77. package/dist/cjs/worker/expo/index.js +12 -0
  78. package/dist/cjs/worker/expo/processors/contractPollProcessor.js +61 -0
  79. package/dist/cjs/worker/expo/processors/index.js +6 -0
  80. package/dist/cjs/worker/expo/taskQueue.js +41 -0
  81. package/dist/cjs/worker/expo/taskRunner.js +57 -0
  82. package/dist/cjs/worker/messageBus.js +252 -0
  83. package/dist/esm/adapters/expo-db.js +27 -0
  84. package/dist/esm/asset/assetGroup.js +137 -0
  85. package/dist/esm/asset/assetId.js +84 -0
  86. package/dist/esm/asset/assetInput.js +199 -0
  87. package/dist/esm/asset/assetOutput.js +154 -0
  88. package/dist/esm/asset/assetRef.js +78 -0
  89. package/dist/esm/asset/index.js +8 -0
  90. package/dist/esm/asset/metadata.js +167 -0
  91. package/dist/esm/asset/packet.js +159 -0
  92. package/dist/esm/asset/types.js +22 -0
  93. package/dist/esm/asset/utils.js +99 -0
  94. package/dist/esm/contracts/arkcontract.js +141 -0
  95. package/dist/esm/contracts/contractManager.js +432 -0
  96. package/dist/esm/contracts/contractWatcher.js +563 -0
  97. package/dist/esm/contracts/handlers/default.js +82 -0
  98. package/dist/esm/contracts/handlers/delegate.js +86 -0
  99. package/dist/esm/contracts/handlers/helpers.js +66 -0
  100. package/dist/esm/contracts/handlers/index.js +12 -0
  101. package/dist/esm/contracts/handlers/registry.js +86 -0
  102. package/dist/esm/contracts/handlers/vhtlc.js +190 -0
  103. package/dist/esm/contracts/index.js +13 -0
  104. package/dist/esm/contracts/types.js +1 -0
  105. package/dist/esm/db/manager.js +92 -0
  106. package/dist/esm/forfeit.js +11 -8
  107. package/dist/esm/identity/index.js +1 -0
  108. package/dist/esm/identity/seedIdentity.js +249 -0
  109. package/dist/esm/index.js +25 -15
  110. package/dist/esm/intent/index.js +28 -2
  111. package/dist/esm/providers/ark.js +7 -0
  112. package/dist/esm/providers/delegator.js +62 -0
  113. package/dist/esm/providers/expoIndexer.js +5 -0
  114. package/dist/esm/providers/indexer.js +68 -1
  115. package/dist/esm/providers/onchain.js +2 -2
  116. package/dist/esm/providers/utils.js +1 -0
  117. package/dist/esm/repositories/contractRepository.js +1 -101
  118. package/dist/esm/repositories/inMemory/contractRepository.js +51 -0
  119. package/dist/esm/repositories/inMemory/walletRepository.js +76 -0
  120. package/dist/esm/repositories/index.js +8 -0
  121. package/dist/esm/repositories/indexedDB/contractRepository.js +183 -0
  122. package/dist/esm/repositories/indexedDB/db.js +42 -0
  123. package/dist/esm/repositories/indexedDB/schema.js +155 -0
  124. package/dist/esm/repositories/indexedDB/walletRepository.js +334 -0
  125. package/dist/esm/repositories/indexedDB/websqlAdapter.js +138 -0
  126. package/dist/esm/repositories/migrations/contractRepositoryImpl.js +121 -0
  127. package/dist/esm/repositories/migrations/fromStorageAdapter.js +58 -0
  128. package/dist/esm/repositories/migrations/walletRepositoryImpl.js +176 -0
  129. package/dist/esm/repositories/walletRepository.js +1 -167
  130. package/dist/esm/script/base.js +21 -1
  131. package/dist/esm/script/delegate.js +46 -0
  132. package/dist/esm/storage/asyncStorage.js +4 -1
  133. package/dist/esm/storage/fileSystem.js +3 -0
  134. package/dist/esm/storage/inMemory.js +3 -0
  135. package/dist/esm/storage/indexedDB.js +5 -1
  136. package/dist/esm/storage/localStorage.js +3 -0
  137. package/dist/esm/utils/arkTransaction.js +15 -0
  138. package/dist/esm/utils/transactionHistory.js +50 -0
  139. package/dist/esm/utils/txSizeEstimator.js +39 -14
  140. package/dist/esm/wallet/asset-manager.js +333 -0
  141. package/dist/esm/wallet/asset.js +111 -0
  142. package/dist/esm/wallet/batch.js +1 -1
  143. package/dist/esm/wallet/delegator.js +231 -0
  144. package/dist/esm/wallet/expo/background.js +128 -0
  145. package/dist/esm/wallet/expo/index.js +2 -0
  146. package/dist/esm/wallet/expo/wallet.js +194 -0
  147. package/dist/esm/wallet/onchain.js +57 -12
  148. package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +564 -0
  149. package/dist/esm/wallet/serviceWorker/wallet.js +382 -101
  150. package/dist/esm/wallet/unroll.js +7 -2
  151. package/dist/esm/wallet/utils.js +55 -0
  152. package/dist/esm/wallet/validation.js +139 -0
  153. package/dist/esm/wallet/vtxo-manager.js +1 -1
  154. package/dist/esm/wallet/wallet.js +704 -229
  155. package/dist/esm/worker/browser/service-worker-manager.js +76 -0
  156. package/dist/esm/{wallet/serviceWorker → worker/browser}/utils.js +2 -1
  157. package/dist/esm/worker/expo/asyncStorageTaskQueue.js +74 -0
  158. package/dist/esm/worker/expo/index.js +4 -0
  159. package/dist/esm/worker/expo/processors/contractPollProcessor.js +58 -0
  160. package/dist/esm/worker/expo/processors/index.js +1 -0
  161. package/dist/esm/worker/expo/taskQueue.js +37 -0
  162. package/dist/esm/worker/expo/taskRunner.js +54 -0
  163. package/dist/esm/worker/messageBus.js +248 -0
  164. package/dist/types/adapters/expo-db.d.ts +7 -0
  165. package/dist/types/asset/assetGroup.d.ts +28 -0
  166. package/dist/types/asset/assetId.d.ts +19 -0
  167. package/dist/types/asset/assetInput.d.ts +46 -0
  168. package/dist/types/asset/assetOutput.d.ts +39 -0
  169. package/dist/types/asset/assetRef.d.ts +25 -0
  170. package/dist/types/asset/index.d.ts +8 -0
  171. package/dist/types/asset/metadata.d.ts +37 -0
  172. package/dist/types/asset/packet.d.ts +27 -0
  173. package/dist/types/asset/types.d.ts +18 -0
  174. package/dist/types/asset/utils.d.ts +21 -0
  175. package/dist/types/contracts/arkcontract.d.ts +101 -0
  176. package/dist/types/contracts/contractManager.d.ts +331 -0
  177. package/dist/types/contracts/contractWatcher.d.ts +192 -0
  178. package/dist/types/contracts/handlers/default.d.ts +19 -0
  179. package/dist/types/contracts/handlers/delegate.d.ts +21 -0
  180. package/dist/types/contracts/handlers/helpers.d.ts +18 -0
  181. package/dist/types/contracts/handlers/index.d.ts +7 -0
  182. package/dist/types/contracts/handlers/registry.d.ts +65 -0
  183. package/dist/types/contracts/handlers/vhtlc.d.ts +32 -0
  184. package/dist/types/contracts/index.d.ts +14 -0
  185. package/dist/types/contracts/types.d.ts +222 -0
  186. package/dist/types/db/manager.d.ts +22 -0
  187. package/dist/types/forfeit.d.ts +2 -1
  188. package/dist/types/identity/index.d.ts +1 -0
  189. package/dist/types/identity/seedIdentity.d.ts +128 -0
  190. package/dist/types/index.d.ts +21 -12
  191. package/dist/types/intent/index.d.ts +2 -1
  192. package/dist/types/providers/ark.d.ts +11 -2
  193. package/dist/types/providers/delegator.d.ts +29 -0
  194. package/dist/types/providers/indexer.d.ts +11 -1
  195. package/dist/types/repositories/contractRepository.d.ts +30 -19
  196. package/dist/types/repositories/inMemory/contractRepository.d.ts +17 -0
  197. package/dist/types/repositories/inMemory/walletRepository.d.ts +26 -0
  198. package/dist/types/repositories/index.d.ts +7 -0
  199. package/dist/types/repositories/indexedDB/contractRepository.d.ts +21 -0
  200. package/dist/types/repositories/indexedDB/db.d.ts +56 -0
  201. package/dist/types/repositories/indexedDB/schema.d.ts +8 -0
  202. package/dist/types/repositories/indexedDB/walletRepository.d.ts +25 -0
  203. package/dist/types/repositories/indexedDB/websqlAdapter.d.ts +49 -0
  204. package/dist/types/repositories/migrations/contractRepositoryImpl.d.ts +24 -0
  205. package/dist/types/repositories/migrations/fromStorageAdapter.d.ts +19 -0
  206. package/dist/types/repositories/migrations/walletRepositoryImpl.d.ts +27 -0
  207. package/dist/types/repositories/walletRepository.d.ts +13 -24
  208. package/dist/types/script/base.d.ts +1 -0
  209. package/dist/types/script/delegate.d.ts +36 -0
  210. package/dist/types/storage/asyncStorage.d.ts +4 -0
  211. package/dist/types/storage/fileSystem.d.ts +3 -0
  212. package/dist/types/storage/inMemory.d.ts +3 -0
  213. package/dist/types/storage/index.d.ts +3 -0
  214. package/dist/types/storage/indexedDB.d.ts +3 -0
  215. package/dist/types/storage/localStorage.d.ts +3 -0
  216. package/dist/types/utils/arkTransaction.d.ts +6 -0
  217. package/dist/types/utils/txSizeEstimator.d.ts +12 -2
  218. package/dist/types/wallet/asset-manager.d.ts +78 -0
  219. package/dist/types/wallet/asset.d.ts +21 -0
  220. package/dist/types/wallet/batch.d.ts +1 -1
  221. package/dist/types/wallet/delegator.d.ts +24 -0
  222. package/dist/types/wallet/expo/background.d.ts +66 -0
  223. package/dist/types/wallet/expo/index.d.ts +4 -0
  224. package/dist/types/wallet/expo/wallet.d.ts +97 -0
  225. package/dist/types/wallet/index.d.ts +75 -2
  226. package/dist/types/wallet/onchain.d.ts +22 -1
  227. package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +366 -0
  228. package/dist/types/wallet/serviceWorker/wallet.d.ts +20 -11
  229. package/dist/types/wallet/utils.d.ts +13 -1
  230. package/dist/types/wallet/validation.d.ts +24 -0
  231. package/dist/types/wallet/wallet.d.ts +111 -17
  232. package/dist/types/worker/browser/service-worker-manager.d.ts +21 -0
  233. package/dist/types/{wallet/serviceWorker → worker/browser}/utils.d.ts +2 -1
  234. package/dist/types/worker/expo/asyncStorageTaskQueue.d.ts +46 -0
  235. package/dist/types/worker/expo/index.d.ts +7 -0
  236. package/dist/types/worker/expo/processors/contractPollProcessor.d.ts +14 -0
  237. package/dist/types/worker/expo/processors/index.d.ts +1 -0
  238. package/dist/types/worker/expo/taskQueue.d.ts +50 -0
  239. package/dist/types/worker/expo/taskRunner.d.ts +42 -0
  240. package/dist/types/worker/messageBus.d.ts +109 -0
  241. package/package.json +71 -17
  242. package/dist/cjs/wallet/serviceWorker/request.js +0 -78
  243. package/dist/cjs/wallet/serviceWorker/response.js +0 -222
  244. package/dist/cjs/wallet/serviceWorker/worker.js +0 -655
  245. package/dist/esm/wallet/serviceWorker/request.js +0 -75
  246. package/dist/esm/wallet/serviceWorker/response.js +0 -219
  247. package/dist/esm/wallet/serviceWorker/worker.js +0 -651
  248. package/dist/types/wallet/serviceWorker/request.d.ts +0 -74
  249. package/dist/types/wallet/serviceWorker/response.d.ts +0 -123
  250. package/dist/types/wallet/serviceWorker/worker.d.ts +0 -53
@@ -1,3 +1,20 @@
1
+ import { Address, OutScript } from "@scure/btc-signer";
2
+ /**
3
+ * Calculates the byte size required to store a variable-length integer (VarInt).
4
+ * Bitcoin uses VarInts to compact integer data (like array lengths).
5
+ *
6
+ * @param n - The integer value to check
7
+ * @returns The size in bytes (1, 3, 5, or 9)
8
+ */
9
+ const getVarIntSize = (n) => {
10
+ if (n < 0xfd)
11
+ return 1;
12
+ if (n <= 0xffff)
13
+ return 3;
14
+ if (n <= 0xffffffff)
15
+ return 5;
16
+ return 9;
17
+ };
1
18
  export class TxWeightEstimator {
2
19
  constructor(hasWitness, inputCount, outputCount, inputSize, inputWitnessSize, outputSize) {
3
20
  this.hasWitness = hasWitness;
@@ -38,16 +55,16 @@ export class TxWeightEstimator {
38
55
  1 +
39
56
  leafControlBlockSize;
40
57
  this.inputCount++;
41
- this.inputWitnessSize += leafWitnessSize + controlBlockWitnessSize;
58
+ this.inputWitnessSize += leafWitnessSize + 1 + controlBlockWitnessSize;
42
59
  this.inputSize += TxWeightEstimator.INPUT_SIZE;
43
60
  this.hasWitness = true;
44
- this.inputCount++;
45
61
  return this;
46
62
  }
47
- addP2WKHOutput() {
63
+ addP2WPKHOutput() {
48
64
  this.outputCount++;
49
65
  this.outputSize +=
50
- TxWeightEstimator.OUTPUT_SIZE + TxWeightEstimator.P2WKH_OUTPUT_SIZE;
66
+ TxWeightEstimator.OUTPUT_SIZE +
67
+ TxWeightEstimator.P2WPKH_OUTPUT_SIZE;
51
68
  return this;
52
69
  }
53
70
  addP2TROutput() {
@@ -56,16 +73,24 @@ export class TxWeightEstimator {
56
73
  TxWeightEstimator.OUTPUT_SIZE + TxWeightEstimator.P2TR_OUTPUT_SIZE;
57
74
  return this;
58
75
  }
76
+ /**
77
+ * Adds an output given a raw script.
78
+ * Cost = 8 bytes (amount) + varint(scriptLen) + scriptLen
79
+ */
80
+ addOutputScript(script) {
81
+ this.outputCount++;
82
+ this.outputSize += 8 + getVarIntSize(script.length) + script.length;
83
+ return this;
84
+ }
85
+ /**
86
+ * Adds an output by decoding the address to get the exact script size.
87
+ */
88
+ addOutputAddress(address, network) {
89
+ const payment = Address(network).decode(address);
90
+ const script = OutScript.encode(payment);
91
+ return this.addOutputScript(script);
92
+ }
59
93
  vsize() {
60
- const getVarIntSize = (n) => {
61
- if (n < 0xfd)
62
- return 1;
63
- if (n < 0xffff)
64
- return 3;
65
- if (n < 0xffffffff)
66
- return 5;
67
- return 9;
68
- };
69
94
  const inputCount = getVarIntSize(this.inputCount);
70
95
  const outputCount = getVarIntSize(this.outputCount);
71
96
  // Calculate the size of the transaction without witness data
@@ -89,7 +114,7 @@ TxWeightEstimator.P2PKH_SCRIPT_SIG_SIZE = 1 + 73 + 1 + 33;
89
114
  TxWeightEstimator.INPUT_SIZE = 32 + 4 + 1 + 4;
90
115
  TxWeightEstimator.BASE_CONTROL_BLOCK_SIZE = 1 + 32;
91
116
  TxWeightEstimator.OUTPUT_SIZE = 8 + 1;
92
- TxWeightEstimator.P2WKH_OUTPUT_SIZE = 1 + 1 + 20;
117
+ TxWeightEstimator.P2WPKH_OUTPUT_SIZE = 1 + 1 + 20;
93
118
  TxWeightEstimator.BASE_TX_SIZE = 8 + 2; // Version + LockTime
94
119
  TxWeightEstimator.WITNESS_HEADER_SIZE = 2; // Flag + Marker
95
120
  TxWeightEstimator.WITNESS_SCALE_FACTOR = 4;
@@ -0,0 +1,333 @@
1
+ import { AssetGroup, AssetId, AssetInput, AssetOutput, AssetRef, Metadata, Packet, } from '../asset/index.js';
2
+ import { ArkAddress } from '../script/address.js';
3
+ import { selectedCoinsToAssetInputs, selectCoinsWithAsset } from './asset.js';
4
+ import { selectVirtualCoins } from './wallet.js';
5
+ export class ReadonlyAssetManager {
6
+ constructor(indexer) {
7
+ this.indexer = indexer;
8
+ }
9
+ async getAssetDetails(assetId) {
10
+ // TODO: rely on db, and fallback to indexer if not found
11
+ return this.indexer.getAssetDetails(assetId);
12
+ }
13
+ }
14
+ export class AssetManager extends ReadonlyAssetManager {
15
+ constructor(wallet) {
16
+ super(wallet.indexerProvider);
17
+ this.wallet = wallet;
18
+ }
19
+ /**
20
+ * Issue a new asset.
21
+ * @param params - Parameters for asset issuance
22
+ * @param params.amount - Amount of asset units to issue
23
+ * @param params.controlAsset - Optional control asset (for reissuable assets)
24
+ * @param params.metadata - Optional metadata to attach to the asset
25
+ * @returns Promise resolving to the ark transaction ID and asset ID
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * // Issue a simple non-reissuable asset
30
+ * const result = await wallet.issueAsset({ amount: 1000 });
31
+ * console.log('Asset ID:', result.assetId);
32
+ *
33
+ * // Issue a reissuable asset with a new control asset
34
+ * const result = await wallet.issueAsset({
35
+ * amount: 1000,
36
+ * controlAsset: 1 // creates new control asset with amount 1
37
+ * });
38
+ * console.log('Control Asset ID:', result.controlAssetId);
39
+ * console.log('Asset ID:', result.assetId);
40
+ *
41
+ * // Issue a reissuable asset with an existing control asset
42
+ * const result = await wallet.issueAsset({
43
+ * amount: 1000,
44
+ * controlAsset: 'controlAssetId'
45
+ * });
46
+ * console.log('Control Asset ID:', result.controlAssetId);
47
+ * console.log('Asset ID:', result.assetId);
48
+ * ```
49
+ */
50
+ async issue(params) {
51
+ if (params.amount <= 0) {
52
+ throw new Error(`Issue amount must be greater than 0, got ${params.amount}`);
53
+ }
54
+ const metadata = castMetadata(params.metadata);
55
+ const virtualCoins = await this.wallet.getVtxos({
56
+ withRecoverable: false,
57
+ });
58
+ const controlAssetRef = params.controlAssetId
59
+ ? AssetRef.fromId(AssetId.fromString(params.controlAssetId))
60
+ : null;
61
+ const coinSelection = selectVirtualCoins(virtualCoins, Number(this.wallet.dustAmount));
62
+ let totalBtcSelected = 0n;
63
+ // keep track of asset changes
64
+ const assetChanges = new Map();
65
+ for (const coin of coinSelection.inputs) {
66
+ totalBtcSelected += BigInt(coin.value);
67
+ if (!coin.assets)
68
+ continue;
69
+ for (const { assetId, amount } of coin.assets) {
70
+ const existing = assetChanges.get(assetId) ?? 0n;
71
+ assetChanges.set(assetId, existing + BigInt(amount));
72
+ }
73
+ }
74
+ const groups = [];
75
+ // issued asset group
76
+ const issuedAssetOutput = AssetOutput.create(0, BigInt(params.amount));
77
+ const issuedAssetGroup = AssetGroup.create(null, controlAssetRef, [], [issuedAssetOutput], metadata);
78
+ groups.push(issuedAssetGroup);
79
+ // add asset groups for each asset change
80
+ if (assetChanges.size > 0) {
81
+ const assetInputs = selectedCoinsToAssetInputs(coinSelection.inputs);
82
+ for (const [assetId, amount] of assetChanges) {
83
+ const changeInputs = [];
84
+ // collect all inputs for the asset change
85
+ for (const [inputIndex, assets] of assetInputs) {
86
+ for (const asset of assets) {
87
+ if (asset.assetId !== assetId)
88
+ continue;
89
+ changeInputs.push(AssetInput.create(inputIndex, BigInt(asset.amount)));
90
+ }
91
+ }
92
+ // add the change asset group
93
+ groups.push(AssetGroup.create(AssetId.fromString(assetId), null, changeInputs, [AssetOutput.create(0, amount)], []));
94
+ }
95
+ }
96
+ // build transaction outputs
97
+ const address = await this.wallet.getAddress();
98
+ const outputAddress = ArkAddress.decode(address);
99
+ const outputs = [
100
+ {
101
+ script: outputAddress.pkScript,
102
+ amount: BigInt(totalBtcSelected),
103
+ },
104
+ Packet.create(groups).txOut(),
105
+ ];
106
+ const { arkTxid } = await this.wallet.buildAndSubmitOffchainTx(coinSelection.inputs, outputs);
107
+ return {
108
+ arkTxId: arkTxid,
109
+ assetId: AssetId.create(arkTxid, 0).toString(),
110
+ };
111
+ }
112
+ /**
113
+ * Reissue more units of an existing asset.
114
+ * Requires ownership of the control asset.
115
+ *
116
+ * @param params - Parameters for asset reissuance
117
+ * @param params.assetId - The asset ID to reissue (control asset ID is resolved via getAssetDetails)
118
+ * @param params.amount - Amount of additional units to issue
119
+ * @returns Promise resolving to the ark transaction ID
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * const txid = await wallet.assetManager.reissue({
124
+ * assetId: 'def456...',
125
+ * amount: 500
126
+ * });
127
+ * ```
128
+ */
129
+ async reissue(params) {
130
+ if (params.amount <= 0) {
131
+ throw new Error(`Reissuance amount must be greater than 0, got ${params.amount}`);
132
+ }
133
+ const { controlAssetId } = await this.getAssetDetails(params.assetId);
134
+ if (!controlAssetId) {
135
+ throw new Error(`Asset ${params.assetId} is not reissuable`);
136
+ }
137
+ const virtualCoins = await this.wallet.getVtxos({
138
+ withRecoverable: false,
139
+ });
140
+ const assetChanges = new Map();
141
+ // select control asset inputs
142
+ const { selected: controlCoins } = selectCoinsWithAsset(virtualCoins, controlAssetId, 1n);
143
+ let selectedCoins = [...controlCoins];
144
+ let assetToReissueAmount = 0n;
145
+ // all control inputs assets go to asset changes, including the control asset itself
146
+ for (const coin of controlCoins) {
147
+ if (!coin.assets)
148
+ continue;
149
+ for (const { assetId, amount } of coin.assets) {
150
+ if (assetId === params.assetId) {
151
+ assetToReissueAmount += BigInt(amount);
152
+ continue;
153
+ }
154
+ const existing = assetChanges.get(assetId) ?? 0n;
155
+ assetChanges.set(assetId, existing + BigInt(amount));
156
+ }
157
+ }
158
+ // select at least dust amount
159
+ const minBtcNeeded = Number(this.wallet.dustAmount);
160
+ let totalBtcSelected = selectedCoins.reduce((sum, c) => sum + c.value, 0);
161
+ if (totalBtcSelected < minBtcNeeded) {
162
+ const remainingCoins = virtualCoins.filter((c) => !selectedCoins.find((sc) => sc.txid === c.txid && sc.vout === c.vout));
163
+ const additional = selectVirtualCoins(remainingCoins, minBtcNeeded - totalBtcSelected);
164
+ for (const coin of additional.inputs) {
165
+ // additional inputs assets go to asset changes
166
+ if (!coin.assets)
167
+ continue;
168
+ for (const { assetId, amount } of coin.assets) {
169
+ if (assetId === params.assetId) {
170
+ assetToReissueAmount += BigInt(amount);
171
+ continue;
172
+ }
173
+ const existing = assetChanges.get(assetId) ?? 0n;
174
+ assetChanges.set(assetId, existing + BigInt(amount));
175
+ }
176
+ }
177
+ selectedCoins = [...selectedCoins, ...additional.inputs];
178
+ totalBtcSelected += additional.inputs.reduce((sum, c) => sum + c.value, 0);
179
+ }
180
+ const assetInputs = selectedCoinsToAssetInputs(selectedCoins);
181
+ // collect all inputs for the asset to reissue
182
+ const reissueInputs = [];
183
+ for (const [inputIndex, assets] of assetInputs) {
184
+ for (const asset of assets) {
185
+ if (asset.assetId !== params.assetId)
186
+ continue;
187
+ reissueInputs.push(AssetInput.create(inputIndex, BigInt(asset.amount)));
188
+ }
189
+ }
190
+ // the total output amount of the asset to reissue = new + (optional) selected amount
191
+ const totalAssetAmount = assetToReissueAmount + BigInt(params.amount);
192
+ const reissueAssetIdObj = AssetId.fromString(params.assetId);
193
+ // create the reissuance asset group
194
+ const reissueAssetGroup = AssetGroup.create(reissueAssetIdObj, null, reissueInputs, [AssetOutput.create(0, totalAssetAmount)], []);
195
+ const groups = [reissueAssetGroup];
196
+ // for each asset change, create a new asset group
197
+ for (const [assetId, amount] of assetChanges) {
198
+ const changeInputs = [];
199
+ for (const [inputIndex, assets] of assetInputs) {
200
+ for (const asset of assets) {
201
+ if (asset.assetId !== assetId)
202
+ continue;
203
+ changeInputs.push(AssetInput.create(inputIndex, BigInt(asset.amount)));
204
+ }
205
+ }
206
+ groups.push(AssetGroup.create(AssetId.fromString(assetId), null, changeInputs, [AssetOutput.create(0, amount)], []));
207
+ }
208
+ // build transaction outputs
209
+ const address = await this.wallet.getAddress();
210
+ const outputAddress = ArkAddress.decode(address);
211
+ const outputs = [
212
+ {
213
+ script: outputAddress.pkScript,
214
+ amount: BigInt(totalBtcSelected),
215
+ },
216
+ Packet.create(groups).txOut(),
217
+ ];
218
+ const { arkTxid } = await this.wallet.buildAndSubmitOffchainTx(selectedCoins, outputs);
219
+ return arkTxid;
220
+ }
221
+ /**
222
+ * Burn assets.
223
+ * @param params - Parameters for burning
224
+ * @param params.assetId - The asset ID to burn
225
+ * @param params.amount - Amount of units to burn
226
+ * @returns Promise resolving to the ark transaction ID
227
+ *
228
+ * @example
229
+ * ```typescript
230
+ * const txid = await wallet.assetManager.burn({
231
+ * assetId: 'abc123...',
232
+ * amount: 100
233
+ * });
234
+ * ```
235
+ */
236
+ async burn(params) {
237
+ if (params.amount <= 0) {
238
+ throw new Error(`Burn amount must be greater than 0, got ${params.amount}`);
239
+ }
240
+ const virtualCoins = await this.wallet.getVtxos({
241
+ withRecoverable: false,
242
+ });
243
+ const assetChanges = new Map();
244
+ // select vtxos with the asset to burn
245
+ const { selected: assetCoins } = selectCoinsWithAsset(virtualCoins, params.assetId, BigInt(params.amount));
246
+ const selectedCoins = [...assetCoins];
247
+ let totalBtcSelected = 0;
248
+ // add the selected coins to asset changes, including the asset to burn
249
+ for (const coin of assetCoins) {
250
+ totalBtcSelected += coin.value;
251
+ if (!coin.assets)
252
+ continue;
253
+ for (const { assetId, amount } of coin.assets) {
254
+ const existing = assetChanges.get(assetId) ?? 0n;
255
+ assetChanges.set(assetId, existing + BigInt(amount));
256
+ }
257
+ }
258
+ // subtract the amount to burn from the asset change
259
+ assetChanges.set(params.assetId, (assetChanges.get(params.assetId) ?? 0n) - BigInt(params.amount));
260
+ const minBtcNeeded = Number(this.wallet.dustAmount);
261
+ // we need to ensure at least dust amount is selected
262
+ // if not, select additional coins
263
+ if (totalBtcSelected < minBtcNeeded) {
264
+ const remainingCoins = virtualCoins.filter((c) => !selectedCoins.find((sc) => sc.txid === c.txid && sc.vout === c.vout));
265
+ const additional = selectVirtualCoins(remainingCoins, minBtcNeeded - totalBtcSelected);
266
+ // additional inputs assets go to asset changes
267
+ for (const coin of additional.inputs) {
268
+ totalBtcSelected += coin.value;
269
+ if (!coin.assets)
270
+ continue;
271
+ for (const { assetId, amount } of coin.assets) {
272
+ const existing = assetChanges.get(assetId) ?? 0n;
273
+ assetChanges.set(assetId, existing + BigInt(amount));
274
+ }
275
+ }
276
+ selectedCoins.push(...additional.inputs);
277
+ }
278
+ const groups = [];
279
+ const assetInputs = selectedCoinsToAssetInputs(selectedCoins);
280
+ // for each asset, create a new asset group
281
+ for (const [assetId, amount] of assetChanges) {
282
+ const changeInputs = [];
283
+ for (const [inputIndex, assets] of assetInputs) {
284
+ for (const asset of assets) {
285
+ if (asset.assetId !== assetId)
286
+ continue;
287
+ changeInputs.push(AssetInput.create(inputIndex, BigInt(asset.amount)));
288
+ }
289
+ }
290
+ groups.push(AssetGroup.create(AssetId.fromString(assetId), null, changeInputs, amount > 0n ? [AssetOutput.create(0, amount)] : [], []));
291
+ }
292
+ // build transaction outputs
293
+ const address = await this.wallet.getAddress();
294
+ const outputAddress = ArkAddress.decode(address);
295
+ const outputs = [
296
+ {
297
+ script: outputAddress.pkScript,
298
+ amount: BigInt(totalBtcSelected),
299
+ },
300
+ Packet.create(groups).txOut(),
301
+ ];
302
+ const { arkTxid } = await this.wallet.buildAndSubmitOffchainTx(selectedCoins, outputs);
303
+ return arkTxid;
304
+ }
305
+ }
306
+ function castMetadata(metadata) {
307
+ if (!metadata) {
308
+ return [];
309
+ }
310
+ const md = [];
311
+ const textEncoder = new TextEncoder();
312
+ for (const [key, value] of Object.entries(metadata)) {
313
+ // convert value to bytes
314
+ let valueBytes;
315
+ if (typeof value === "string") {
316
+ valueBytes = textEncoder.encode(value);
317
+ }
318
+ else if (typeof value === "number") {
319
+ valueBytes = textEncoder.encode(String(value));
320
+ }
321
+ else if (value instanceof Uint8Array) {
322
+ valueBytes = value;
323
+ }
324
+ else if (value instanceof ArrayBuffer) {
325
+ valueBytes = new Uint8Array(value);
326
+ }
327
+ else {
328
+ throw new Error("Invalid metadata value type");
329
+ }
330
+ md.push(Metadata.create(textEncoder.encode(key), valueBytes));
331
+ }
332
+ return md;
333
+ }
@@ -0,0 +1,111 @@
1
+ import { AssetGroup, AssetId, AssetInput, AssetOutput, Packet, } from '../asset/index.js';
2
+ /**
3
+ * Creates an asset packet from asset inputs and receivers.
4
+ * Groups inputs and outputs by asset ID and creates the Packet object
5
+ * @param assetInputs - map input index -> assets
6
+ * @param receivers - array of recipients with their asset allocations
7
+ * @param changeReceiver - (optional) change receiver containing remaining assets
8
+ * @returns packet containing all asset groups
9
+ */
10
+ export function createAssetPacket(assetInputs, receivers, changeReceiver) {
11
+ // map inputs by asset id
12
+ const inputsByAssetId = new Map();
13
+ for (const [inputIndex, assets] of assetInputs) {
14
+ for (const asset of assets) {
15
+ const existing = inputsByAssetId.get(asset.assetId);
16
+ inputsByAssetId.set(asset.assetId, [
17
+ ...(existing ?? []),
18
+ AssetInput.create(inputIndex, BigInt(asset.amount)),
19
+ ]);
20
+ }
21
+ }
22
+ // map outputs by asset id
23
+ const outputsByAssetId = new Map();
24
+ // track tx output index
25
+ let outputIndex = 0;
26
+ for (const receiver of receivers) {
27
+ if (receiver.assets) {
28
+ for (const asset of receiver.assets) {
29
+ const existing = outputsByAssetId.get(asset.assetId);
30
+ outputsByAssetId.set(asset.assetId, [
31
+ ...(existing ?? []),
32
+ AssetOutput.create(outputIndex, BigInt(asset.amount)),
33
+ ]);
34
+ }
35
+ }
36
+ outputIndex++;
37
+ }
38
+ // add change receiver assets if present
39
+ if (changeReceiver?.assets) {
40
+ for (const asset of changeReceiver.assets) {
41
+ const existing = outputsByAssetId.get(asset.assetId);
42
+ outputsByAssetId.set(asset.assetId, [
43
+ ...(existing ?? []),
44
+ AssetOutput.create(outputIndex, BigInt(asset.amount)),
45
+ ]);
46
+ }
47
+ }
48
+ const groups = [];
49
+ // get all unique asset ids from both inputs and outputs
50
+ const allAssetIds = new Set([
51
+ ...inputsByAssetId.keys(),
52
+ ...outputsByAssetId.keys(),
53
+ ]);
54
+ for (const assetIdStr of allAssetIds) {
55
+ const inputs = inputsByAssetId.get(assetIdStr);
56
+ const outputs = outputsByAssetId.get(assetIdStr);
57
+ const assetId = AssetId.fromString(assetIdStr);
58
+ const group = AssetGroup.create(assetId, null, inputs ?? [], outputs ?? [], []);
59
+ groups.push(group);
60
+ }
61
+ return Packet.create(groups);
62
+ }
63
+ /**
64
+ * Selects coins that contain a specific asset.
65
+ * Returns coins sorted by amount (smallest first for better coin selection).
66
+ */
67
+ export function selectCoinsWithAsset(coins, assetId, requiredAmount) {
68
+ // filter only coins that have the specified asset
69
+ const coinsWithAsset = coins.filter((coin) => coin.assets?.some((a) => a.assetId === assetId));
70
+ // sort by asset amount (smallest first for better selection)
71
+ coinsWithAsset.sort((a, b) => {
72
+ const amountA = a.assets?.find((asset) => asset.assetId === assetId)?.amount ?? 0;
73
+ const amountB = b.assets?.find((asset) => asset.assetId === assetId)?.amount ?? 0;
74
+ return amountA - amountB;
75
+ });
76
+ const selected = [];
77
+ let totalAssetAmount = 0n;
78
+ for (const coin of coinsWithAsset) {
79
+ if (totalAssetAmount >= requiredAmount)
80
+ break;
81
+ selected.push(coin);
82
+ const assetAmount = coin.assets?.find((a) => a.assetId === assetId)?.amount ?? 0;
83
+ totalAssetAmount += BigInt(assetAmount);
84
+ }
85
+ if (totalAssetAmount < requiredAmount) {
86
+ throw new Error(`Insufficient asset balance: have ${totalAssetAmount}, need ${requiredAmount}`);
87
+ }
88
+ return { selected, totalAssetAmount };
89
+ }
90
+ export function computeAssetChange(inputAssets, outputAssets) {
91
+ const change = new Map();
92
+ for (const [assetId, inputAmount] of inputAssets) {
93
+ const outputAmount = outputAssets.get(assetId) ?? 0n;
94
+ const changeAmount = inputAmount - outputAmount;
95
+ if (changeAmount > 0n) {
96
+ change.set(assetId, changeAmount);
97
+ }
98
+ }
99
+ return change;
100
+ }
101
+ export function selectedCoinsToAssetInputs(selectedCoins) {
102
+ const assetInputs = new Map();
103
+ for (let inputIndex = 0; inputIndex < selectedCoins.length; inputIndex++) {
104
+ const coin = selectedCoins[inputIndex];
105
+ if (!coin.assets || coin.assets.length === 0) {
106
+ continue;
107
+ }
108
+ assetInputs.set(inputIndex, coin.assets);
109
+ }
110
+ return assetInputs;
111
+ }
@@ -8,7 +8,7 @@ import { hex } from "@scure/base";
8
8
  * @example
9
9
  * ```typescript
10
10
  * // use wallet handler or create a custom one
11
- * const handler = wallet.createBatchHandler(intentId, inputs, musig2session);
11
+ * const handler = wallet.createBatchHandler(intentId, inputs, expectedRecipients, musig2session);
12
12
  *
13
13
  * const abortController = new AbortController();
14
14
  * // Get event stream from Ark provider