@hardkas/sdk 0.8.19-alpha → 0.9.0-alpha
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.
- package/README.md +38 -27
- package/dist/index.js +65 -114
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -1,40 +1,51 @@
|
|
|
1
1
|
# `@hardkas/sdk`
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The HardKas SDK is the programmatic API for local-first transaction workflows. It exposes the same core model as the CLI: plan, sign, simulate or send, then inspect artifacts and lineage.
|
|
4
4
|
|
|
5
|
-
## 1.
|
|
5
|
+
## 1. Create A Local SDK
|
|
6
6
|
|
|
7
|
-
The SDK injects dependencies via a `RuntimeContext` container. This container abstracts the filesystem, logging, and RPC configurations.
|
|
8
|
-
|
|
9
|
-
### Flow: Auto-Bootstrap
|
|
10
7
|
```typescript
|
|
11
|
-
|
|
8
|
+
import { Hardkas } from "@hardkas/sdk";
|
|
9
|
+
|
|
10
|
+
const sdk = await Hardkas.create({
|
|
11
|
+
cwd: process.cwd(),
|
|
12
|
+
autoBootstrap: true,
|
|
13
|
+
network: "simulated"
|
|
14
|
+
});
|
|
12
15
|
```
|
|
13
|
-
1. The engine scans upward from `process.cwd()` to find `.hardkas/`.
|
|
14
|
-
2. It acquires necessary read/write workspace locks.
|
|
15
|
-
3. If the workspace does not exist, it automatically creates it, generating a new developer identity and local configuration.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
2. It calls `hardkas doctor --json` internally before initializing.
|
|
21
|
-
3. If the environment fails policy checks (e.g., trying to write to mainnet without the explicit `--unsafe-mainnet` flag), the instantiation throws synchronously.
|
|
17
|
+
`autoBootstrap: true` is the easiest local path. It creates or loads the workspace data needed for simulated accounts, artifacts, and local execution.
|
|
18
|
+
|
|
19
|
+
## 2. Transaction Flow
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
```typescript
|
|
22
|
+
const plan = await sdk.tx.plan({
|
|
23
|
+
from: "alice",
|
|
24
|
+
to: "bob",
|
|
25
|
+
amount: "1",
|
|
26
|
+
network: "simulated"
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const signed = await sdk.tx.sign(plan, {
|
|
30
|
+
account: "alice"
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const receipt = await sdk.tx.simulate(signed);
|
|
34
|
+
```
|
|
24
35
|
|
|
25
|
-
|
|
36
|
+
For a real RPC-backed node, create the SDK with an explicit network/provider configuration and treat the send step as network-state dependent. Mainnet should remain outside the default local development flow.
|
|
26
37
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
3. It emits a `PlanCreated` event to the `events.jsonl` ledger.
|
|
31
|
-
4. It persists a `TxPlan` artifact in `.hardkas/artifacts/`.
|
|
38
|
+
## 3. Artifacts And Queries
|
|
39
|
+
|
|
40
|
+
The SDK can read artifacts, trace lineage, replay local records, and query the local projection:
|
|
32
41
|
|
|
33
|
-
### Variant: Dry-Run Execution
|
|
34
42
|
```typescript
|
|
35
|
-
const
|
|
43
|
+
const artifacts = await sdk.query.artifacts.list();
|
|
44
|
+
const trace = await sdk.lineage.trace(receipt.txId);
|
|
36
45
|
```
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
4.
|
|
46
|
+
|
|
47
|
+
The SQLite query store is rebuildable. The durable source of truth is the workspace artifact and event data.
|
|
48
|
+
|
|
49
|
+
## 4. Boundary
|
|
50
|
+
|
|
51
|
+
The SDK should be used from Node.js. Browser applications should talk to the dev server through `@hardkas/client`, not import `@hardkas/sdk` directly.
|
package/dist/index.js
CHANGED
|
@@ -90,7 +90,6 @@ var HardkasAccounts = class {
|
|
|
90
90
|
// src/tx.ts
|
|
91
91
|
import { systemRuntimeContext, deterministicCompare } from "@hardkas/core";
|
|
92
92
|
import {
|
|
93
|
-
buildPaymentPlan,
|
|
94
93
|
verifySignedTxSemantics
|
|
95
94
|
} from "@hardkas/tx-builder";
|
|
96
95
|
import {
|
|
@@ -108,6 +107,7 @@ import {
|
|
|
108
107
|
import { coreEvents } from "@hardkas/core";
|
|
109
108
|
import { signTxPlanArtifact, validateAddressNetwork } from "@hardkas/accounts";
|
|
110
109
|
import { parseKasToSompi } from "@hardkas/core";
|
|
110
|
+
import { TxPlanService } from "@hardkas/tx-builder";
|
|
111
111
|
function normalizeSimulatedPlanInput(target, fallbackId) {
|
|
112
112
|
if (target.schema === ARTIFACT_SCHEMAS.TX_PLAN && Array.isArray(target.inputs)) {
|
|
113
113
|
return target;
|
|
@@ -165,83 +165,55 @@ var HardkasTx = class {
|
|
|
165
165
|
if (options.changeAddress) {
|
|
166
166
|
validateAddressNetwork(options.changeAddress, activeNetwork, allowMainnet);
|
|
167
167
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
let selectedAmount = 0n;
|
|
208
|
-
let selectedInputsCount = 0;
|
|
209
|
-
const builderUtxos = [];
|
|
210
|
-
for (const utxo of sortedUtxos) {
|
|
211
|
-
builderUtxos.push(utxo);
|
|
212
|
-
selectedAmount += utxo.amountSompi;
|
|
213
|
-
selectedInputsCount++;
|
|
214
|
-
const requiredTotal = amountSompi + BigInt(selectedInputsCount) * MARGIN_FEE_PER_INPUT;
|
|
215
|
-
if (selectedAmount >= requiredTotal) {
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
if (selectedInputsCount >= HARD_LIMIT) {
|
|
219
|
-
break;
|
|
168
|
+
const utxoProvider = {
|
|
169
|
+
getUtxos: async (address) => {
|
|
170
|
+
if (activeNetwork === "simulated" || this.sdk.config.config.networks?.[activeNetwork]?.kind === "simulated") {
|
|
171
|
+
const { loadOrCreateLocalnetState, getSpendableUtxos } = await import("@hardkas/localnet");
|
|
172
|
+
const localState = await loadOrCreateLocalnetState({ cwd: this.sdk.workspace.root });
|
|
173
|
+
const unspent = getSpendableUtxos(localState, address);
|
|
174
|
+
return unspent.map((u) => {
|
|
175
|
+
const parts = u.id.split(":");
|
|
176
|
+
const index = Number(parts[parts.length - 1]);
|
|
177
|
+
const transactionId = parts.slice(0, -1).join(":");
|
|
178
|
+
return {
|
|
179
|
+
outpoint: { transactionId, index },
|
|
180
|
+
address: u.address,
|
|
181
|
+
amountSompi: BigInt(u.amountSompi),
|
|
182
|
+
scriptPublicKey: "mock-script"
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
} else {
|
|
186
|
+
const rpcUtxos = await this.sdk.rpc.getUtxosByAddress(address);
|
|
187
|
+
return rpcUtxos.map((u) => ({
|
|
188
|
+
outpoint: {
|
|
189
|
+
transactionId: u.outpoint.transactionId,
|
|
190
|
+
index: u.outpoint.index
|
|
191
|
+
},
|
|
192
|
+
address: u.address,
|
|
193
|
+
amountSompi: BigInt(u.amountSompi),
|
|
194
|
+
scriptPublicKey: u.scriptPublicKey || "",
|
|
195
|
+
blockDaaScore: u.blockDaaScore !== void 0 ? BigInt(u.blockDaaScore) : void 0,
|
|
196
|
+
isCoinbase: u.isCoinbase
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
getVirtualDaaScore: async () => {
|
|
201
|
+
try {
|
|
202
|
+
const dagInfo = await this.sdk.rpc.getBlockDagInfo();
|
|
203
|
+
return dagInfo.virtualDaaScore;
|
|
204
|
+
} catch {
|
|
205
|
+
return void 0;
|
|
206
|
+
}
|
|
220
207
|
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
if (selectedInputsCount > MAX_INPUTS_PER_TX) {
|
|
226
|
-
const err = new Error(`TOO_MANY_INPUTS_FOR_SINGLE_TX: Transaction requires ${selectedInputsCount} inputs to cover the amount, which exceeds the safe limit of ${MAX_INPUTS_PER_TX} inputs.
|
|
227
|
-
Hint: Run 'hardkas accounts consolidate' to merge dust UTXOs.`);
|
|
228
|
-
err.code = "TOO_MANY_INPUTS_FOR_SINGLE_TX";
|
|
229
|
-
throw err;
|
|
230
|
-
}
|
|
231
|
-
if (selectedInputsCount >= WARN_INPUTS) {
|
|
232
|
-
console.warn(`\u26A0\uFE0F WARNING: Transaction requires ${selectedInputsCount} inputs. Consider running 'hardkas accounts consolidate'.`);
|
|
233
|
-
}
|
|
234
|
-
const builderPlan = buildPaymentPlan({
|
|
208
|
+
};
|
|
209
|
+
const planService = new TxPlanService(utxoProvider);
|
|
210
|
+
const result = await planService.planTransaction({
|
|
235
211
|
fromAddress: fromAccount.address,
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
address: toAccount.address,
|
|
240
|
-
amountSompi
|
|
241
|
-
}
|
|
242
|
-
],
|
|
243
|
-
feeRateSompiPerMass: options.feeRate ?? 1n
|
|
212
|
+
toAddress: toAccount.address,
|
|
213
|
+
amountSompi,
|
|
214
|
+
...options.feeRate !== void 0 ? { feeRate: options.feeRate } : {}
|
|
244
215
|
});
|
|
216
|
+
const builderPlan = result.plan;
|
|
245
217
|
const isSimulated = activeNetwork === "simulated" || this.sdk.config.config.networks?.[activeNetwork]?.kind === "simulated";
|
|
246
218
|
let resolvedAssumptionLevel = options.assumption;
|
|
247
219
|
if (!resolvedAssumptionLevel) {
|
|
@@ -271,11 +243,7 @@ Hint: Run 'hardkas accounts consolidate' to merge dust UTXOs.`);
|
|
|
271
243
|
...systemRuntimeContext,
|
|
272
244
|
...options.workflowId ? { workflowId: options.workflowId } : {},
|
|
273
245
|
assumptionLevel: resolvedAssumptionLevel,
|
|
274
|
-
utxoSelection:
|
|
275
|
-
totalUtxosSeen: allFetchedUtxos.length,
|
|
276
|
-
selectedUtxos: selectedInputsCount,
|
|
277
|
-
selectionStrategy: "largest-first"
|
|
278
|
-
}
|
|
246
|
+
utxoSelection: result.utxoSelection
|
|
279
247
|
}
|
|
280
248
|
});
|
|
281
249
|
if (options.policy || options.policies) {
|
|
@@ -344,39 +312,22 @@ Hint: Run 'hardkas accounts consolidate' to merge dust UTXOs.`);
|
|
|
344
312
|
}
|
|
345
313
|
const activeNetwork = options.network || this.sdk.config.config.defaultNetwork || "simnet";
|
|
346
314
|
const isSimulated = activeNetwork === "simulated" || this.sdk.config.config.networks?.[activeNetwork]?.kind === "simulated";
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
outpoint: {
|
|
353
|
-
transactionId: u.outpoint.transactionId,
|
|
354
|
-
index: u.outpoint.index
|
|
355
|
-
},
|
|
356
|
-
address: u.address,
|
|
357
|
-
amountSompi: amount,
|
|
358
|
-
scriptPublicKey: u.scriptPublicKey || ""
|
|
359
|
-
};
|
|
360
|
-
});
|
|
361
|
-
const feeRate = options.feeRate ?? 1n;
|
|
362
|
-
const massPerInput = 1500n;
|
|
363
|
-
const estimatedMass = BigInt(options.selectedUtxos.length) * massPerInput + 500n;
|
|
364
|
-
const expectedFee = estimatedMass * feeRate;
|
|
365
|
-
if (totalAmount <= expectedFee) {
|
|
366
|
-
throw new Error(`Consolidation failed: Total selected UTXO amount (${totalAmount}) is less than or equal to the estimated fee (${expectedFee}).`);
|
|
367
|
-
}
|
|
368
|
-
const outputAmount = totalAmount - expectedFee;
|
|
369
|
-
const builderPlan = buildPaymentPlan({
|
|
315
|
+
const dummyProvider = {
|
|
316
|
+
getUtxos: async () => []
|
|
317
|
+
};
|
|
318
|
+
const planService = new TxPlanService(dummyProvider);
|
|
319
|
+
const result = await planService.planConsolidation({
|
|
370
320
|
fromAddress: resolvedAccount.address,
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
address: options.destination,
|
|
375
|
-
amountSompi: outputAmount
|
|
376
|
-
}
|
|
377
|
-
],
|
|
378
|
-
feeRateSompiPerMass: feeRate
|
|
321
|
+
selectedUtxos: options.selectedUtxos,
|
|
322
|
+
toAddress: options.destination,
|
|
323
|
+
...options.feeRate !== void 0 ? { feeRate: options.feeRate } : {}
|
|
379
324
|
});
|
|
325
|
+
const builderPlan = result.plan;
|
|
326
|
+
const firstOutput = builderPlan.outputs[0];
|
|
327
|
+
if (!firstOutput) {
|
|
328
|
+
throw new Error("Consolidation failed: transaction builder produced no outputs.");
|
|
329
|
+
}
|
|
330
|
+
const outputAmount = firstOutput.amountSompi;
|
|
380
331
|
let resolvedAssumptionLevel = isSimulated ? "local-simulated" : "local-rpc";
|
|
381
332
|
const basePlan = createTxPlanArtifact({
|
|
382
333
|
networkId: activeNetwork,
|
|
@@ -396,10 +347,10 @@ Hint: Run 'hardkas accounts consolidate' to merge dust UTXOs.`);
|
|
|
396
347
|
...systemRuntimeContext,
|
|
397
348
|
assumptionLevel: resolvedAssumptionLevel,
|
|
398
349
|
utxoSelection: {
|
|
399
|
-
strategy:
|
|
350
|
+
strategy: result.utxoSelection.selectionStrategy,
|
|
400
351
|
totalUtxosSeen: options.totalUtxosSeen ?? options.selectedUtxos.length,
|
|
401
|
-
selectedUtxos:
|
|
402
|
-
purpose:
|
|
352
|
+
selectedUtxos: result.utxoSelection.selectedUtxos,
|
|
353
|
+
purpose: result.utxoSelection.purpose
|
|
403
354
|
}
|
|
404
355
|
}
|
|
405
356
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hardkas/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0-alpha",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -24,18 +24,18 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@kaspa/core-lib": "^1.6.5",
|
|
27
|
-
"@hardkas/
|
|
28
|
-
"@hardkas/
|
|
29
|
-
"@hardkas/
|
|
30
|
-
"@hardkas/
|
|
31
|
-
"@hardkas/
|
|
32
|
-
"@hardkas/
|
|
33
|
-
"@hardkas/
|
|
34
|
-
"@hardkas/
|
|
35
|
-
"@hardkas/
|
|
36
|
-
"@hardkas/
|
|
37
|
-
"@hardkas/
|
|
38
|
-
"@hardkas/
|
|
27
|
+
"@hardkas/config": "0.9.0-alpha",
|
|
28
|
+
"@hardkas/artifacts": "0.9.0-alpha",
|
|
29
|
+
"@hardkas/accounts": "0.9.0-alpha",
|
|
30
|
+
"@hardkas/kaspa-rpc": "0.9.0-alpha",
|
|
31
|
+
"@hardkas/localnet": "0.9.0-alpha",
|
|
32
|
+
"@hardkas/query": "0.9.0-alpha",
|
|
33
|
+
"@hardkas/core": "0.9.0-alpha",
|
|
34
|
+
"@hardkas/l2": "0.9.0-alpha",
|
|
35
|
+
"@hardkas/tx-builder": "0.9.0-alpha",
|
|
36
|
+
"@hardkas/wallet-adapter": "0.9.0-alpha",
|
|
37
|
+
"@hardkas/query-store": "0.9.0-alpha",
|
|
38
|
+
"@hardkas/simulator": "0.9.0-alpha"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^20.12.7",
|