@bitshard.io/bitshard-sdk 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist/BitShardSDK.d.ts +156 -0
  2. package/dist/BitShardSDK.d.ts.map +1 -0
  3. package/dist/BitShardSDK.js +350 -0
  4. package/dist/BitShardSDK.js.map +1 -0
  5. package/dist/chains/bitcoin/BitcoinChain.d.ts +6 -0
  6. package/dist/chains/bitcoin/BitcoinChain.d.ts.map +1 -0
  7. package/dist/chains/bitcoin/BitcoinChain.js +10 -0
  8. package/dist/chains/bitcoin/BitcoinChain.js.map +1 -0
  9. package/dist/chains/config.d.ts +5 -0
  10. package/dist/chains/config.d.ts.map +1 -0
  11. package/dist/chains/config.js +7 -0
  12. package/dist/chains/config.js.map +1 -0
  13. package/dist/chains/evm/EVMChain.d.ts +6 -0
  14. package/dist/chains/evm/EVMChain.d.ts.map +1 -0
  15. package/dist/chains/evm/EVMChain.js +10 -0
  16. package/dist/chains/evm/EVMChain.js.map +1 -0
  17. package/dist/core/DKLSParty.d.ts +132 -0
  18. package/dist/core/DKLSParty.d.ts.map +1 -0
  19. package/dist/core/DKLSParty.js +267 -0
  20. package/dist/core/DKLSParty.js.map +1 -0
  21. package/dist/core/DKLSService.d.ts +83 -0
  22. package/dist/core/DKLSService.d.ts.map +1 -0
  23. package/dist/core/DKLSService.js +325 -0
  24. package/dist/core/DKLSService.js.map +1 -0
  25. package/dist/core/ThresholdConfig.d.ts +76 -0
  26. package/dist/core/ThresholdConfig.d.ts.map +1 -0
  27. package/dist/core/ThresholdConfig.js +127 -0
  28. package/dist/core/ThresholdConfig.js.map +1 -0
  29. package/dist/core/types.d.ts +238 -0
  30. package/dist/core/types.d.ts.map +1 -0
  31. package/dist/core/types.js +3 -0
  32. package/dist/core/types.js.map +1 -0
  33. package/dist/crypto/addresses.d.ts +82 -0
  34. package/dist/crypto/addresses.d.ts.map +1 -0
  35. package/dist/crypto/addresses.js +242 -0
  36. package/dist/crypto/addresses.js.map +1 -0
  37. package/dist/crypto/elliptic.d.ts +19 -0
  38. package/dist/crypto/elliptic.d.ts.map +1 -0
  39. package/dist/crypto/elliptic.js +114 -0
  40. package/dist/crypto/elliptic.js.map +1 -0
  41. package/dist/crypto/encoding.d.ts +111 -0
  42. package/dist/crypto/encoding.d.ts.map +1 -0
  43. package/dist/crypto/encoding.js +224 -0
  44. package/dist/crypto/encoding.js.map +1 -0
  45. package/dist/index.d.ts +23 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +58 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/protocols/keygen.d.ts +6 -0
  50. package/dist/protocols/keygen.d.ts.map +1 -0
  51. package/dist/protocols/keygen.js +12 -0
  52. package/dist/protocols/keygen.js.map +1 -0
  53. package/dist/protocols/presignature.d.ts +68 -0
  54. package/dist/protocols/presignature.d.ts.map +1 -0
  55. package/dist/protocols/presignature.js +147 -0
  56. package/dist/protocols/presignature.js.map +1 -0
  57. package/dist/protocols/refresh.d.ts +5 -0
  58. package/dist/protocols/refresh.d.ts.map +1 -0
  59. package/dist/protocols/refresh.js +10 -0
  60. package/dist/protocols/refresh.js.map +1 -0
  61. package/dist/protocols/signing.d.ts +7 -0
  62. package/dist/protocols/signing.d.ts.map +1 -0
  63. package/dist/protocols/signing.js +12 -0
  64. package/dist/protocols/signing.js.map +1 -0
  65. package/dist/rpc/RPCProvider.d.ts +6 -0
  66. package/dist/rpc/RPCProvider.d.ts.map +1 -0
  67. package/dist/rpc/RPCProvider.js +6 -0
  68. package/dist/rpc/RPCProvider.js.map +1 -0
  69. package/dist/rpc/methods.d.ts +5 -0
  70. package/dist/rpc/methods.d.ts.map +1 -0
  71. package/dist/rpc/methods.js +10 -0
  72. package/dist/rpc/methods.js.map +1 -0
  73. package/dist/websocket/coordinator.d.ts +6 -0
  74. package/dist/websocket/coordinator.d.ts.map +1 -0
  75. package/dist/websocket/coordinator.js +10 -0
  76. package/dist/websocket/coordinator.js.map +1 -0
  77. package/dist/websocket/messages.d.ts +9 -0
  78. package/dist/websocket/messages.d.ts.map +1 -0
  79. package/dist/websocket/messages.js +7 -0
  80. package/dist/websocket/messages.js.map +1 -0
  81. package/dist/websocket/session.d.ts +5 -0
  82. package/dist/websocket/session.d.ts.map +1 -0
  83. package/dist/websocket/session.js +7 -0
  84. package/dist/websocket/session.js.map +1 -0
  85. package/dist/wire/format.d.ts +8 -0
  86. package/dist/wire/format.d.ts.map +1 -0
  87. package/dist/wire/format.js +11 -0
  88. package/dist/wire/format.js.map +1 -0
  89. package/dist/wire/validation.d.ts +6 -0
  90. package/dist/wire/validation.d.ts.map +1 -0
  91. package/dist/wire/validation.js +13 -0
  92. package/dist/wire/validation.js.map +1 -0
  93. package/package.json +67 -0
  94. package/src/BitShardSDK.ts +428 -0
  95. package/src/chains/bitcoin/BitcoinChain.ts +7 -0
  96. package/src/chains/config.ts +7 -0
  97. package/src/chains/evm/EVMChain.ts +7 -0
  98. package/src/core/DKLSParty.ts +317 -0
  99. package/src/core/DKLSService.ts +426 -0
  100. package/src/core/ThresholdConfig.ts +159 -0
  101. package/src/core/types.ts +253 -0
  102. package/src/crypto/addresses.ts +282 -0
  103. package/src/crypto/elliptic.ts +133 -0
  104. package/src/crypto/encoding.ts +227 -0
  105. package/src/index.ts +40 -0
  106. package/src/protocols/keygen.ts +8 -0
  107. package/src/protocols/presignature.ts +196 -0
  108. package/src/protocols/refresh.ts +7 -0
  109. package/src/protocols/signing.ts +9 -0
  110. package/src/rpc/RPCProvider.ts +7 -0
  111. package/src/rpc/methods.ts +7 -0
  112. package/src/websocket/coordinator.ts +7 -0
  113. package/src/websocket/messages.ts +11 -0
  114. package/src/websocket/session.ts +7 -0
  115. package/src/wire/format.ts +10 -0
  116. package/src/wire/validation.ts +14 -0
  117. package/test-sdk.js +234 -0
@@ -0,0 +1,253 @@
1
+ import type { Keyshare } from '@silencelaboratories/dkls-wasm-ll-node';
2
+
3
+ /**
4
+ * Result of distributed key generation
5
+ */
6
+ export interface DKGResult {
7
+ /** SHA256 hash of the public key */
8
+ dkgCommitment: string;
9
+ /** SHA256 hash of the server share */
10
+ serverShareCommitment: string;
11
+ /** Master public key in hex format with 0x prefix */
12
+ masterPublicKey: string;
13
+ /** Base64 encoded server keyshare */
14
+ serverShare: string;
15
+ /** Base64 encoded backup keyshare */
16
+ backupShare: string;
17
+ /** Base64 encoded mobile keyshare */
18
+ mobileShare: string;
19
+ /** Public key in hex format with 0x prefix */
20
+ publicKey: string;
21
+ /** Ethereum address */
22
+ ethAddress: string;
23
+ /** Bitcoin address (P2PKH) */
24
+ btcAddress: string;
25
+ /** Cosmos address */
26
+ cosmosAddress: string;
27
+ /** BNB Smart Chain address (same as ETH) */
28
+ bnbAddress: string;
29
+ /** Polygon address (same as ETH) */
30
+ polygonAddress: string;
31
+ /** Avalanche C-Chain address (same as ETH) */
32
+ avaxAddress: string;
33
+ /** Arbitrum address (same as ETH) */
34
+ arbAddress: string;
35
+ }
36
+
37
+ /**
38
+ * Extended DKG result for flexible threshold configurations
39
+ */
40
+ export interface FlexibleDKGResult {
41
+ /** Total number of parties */
42
+ totalParties: number;
43
+ /** Threshold required for signing */
44
+ threshold: number;
45
+ /** Public key in hex format with 0x prefix */
46
+ publicKey: string;
47
+ /** SHA256 hash of the public key */
48
+ dkgCommitment: string;
49
+ /** Array of keyshares for each party */
50
+ keyshares: PartyKeyshare[];
51
+ /** Derived blockchain addresses */
52
+ addresses: BlockchainAddresses;
53
+ }
54
+
55
+ /**
56
+ * Keyshare data for a specific party
57
+ */
58
+ export interface PartyKeyshare {
59
+ /** Party identifier (0-based index) */
60
+ partyId: number;
61
+ /** Base64 encoded keyshare data */
62
+ share: string;
63
+ /** SHA256 hash of the keyshare */
64
+ commitment: string;
65
+ }
66
+
67
+ /**
68
+ * Blockchain addresses derived from public key
69
+ */
70
+ export interface BlockchainAddresses {
71
+ /** Ethereum address */
72
+ ethereum: string;
73
+ /** Bitcoin address (P2PKH) */
74
+ bitcoin: string;
75
+ /** Cosmos address */
76
+ cosmos: string;
77
+ /** Arbitrum address (same as ETH) */
78
+ arbitrum: string;
79
+ /** Polygon address (same as ETH) */
80
+ polygon: string;
81
+ /** BNB Smart Chain address (same as ETH) */
82
+ bnb: string;
83
+ /** Avalanche C-Chain address (same as ETH) */
84
+ avalanche: string;
85
+ }
86
+
87
+ /**
88
+ * MPC session for tracking protocol execution
89
+ */
90
+ export interface MPCSession {
91
+ /** Unique session identifier */
92
+ id: string;
93
+ /** Associated wallet ID */
94
+ walletId: string;
95
+ /** Type of MPC operation */
96
+ sessionType: 'keygen' | 'signing' | 'refresh';
97
+ /** Current session status */
98
+ status: 'active' | 'completed' | 'failed';
99
+ /** List of participant IDs */
100
+ participants: string[];
101
+ /** Current protocol round */
102
+ currentRound?: number;
103
+ /** Total number of rounds */
104
+ totalRounds?: number;
105
+ /** Creation timestamp */
106
+ createdAt: Date;
107
+ /** Last update timestamp */
108
+ updatedAt: Date;
109
+ }
110
+
111
+ /**
112
+ * Signature result from threshold signing
113
+ */
114
+ export interface SignatureResult {
115
+ /** Full signature in hex format with 0x prefix */
116
+ signature: string;
117
+ /** R component of signature */
118
+ r: string;
119
+ /** S component of signature */
120
+ s: string;
121
+ /** V value for Ethereum signatures */
122
+ v?: number;
123
+ }
124
+
125
+ /**
126
+ * Wire format for DKLS messages over network
127
+ */
128
+ export interface WireMessage {
129
+ /** Sender party ID */
130
+ from_id: number;
131
+ /** Recipient party ID (optional for broadcast) */
132
+ to_id?: number;
133
+ /** Base64 encoded message payload */
134
+ payload: string;
135
+ }
136
+
137
+ /**
138
+ * Party configuration for distributed setup
139
+ */
140
+ export interface PartyConfig {
141
+ /** Unique party identifier (0-based) */
142
+ partyId: number;
143
+ /** Total number of parties */
144
+ totalParties: number;
145
+ /** Threshold required for operations */
146
+ threshold: number;
147
+ /** Role of this party */
148
+ role?: 'coordinator' | 'signer';
149
+ }
150
+
151
+ /**
152
+ * Local wallet for testing (all parties in one process)
153
+ */
154
+ export interface LocalWallet {
155
+ /** Public key in hex format */
156
+ publicKey: string;
157
+ /** All keyshares for local testing */
158
+ keyshares: Keyshare[];
159
+ /** Threshold configuration */
160
+ config: {
161
+ totalParties: number;
162
+ threshold: number;
163
+ partyIds: number[];
164
+ };
165
+ }
166
+
167
+ /**
168
+ * Pre-signature data structure
169
+ */
170
+ export interface PreSignature {
171
+ /** Unique identifier for tracking */
172
+ id: string;
173
+ /** Sign sessions ready for final round */
174
+ parties: any[]; // Will be SignSession[] but avoiding import
175
+ /** Creation timestamp */
176
+ createdAt: Date;
177
+ /** Whether this pre-signature has been consumed */
178
+ consumed: boolean;
179
+ }
180
+
181
+ /**
182
+ * Chain configuration for multi-chain support
183
+ */
184
+ export interface ChainConfig {
185
+ /** Chain ID */
186
+ chainId: number;
187
+ /** Human-readable chain name */
188
+ name: string;
189
+ /** RPC endpoint URL */
190
+ rpcUrl: string;
191
+ /** Block explorer URL */
192
+ explorer?: string;
193
+ /** Native currency info */
194
+ nativeCurrency: {
195
+ name: string;
196
+ symbol: string;
197
+ decimals: number;
198
+ };
199
+ /** Gas configuration */
200
+ gasConfig?: {
201
+ maxFeePerGas?: string;
202
+ maxPriorityFeePerGas?: string;
203
+ };
204
+ }
205
+
206
+ /**
207
+ * Bitcoin network configuration
208
+ */
209
+ export interface BitcoinConfig {
210
+ /** Network type */
211
+ network: 'mainnet' | 'testnet' | 'regtest';
212
+ /** RPC endpoint URL */
213
+ rpcUrl?: string;
214
+ /** API URL for UTXO queries */
215
+ apiUrl?: string;
216
+ /** Default fee rate in sats/vbyte */
217
+ feeRate?: number;
218
+ }
219
+
220
+ /**
221
+ * WebSocket session for protocol coordination
222
+ */
223
+ export interface WebSocketSession {
224
+ /** Session identifier */
225
+ sessionId: string;
226
+ /** Session type */
227
+ type: 'keygen' | 'signing' | 'refresh';
228
+ /** Connected parties */
229
+ parties: Set<number>;
230
+ /** Message queue */
231
+ messages: WireMessage[];
232
+ /** Session metadata */
233
+ metadata?: Record<string, any>;
234
+ }
235
+
236
+ /**
237
+ * SDK configuration options
238
+ */
239
+ export interface SDKConfig {
240
+ /** WebSocket server URL for coordination */
241
+ websocketUrl?: string;
242
+ /** Default chain configuration */
243
+ defaultChain?: string;
244
+ /** Custom chain configurations */
245
+ chains?: Record<string, ChainConfig>;
246
+ /** Bitcoin network configuration */
247
+ bitcoin?: BitcoinConfig;
248
+ /** Enable debug logging */
249
+ debug?: boolean;
250
+ }
251
+
252
+ // Re-export types from DKLS library for convenience
253
+ export type { Message, Keyshare } from '@silencelaboratories/dkls-wasm-ll-node';
@@ -0,0 +1,282 @@
1
+ import crypto from 'crypto';
2
+ import { keccak256 } from 'viem';
3
+ import type { BlockchainAddresses } from '../core/types';
4
+ import { getUncompressedPublicKey } from './elliptic';
5
+
6
+ /**
7
+ * Derive Ethereum address from ECDSA public key
8
+ * @param publicKeyHex - Hex string of public key (compressed or uncompressed)
9
+ * @returns Ethereum address with 0x prefix
10
+ */
11
+ export function deriveEthereumAddress(publicKeyHex: string): string {
12
+ // Remove 0x prefix if present
13
+ const cleanHex = publicKeyHex.replace(/^0x/, '');
14
+
15
+ // Get uncompressed public key (64 bytes, no 04 prefix)
16
+ const uncompressedKey = getUncompressedPublicKey(cleanHex);
17
+
18
+ // Keccak256 hash of the uncompressed public key
19
+ const hash = keccak256(`0x${uncompressedKey}`);
20
+
21
+ // Take the last 20 bytes (40 hex chars) as the address
22
+ const address = '0x' + hash.slice(-40);
23
+
24
+ return address.toLowerCase();
25
+ }
26
+
27
+ /**
28
+ * Derive Ethereum address from public key bytes
29
+ * @param publicKey - Uint8Array of the public key
30
+ * @returns Ethereum address with 0x prefix
31
+ */
32
+ export function deriveEthereumAddressFromBytes(publicKey: Uint8Array): string {
33
+ // Remove the first byte (0x04 for uncompressed key) if present
34
+ const pubKeyBytes = publicKey[0] === 0x04 ? publicKey.slice(1) : publicKey;
35
+
36
+ // Convert to hex and derive address
37
+ const pubKeyHex = Buffer.from(pubKeyBytes).toString('hex');
38
+ return deriveEthereumAddress(pubKeyHex);
39
+ }
40
+
41
+ /**
42
+ * Derive Bitcoin P2PKH address from ECDSA public key
43
+ * @param publicKeyHex - Hex string of the public key
44
+ * @param network - Network type: 'mainnet', 'testnet', or 'regtest'
45
+ * @returns Bitcoin address in base58check format
46
+ */
47
+ export function deriveBitcoinAddress(
48
+ publicKeyHex: string,
49
+ network: 'mainnet' | 'testnet' | 'regtest' = 'mainnet'
50
+ ): string {
51
+ // Remove 0x prefix if present
52
+ const cleanHex = publicKeyHex.replace(/^0x/, '');
53
+
54
+ // Convert to compressed public key format if uncompressed
55
+ const compressedPubKey = compressPublicKey(cleanHex);
56
+
57
+ // Step 1: SHA256 hash of the public key
58
+ const sha256Hash = crypto.createHash('sha256')
59
+ .update(Buffer.from(compressedPubKey, 'hex'))
60
+ .digest();
61
+
62
+ // Step 2: RIPEMD160 hash of the SHA256 hash
63
+ const ripemd160Hash = crypto.createHash('ripemd160')
64
+ .update(sha256Hash)
65
+ .digest();
66
+
67
+ // Step 3: Add version byte
68
+ // 0x00 for mainnet P2PKH
69
+ // 0x6f for testnet/regtest P2PKH
70
+ const versionByte = network === 'mainnet' ? 0x00 : 0x6f;
71
+ const versionedPayload = Buffer.concat([
72
+ Buffer.from([versionByte]),
73
+ ripemd160Hash
74
+ ]);
75
+
76
+ // Step 4: Calculate checksum (first 4 bytes of double SHA256)
77
+ const checksum = crypto.createHash('sha256')
78
+ .update(crypto.createHash('sha256').update(versionedPayload).digest())
79
+ .digest()
80
+ .slice(0, 4);
81
+
82
+ // Step 5: Append checksum
83
+ const finalPayload = Buffer.concat([versionedPayload, checksum]);
84
+
85
+ // Step 6: Base58 encode
86
+ return base58Encode(finalPayload);
87
+ }
88
+
89
+ /**
90
+ * Derive Bitcoin address from public key bytes
91
+ * @param publicKey - Uint8Array of the public key
92
+ * @param network - Network type
93
+ * @returns Bitcoin address in base58check format
94
+ */
95
+ export function deriveBitcoinAddressFromBytes(
96
+ publicKey: Uint8Array,
97
+ network: 'mainnet' | 'testnet' | 'regtest' = 'mainnet'
98
+ ): string {
99
+ const pubKeyHex = Buffer.from(publicKey).toString('hex');
100
+ return deriveBitcoinAddress(pubKeyHex, network);
101
+ }
102
+
103
+ /**
104
+ * Derive Cosmos address from ECDSA public key
105
+ * @param publicKeyHex - Hex string of the public key
106
+ * @param prefix - Bech32 prefix (default: 'cosmos')
107
+ * @returns Cosmos address with bech32 encoding
108
+ */
109
+ export function deriveCosmosAddress(publicKeyHex: string, prefix: string = 'cosmos'): string {
110
+ // Remove 0x prefix if present
111
+ const cleanHex = publicKeyHex.replace(/^0x/, '');
112
+
113
+ // Convert to compressed format
114
+ const compressedPubKey = compressPublicKey(cleanHex);
115
+
116
+ // SHA256 hash
117
+ const sha256Hash = crypto.createHash('sha256')
118
+ .update(Buffer.from(compressedPubKey, 'hex'))
119
+ .digest();
120
+
121
+ // RIPEMD160 hash
122
+ const ripemd160Hash = crypto.createHash('ripemd160')
123
+ .update(sha256Hash)
124
+ .digest();
125
+
126
+ // TODO: Implement proper bech32 encoding
127
+ // For now, return a simplified version
128
+ return prefix + '1' + ripemd160Hash.toString('hex').substring(0, 38);
129
+ }
130
+
131
+ /**
132
+ * Compress an uncompressed ECDSA public key
133
+ * @param publicKeyHex - Hex string of uncompressed public key (with or without 04 prefix)
134
+ * @returns Compressed public key hex string (33 bytes)
135
+ */
136
+ export function compressPublicKey(publicKeyHex: string): string {
137
+ // Remove 04 prefix if present (uncompressed marker)
138
+ const cleanHex = publicKeyHex.startsWith('04') ? publicKeyHex.slice(2) : publicKeyHex;
139
+
140
+ // Split into x and y coordinates (32 bytes each)
141
+ const x = cleanHex.slice(0, 64);
142
+ const y = cleanHex.slice(64, 128);
143
+
144
+ // Determine prefix based on y coordinate parity
145
+ const yBigInt = BigInt('0x' + y);
146
+ const prefix = (yBigInt % 2n === 0n) ? '02' : '03';
147
+
148
+ return prefix + x;
149
+ }
150
+
151
+ /**
152
+ * Base58 encoding (for Bitcoin addresses)
153
+ */
154
+ export function base58Encode(buffer: Buffer): string {
155
+ const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
156
+ const base = BigInt(58);
157
+
158
+ let num = BigInt('0x' + buffer.toString('hex'));
159
+ let encoded = '';
160
+
161
+ while (num > 0n) {
162
+ const remainder = num % base;
163
+ num = num / base;
164
+ encoded = ALPHABET[Number(remainder)] + encoded;
165
+ }
166
+
167
+ // Handle leading zeros
168
+ for (let i = 0; i < buffer.length && buffer[i] === 0; i++) {
169
+ encoded = '1' + encoded;
170
+ }
171
+
172
+ return encoded;
173
+ }
174
+
175
+ /**
176
+ * Base58 decoding (for Bitcoin addresses)
177
+ */
178
+ export function base58Decode(encoded: string): Buffer {
179
+ const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
180
+ const ALPHABET_MAP: { [key: string]: bigint } = {};
181
+ for (let i = 0; i < ALPHABET.length; i++) {
182
+ ALPHABET_MAP[ALPHABET[i]!] = BigInt(i);
183
+ }
184
+
185
+ let num = 0n;
186
+ const base = 58n;
187
+
188
+ for (const char of encoded) {
189
+ num = num * base + ALPHABET_MAP[char]!;
190
+ }
191
+
192
+ // Convert to hex and then to buffer
193
+ let hex = num.toString(16);
194
+ // Ensure even length
195
+ if (hex.length % 2 !== 0) {
196
+ hex = '0' + hex;
197
+ }
198
+
199
+ const buffer = Buffer.from(hex, 'hex');
200
+
201
+ // Add leading zeros for each leading '1' in the input
202
+ let leadingZeros = 0;
203
+ for (const char of encoded) {
204
+ if (char === '1') {
205
+ leadingZeros++;
206
+ } else {
207
+ break;
208
+ }
209
+ }
210
+
211
+ return Buffer.concat([Buffer.alloc(leadingZeros), buffer]);
212
+ }
213
+
214
+ /**
215
+ * Derive addresses for all supported blockchains from public key
216
+ * @param publicKeyHex - Hex string of the public key
217
+ * @returns Object with addresses for all supported chains
218
+ */
219
+ export function deriveAddresses(publicKeyHex: string): {
220
+ ethereum: string;
221
+ bitcoin: string;
222
+ bitcoinTestnet: string;
223
+ cosmos: string;
224
+ arbitrum: string;
225
+ polygon: string;
226
+ bnb: string;
227
+ avalanche: string;
228
+ } {
229
+ const ethAddress = deriveEthereumAddress(publicKeyHex);
230
+
231
+ return {
232
+ ethereum: ethAddress,
233
+ bitcoin: deriveBitcoinAddress(publicKeyHex, 'mainnet'),
234
+ bitcoinTestnet: deriveBitcoinAddress(publicKeyHex, 'testnet'),
235
+ cosmos: deriveCosmosAddress(publicKeyHex),
236
+ // EVM-compatible chains use the same address as Ethereum
237
+ arbitrum: ethAddress,
238
+ polygon: ethAddress,
239
+ bnb: ethAddress,
240
+ avalanche: ethAddress
241
+ };
242
+ }
243
+
244
+ /**
245
+ * Derive addresses from public key bytes
246
+ * @param publicKey - Uint8Array of the public key
247
+ * @returns BlockchainAddresses object
248
+ */
249
+ export function deriveAddressesFromBytes(publicKey: Uint8Array): BlockchainAddresses {
250
+ const pubKeyHex = Buffer.from(publicKey).toString('hex');
251
+ const addresses = deriveAddresses(pubKeyHex);
252
+
253
+ return {
254
+ ethereum: addresses.ethereum,
255
+ bitcoin: addresses.bitcoin,
256
+ cosmos: addresses.cosmos,
257
+ arbitrum: addresses.arbitrum,
258
+ polygon: addresses.polygon,
259
+ bnb: addresses.bnb,
260
+ avalanche: addresses.avalanche
261
+ };
262
+ }
263
+
264
+ /**
265
+ * Validate Ethereum address format
266
+ * @param address - Address to validate
267
+ * @returns True if valid Ethereum address
268
+ */
269
+ export function isValidEthereumAddress(address: string): boolean {
270
+ return /^0x[a-fA-F0-9]{40}$/.test(address);
271
+ }
272
+
273
+ /**
274
+ * Validate Bitcoin address format (basic validation)
275
+ * @param address - Address to validate
276
+ * @returns True if valid Bitcoin address format
277
+ */
278
+ export function isValidBitcoinAddress(address: string): boolean {
279
+ // Basic format check - proper validation would verify checksum
280
+ return /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/.test(address) || // P2PKH/P2SH
281
+ /^bc1[a-z0-9]{39,59}$/.test(address); // Bech32
282
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Decompress a compressed ECDSA public key
3
+ * @param compressedHex - Hex string of compressed public key (33 bytes)
4
+ * @returns Uncompressed public key hex string (64 bytes, without 04 prefix)
5
+ */
6
+ export function decompressPublicKey(compressedHex: string): string {
7
+ // Remove 0x prefix if present
8
+ const cleanHex = compressedHex.replace(/^0x/, '');
9
+
10
+ if (cleanHex.length !== 66) {
11
+ throw new Error('Invalid compressed public key length');
12
+ }
13
+
14
+ const prefix = cleanHex.slice(0, 2);
15
+ if (prefix !== '02' && prefix !== '03') {
16
+ throw new Error('Invalid compressed public key prefix');
17
+ }
18
+
19
+ // Get x coordinate
20
+ const x = BigInt('0x' + cleanHex.slice(2));
21
+
22
+ // secp256k1 curve parameters
23
+ const p = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F');
24
+ const a = BigInt(0);
25
+ const b = BigInt(7);
26
+
27
+ // Calculate y^2 = x^3 + ax + b (mod p)
28
+ const x3 = modPow(x, 3n, p);
29
+ const ax = (a * x) % p;
30
+ const y2 = (x3 + ax + b) % p;
31
+
32
+ // Calculate y = sqrt(y^2) mod p
33
+ let y = modSqrt(y2, p);
34
+
35
+ if (y === null) {
36
+ throw new Error('Invalid public key - no valid y coordinate');
37
+ }
38
+
39
+ // Choose the correct y based on prefix
40
+ const isEven = y % 2n === 0n;
41
+ const shouldBeEven = prefix === '02';
42
+
43
+ if (isEven !== shouldBeEven) {
44
+ y = p - y;
45
+ }
46
+
47
+ // Convert to hex strings (32 bytes each)
48
+ const xHex = x.toString(16).padStart(64, '0');
49
+ const yHex = y.toString(16).padStart(64, '0');
50
+
51
+ return xHex + yHex;
52
+ }
53
+
54
+ /**
55
+ * Modular exponentiation: (base^exp) % mod
56
+ */
57
+ function modPow(base: bigint, exp: bigint, mod: bigint): bigint {
58
+ let result = 1n;
59
+ base = base % mod;
60
+
61
+ while (exp > 0n) {
62
+ if (exp % 2n === 1n) {
63
+ result = (result * base) % mod;
64
+ }
65
+ exp = exp >> 1n;
66
+ base = (base * base) % mod;
67
+ }
68
+
69
+ return result;
70
+ }
71
+
72
+ /**
73
+ * Modular square root using Tonelli-Shanks algorithm
74
+ * For secp256k1, p ≡ 3 (mod 4), so we can use the simpler formula
75
+ */
76
+ function modSqrt(a: bigint, p: bigint): bigint | null {
77
+ // Check if a is a quadratic residue
78
+ const legendreSymbol = modPow(a, (p - 1n) / 2n, p);
79
+ if (legendreSymbol !== 1n) {
80
+ return null; // No square root exists
81
+ }
82
+
83
+ // For p ≡ 3 (mod 4), sqrt(a) = a^((p+1)/4) mod p
84
+ if (p % 4n === 3n) {
85
+ return modPow(a, (p + 1n) / 4n, p);
86
+ }
87
+
88
+ // For other cases, would need full Tonelli-Shanks
89
+ throw new Error('Unsupported prime for square root');
90
+ }
91
+
92
+ /**
93
+ * Check if a public key is compressed
94
+ * @param publicKeyHex - Hex string of public key
95
+ * @returns True if compressed (33 bytes), false if uncompressed (64 or 65 bytes)
96
+ */
97
+ export function isCompressedPublicKey(publicKeyHex: string): boolean {
98
+ const cleanHex = publicKeyHex.replace(/^0x/, '');
99
+
100
+ // Compressed keys are 33 bytes (66 hex chars)
101
+ if (cleanHex.length === 66) {
102
+ const prefix = cleanHex.slice(0, 2);
103
+ return prefix === '02' || prefix === '03';
104
+ }
105
+
106
+ return false;
107
+ }
108
+
109
+ /**
110
+ * Get uncompressed public key, handling both compressed and uncompressed inputs
111
+ * @param publicKeyHex - Hex string of public key (compressed or uncompressed)
112
+ * @returns Uncompressed public key hex string (64 bytes, without 04 prefix)
113
+ */
114
+ export function getUncompressedPublicKey(publicKeyHex: string): string {
115
+ const cleanHex = publicKeyHex.replace(/^0x/, '');
116
+
117
+ // Check if compressed
118
+ if (isCompressedPublicKey(cleanHex)) {
119
+ return decompressPublicKey(cleanHex);
120
+ }
121
+
122
+ // If uncompressed with 04 prefix, remove it
123
+ if (cleanHex.startsWith('04')) {
124
+ return cleanHex.slice(2);
125
+ }
126
+
127
+ // If already uncompressed without prefix (128 chars = 64 bytes)
128
+ if (cleanHex.length === 128) {
129
+ return cleanHex;
130
+ }
131
+
132
+ throw new Error(`Invalid public key format: ${cleanHex.length} hex chars`);
133
+ }