@arkade-os/sdk 0.4.0 → 0.4.1

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.
@@ -7,6 +7,8 @@ const base_2 = require("../script/base");
7
7
  const forfeit_1 = require("../forfeit");
8
8
  const btc_signer_1 = require("@scure/btc-signer");
9
9
  const networks_1 = require("../networks");
10
+ const asset_1 = require("./asset");
11
+ const extension_1 = require("../extension");
10
12
  class DelegatorManagerImpl {
11
13
  constructor(delegatorProvider, arkInfoProvider, identity) {
12
14
  this.delegatorProvider = delegatorProvider;
@@ -217,6 +219,48 @@ async function makeDelegateForfeitTx(input, connectorAmount, delegatePubkey, for
217
219
  return identity.sign(tx);
218
220
  }
219
221
  async function makeSignedDelegateIntent(identity, coins, outputs, onchainOutputsIndexes, cosignerPubKeys, validAt) {
222
+ // if some of the inputs hold assets, build the asset packet and append as output
223
+ // in the intent proof tx, there is a "fake" input at index 0
224
+ // so the real coin indices are offset by +1
225
+ const assetInputs = new Map();
226
+ for (let i = 0; i < coins.length; i++) {
227
+ if ("assets" in coins[i]) {
228
+ const assets = coins[i].assets;
229
+ if (assets && assets.length > 0) {
230
+ assetInputs.set(i + 1, assets);
231
+ }
232
+ }
233
+ }
234
+ let outputAssets;
235
+ let assetOutputIndex; // where to send the assets
236
+ if (assetInputs.size > 0) {
237
+ // collect all input assets and assign them to the first offchain output
238
+ const allAssets = new Map();
239
+ for (const [, assets] of assetInputs) {
240
+ for (const asset of assets) {
241
+ const existing = allAssets.get(asset.assetId) ?? 0n;
242
+ allAssets.set(asset.assetId, existing + BigInt(asset.amount));
243
+ }
244
+ }
245
+ outputAssets = [];
246
+ for (const [assetId, amount] of allAssets) {
247
+ outputAssets.push({ assetId, amount: Number(amount) });
248
+ }
249
+ const firstOffchainIndex = outputs.findIndex((_, i) => !onchainOutputsIndexes.includes(i));
250
+ if (firstOffchainIndex === -1) {
251
+ throw new Error("Cannot settle assets without an offchain output");
252
+ }
253
+ assetOutputIndex = firstOffchainIndex;
254
+ }
255
+ const recipients = outputs.map((output, i) => ({
256
+ address: "", // not needed for asset packet creation
257
+ amount: Number(output.amount),
258
+ assets: i === assetOutputIndex ? outputAssets : undefined,
259
+ }));
260
+ if (outputAssets && outputAssets.length > 0) {
261
+ const assetPacket = (0, asset_1.createAssetPacket)(assetInputs, recipients);
262
+ outputs.push(extension_1.Extension.create([assetPacket]).txOut());
263
+ }
220
264
  const message = {
221
265
  type: "register",
222
266
  onchain_output_indexes: onchainOutputsIndexes,
@@ -66,7 +66,7 @@ function defineExpoBackgroundTask(taskName, options) {
66
66
  const arkProvider = new expoArk_1.ExpoArkProvider(config.arkServerUrl);
67
67
  // Reconstruct default offchainTapscript as fallback
68
68
  // for VTXOs not associated with a contract.
69
- const defaultTapscript = new default_1.DefaultVtxo.Script({
69
+ const offchainTapscript = new default_1.DefaultVtxo.Script({
70
70
  pubKey: base_1.hex.decode(config.pubkeyHex),
71
71
  serverPubKey: base_1.hex.decode(config.serverPubKeyHex),
72
72
  csvTimelock: {
@@ -74,18 +74,14 @@ function defineExpoBackgroundTask(taskName, options) {
74
74
  type: config.exitTimelockType,
75
75
  },
76
76
  });
77
- await (0, taskRunner_1.runTasks)(taskQueue, processors, {
77
+ const deps = (0, taskRunner_1.createTaskDependencies)({
78
78
  walletRepository,
79
79
  contractRepository,
80
80
  indexerProvider,
81
81
  arkProvider,
82
- extendVtxo: (vtxo, contract) => {
83
- if (contract) {
84
- return (0, utils_1.extendVtxoFromContract)(vtxo, contract);
85
- }
86
- return (0, utils_1.extendVirtualCoin)({ offchainTapscript: defaultTapscript }, vtxo);
87
- },
82
+ offchainTapscript,
88
83
  });
84
+ await (0, taskRunner_1.runTasks)(taskQueue, processors, deps);
89
85
  // Acknowledge outbox results (no foreground to consume them)
90
86
  const results = await taskQueue.getResults();
91
87
  if (results.length > 0) {
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CONTRACT_POLL_TASK_TYPE = exports.contractPollProcessor = exports.runTasks = exports.AsyncStorageTaskQueue = exports.InMemoryTaskQueue = void 0;
3
+ exports.CONTRACT_POLL_TASK_TYPE = exports.contractPollProcessor = exports.createTaskDependencies = exports.runTasks = exports.AsyncStorageTaskQueue = exports.InMemoryTaskQueue = void 0;
4
4
  var taskQueue_1 = require("./taskQueue");
5
5
  Object.defineProperty(exports, "InMemoryTaskQueue", { enumerable: true, get: function () { return taskQueue_1.InMemoryTaskQueue; } });
6
6
  var asyncStorageTaskQueue_1 = require("./asyncStorageTaskQueue");
7
7
  Object.defineProperty(exports, "AsyncStorageTaskQueue", { enumerable: true, get: function () { return asyncStorageTaskQueue_1.AsyncStorageTaskQueue; } });
8
8
  var taskRunner_1 = require("./taskRunner");
9
9
  Object.defineProperty(exports, "runTasks", { enumerable: true, get: function () { return taskRunner_1.runTasks; } });
10
+ Object.defineProperty(exports, "createTaskDependencies", { enumerable: true, get: function () { return taskRunner_1.createTaskDependencies; } });
10
11
  var processors_1 = require("./processors");
11
12
  Object.defineProperty(exports, "contractPollProcessor", { enumerable: true, get: function () { return processors_1.contractPollProcessor; } });
12
13
  Object.defineProperty(exports, "CONTRACT_POLL_TASK_TYPE", { enumerable: true, get: function () { return processors_1.CONTRACT_POLL_TASK_TYPE; } });
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runTasks = runTasks;
4
+ exports.createTaskDependencies = createTaskDependencies;
4
5
  const utils_1 = require("../../wallet/utils");
5
6
  /**
6
7
  * Run all pending tasks from the queue through matching processors.
@@ -55,3 +56,27 @@ async function runTasks(queue, processors, deps) {
55
56
  }
56
57
  return results;
57
58
  }
59
+ /**
60
+ * Build the {@link TaskDependencies} needed by task processors
61
+ * (e.g. {@link import("./processors").contractPollProcessor}).
62
+ *
63
+ * This is the same construction that `defineExpoBackgroundTask` does
64
+ * internally, extracted so that consumers with custom schedulers
65
+ * (e.g. bare React Native with `react-native-background-fetch`)
66
+ * can build deps without depending on Expo.
67
+ */
68
+ function createTaskDependencies(options) {
69
+ const { walletRepository, contractRepository, indexerProvider, arkProvider, offchainTapscript, } = options;
70
+ return {
71
+ walletRepository,
72
+ contractRepository,
73
+ indexerProvider,
74
+ arkProvider,
75
+ extendVtxo: (vtxo, contract) => {
76
+ if (contract) {
77
+ return (0, utils_1.extendVtxoFromContract)(vtxo, contract);
78
+ }
79
+ return (0, utils_1.extendVirtualCoin)({ offchainTapscript }, vtxo);
80
+ },
81
+ };
82
+ }
@@ -4,6 +4,8 @@ import { scriptFromTapLeafScript } from '../script/base.js';
4
4
  import { buildForfeitTxWithOutput } from '../forfeit.js';
5
5
  import { Address, OutScript, SigHash } from "@scure/btc-signer";
6
6
  import { getNetwork } from '../networks.js';
7
+ import { createAssetPacket } from './asset.js';
8
+ import { Extension } from '../extension/index.js';
7
9
  export class DelegatorManagerImpl {
8
10
  constructor(delegatorProvider, arkInfoProvider, identity) {
9
11
  this.delegatorProvider = delegatorProvider;
@@ -213,6 +215,48 @@ async function makeDelegateForfeitTx(input, connectorAmount, delegatePubkey, for
213
215
  return identity.sign(tx);
214
216
  }
215
217
  async function makeSignedDelegateIntent(identity, coins, outputs, onchainOutputsIndexes, cosignerPubKeys, validAt) {
218
+ // if some of the inputs hold assets, build the asset packet and append as output
219
+ // in the intent proof tx, there is a "fake" input at index 0
220
+ // so the real coin indices are offset by +1
221
+ const assetInputs = new Map();
222
+ for (let i = 0; i < coins.length; i++) {
223
+ if ("assets" in coins[i]) {
224
+ const assets = coins[i].assets;
225
+ if (assets && assets.length > 0) {
226
+ assetInputs.set(i + 1, assets);
227
+ }
228
+ }
229
+ }
230
+ let outputAssets;
231
+ let assetOutputIndex; // where to send the assets
232
+ if (assetInputs.size > 0) {
233
+ // collect all input assets and assign them to the first offchain output
234
+ const allAssets = new Map();
235
+ for (const [, assets] of assetInputs) {
236
+ for (const asset of assets) {
237
+ const existing = allAssets.get(asset.assetId) ?? 0n;
238
+ allAssets.set(asset.assetId, existing + BigInt(asset.amount));
239
+ }
240
+ }
241
+ outputAssets = [];
242
+ for (const [assetId, amount] of allAssets) {
243
+ outputAssets.push({ assetId, amount: Number(amount) });
244
+ }
245
+ const firstOffchainIndex = outputs.findIndex((_, i) => !onchainOutputsIndexes.includes(i));
246
+ if (firstOffchainIndex === -1) {
247
+ throw new Error("Cannot settle assets without an offchain output");
248
+ }
249
+ assetOutputIndex = firstOffchainIndex;
250
+ }
251
+ const recipients = outputs.map((output, i) => ({
252
+ address: "", // not needed for asset packet creation
253
+ amount: Number(output.amount),
254
+ assets: i === assetOutputIndex ? outputAssets : undefined,
255
+ }));
256
+ if (outputAssets && outputAssets.length > 0) {
257
+ const assetPacket = createAssetPacket(assetInputs, recipients);
258
+ outputs.push(Extension.create([assetPacket]).txOut());
259
+ }
216
260
  const message = {
217
261
  type: "register",
218
262
  onchain_output_indexes: onchainOutputsIndexes,
@@ -1,10 +1,10 @@
1
1
  import { hex } from "@scure/base";
2
- import { runTasks } from '../../worker/expo/taskRunner.js';
2
+ import { runTasks, createTaskDependencies } from '../../worker/expo/taskRunner.js';
3
3
  import { contractPollProcessor, CONTRACT_POLL_TASK_TYPE, } from '../../worker/expo/processors/index.js';
4
4
  import { DefaultVtxo } from '../../script/default.js';
5
5
  import { ExpoArkProvider } from '../../providers/expoArk.js';
6
6
  import { ExpoIndexerProvider } from '../../providers/expoIndexer.js';
7
- import { extendVirtualCoin, extendVtxoFromContract, getRandomId, } from '../utils.js';
7
+ import { getRandomId } from '../utils.js';
8
8
  function requireTaskManager() {
9
9
  try {
10
10
  return require("expo-task-manager");
@@ -61,7 +61,7 @@ export function defineExpoBackgroundTask(taskName, options) {
61
61
  const arkProvider = new ExpoArkProvider(config.arkServerUrl);
62
62
  // Reconstruct default offchainTapscript as fallback
63
63
  // for VTXOs not associated with a contract.
64
- const defaultTapscript = new DefaultVtxo.Script({
64
+ const offchainTapscript = new DefaultVtxo.Script({
65
65
  pubKey: hex.decode(config.pubkeyHex),
66
66
  serverPubKey: hex.decode(config.serverPubKeyHex),
67
67
  csvTimelock: {
@@ -69,18 +69,14 @@ export function defineExpoBackgroundTask(taskName, options) {
69
69
  type: config.exitTimelockType,
70
70
  },
71
71
  });
72
- await runTasks(taskQueue, processors, {
72
+ const deps = createTaskDependencies({
73
73
  walletRepository,
74
74
  contractRepository,
75
75
  indexerProvider,
76
76
  arkProvider,
77
- extendVtxo: (vtxo, contract) => {
78
- if (contract) {
79
- return extendVtxoFromContract(vtxo, contract);
80
- }
81
- return extendVirtualCoin({ offchainTapscript: defaultTapscript }, vtxo);
82
- },
77
+ offchainTapscript,
83
78
  });
79
+ await runTasks(taskQueue, processors, deps);
84
80
  // Acknowledge outbox results (no foreground to consume them)
85
81
  const results = await taskQueue.getResults();
86
82
  if (results.length > 0) {
@@ -1,4 +1,4 @@
1
1
  export { InMemoryTaskQueue } from './taskQueue.js';
2
2
  export { AsyncStorageTaskQueue } from './asyncStorageTaskQueue.js';
3
- export { runTasks } from './taskRunner.js';
3
+ export { runTasks, createTaskDependencies } from './taskRunner.js';
4
4
  export { contractPollProcessor, CONTRACT_POLL_TASK_TYPE } from './processors/index.js';
@@ -1,4 +1,4 @@
1
- import { getRandomId } from '../../wallet/utils.js';
1
+ import { getRandomId, extendVirtualCoin, extendVtxoFromContract, } from '../../wallet/utils.js';
2
2
  /**
3
3
  * Run all pending tasks from the queue through matching processors.
4
4
  *
@@ -52,3 +52,27 @@ export async function runTasks(queue, processors, deps) {
52
52
  }
53
53
  return results;
54
54
  }
55
+ /**
56
+ * Build the {@link TaskDependencies} needed by task processors
57
+ * (e.g. {@link import("./processors").contractPollProcessor}).
58
+ *
59
+ * This is the same construction that `defineExpoBackgroundTask` does
60
+ * internally, extracted so that consumers with custom schedulers
61
+ * (e.g. bare React Native with `react-native-background-fetch`)
62
+ * can build deps without depending on Expo.
63
+ */
64
+ export function createTaskDependencies(options) {
65
+ const { walletRepository, contractRepository, indexerProvider, arkProvider, offchainTapscript, } = options;
66
+ return {
67
+ walletRepository,
68
+ contractRepository,
69
+ indexerProvider,
70
+ arkProvider,
71
+ extendVtxo: (vtxo, contract) => {
72
+ if (contract) {
73
+ return extendVtxoFromContract(vtxo, contract);
74
+ }
75
+ return extendVirtualCoin({ offchainTapscript }, vtxo);
76
+ },
77
+ };
78
+ }
@@ -2,6 +2,6 @@ export type { TaskItem, TaskResult, TaskQueue } from "./taskQueue";
2
2
  export { InMemoryTaskQueue } from "./taskQueue";
3
3
  export type { AsyncStorageLike } from "./asyncStorageTaskQueue";
4
4
  export { AsyncStorageTaskQueue } from "./asyncStorageTaskQueue";
5
- export type { TaskProcessor, TaskDependencies } from "./taskRunner";
6
- export { runTasks } from "./taskRunner";
5
+ export type { TaskProcessor, TaskDependencies, CreateTaskDependenciesOptions, } from "./taskRunner";
6
+ export { runTasks, createTaskDependencies } from "./taskRunner";
7
7
  export { contractPollProcessor, CONTRACT_POLL_TASK_TYPE } from "./processors";
@@ -5,6 +5,7 @@ import type { IndexerProvider } from "../../providers/indexer";
5
5
  import type { ArkProvider } from "../../providers/ark";
6
6
  import type { ExtendedVirtualCoin, VirtualCoin } from "../../wallet";
7
7
  import type { Contract } from "../../contracts/types";
8
+ import type { ReadonlyWallet } from "../../wallet/wallet";
8
9
  /**
9
10
  * Shared dependencies injected into every processor at runtime.
10
11
  */
@@ -40,3 +41,23 @@ export interface TaskProcessor<TDeps = TaskDependencies> {
40
41
  * Processor errors produce a `"failed"` result with the error message.
41
42
  */
42
43
  export declare function runTasks<TDeps = TaskDependencies>(queue: TaskQueue, processors: TaskProcessor<TDeps>[], deps: TDeps): Promise<TaskResult[]>;
44
+ /**
45
+ * Options for {@link createTaskDependencies}.
46
+ */
47
+ export interface CreateTaskDependenciesOptions {
48
+ walletRepository: WalletRepository;
49
+ contractRepository: ContractRepository;
50
+ indexerProvider: IndexerProvider;
51
+ arkProvider: ArkProvider;
52
+ offchainTapscript: ReadonlyWallet["offchainTapscript"];
53
+ }
54
+ /**
55
+ * Build the {@link TaskDependencies} needed by task processors
56
+ * (e.g. {@link import("./processors").contractPollProcessor}).
57
+ *
58
+ * This is the same construction that `defineExpoBackgroundTask` does
59
+ * internally, extracted so that consumers with custom schedulers
60
+ * (e.g. bare React Native with `react-native-background-fetch`)
61
+ * can build deps without depending on Expo.
62
+ */
63
+ export declare function createTaskDependencies(options: CreateTaskDependenciesOptions): TaskDependencies;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",