@originals/sdk 1.8.2 → 1.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/package.json +5 -6
  2. package/src/adapters/FeeOracleMock.ts +0 -9
  3. package/src/adapters/index.ts +0 -5
  4. package/src/adapters/providers/OrdHttpProvider.ts +0 -126
  5. package/src/adapters/providers/OrdMockProvider.ts +0 -101
  6. package/src/adapters/types.ts +0 -66
  7. package/src/bitcoin/BitcoinManager.ts +0 -329
  8. package/src/bitcoin/BroadcastClient.ts +0 -54
  9. package/src/bitcoin/OrdinalsClient.ts +0 -120
  10. package/src/bitcoin/PSBTBuilder.ts +0 -106
  11. package/src/bitcoin/fee-calculation.ts +0 -38
  12. package/src/bitcoin/providers/OrdNodeProvider.ts +0 -92
  13. package/src/bitcoin/providers/OrdinalsProvider.ts +0 -56
  14. package/src/bitcoin/providers/types.ts +0 -59
  15. package/src/bitcoin/transactions/commit.ts +0 -465
  16. package/src/bitcoin/transactions/index.ts +0 -13
  17. package/src/bitcoin/transfer.ts +0 -43
  18. package/src/bitcoin/utxo-selection.ts +0 -322
  19. package/src/bitcoin/utxo.ts +0 -113
  20. package/src/cel/ExternalReferenceManager.ts +0 -87
  21. package/src/cel/OriginalsCel.ts +0 -460
  22. package/src/cel/algorithms/createEventLog.ts +0 -68
  23. package/src/cel/algorithms/deactivateEventLog.ts +0 -109
  24. package/src/cel/algorithms/index.ts +0 -11
  25. package/src/cel/algorithms/updateEventLog.ts +0 -99
  26. package/src/cel/algorithms/verifyEventLog.ts +0 -306
  27. package/src/cel/algorithms/witnessEvent.ts +0 -87
  28. package/src/cel/cli/create.ts +0 -330
  29. package/src/cel/cli/index.ts +0 -383
  30. package/src/cel/cli/inspect.ts +0 -549
  31. package/src/cel/cli/migrate.ts +0 -473
  32. package/src/cel/cli/verify.ts +0 -249
  33. package/src/cel/hash.ts +0 -71
  34. package/src/cel/index.ts +0 -16
  35. package/src/cel/layers/BtcoCelManager.ts +0 -408
  36. package/src/cel/layers/PeerCelManager.ts +0 -371
  37. package/src/cel/layers/WebVHCelManager.ts +0 -361
  38. package/src/cel/layers/index.ts +0 -27
  39. package/src/cel/serialization/cbor.ts +0 -189
  40. package/src/cel/serialization/index.ts +0 -10
  41. package/src/cel/serialization/json.ts +0 -209
  42. package/src/cel/types.ts +0 -160
  43. package/src/cel/witnesses/BitcoinWitness.ts +0 -184
  44. package/src/cel/witnesses/HttpWitness.ts +0 -241
  45. package/src/cel/witnesses/WitnessService.ts +0 -51
  46. package/src/cel/witnesses/index.ts +0 -11
  47. package/src/contexts/credentials-v1.json +0 -237
  48. package/src/contexts/credentials-v2-examples.json +0 -5
  49. package/src/contexts/credentials-v2.json +0 -340
  50. package/src/contexts/credentials.json +0 -237
  51. package/src/contexts/data-integrity-v2.json +0 -81
  52. package/src/contexts/dids.json +0 -58
  53. package/src/contexts/ed255192020.json +0 -93
  54. package/src/contexts/ordinals-plus.json +0 -23
  55. package/src/contexts/originals.json +0 -22
  56. package/src/core/OriginalsSDK.ts +0 -420
  57. package/src/crypto/Multikey.ts +0 -194
  58. package/src/crypto/Signer.ts +0 -262
  59. package/src/crypto/noble-init.ts +0 -138
  60. package/src/did/BtcoDidResolver.ts +0 -231
  61. package/src/did/DIDManager.ts +0 -705
  62. package/src/did/Ed25519Verifier.ts +0 -68
  63. package/src/did/KeyManager.ts +0 -239
  64. package/src/did/WebVHManager.ts +0 -499
  65. package/src/did/createBtcoDidDocument.ts +0 -60
  66. package/src/did/providers/OrdinalsClientProviderAdapter.ts +0 -68
  67. package/src/events/EventEmitter.ts +0 -222
  68. package/src/events/index.ts +0 -19
  69. package/src/events/types.ts +0 -331
  70. package/src/examples/basic-usage.ts +0 -78
  71. package/src/examples/create-module-original.ts +0 -435
  72. package/src/examples/full-lifecycle-flow.ts +0 -514
  73. package/src/examples/run.ts +0 -60
  74. package/src/index.ts +0 -204
  75. package/src/kinds/KindRegistry.ts +0 -320
  76. package/src/kinds/index.ts +0 -74
  77. package/src/kinds/types.ts +0 -470
  78. package/src/kinds/validators/AgentValidator.ts +0 -257
  79. package/src/kinds/validators/AppValidator.ts +0 -211
  80. package/src/kinds/validators/DatasetValidator.ts +0 -242
  81. package/src/kinds/validators/DocumentValidator.ts +0 -311
  82. package/src/kinds/validators/MediaValidator.ts +0 -269
  83. package/src/kinds/validators/ModuleValidator.ts +0 -225
  84. package/src/kinds/validators/base.ts +0 -276
  85. package/src/kinds/validators/index.ts +0 -12
  86. package/src/lifecycle/BatchOperations.ts +0 -381
  87. package/src/lifecycle/LifecycleManager.ts +0 -2156
  88. package/src/lifecycle/OriginalsAsset.ts +0 -524
  89. package/src/lifecycle/ProvenanceQuery.ts +0 -280
  90. package/src/lifecycle/ResourceVersioning.ts +0 -163
  91. package/src/migration/MigrationManager.ts +0 -587
  92. package/src/migration/audit/AuditLogger.ts +0 -176
  93. package/src/migration/checkpoint/CheckpointManager.ts +0 -112
  94. package/src/migration/checkpoint/CheckpointStorage.ts +0 -101
  95. package/src/migration/index.ts +0 -33
  96. package/src/migration/operations/BaseMigration.ts +0 -126
  97. package/src/migration/operations/PeerToBtcoMigration.ts +0 -105
  98. package/src/migration/operations/PeerToWebvhMigration.ts +0 -62
  99. package/src/migration/operations/WebvhToBtcoMigration.ts +0 -105
  100. package/src/migration/rollback/RollbackManager.ts +0 -170
  101. package/src/migration/state/StateMachine.ts +0 -92
  102. package/src/migration/state/StateTracker.ts +0 -156
  103. package/src/migration/types.ts +0 -356
  104. package/src/migration/validation/BitcoinValidator.ts +0 -107
  105. package/src/migration/validation/CredentialValidator.ts +0 -62
  106. package/src/migration/validation/DIDCompatibilityValidator.ts +0 -151
  107. package/src/migration/validation/LifecycleValidator.ts +0 -64
  108. package/src/migration/validation/StorageValidator.ts +0 -79
  109. package/src/migration/validation/ValidationPipeline.ts +0 -213
  110. package/src/resources/ResourceManager.ts +0 -655
  111. package/src/resources/index.ts +0 -21
  112. package/src/resources/types.ts +0 -202
  113. package/src/storage/LocalStorageAdapter.ts +0 -64
  114. package/src/storage/MemoryStorageAdapter.ts +0 -29
  115. package/src/storage/StorageAdapter.ts +0 -25
  116. package/src/storage/index.ts +0 -3
  117. package/src/types/bitcoin.ts +0 -98
  118. package/src/types/common.ts +0 -92
  119. package/src/types/credentials.ts +0 -89
  120. package/src/types/did.ts +0 -31
  121. package/src/types/external-shims.d.ts +0 -53
  122. package/src/types/index.ts +0 -7
  123. package/src/types/network.ts +0 -178
  124. package/src/utils/EventLogger.ts +0 -298
  125. package/src/utils/Logger.ts +0 -324
  126. package/src/utils/MetricsCollector.ts +0 -358
  127. package/src/utils/bitcoin-address.ts +0 -132
  128. package/src/utils/cbor.ts +0 -31
  129. package/src/utils/encoding.ts +0 -135
  130. package/src/utils/hash.ts +0 -12
  131. package/src/utils/retry.ts +0 -46
  132. package/src/utils/satoshi-validation.ts +0 -196
  133. package/src/utils/serialization.ts +0 -102
  134. package/src/utils/telemetry.ts +0 -44
  135. package/src/utils/validation.ts +0 -123
  136. package/src/vc/CredentialManager.ts +0 -955
  137. package/src/vc/Issuer.ts +0 -105
  138. package/src/vc/Verifier.ts +0 -54
  139. package/src/vc/cryptosuites/bbs.ts +0 -253
  140. package/src/vc/cryptosuites/bbsSimple.ts +0 -21
  141. package/src/vc/cryptosuites/eddsa.ts +0 -99
  142. package/src/vc/documentLoader.ts +0 -81
  143. package/src/vc/proofs/data-integrity.ts +0 -33
  144. package/src/vc/utils/jsonld.ts +0 -18
package/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "@originals/sdk",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
4
4
  "description": "TypeScript SDK for the Originals Protocol - creating, discovering, and transferring digital assets with cryptographically verifiable provenance",
5
5
  "type": "module",
6
- "main": "src/index.ts",
7
- "types": "src/index.ts",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
8
  "files": [
9
- "src",
10
9
  "dist"
11
10
  ],
12
11
  "bin": {
@@ -14,8 +13,8 @@
14
13
  },
15
14
  "exports": {
16
15
  ".": {
17
- "types": "./src/index.ts",
18
- "import": "./src/index.ts"
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js"
19
18
  }
20
19
  },
21
20
  "scripts": {
@@ -1,9 +0,0 @@
1
- import { FeeOracleAdapter } from './types';
2
-
3
- export class FeeOracleMock implements FeeOracleAdapter {
4
- constructor(private feeRate = 7) {}
5
- estimateFeeRate(targetBlocks = 1): Promise<number> {
6
- return Promise.resolve(Math.max(1, this.feeRate - (targetBlocks - 1)));
7
- }
8
- }
9
-
@@ -1,5 +0,0 @@
1
- export * from './types';
2
- export * from './FeeOracleMock';
3
- export * from './providers/OrdMockProvider';
4
- export * from './providers/OrdHttpProvider';
5
-
@@ -1,126 +0,0 @@
1
- /* istanbul ignore file */
2
- import type { OrdinalsProvider } from '../types';
3
-
4
- interface HttpProviderOptions {
5
- baseUrl: string;
6
- }
7
-
8
- function buildUrl(baseUrl: string, path: string): string {
9
- const base = baseUrl.replace(/\/$/, '');
10
- return `${base}${path.startsWith('/') ? '' : '/'}${path}`;
11
- }
12
-
13
- async function fetchJson<T>(url: string): Promise<T | null> {
14
- const res = await (globalThis as any).fetch(url, {
15
- headers: {
16
- 'Accept': 'application/json'
17
- }
18
- });
19
- if (!res.ok) return null;
20
- return (await res.json()) as T;
21
- }
22
-
23
- export class OrdHttpProvider implements OrdinalsProvider {
24
- private readonly baseUrl: string;
25
-
26
- constructor(options: HttpProviderOptions) {
27
- if (!options?.baseUrl) {
28
- throw new Error('OrdHttpProvider requires baseUrl');
29
- }
30
- this.baseUrl = options.baseUrl;
31
- }
32
-
33
- async getInscriptionById(id: string) {
34
- if (!id) return null;
35
- const data = await fetchJson<any>(buildUrl(this.baseUrl, `/inscription/${id}`));
36
- if (!data) return null;
37
- // Expecting a shape similar to Ordinals indexers; adapt minimally
38
- const ownerOutput: string | undefined = data.owner_output;
39
- let txid = data.txid || 'unknown';
40
- let vout = typeof data.vout === 'number' ? data.vout : 0;
41
- if (ownerOutput && ownerOutput.includes(':')) {
42
- const [tid, v] = ownerOutput.split(':');
43
- txid = tid;
44
- vout = Number(v) || 0;
45
- }
46
-
47
- const contentType = data.content_type || 'application/octet-stream';
48
- const contentUrl = data.content_url || buildUrl(this.baseUrl, `/content/${id}`);
49
- const contentRes = await (globalThis as any).fetch(contentUrl);
50
- if (!contentRes.ok) return null;
51
- const buf = (globalThis as any).Buffer
52
- ? (globalThis as any).Buffer.from(new Uint8Array(await contentRes.arrayBuffer()))
53
- : new Uint8Array(await contentRes.arrayBuffer()) as any;
54
-
55
- return {
56
- inscriptionId: data.inscription_id || id,
57
- content: buf,
58
- contentType,
59
- txid,
60
- vout,
61
- satoshi: String(data.sat ?? ''),
62
- blockHeight: data.block_height
63
- };
64
- }
65
-
66
- async getInscriptionsBySatoshi(satoshi: string) {
67
- if (!satoshi) return [];
68
- const data = await fetchJson<any>(buildUrl(this.baseUrl, `/sat/${satoshi}`));
69
- const ids: string[] = Array.isArray(data?.inscription_ids) ? data.inscription_ids : [];
70
- return ids.map((inscriptionId) => ({ inscriptionId }));
71
- }
72
-
73
- async broadcastTransaction(_txHexOrObj: unknown): Promise<string> {
74
- // For example purposes only, return a placeholder
75
- return 'broadcast-txid';
76
- }
77
-
78
- async getTransactionStatus(_txid: string) {
79
- return { confirmed: false };
80
- }
81
-
82
- async estimateFee(blocks: number = 1): Promise<number> {
83
- // Basic fallback: some providers expose fee estimates; for example purposes, return linear estimate
84
- return 5 * Math.max(1, blocks);
85
- }
86
-
87
- async createInscription(params: { data: any; contentType: string; feeRate?: number; }) {
88
- // Example placeholder: a real implementation would POST to a service
89
- // Here we return a deterministic mock-like result to avoid network coupling in code
90
- const inscriptionId = `insc-${Math.random().toString(36).slice(2)}`;
91
- const txid = `tx-${Math.random().toString(36).slice(2)}`;
92
- return {
93
- inscriptionId,
94
- revealTxId: txid,
95
- txid,
96
- vout: 0,
97
- blockHeight: undefined,
98
- content: params.data,
99
- contentType: params.contentType,
100
- feeRate: params.feeRate
101
- };
102
- }
103
-
104
- async transferInscription(inscriptionId: string, _toAddress: string, _options?: { feeRate?: number }) {
105
- if (!inscriptionId) throw new Error('inscriptionId required');
106
- const txid = `tx-${Math.random().toString(36).slice(2)}`;
107
- return {
108
- txid,
109
- vin: [{ txid: 'prev', vout: 0 }],
110
- vout: [{ value: 546, scriptPubKey: 'script' }],
111
- fee: 100,
112
- confirmations: 0
113
- };
114
- }
115
- }
116
-
117
- export async function createOrdinalsProviderFromEnv(): Promise<OrdinalsProvider> {
118
- const useLive = String(((globalThis as any).process?.env?.USE_LIVE_ORD_PROVIDER) || '').toLowerCase() === 'true';
119
- if (useLive) {
120
- const baseUrl = ((globalThis as any).process?.env?.ORD_PROVIDER_BASE_URL) || 'https://ord.example.com/api';
121
- return new OrdHttpProvider({ baseUrl });
122
- }
123
- const mod = await import('./OrdMockProvider');
124
- return new mod.OrdMockProvider();
125
- }
126
-
@@ -1,101 +0,0 @@
1
- import { OrdinalsProvider } from '../types';
2
-
3
- export interface OrdMockState {
4
- inscriptionsById: Map<string, {
5
- inscriptionId: string;
6
- content: Buffer;
7
- contentType: string;
8
- txid: string;
9
- vout: number;
10
- satoshi?: string;
11
- blockHeight?: number;
12
- }>;
13
- inscriptionsBySatoshi: Map<string, string[]>;
14
- feeRate: number;
15
- }
16
-
17
- export class OrdMockProvider implements OrdinalsProvider {
18
- private state: OrdMockState;
19
-
20
- constructor(state?: Partial<OrdMockState>) {
21
- this.state = {
22
- inscriptionsById: new Map(),
23
- inscriptionsBySatoshi: new Map(),
24
- feeRate: 5,
25
- ...state
26
- } as OrdMockState;
27
- }
28
-
29
- async getInscriptionById(id: string) {
30
- const rec = this.state.inscriptionsById.get(id);
31
- return rec ? { ...rec } : null;
32
- }
33
-
34
- async getInscriptionsBySatoshi(satoshi: string) {
35
- const list = this.state.inscriptionsBySatoshi.get(satoshi) || [];
36
- return list.map((inscriptionId) => ({ inscriptionId }));
37
- }
38
-
39
- async broadcastTransaction(_txHexOrObj: unknown): Promise<string> {
40
- return 'mock-broadcast-txid';
41
- }
42
-
43
- async getTransactionStatus(txid: string) {
44
- return { confirmed: true, blockHeight: 1, confirmations: 1 };
45
- }
46
-
47
- async estimateFee(blocks = 1): Promise<number> {
48
- return Math.max(1, this.state.feeRate - (blocks - 1));
49
- }
50
-
51
- async createInscription(params: { data: Buffer; contentType: string; feeRate?: number; }) {
52
- const inscriptionId = `insc-${Math.random().toString(36).slice(2)}`;
53
- const txid = `tx-${Math.random().toString(36).slice(2)}`;
54
- // Generate a valid numeric satoshi identifier (not sat-123 format)
55
- const satoshi = `${Math.floor(Math.random() * 1e12)}`;
56
- const vout = 0;
57
- const record = {
58
- inscriptionId,
59
- content: params.data,
60
- contentType: params.contentType,
61
- txid,
62
- vout,
63
- satoshi,
64
- blockHeight: 1
65
- };
66
- this.state.inscriptionsById.set(inscriptionId, record);
67
- const list = this.state.inscriptionsBySatoshi.get(satoshi) || [];
68
- list.push(inscriptionId);
69
- this.state.inscriptionsBySatoshi.set(satoshi, list);
70
- return {
71
- inscriptionId,
72
- revealTxId: txid,
73
- commitTxId: undefined,
74
- satoshi,
75
- txid,
76
- vout,
77
- blockHeight: 1,
78
- content: params.data,
79
- contentType: params.contentType,
80
- feeRate: params.feeRate
81
- };
82
- }
83
-
84
- async transferInscription(inscriptionId: string, _toAddress: string, _options?: { feeRate?: number }) {
85
- const rec = this.state.inscriptionsById.get(inscriptionId);
86
- if (!rec) {
87
- return Promise.reject(new Error('inscription not found'));
88
- }
89
- const txid = `tx-${Math.random().toString(36).slice(2)}`;
90
- return {
91
- txid,
92
- vin: [{ txid: rec.txid, vout: rec.vout }],
93
- vout: [{ value: 546, scriptPubKey: 'script' }],
94
- fee: 100,
95
- blockHeight: 1,
96
- confirmations: 0,
97
- satoshi: rec.satoshi
98
- };
99
- }
100
- }
101
-
@@ -1,66 +0,0 @@
1
- export interface StoragePutOptions {
2
- contentType?: string;
3
- cacheControl?: string;
4
- }
5
-
6
- export interface StorageGetResult {
7
- content: Buffer;
8
- contentType: string;
9
- }
10
-
11
- export interface StorageAdapter {
12
- put(objectKey: string, data: Buffer | string, options?: StoragePutOptions): Promise<string>;
13
- get(objectKey: string): Promise<StorageGetResult | null>;
14
- delete?(objectKey: string): Promise<boolean>;
15
- }
16
-
17
- export interface FeeOracleAdapter {
18
- // Returns sats/vB (or feerate unit appropriate to the network) for the given target blocks
19
- estimateFeeRate(targetBlocks?: number): Promise<number>;
20
- }
21
-
22
- export interface OrdinalsProvider {
23
- getInscriptionById(id: string): Promise<{
24
- inscriptionId: string;
25
- content: Buffer;
26
- contentType: string;
27
- txid: string;
28
- vout: number;
29
- satoshi?: string;
30
- blockHeight?: number;
31
- } | null>;
32
- getInscriptionsBySatoshi(satoshi: string): Promise<Array<{ inscriptionId: string }>>;
33
- broadcastTransaction(txHexOrObj: unknown): Promise<string>;
34
- getTransactionStatus(txid: string): Promise<{ confirmed: boolean; blockHeight?: number; confirmations?: number }>;
35
- estimateFee(blocks?: number): Promise<number>;
36
- createInscription(params: {
37
- data: Buffer;
38
- contentType: string;
39
- feeRate?: number;
40
- }): Promise<{
41
- inscriptionId: string;
42
- revealTxId: string;
43
- commitTxId?: string;
44
- satoshi?: string;
45
- txid?: string;
46
- vout?: number;
47
- blockHeight?: number;
48
- content?: Buffer;
49
- contentType?: string;
50
- feeRate?: number;
51
- }>;
52
- transferInscription(
53
- inscriptionId: string,
54
- toAddress: string,
55
- options?: { feeRate?: number }
56
- ): Promise<{
57
- txid: string;
58
- vin: Array<{ txid: string; vout: number }>;
59
- vout: Array<{ value: number; scriptPubKey: string; address?: string }>;
60
- fee: number;
61
- blockHeight?: number;
62
- confirmations?: number;
63
- satoshi?: string;
64
- }>;
65
- }
66
-
@@ -1,329 +0,0 @@
1
- import {
2
- OriginalsConfig,
3
- OrdinalsInscription,
4
- BitcoinTransaction,
5
- DUST_LIMIT_SATS
6
- } from '../types';
7
- import type { FeeOracleAdapter, OrdinalsProvider } from '../adapters';
8
- import { emitTelemetry, StructuredError } from '../utils/telemetry';
9
- import { validateBitcoinAddress } from '../utils/bitcoin-address';
10
- import { validateSatoshiNumber } from '../utils/satoshi-validation';
11
-
12
- export class BitcoinManager {
13
- private readonly feeOracle?: FeeOracleAdapter;
14
- private readonly ord?: OrdinalsProvider;
15
-
16
- constructor(private config: OriginalsConfig) {
17
- this.feeOracle = config.feeOracle;
18
- this.ord = config.ordinalsProvider;
19
- }
20
-
21
- private async resolveFeeRate(targetBlocks = 1, provided?: number): Promise<number | undefined> {
22
- // 1) Prefer external fee oracle
23
- if (this.feeOracle) {
24
- try {
25
- const estimated = await this.feeOracle.estimateFeeRate(targetBlocks);
26
- if (typeof estimated === 'number' && Number.isFinite(estimated) && estimated > 0) {
27
- emitTelemetry(this.config.telemetry, {
28
- name: 'bitcoin.fee.estimated',
29
- attributes: { feeRate: estimated, source: 'feeOracle' }
30
- });
31
- return estimated;
32
- }
33
- } catch (error) {
34
- emitTelemetry(this.config.telemetry, {
35
- name: 'bitcoin.fee.error',
36
- level: 'warn',
37
- attributes: { error: String(error), source: 'feeOracle' }
38
- });
39
- }
40
- }
41
-
42
- // 2) Fallback to ordinals provider if present
43
- if (this.ord) {
44
- try {
45
- const estimated = await this.ord.estimateFee(targetBlocks);
46
- if (typeof estimated === 'number' && Number.isFinite(estimated) && estimated > 0) {
47
- emitTelemetry(this.config.telemetry, {
48
- name: 'bitcoin.fee.estimated',
49
- attributes: { feeRate: estimated, source: 'ordinalsProvider' }
50
- });
51
- return estimated;
52
- }
53
- } catch (error) {
54
- emitTelemetry(this.config.telemetry, {
55
- name: 'bitcoin.fee.error',
56
- level: 'warn',
57
- attributes: { error: String(error), source: 'ordinalsProvider' }
58
- });
59
- }
60
- }
61
-
62
- // 3) If caller provided a valid non-zero fee rate, use it as last resort
63
- if (typeof provided === 'number' && Number.isFinite(provided) && provided > 0) {
64
- return provided;
65
- }
66
-
67
- return undefined;
68
- }
69
-
70
- async inscribeData(
71
- data: any,
72
- contentType: string,
73
- feeRate?: number
74
- ): Promise<OrdinalsInscription> {
75
- // Input validation
76
- if (!data) {
77
- throw new StructuredError('INVALID_INPUT', 'Data to inscribe cannot be null or undefined');
78
- }
79
- if (!contentType || typeof contentType !== 'string') {
80
- throw new StructuredError('INVALID_INPUT', 'Content type must be a non-empty string');
81
- }
82
- // Validate contentType is a valid MIME type
83
- if (!/^[a-zA-Z0-9][a-zA-Z0-9!#$&^_.+-]{0,126}\/[a-zA-Z0-9][a-zA-Z0-9!#$&^_.+-]{0,126}$/.test(contentType)) {
84
- throw new StructuredError('INVALID_INPUT', `Invalid MIME type format: ${contentType}`);
85
- }
86
- if (feeRate !== undefined && (typeof feeRate !== 'number' || feeRate <= 0 || !Number.isFinite(feeRate))) {
87
- throw new StructuredError('INVALID_INPUT', 'Fee rate must be a positive number');
88
- }
89
- // Security: Reject extremely high fee rates to prevent accidental fund drainage
90
- const MAX_REASONABLE_FEE_RATE = 10_000; // sat/vB
91
- if (feeRate !== undefined && feeRate > MAX_REASONABLE_FEE_RATE) {
92
- throw new StructuredError('INVALID_INPUT', `Fee rate ${feeRate} exceeds maximum reasonable fee rate of ${MAX_REASONABLE_FEE_RATE} sat/vB`);
93
- }
94
-
95
- const effectiveFeeRate = await this.resolveFeeRate(1, feeRate);
96
-
97
- if (!this.ord) {
98
- throw new StructuredError(
99
- 'ORD_PROVIDER_REQUIRED',
100
- 'Ordinals provider must be configured to inscribe data on Bitcoin. ' +
101
- 'Please provide an ordinalsProvider in your SDK configuration. ' +
102
- 'For testing, use: import { OrdMockProvider } from \'@originals/sdk\';'
103
- );
104
- }
105
-
106
- if (typeof this.ord.createInscription !== 'function') {
107
- throw new StructuredError(
108
- 'ORD_PROVIDER_UNSUPPORTED',
109
- 'Configured ordinals provider does not support inscription creation'
110
- );
111
- }
112
-
113
- const creation = await this.ord.createInscription({ data, contentType, feeRate: effectiveFeeRate });
114
- const txid = creation.txid ?? creation.revealTxId;
115
- if (!creation.inscriptionId || !txid) {
116
- throw new StructuredError(
117
- 'ORD_PROVIDER_INVALID_RESPONSE',
118
- 'Ordinals provider did not return a valid inscription identifier or transaction id'
119
- );
120
- }
121
-
122
- let satoshi = creation.satoshi ?? '';
123
- if (!satoshi) {
124
- satoshi = (await this.getSatoshiFromInscription(creation.inscriptionId)) ?? '';
125
- }
126
-
127
- // Validate satoshi before using it
128
- if (satoshi) {
129
- const validation = validateSatoshiNumber(satoshi);
130
- if (!validation.valid) {
131
- throw new StructuredError(
132
- 'INVALID_SATOSHI',
133
- `Ordinals provider returned invalid satoshi identifier: ${validation.error}`
134
- );
135
- }
136
- }
137
-
138
- let recordedFeeRate: number | undefined;
139
- if (this.feeOracle) {
140
- recordedFeeRate = effectiveFeeRate;
141
- } else if (typeof feeRate === 'number' && Number.isFinite(feeRate) && feeRate > 0) {
142
- recordedFeeRate = feeRate;
143
- } else {
144
- recordedFeeRate = creation.feeRate ?? effectiveFeeRate;
145
- }
146
-
147
- const inscription: OrdinalsInscription & {
148
- revealTxId?: string;
149
- commitTxId?: string;
150
- feeRate?: number;
151
- } = {
152
- satoshi,
153
- inscriptionId: creation.inscriptionId,
154
- content: creation.content ?? data,
155
- contentType: creation.contentType ?? contentType,
156
- txid,
157
- vout: typeof creation.vout === 'number' ? creation.vout : 0,
158
- blockHeight: creation.blockHeight,
159
- revealTxId: creation.revealTxId,
160
- commitTxId: creation.commitTxId,
161
- feeRate: recordedFeeRate
162
- };
163
-
164
- return inscription;
165
- }
166
-
167
- async trackInscription(inscriptionId: string): Promise<OrdinalsInscription | null> {
168
- if (this.ord) {
169
- const info = await this.ord.getInscriptionById(inscriptionId);
170
- if (!info) return null;
171
- return {
172
- satoshi: info.satoshi ?? '',
173
- inscriptionId: info.inscriptionId,
174
- content: info.content,
175
- contentType: info.contentType,
176
- txid: info.txid,
177
- vout: info.vout,
178
- blockHeight: info.blockHeight
179
- };
180
- }
181
- return null;
182
- }
183
-
184
- async transferInscription(
185
- inscription: OrdinalsInscription,
186
- toAddress: string
187
- ): Promise<BitcoinTransaction> {
188
- // Input validation
189
- if (!inscription || typeof inscription !== 'object') {
190
- throw new StructuredError('INVALID_INPUT', 'Inscription must be a valid OrdinalsInscription object');
191
- }
192
- if (!inscription.inscriptionId || typeof inscription.inscriptionId !== 'string') {
193
- throw new StructuredError('INVALID_INPUT', 'Inscription must have a valid inscriptionId');
194
- }
195
- if (!toAddress || typeof toAddress !== 'string') {
196
- throw new StructuredError('INVALID_INPUT', 'Destination address must be a non-empty string');
197
- }
198
-
199
- // Validate Bitcoin address format and checksum
200
- try {
201
- validateBitcoinAddress(toAddress, this.config.network);
202
- } catch (error) {
203
- const message = error instanceof Error ? error.message : 'Invalid Bitcoin address';
204
- throw new StructuredError('INVALID_ADDRESS', message);
205
- }
206
-
207
- const effectiveFeeRate = await this.resolveFeeRate(1);
208
-
209
- if (!this.ord) {
210
- throw new StructuredError(
211
- 'ORD_PROVIDER_REQUIRED',
212
- 'Ordinals provider must be configured to transfer inscriptions on Bitcoin. ' +
213
- 'Please provide an ordinalsProvider in your SDK configuration. ' +
214
- 'For testing, use: import { OrdMockProvider } from \'@originals/sdk\';'
215
- );
216
- }
217
-
218
- if (typeof this.ord.transferInscription !== 'function') {
219
- throw new StructuredError(
220
- 'ORD_PROVIDER_UNSUPPORTED',
221
- 'Configured ordinals provider does not support inscription transfers'
222
- );
223
- }
224
-
225
- const response = await this.ord.transferInscription(inscription.inscriptionId, toAddress, {
226
- feeRate: effectiveFeeRate
227
- });
228
-
229
- if (!response || !response.txid) {
230
- throw new StructuredError(
231
- 'ORD_PROVIDER_INVALID_RESPONSE',
232
- 'Ordinals provider did not return a valid transfer transaction'
233
- );
234
- }
235
-
236
- if (response.satoshi) {
237
- inscription.satoshi = response.satoshi;
238
- }
239
-
240
- return {
241
- txid: response.txid,
242
- vin: response.vin ?? [{ txid: inscription.txid, vout: inscription.vout }],
243
- vout:
244
- response.vout?.length
245
- ? response.vout
246
- : [{ value: DUST_LIMIT_SATS, scriptPubKey: 'script', address: toAddress }],
247
- fee: response.fee,
248
- blockHeight: response.blockHeight,
249
- confirmations: response.confirmations
250
- };
251
- }
252
-
253
- async preventFrontRunning(satoshi: string): Promise<boolean> {
254
- if (!satoshi) throw new StructuredError('SATOSHI_REQUIRED', 'Satoshi identifier is required');
255
- // Naive implementation: check for multiple inscriptions on same satoshi via provider
256
- if (this.ord) {
257
- const list = await this.ord.getInscriptionsBySatoshi(satoshi);
258
- return list.length <= 1;
259
- }
260
- return true;
261
- }
262
-
263
- async getSatoshiFromInscription(inscriptionId: string): Promise<string | null> {
264
- if (this.ord) {
265
- const info = await this.ord.getInscriptionById(inscriptionId);
266
- const satoshi = info?.satoshi;
267
-
268
- // Validate satoshi before returning
269
- if (satoshi) {
270
- const validation = validateSatoshiNumber(satoshi);
271
- if (!validation.valid) {
272
- // Return null if validation fails (don't return empty or invalid string)
273
- return null;
274
- }
275
- return satoshi;
276
- }
277
- return null;
278
- }
279
- return null;
280
- }
281
-
282
- async validateBTCODID(didId: string): Promise<boolean> {
283
- // Validate that a did:btco DID exists on Bitcoin
284
- const satoshi = this.extractSatoshiFromBTCODID(didId);
285
- if (!satoshi) return false;
286
-
287
- // Validate the extracted satoshi number
288
- const validation = validateSatoshiNumber(satoshi);
289
- if (!validation.valid) return false;
290
-
291
- if (!this.ord) return false;
292
- const inscriptions = await this.ord.getInscriptionsBySatoshi(satoshi);
293
- return inscriptions.length > 0;
294
- }
295
-
296
- private extractSatoshiFromBTCODID(didId: string): string | null {
297
- // Extract satoshi identifier from did:btco DID
298
- if (!didId.startsWith('did:btco:')) return null;
299
-
300
- const parts = didId.split(':');
301
- let satoshi: string | null = null;
302
-
303
- // Handle different network prefixes:
304
- // did:btco:123456 (mainnet) - 3 parts
305
- // did:btco:test:123456 or did:btco:sig:123456 - 4 parts
306
- if (parts.length === 3) {
307
- satoshi = parts[2];
308
- } else if (parts.length === 4) {
309
- // Validate network prefix - only 'test' and 'sig' are allowed
310
- const network = parts[2];
311
- if (network !== 'test' && network !== 'sig') {
312
- return null;
313
- }
314
- satoshi = parts[3];
315
- }
316
-
317
- // Validate the extracted satoshi format
318
- if (satoshi) {
319
- const validation = validateSatoshiNumber(satoshi);
320
- if (!validation.valid) {
321
- return null;
322
- }
323
- }
324
-
325
- return satoshi;
326
- }
327
- }
328
-
329
-