* Derives an Ethereum address from a private key using secp256k1. * Uses Node.js native crypto for the EC operation.
(privateKeyHex: string)
| 36 | * Uses Node.js native crypto for the EC operation. |
| 37 | */ |
| 38 | function deriveAddress(privateKeyHex: string): string { |
| 39 | const { createPublicKey, createPrivateKey } = |
| 40 | require('crypto') as typeof import('crypto') |
| 41 | |
| 42 | const keyHex = privateKeyHex.startsWith('0x') |
| 43 | ? privateKeyHex.slice(2) |
| 44 | : privateKeyHex |
| 45 | const keyBuffer = Buffer.from(keyHex, 'hex') |
| 46 | |
| 47 | // Create an EC private key in DER format for secp256k1 |
| 48 | // DER prefix for secp256k1 private key (RFC 5915) |
| 49 | const derPrefix = Buffer.from( |
| 50 | '30740201010420', |
| 51 | 'hex', |
| 52 | ) |
| 53 | const derMiddle = Buffer.from( |
| 54 | 'a00706052b8104000aa144034200', |
| 55 | 'hex', |
| 56 | ) |
| 57 | |
| 58 | const privateKey = createPrivateKey({ |
| 59 | key: Buffer.concat([derPrefix, keyBuffer, derMiddle]), |
| 60 | format: 'der', |
| 61 | type: 'sec1', |
| 62 | }) |
| 63 | |
| 64 | const publicKey = createPublicKey(privateKey) |
| 65 | const pubKeyDer = publicKey.export({ type: 'spki', format: 'der' }) |
| 66 | |
| 67 | // Extract the 65-byte uncompressed public key (last 65 bytes of SPKI DER) |
| 68 | const uncompressedPubKey = pubKeyDer.subarray(pubKeyDer.length - 65) |
| 69 | |
| 70 | // Ethereum address = last 20 bytes of keccak256(pubkey[1:]) |
| 71 | // pubkey[0] is 0x04 prefix for uncompressed key |
| 72 | const { createHash } = require('crypto') as typeof import('crypto') |
| 73 | const hash = createHash('sha3-256') |
| 74 | .update(uncompressedPubKey.subarray(1)) |
| 75 | .digest() |
| 76 | const rawAddress = '0x' + hash.subarray(hash.length - 20).toString('hex') |
| 77 | |
| 78 | return toChecksumAddress(rawAddress) |
| 79 | } |
| 80 | |
| 81 | /** Retrieves x402 config from global config */ |
| 82 | export function getX402Config(): X402WalletConfig { |
no test coverage detected