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