@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
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
BitcoinTransaction,
|
|
5
5
|
KeyStore,
|
|
6
6
|
ExternalSigner,
|
|
7
|
-
VerifiableCredential
|
|
7
|
+
VerifiableCredential,
|
|
8
|
+
LayerType
|
|
8
9
|
} from '../types';
|
|
9
10
|
import { BitcoinManager } from '../bitcoin/BitcoinManager';
|
|
10
11
|
import { DIDManager } from '../did/DIDManager';
|
|
@@ -26,6 +27,97 @@ import {
|
|
|
26
27
|
type BatchOperationOptions,
|
|
27
28
|
type BatchInscriptionOptions,
|
|
28
29
|
} from './BatchOperations';
|
|
30
|
+
import {
|
|
31
|
+
type OriginalKind,
|
|
32
|
+
type OriginalManifest,
|
|
33
|
+
type CreateTypedOriginalOptions,
|
|
34
|
+
KindRegistry,
|
|
35
|
+
} from '../kinds';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Cost estimation result for migration operations
|
|
39
|
+
*/
|
|
40
|
+
export interface CostEstimate {
|
|
41
|
+
/** Total estimated cost in satoshis */
|
|
42
|
+
totalSats: number;
|
|
43
|
+
/** Breakdown of costs */
|
|
44
|
+
breakdown: {
|
|
45
|
+
/** Network fee in satoshis */
|
|
46
|
+
networkFee: number;
|
|
47
|
+
/** Data cost for inscription (sat/vB * size) */
|
|
48
|
+
dataCost: number;
|
|
49
|
+
/** Dust output value */
|
|
50
|
+
dustValue: number;
|
|
51
|
+
};
|
|
52
|
+
/** Fee rate used for estimation (sat/vB) */
|
|
53
|
+
feeRate: number;
|
|
54
|
+
/** Data size in bytes */
|
|
55
|
+
dataSize: number;
|
|
56
|
+
/** Target layer for the migration */
|
|
57
|
+
targetLayer: LayerType;
|
|
58
|
+
/** Confidence level of estimate */
|
|
59
|
+
confidence: 'low' | 'medium' | 'high';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Migration validation result
|
|
64
|
+
*/
|
|
65
|
+
export interface MigrationValidation {
|
|
66
|
+
/** Whether the migration is valid */
|
|
67
|
+
valid: boolean;
|
|
68
|
+
/** List of validation errors */
|
|
69
|
+
errors: string[];
|
|
70
|
+
/** List of warnings (non-blocking) */
|
|
71
|
+
warnings: string[];
|
|
72
|
+
/** Current layer of the asset */
|
|
73
|
+
currentLayer: LayerType;
|
|
74
|
+
/** Target layer for migration */
|
|
75
|
+
targetLayer: LayerType;
|
|
76
|
+
/** Checks performed */
|
|
77
|
+
checks: {
|
|
78
|
+
layerTransition: boolean;
|
|
79
|
+
resourcesValid: boolean;
|
|
80
|
+
credentialsValid: boolean;
|
|
81
|
+
didDocumentValid: boolean;
|
|
82
|
+
bitcoinReadiness?: boolean;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Progress callback for long-running operations
|
|
88
|
+
*/
|
|
89
|
+
export type ProgressCallback = (progress: LifecycleProgress) => void;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Progress information for lifecycle operations
|
|
93
|
+
*/
|
|
94
|
+
export interface LifecycleProgress {
|
|
95
|
+
/** Current operation phase */
|
|
96
|
+
phase: 'preparing' | 'validating' | 'processing' | 'committing' | 'confirming' | 'complete' | 'failed';
|
|
97
|
+
/** Progress percentage (0-100) */
|
|
98
|
+
percentage: number;
|
|
99
|
+
/** Human-readable message */
|
|
100
|
+
message: string;
|
|
101
|
+
/** Current operation details */
|
|
102
|
+
details?: {
|
|
103
|
+
currentStep?: number;
|
|
104
|
+
totalSteps?: number;
|
|
105
|
+
transactionId?: string;
|
|
106
|
+
confirmations?: number;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Options for lifecycle operations with progress tracking
|
|
112
|
+
*/
|
|
113
|
+
export interface LifecycleOperationOptions {
|
|
114
|
+
/** Fee rate for Bitcoin operations (sat/vB) */
|
|
115
|
+
feeRate?: number;
|
|
116
|
+
/** Progress callback for operation updates */
|
|
117
|
+
onProgress?: ProgressCallback;
|
|
118
|
+
/** Enable atomic rollback on failure (default: true) */
|
|
119
|
+
atomicRollback?: boolean;
|
|
120
|
+
}
|
|
29
121
|
|
|
30
122
|
export class LifecycleManager {
|
|
31
123
|
private eventEmitter: EventEmitter;
|
|
@@ -219,6 +311,239 @@ export class LifecycleManager {
|
|
|
219
311
|
}
|
|
220
312
|
}
|
|
221
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Create a typed Original with kind-specific validation
|
|
316
|
+
*
|
|
317
|
+
* This is the recommended way to create Originals with proper typing and validation.
|
|
318
|
+
* Each kind (App, Agent, Module, Dataset, Media, Document) has specific metadata
|
|
319
|
+
* requirements that are validated before creation.
|
|
320
|
+
*
|
|
321
|
+
* @param kind - The kind of Original to create
|
|
322
|
+
* @param manifest - The manifest containing name, version, resources, and kind-specific metadata
|
|
323
|
+
* @param options - Optional creation options (skipValidation, strictMode)
|
|
324
|
+
* @returns The created OriginalsAsset
|
|
325
|
+
* @throws Error if validation fails (unless skipValidation is true)
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```typescript
|
|
329
|
+
* // Create a Module Original
|
|
330
|
+
* const moduleAsset = await sdk.lifecycle.createTypedOriginal(
|
|
331
|
+
* OriginalKind.Module,
|
|
332
|
+
* {
|
|
333
|
+
* kind: OriginalKind.Module,
|
|
334
|
+
* name: 'my-utility',
|
|
335
|
+
* version: '1.0.0',
|
|
336
|
+
* resources: [{ id: 'index.js', type: 'code', hash: '...', contentType: 'application/javascript' }],
|
|
337
|
+
* metadata: {
|
|
338
|
+
* format: 'esm',
|
|
339
|
+
* main: 'index.js',
|
|
340
|
+
* }
|
|
341
|
+
* }
|
|
342
|
+
* );
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
async createTypedOriginal<K extends OriginalKind>(
|
|
346
|
+
kind: K,
|
|
347
|
+
manifest: OriginalManifest<K>,
|
|
348
|
+
options?: CreateTypedOriginalOptions
|
|
349
|
+
): Promise<OriginalsAsset> {
|
|
350
|
+
const stopTimer = this.logger.startTimer('createTypedOriginal');
|
|
351
|
+
this.logger.info('Creating typed Original', { kind, name: manifest.name, version: manifest.version });
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
// Verify kind matches
|
|
355
|
+
if (manifest.kind !== kind) {
|
|
356
|
+
throw new Error(`Manifest kind "${manifest.kind}" does not match requested kind "${kind}"`);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Validate manifest using KindRegistry
|
|
360
|
+
const registry = KindRegistry.getInstance();
|
|
361
|
+
registry.validateOrThrow(manifest, options);
|
|
362
|
+
|
|
363
|
+
// Log warnings if any
|
|
364
|
+
if (!options?.skipValidation) {
|
|
365
|
+
const validationResult = registry.validate(manifest, options);
|
|
366
|
+
if (validationResult.warnings.length > 0) {
|
|
367
|
+
for (const warning of validationResult.warnings) {
|
|
368
|
+
this.logger.warn(`[${warning.code}] ${warning.message}`, { path: warning.path });
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Create the asset using existing createAsset method
|
|
374
|
+
const asset = await this.createAsset(manifest.resources);
|
|
375
|
+
|
|
376
|
+
// Store the manifest metadata on the asset for future reference
|
|
377
|
+
// We attach it as a non-enumerable property to avoid serialization issues
|
|
378
|
+
Object.defineProperty(asset, '_manifest', {
|
|
379
|
+
value: manifest,
|
|
380
|
+
writable: false,
|
|
381
|
+
enumerable: false,
|
|
382
|
+
configurable: false,
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Emit typed:created event
|
|
386
|
+
queueMicrotask(() => {
|
|
387
|
+
const event = {
|
|
388
|
+
type: 'asset:created' as const,
|
|
389
|
+
timestamp: new Date().toISOString(),
|
|
390
|
+
asset: {
|
|
391
|
+
id: asset.id,
|
|
392
|
+
layer: asset.currentLayer,
|
|
393
|
+
resourceCount: manifest.resources.length,
|
|
394
|
+
createdAt: asset.getProvenance().createdAt,
|
|
395
|
+
kind: kind,
|
|
396
|
+
name: manifest.name,
|
|
397
|
+
version: manifest.version,
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
// Emit from LifecycleManager
|
|
402
|
+
this.eventEmitter.emit(event);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
stopTimer();
|
|
406
|
+
this.logger.info('Typed Original created successfully', {
|
|
407
|
+
assetId: asset.id,
|
|
408
|
+
kind,
|
|
409
|
+
name: manifest.name,
|
|
410
|
+
version: manifest.version,
|
|
411
|
+
});
|
|
412
|
+
this.metrics.recordAssetCreated();
|
|
413
|
+
|
|
414
|
+
return asset;
|
|
415
|
+
} catch (error) {
|
|
416
|
+
stopTimer();
|
|
417
|
+
this.logger.error('Typed Original creation failed', error as Error, {
|
|
418
|
+
kind,
|
|
419
|
+
name: manifest.name,
|
|
420
|
+
version: manifest.version,
|
|
421
|
+
});
|
|
422
|
+
this.metrics.recordError('TYPED_ASSET_CREATION_FAILED', 'createTypedOriginal');
|
|
423
|
+
throw error;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Get the manifest from a typed Original asset
|
|
429
|
+
* Returns undefined if the asset was not created with createTypedOriginal
|
|
430
|
+
*
|
|
431
|
+
* @param asset - The OriginalsAsset to get manifest from
|
|
432
|
+
* @returns The manifest or undefined
|
|
433
|
+
*/
|
|
434
|
+
getManifest<K extends OriginalKind>(asset: OriginalsAsset): OriginalManifest<K> | undefined {
|
|
435
|
+
return (asset as any)._manifest as OriginalManifest<K> | undefined;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Estimate the cost of creating a typed Original
|
|
440
|
+
* Useful for showing users estimated fees before creation
|
|
441
|
+
*
|
|
442
|
+
* @param manifest - The manifest to estimate
|
|
443
|
+
* @param targetLayer - The target layer (did:webvh or did:btco)
|
|
444
|
+
* @param feeRate - Optional fee rate override (sat/vB)
|
|
445
|
+
* @returns Cost estimate including fees
|
|
446
|
+
*/
|
|
447
|
+
async estimateTypedOriginalCost<K extends OriginalKind>(
|
|
448
|
+
manifest: OriginalManifest<K>,
|
|
449
|
+
targetLayer: LayerType,
|
|
450
|
+
feeRate?: number
|
|
451
|
+
): Promise<CostEstimate> {
|
|
452
|
+
// For webvh, costs are minimal
|
|
453
|
+
if (targetLayer === 'did:webvh') {
|
|
454
|
+
return {
|
|
455
|
+
totalSats: 0,
|
|
456
|
+
breakdown: {
|
|
457
|
+
networkFee: 0,
|
|
458
|
+
dataCost: 0,
|
|
459
|
+
dustValue: 0
|
|
460
|
+
},
|
|
461
|
+
feeRate: 0,
|
|
462
|
+
dataSize: 0,
|
|
463
|
+
targetLayer,
|
|
464
|
+
confidence: 'high'
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Calculate total data size including manifest metadata
|
|
469
|
+
let dataSize = 0;
|
|
470
|
+
for (const resource of manifest.resources) {
|
|
471
|
+
if (resource.size) {
|
|
472
|
+
dataSize += resource.size;
|
|
473
|
+
} else if (resource.content) {
|
|
474
|
+
dataSize += Buffer.from(resource.content).length;
|
|
475
|
+
} else {
|
|
476
|
+
// Estimate based on hash length (assume average resource size)
|
|
477
|
+
dataSize += 1000;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Add inscription manifest overhead
|
|
482
|
+
const inscriptionManifest = {
|
|
483
|
+
assetId: `did:peer:placeholder`,
|
|
484
|
+
kind: manifest.kind,
|
|
485
|
+
name: manifest.name,
|
|
486
|
+
version: manifest.version,
|
|
487
|
+
resources: manifest.resources.map(r => ({
|
|
488
|
+
id: r.id,
|
|
489
|
+
hash: r.hash,
|
|
490
|
+
contentType: r.contentType,
|
|
491
|
+
})),
|
|
492
|
+
metadata: manifest.metadata,
|
|
493
|
+
timestamp: new Date().toISOString()
|
|
494
|
+
};
|
|
495
|
+
dataSize += Buffer.from(JSON.stringify(inscriptionManifest)).length;
|
|
496
|
+
|
|
497
|
+
// Get fee rate from oracle or use provided/default
|
|
498
|
+
let effectiveFeeRate = feeRate;
|
|
499
|
+
let confidence: 'low' | 'medium' | 'high' = 'medium';
|
|
500
|
+
|
|
501
|
+
if (!effectiveFeeRate) {
|
|
502
|
+
if (this.config.feeOracle) {
|
|
503
|
+
try {
|
|
504
|
+
effectiveFeeRate = await this.config.feeOracle.estimateFeeRate(1);
|
|
505
|
+
confidence = 'high';
|
|
506
|
+
} catch {
|
|
507
|
+
// Fallback to default
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (!effectiveFeeRate && this.config.ordinalsProvider) {
|
|
512
|
+
try {
|
|
513
|
+
effectiveFeeRate = await this.config.ordinalsProvider.estimateFee(1);
|
|
514
|
+
confidence = 'medium';
|
|
515
|
+
} catch {
|
|
516
|
+
// Fallback to default
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (!effectiveFeeRate) {
|
|
521
|
+
effectiveFeeRate = 10;
|
|
522
|
+
confidence = 'low';
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Transaction overhead (commit + reveal structure)
|
|
527
|
+
const txOverhead = 200 + 122; // Base tx + inscription overhead
|
|
528
|
+
const totalVbytes = txOverhead + Math.ceil(dataSize / 4); // Witness data is ~1/4 weight
|
|
529
|
+
|
|
530
|
+
const networkFee = totalVbytes * effectiveFeeRate;
|
|
531
|
+
const dustValue = 330; // Minimum output value
|
|
532
|
+
|
|
533
|
+
return {
|
|
534
|
+
totalSats: networkFee + dustValue,
|
|
535
|
+
breakdown: {
|
|
536
|
+
networkFee,
|
|
537
|
+
dataCost: Math.ceil(dataSize / 4) * effectiveFeeRate,
|
|
538
|
+
dustValue
|
|
539
|
+
},
|
|
540
|
+
feeRate: effectiveFeeRate,
|
|
541
|
+
dataSize,
|
|
542
|
+
targetLayer,
|
|
543
|
+
confidence
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
222
547
|
async publishToWeb(
|
|
223
548
|
asset: OriginalsAsset,
|
|
224
549
|
publisherDidOrSigner: string | ExternalSigner
|
|
@@ -1213,6 +1538,589 @@ export class LifecycleManager {
|
|
|
1213
1538
|
savingsPercentage
|
|
1214
1539
|
};
|
|
1215
1540
|
}
|
|
1541
|
+
|
|
1542
|
+
// ===== Clean Lifecycle API =====
|
|
1543
|
+
// These methods provide a cleaner, more intuitive API while maintaining
|
|
1544
|
+
// backward compatibility with the existing methods.
|
|
1545
|
+
|
|
1546
|
+
/**
|
|
1547
|
+
* Create a draft asset (did:peer layer)
|
|
1548
|
+
*
|
|
1549
|
+
* This is the entry point for creating new Originals. Draft assets are
|
|
1550
|
+
* stored locally and can be published or inscribed later.
|
|
1551
|
+
*
|
|
1552
|
+
* @param resources - Array of resources to include in the asset
|
|
1553
|
+
* @param options - Optional configuration including progress callback
|
|
1554
|
+
* @returns The newly created OriginalsAsset in did:peer layer
|
|
1555
|
+
*
|
|
1556
|
+
* @example
|
|
1557
|
+
* ```typescript
|
|
1558
|
+
* const draft = await sdk.lifecycle.createDraft([
|
|
1559
|
+
* { id: 'main', type: 'code', contentType: 'text/javascript', hash: '...' }
|
|
1560
|
+
* ], {
|
|
1561
|
+
* onProgress: (p) => console.log(p.message)
|
|
1562
|
+
* });
|
|
1563
|
+
* ```
|
|
1564
|
+
*/
|
|
1565
|
+
async createDraft(
|
|
1566
|
+
resources: AssetResource[],
|
|
1567
|
+
options?: LifecycleOperationOptions
|
|
1568
|
+
): Promise<OriginalsAsset> {
|
|
1569
|
+
const onProgress = options?.onProgress;
|
|
1570
|
+
|
|
1571
|
+
onProgress?.({
|
|
1572
|
+
phase: 'preparing',
|
|
1573
|
+
percentage: 0,
|
|
1574
|
+
message: 'Preparing draft asset...'
|
|
1575
|
+
});
|
|
1576
|
+
|
|
1577
|
+
onProgress?.({
|
|
1578
|
+
phase: 'validating',
|
|
1579
|
+
percentage: 20,
|
|
1580
|
+
message: 'Validating resources...'
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
try {
|
|
1584
|
+
onProgress?.({
|
|
1585
|
+
phase: 'processing',
|
|
1586
|
+
percentage: 50,
|
|
1587
|
+
message: 'Creating DID document...'
|
|
1588
|
+
});
|
|
1589
|
+
|
|
1590
|
+
const asset = await this.createAsset(resources);
|
|
1591
|
+
|
|
1592
|
+
onProgress?.({
|
|
1593
|
+
phase: 'complete',
|
|
1594
|
+
percentage: 100,
|
|
1595
|
+
message: 'Draft asset created successfully'
|
|
1596
|
+
});
|
|
1597
|
+
|
|
1598
|
+
return asset;
|
|
1599
|
+
} catch (error) {
|
|
1600
|
+
onProgress?.({
|
|
1601
|
+
phase: 'failed',
|
|
1602
|
+
percentage: 0,
|
|
1603
|
+
message: `Failed to create draft: ${error instanceof Error ? error.message : String(error)}`
|
|
1604
|
+
});
|
|
1605
|
+
throw error;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
/**
|
|
1610
|
+
* Publish an asset to the web (did:webvh layer)
|
|
1611
|
+
*
|
|
1612
|
+
* Migrates a draft asset from did:peer to did:webvh, making it publicly
|
|
1613
|
+
* discoverable via HTTPS.
|
|
1614
|
+
*
|
|
1615
|
+
* @param asset - The asset to publish (must be in did:peer layer)
|
|
1616
|
+
* @param publisherDidOrSigner - Publisher's DID or external signer
|
|
1617
|
+
* @param options - Optional configuration including progress callback
|
|
1618
|
+
* @returns The published OriginalsAsset in did:webvh layer
|
|
1619
|
+
*
|
|
1620
|
+
* @example
|
|
1621
|
+
* ```typescript
|
|
1622
|
+
* const published = await sdk.lifecycle.publish(draft, 'did:webvh:example.com:user');
|
|
1623
|
+
* ```
|
|
1624
|
+
*/
|
|
1625
|
+
async publish(
|
|
1626
|
+
asset: OriginalsAsset,
|
|
1627
|
+
publisherDidOrSigner: string | ExternalSigner,
|
|
1628
|
+
options?: LifecycleOperationOptions
|
|
1629
|
+
): Promise<OriginalsAsset> {
|
|
1630
|
+
const onProgress = options?.onProgress;
|
|
1631
|
+
|
|
1632
|
+
onProgress?.({
|
|
1633
|
+
phase: 'preparing',
|
|
1634
|
+
percentage: 0,
|
|
1635
|
+
message: 'Preparing to publish...'
|
|
1636
|
+
});
|
|
1637
|
+
|
|
1638
|
+
onProgress?.({
|
|
1639
|
+
phase: 'validating',
|
|
1640
|
+
percentage: 10,
|
|
1641
|
+
message: 'Validating migration...'
|
|
1642
|
+
});
|
|
1643
|
+
|
|
1644
|
+
// Pre-flight validation
|
|
1645
|
+
const validation = await this.validateMigration(asset, 'did:webvh');
|
|
1646
|
+
if (!validation.valid) {
|
|
1647
|
+
onProgress?.({
|
|
1648
|
+
phase: 'failed',
|
|
1649
|
+
percentage: 0,
|
|
1650
|
+
message: `Validation failed: ${validation.errors.join(', ')}`
|
|
1651
|
+
});
|
|
1652
|
+
throw new Error(`Migration validation failed: ${validation.errors.join(', ')}`);
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
try {
|
|
1656
|
+
onProgress?.({
|
|
1657
|
+
phase: 'processing',
|
|
1658
|
+
percentage: 30,
|
|
1659
|
+
message: 'Publishing resources...'
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
onProgress?.({
|
|
1663
|
+
phase: 'committing',
|
|
1664
|
+
percentage: 70,
|
|
1665
|
+
message: 'Finalizing publication...'
|
|
1666
|
+
});
|
|
1667
|
+
|
|
1668
|
+
const published = await this.publishToWeb(asset, publisherDidOrSigner);
|
|
1669
|
+
|
|
1670
|
+
onProgress?.({
|
|
1671
|
+
phase: 'complete',
|
|
1672
|
+
percentage: 100,
|
|
1673
|
+
message: 'Asset published successfully'
|
|
1674
|
+
});
|
|
1675
|
+
|
|
1676
|
+
return published;
|
|
1677
|
+
} catch (error) {
|
|
1678
|
+
onProgress?.({
|
|
1679
|
+
phase: 'failed',
|
|
1680
|
+
percentage: 0,
|
|
1681
|
+
message: `Failed to publish: ${error instanceof Error ? error.message : String(error)}`
|
|
1682
|
+
});
|
|
1683
|
+
throw error;
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
/**
|
|
1688
|
+
* Inscribe an asset on Bitcoin (did:btco layer)
|
|
1689
|
+
*
|
|
1690
|
+
* Permanently anchors an asset on the Bitcoin blockchain via Ordinals inscription.
|
|
1691
|
+
* This is an irreversible operation.
|
|
1692
|
+
*
|
|
1693
|
+
* @param asset - The asset to inscribe (must be in did:peer or did:webvh layer)
|
|
1694
|
+
* @param options - Optional configuration including fee rate and progress callback
|
|
1695
|
+
* @returns The inscribed OriginalsAsset in did:btco layer
|
|
1696
|
+
*
|
|
1697
|
+
* @example
|
|
1698
|
+
* ```typescript
|
|
1699
|
+
* const inscribed = await sdk.lifecycle.inscribe(published, {
|
|
1700
|
+
* feeRate: 15,
|
|
1701
|
+
* onProgress: (p) => console.log(`${p.percentage}%: ${p.message}`)
|
|
1702
|
+
* });
|
|
1703
|
+
* ```
|
|
1704
|
+
*/
|
|
1705
|
+
async inscribe(
|
|
1706
|
+
asset: OriginalsAsset,
|
|
1707
|
+
options?: LifecycleOperationOptions
|
|
1708
|
+
): Promise<OriginalsAsset> {
|
|
1709
|
+
const onProgress = options?.onProgress;
|
|
1710
|
+
const feeRate = options?.feeRate;
|
|
1711
|
+
|
|
1712
|
+
onProgress?.({
|
|
1713
|
+
phase: 'preparing',
|
|
1714
|
+
percentage: 0,
|
|
1715
|
+
message: 'Preparing inscription...'
|
|
1716
|
+
});
|
|
1717
|
+
|
|
1718
|
+
onProgress?.({
|
|
1719
|
+
phase: 'validating',
|
|
1720
|
+
percentage: 10,
|
|
1721
|
+
message: 'Validating migration...'
|
|
1722
|
+
});
|
|
1723
|
+
|
|
1724
|
+
// Pre-flight validation
|
|
1725
|
+
const validation = await this.validateMigration(asset, 'did:btco');
|
|
1726
|
+
if (!validation.valid) {
|
|
1727
|
+
onProgress?.({
|
|
1728
|
+
phase: 'failed',
|
|
1729
|
+
percentage: 0,
|
|
1730
|
+
message: `Validation failed: ${validation.errors.join(', ')}`
|
|
1731
|
+
});
|
|
1732
|
+
throw new Error(`Migration validation failed: ${validation.errors.join(', ')}`);
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
// Show cost estimate
|
|
1736
|
+
if (onProgress) {
|
|
1737
|
+
const estimate = await this.estimateCost(asset, 'did:btco', feeRate);
|
|
1738
|
+
onProgress({
|
|
1739
|
+
phase: 'preparing',
|
|
1740
|
+
percentage: 20,
|
|
1741
|
+
message: `Estimated cost: ${estimate.totalSats} sats (${estimate.feeRate} sat/vB)`
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
try {
|
|
1746
|
+
onProgress?.({
|
|
1747
|
+
phase: 'processing',
|
|
1748
|
+
percentage: 30,
|
|
1749
|
+
message: 'Creating commit transaction...',
|
|
1750
|
+
details: { currentStep: 1, totalSteps: 3 }
|
|
1751
|
+
});
|
|
1752
|
+
|
|
1753
|
+
onProgress?.({
|
|
1754
|
+
phase: 'committing',
|
|
1755
|
+
percentage: 60,
|
|
1756
|
+
message: 'Broadcasting reveal transaction...',
|
|
1757
|
+
details: { currentStep: 2, totalSteps: 3 }
|
|
1758
|
+
});
|
|
1759
|
+
|
|
1760
|
+
const inscribed = await this.inscribeOnBitcoin(asset, feeRate);
|
|
1761
|
+
|
|
1762
|
+
onProgress?.({
|
|
1763
|
+
phase: 'confirming',
|
|
1764
|
+
percentage: 90,
|
|
1765
|
+
message: 'Waiting for confirmation...',
|
|
1766
|
+
details: { currentStep: 3, totalSteps: 3 }
|
|
1767
|
+
});
|
|
1768
|
+
|
|
1769
|
+
onProgress?.({
|
|
1770
|
+
phase: 'complete',
|
|
1771
|
+
percentage: 100,
|
|
1772
|
+
message: 'Asset inscribed successfully'
|
|
1773
|
+
});
|
|
1774
|
+
|
|
1775
|
+
return inscribed;
|
|
1776
|
+
} catch (error) {
|
|
1777
|
+
onProgress?.({
|
|
1778
|
+
phase: 'failed',
|
|
1779
|
+
percentage: 0,
|
|
1780
|
+
message: `Failed to inscribe: ${error instanceof Error ? error.message : String(error)}`
|
|
1781
|
+
});
|
|
1782
|
+
throw error;
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
/**
|
|
1787
|
+
* Transfer ownership of a Bitcoin-inscribed asset
|
|
1788
|
+
*
|
|
1789
|
+
* Transfers an inscribed asset to a new owner. Only works for assets
|
|
1790
|
+
* in the did:btco layer.
|
|
1791
|
+
*
|
|
1792
|
+
* @param asset - The asset to transfer (must be in did:btco layer)
|
|
1793
|
+
* @param newOwnerAddress - Bitcoin address of the new owner
|
|
1794
|
+
* @param options - Optional configuration including progress callback
|
|
1795
|
+
* @returns The Bitcoin transaction for the transfer
|
|
1796
|
+
*
|
|
1797
|
+
* @example
|
|
1798
|
+
* ```typescript
|
|
1799
|
+
* const tx = await sdk.lifecycle.transfer(inscribed, 'bc1q...newowner');
|
|
1800
|
+
* console.log('Transfer txid:', tx.txid);
|
|
1801
|
+
* ```
|
|
1802
|
+
*/
|
|
1803
|
+
async transfer(
|
|
1804
|
+
asset: OriginalsAsset,
|
|
1805
|
+
newOwnerAddress: string,
|
|
1806
|
+
options?: LifecycleOperationOptions
|
|
1807
|
+
): Promise<BitcoinTransaction> {
|
|
1808
|
+
const onProgress = options?.onProgress;
|
|
1809
|
+
|
|
1810
|
+
onProgress?.({
|
|
1811
|
+
phase: 'preparing',
|
|
1812
|
+
percentage: 0,
|
|
1813
|
+
message: 'Preparing transfer...'
|
|
1814
|
+
});
|
|
1815
|
+
|
|
1816
|
+
onProgress?.({
|
|
1817
|
+
phase: 'validating',
|
|
1818
|
+
percentage: 10,
|
|
1819
|
+
message: 'Validating transfer...'
|
|
1820
|
+
});
|
|
1821
|
+
|
|
1822
|
+
// Validate asset is in correct layer
|
|
1823
|
+
if (asset.currentLayer !== 'did:btco') {
|
|
1824
|
+
onProgress?.({
|
|
1825
|
+
phase: 'failed',
|
|
1826
|
+
percentage: 0,
|
|
1827
|
+
message: 'Asset must be inscribed on Bitcoin before transfer'
|
|
1828
|
+
});
|
|
1829
|
+
throw new Error('Asset must be inscribed on Bitcoin before transfer');
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
try {
|
|
1833
|
+
onProgress?.({
|
|
1834
|
+
phase: 'processing',
|
|
1835
|
+
percentage: 30,
|
|
1836
|
+
message: 'Creating transfer transaction...'
|
|
1837
|
+
});
|
|
1838
|
+
|
|
1839
|
+
onProgress?.({
|
|
1840
|
+
phase: 'committing',
|
|
1841
|
+
percentage: 60,
|
|
1842
|
+
message: 'Broadcasting transaction...'
|
|
1843
|
+
});
|
|
1844
|
+
|
|
1845
|
+
const tx = await this.transferOwnership(asset, newOwnerAddress);
|
|
1846
|
+
|
|
1847
|
+
onProgress?.({
|
|
1848
|
+
phase: 'confirming',
|
|
1849
|
+
percentage: 90,
|
|
1850
|
+
message: 'Waiting for confirmation...',
|
|
1851
|
+
details: { transactionId: tx.txid }
|
|
1852
|
+
});
|
|
1853
|
+
|
|
1854
|
+
onProgress?.({
|
|
1855
|
+
phase: 'complete',
|
|
1856
|
+
percentage: 100,
|
|
1857
|
+
message: 'Transfer complete',
|
|
1858
|
+
details: { transactionId: tx.txid }
|
|
1859
|
+
});
|
|
1860
|
+
|
|
1861
|
+
return tx;
|
|
1862
|
+
} catch (error) {
|
|
1863
|
+
onProgress?.({
|
|
1864
|
+
phase: 'failed',
|
|
1865
|
+
percentage: 0,
|
|
1866
|
+
message: `Failed to transfer: ${error instanceof Error ? error.message : String(error)}`
|
|
1867
|
+
});
|
|
1868
|
+
throw error;
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
// ===== Cost Estimation =====
|
|
1873
|
+
|
|
1874
|
+
/**
|
|
1875
|
+
* Estimate the cost of migrating an asset to a target layer
|
|
1876
|
+
*
|
|
1877
|
+
* Returns a detailed breakdown of expected costs for Bitcoin operations.
|
|
1878
|
+
* For did:webvh migrations, costs are minimal (only hosting).
|
|
1879
|
+
*
|
|
1880
|
+
* @param asset - The asset to estimate costs for
|
|
1881
|
+
* @param targetLayer - The target layer for migration
|
|
1882
|
+
* @param feeRate - Optional fee rate override (sat/vB)
|
|
1883
|
+
* @returns Detailed cost estimate
|
|
1884
|
+
*
|
|
1885
|
+
* @example
|
|
1886
|
+
* ```typescript
|
|
1887
|
+
* const cost = await sdk.lifecycle.estimateCost(draft, 'did:btco', 10);
|
|
1888
|
+
* console.log(`Estimated cost: ${cost.totalSats} sats`);
|
|
1889
|
+
* ```
|
|
1890
|
+
*/
|
|
1891
|
+
async estimateCost(
|
|
1892
|
+
asset: OriginalsAsset,
|
|
1893
|
+
targetLayer: LayerType,
|
|
1894
|
+
feeRate?: number
|
|
1895
|
+
): Promise<CostEstimate> {
|
|
1896
|
+
// For webvh, costs are minimal (just hosting costs not applicable here)
|
|
1897
|
+
if (targetLayer === 'did:webvh') {
|
|
1898
|
+
return {
|
|
1899
|
+
totalSats: 0,
|
|
1900
|
+
breakdown: {
|
|
1901
|
+
networkFee: 0,
|
|
1902
|
+
dataCost: 0,
|
|
1903
|
+
dustValue: 0
|
|
1904
|
+
},
|
|
1905
|
+
feeRate: 0,
|
|
1906
|
+
dataSize: 0,
|
|
1907
|
+
targetLayer,
|
|
1908
|
+
confidence: 'high'
|
|
1909
|
+
};
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
// For btco, calculate inscription costs
|
|
1913
|
+
if (targetLayer === 'did:btco') {
|
|
1914
|
+
// Calculate manifest size
|
|
1915
|
+
const manifest = {
|
|
1916
|
+
assetId: asset.id,
|
|
1917
|
+
resources: asset.resources.map(res => ({
|
|
1918
|
+
id: res.id,
|
|
1919
|
+
hash: res.hash,
|
|
1920
|
+
contentType: res.contentType,
|
|
1921
|
+
url: res.url
|
|
1922
|
+
})),
|
|
1923
|
+
timestamp: new Date().toISOString()
|
|
1924
|
+
};
|
|
1925
|
+
const dataSize = Buffer.from(JSON.stringify(manifest)).length;
|
|
1926
|
+
|
|
1927
|
+
// Get fee rate from oracle or use provided/default
|
|
1928
|
+
let effectiveFeeRate = feeRate;
|
|
1929
|
+
let confidence: 'low' | 'medium' | 'high' = 'medium';
|
|
1930
|
+
|
|
1931
|
+
if (!effectiveFeeRate) {
|
|
1932
|
+
// Try to get from fee oracle
|
|
1933
|
+
if (this.config.feeOracle) {
|
|
1934
|
+
try {
|
|
1935
|
+
effectiveFeeRate = await this.config.feeOracle.estimateFeeRate(1);
|
|
1936
|
+
confidence = 'high';
|
|
1937
|
+
} catch {
|
|
1938
|
+
// Fallback to default
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
// Try ordinals provider
|
|
1943
|
+
if (!effectiveFeeRate && this.config.ordinalsProvider) {
|
|
1944
|
+
try {
|
|
1945
|
+
effectiveFeeRate = await this.config.ordinalsProvider.estimateFee(1);
|
|
1946
|
+
confidence = 'medium';
|
|
1947
|
+
} catch {
|
|
1948
|
+
// Fallback to default
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
// Use default if no oracle available
|
|
1953
|
+
if (!effectiveFeeRate) {
|
|
1954
|
+
effectiveFeeRate = 10; // Conservative default
|
|
1955
|
+
confidence = 'low';
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
// Transaction structure estimation:
|
|
1960
|
+
// - Commit transaction: ~200 vB base + input overhead
|
|
1961
|
+
// - Reveal transaction: ~200 vB base + inscription envelope + data
|
|
1962
|
+
// Inscription envelope overhead: ~122 bytes
|
|
1963
|
+
const commitTxSize = 200;
|
|
1964
|
+
const revealTxSize = 200 + 122 + dataSize;
|
|
1965
|
+
const totalSize = commitTxSize + revealTxSize;
|
|
1966
|
+
|
|
1967
|
+
const networkFee = totalSize * effectiveFeeRate;
|
|
1968
|
+
const dustValue = 546; // Standard dust limit for P2TR
|
|
1969
|
+
const totalSats = networkFee + dustValue;
|
|
1970
|
+
|
|
1971
|
+
return {
|
|
1972
|
+
totalSats,
|
|
1973
|
+
breakdown: {
|
|
1974
|
+
networkFee,
|
|
1975
|
+
dataCost: dataSize * effectiveFeeRate,
|
|
1976
|
+
dustValue
|
|
1977
|
+
},
|
|
1978
|
+
feeRate: effectiveFeeRate,
|
|
1979
|
+
dataSize,
|
|
1980
|
+
targetLayer,
|
|
1981
|
+
confidence
|
|
1982
|
+
};
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
// For peer layer (no migration needed)
|
|
1986
|
+
return {
|
|
1987
|
+
totalSats: 0,
|
|
1988
|
+
breakdown: {
|
|
1989
|
+
networkFee: 0,
|
|
1990
|
+
dataCost: 0,
|
|
1991
|
+
dustValue: 0
|
|
1992
|
+
},
|
|
1993
|
+
feeRate: 0,
|
|
1994
|
+
dataSize: 0,
|
|
1995
|
+
targetLayer,
|
|
1996
|
+
confidence: 'high'
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
// ===== Migration Validation =====
|
|
2001
|
+
|
|
2002
|
+
/**
|
|
2003
|
+
* Validate whether an asset can be migrated to a target layer
|
|
2004
|
+
*
|
|
2005
|
+
* Performs comprehensive pre-flight checks including:
|
|
2006
|
+
* - Valid layer transition
|
|
2007
|
+
* - Resource integrity
|
|
2008
|
+
* - Credential validity
|
|
2009
|
+
* - DID document structure
|
|
2010
|
+
* - Bitcoin readiness (for did:btco)
|
|
2011
|
+
*
|
|
2012
|
+
* @param asset - The asset to validate
|
|
2013
|
+
* @param targetLayer - The target layer for migration
|
|
2014
|
+
* @returns Detailed validation result
|
|
2015
|
+
*
|
|
2016
|
+
* @example
|
|
2017
|
+
* ```typescript
|
|
2018
|
+
* const validation = await sdk.lifecycle.validateMigration(draft, 'did:webvh');
|
|
2019
|
+
* if (!validation.valid) {
|
|
2020
|
+
* console.error('Cannot migrate:', validation.errors);
|
|
2021
|
+
* }
|
|
2022
|
+
* ```
|
|
2023
|
+
*/
|
|
2024
|
+
async validateMigration(
|
|
2025
|
+
asset: OriginalsAsset,
|
|
2026
|
+
targetLayer: LayerType
|
|
2027
|
+
): Promise<MigrationValidation> {
|
|
2028
|
+
const errors: string[] = [];
|
|
2029
|
+
const warnings: string[] = [];
|
|
2030
|
+
const checks = {
|
|
2031
|
+
layerTransition: false,
|
|
2032
|
+
resourcesValid: false,
|
|
2033
|
+
credentialsValid: false,
|
|
2034
|
+
didDocumentValid: false,
|
|
2035
|
+
bitcoinReadiness: undefined as boolean | undefined
|
|
2036
|
+
};
|
|
2037
|
+
|
|
2038
|
+
// Check layer transition validity
|
|
2039
|
+
const validTransitions: Record<LayerType, LayerType[]> = {
|
|
2040
|
+
'did:peer': ['did:webvh', 'did:btco'],
|
|
2041
|
+
'did:webvh': ['did:btco'],
|
|
2042
|
+
'did:btco': []
|
|
2043
|
+
};
|
|
2044
|
+
|
|
2045
|
+
if (validTransitions[asset.currentLayer].includes(targetLayer)) {
|
|
2046
|
+
checks.layerTransition = true;
|
|
2047
|
+
} else {
|
|
2048
|
+
errors.push(`Invalid migration from ${asset.currentLayer} to ${targetLayer}`);
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
// Validate resources
|
|
2052
|
+
if (asset.resources.length === 0) {
|
|
2053
|
+
errors.push('Asset must have at least one resource');
|
|
2054
|
+
} else {
|
|
2055
|
+
let resourcesValid = true;
|
|
2056
|
+
for (const resource of asset.resources) {
|
|
2057
|
+
if (!resource.id || !resource.type || !resource.contentType || !resource.hash) {
|
|
2058
|
+
resourcesValid = false;
|
|
2059
|
+
errors.push(`Resource ${resource.id || 'unknown'} is missing required fields`);
|
|
2060
|
+
}
|
|
2061
|
+
if (resource.hash && !/^[0-9a-fA-F]+$/.test(resource.hash)) {
|
|
2062
|
+
resourcesValid = false;
|
|
2063
|
+
errors.push(`Resource ${resource.id} has invalid hash format`);
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
checks.resourcesValid = resourcesValid;
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
// Validate DID document
|
|
2070
|
+
if (asset.did && asset.did.id) {
|
|
2071
|
+
checks.didDocumentValid = true;
|
|
2072
|
+
} else {
|
|
2073
|
+
errors.push('Asset has invalid or missing DID document');
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
// Validate credentials (structural check)
|
|
2077
|
+
if (asset.credentials.length > 0) {
|
|
2078
|
+
let credentialsValid = true;
|
|
2079
|
+
for (const cred of asset.credentials) {
|
|
2080
|
+
if (!cred.type || !cred.issuer || !cred.issuanceDate) {
|
|
2081
|
+
credentialsValid = false;
|
|
2082
|
+
warnings.push('Asset has credentials with missing fields');
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
checks.credentialsValid = credentialsValid;
|
|
2086
|
+
} else {
|
|
2087
|
+
checks.credentialsValid = true; // No credentials is valid
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
// Bitcoin-specific checks
|
|
2091
|
+
if (targetLayer === 'did:btco') {
|
|
2092
|
+
checks.bitcoinReadiness = true;
|
|
2093
|
+
|
|
2094
|
+
// Check if ordinals provider is configured
|
|
2095
|
+
if (!this.config.ordinalsProvider) {
|
|
2096
|
+
checks.bitcoinReadiness = false;
|
|
2097
|
+
errors.push('Bitcoin inscription requires an ordinalsProvider to be configured');
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
// Warn about large data sizes
|
|
2101
|
+
const manifestSize = JSON.stringify({
|
|
2102
|
+
assetId: asset.id,
|
|
2103
|
+
resources: asset.resources.map(r => ({
|
|
2104
|
+
id: r.id,
|
|
2105
|
+
hash: r.hash,
|
|
2106
|
+
contentType: r.contentType
|
|
2107
|
+
}))
|
|
2108
|
+
}).length;
|
|
2109
|
+
|
|
2110
|
+
if (manifestSize > 100000) {
|
|
2111
|
+
warnings.push(`Large manifest size (${manifestSize} bytes) may result in high inscription costs`);
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
return {
|
|
2116
|
+
valid: errors.length === 0,
|
|
2117
|
+
errors,
|
|
2118
|
+
warnings,
|
|
2119
|
+
currentLayer: asset.currentLayer,
|
|
2120
|
+
targetLayer,
|
|
2121
|
+
checks
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
1216
2124
|
}
|
|
1217
2125
|
|
|
1218
2126
|
|