Skip to main content

Try it Live

Run Address examples in the interactive playground
View the complete executable example at playground/src/examples/primitives/address/from-public-key.ts.

FromPublicKey({ keccak256 })

Create Address from secp256k1 public key coordinates using factory pattern. Enables tree-shakeable imports without bundling unnecessary crypto.Parameters:
  • deps.keccak256: (data: Uint8Array) => Uint8Array - Keccak256 hash function
Returns: (x: bigint, y: bigint) => AddressType - Function that creates Address from public keyExample:
import { FromPublicKey } from 'tevm/Address/AddressType'
import { hash as keccak256 } from 'tevm/crypto/Keccak256'

// Create factory with injected crypto
const fromPublicKey = FromPublicKey({ keccak256 })

// Use it to derive addresses
const x = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n
const y = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n
const addr = fromPublicKey(x, y)
console.log(addr.length) // 20
Bundle Size: Tree-shakeable. Only includes keccak256 if used.Use Case: Optimal for libraries that need to avoid bundling crypto dependencies by default.

Algorithm

The address derivation follows Ethereum’s standard process:
  1. Concatenate coordinates - Combine x and y into 64-byte uncompressed public key
  2. Hash with keccak256 - Compute keccak256 hash (32 bytes output)
  3. Extract address - Take last 20 bytes of hash
Pseudocode:
pubkey = x (32 bytes) || y (32 bytes)  // 64 bytes total
hash = keccak256(pubkey)               // 32 bytes
address = hash[12:32]                  // Last 20 bytes

Public Key Format

Ethereum uses uncompressed secp256k1 public keys:
  • Curve: secp256k1 (same as Bitcoin)
  • Coordinates: x and y, each 256 bits (32 bytes)
  • Total size: 64 bytes (no 0x04 prefix in Ethereum)
The x and y coordinates must be valid points on the secp256k1 curve.

Complete Example

import { Address } from 'tevm'
import * as Secp256k1 from 'tevm/crypto/Secp256k1'

// Start with private key
const privateKey = Bytes32()
// ... fill with secure random bytes

// Derive public key from private key
const publicKey = Secp256k1.derivePublicKey(privateKey) // 64 bytes

// Extract coordinates (big-endian)
let x = 0n
let y = 0n
for (let i = 0; i < 32; i++) {
  x = (x << 8n) | BigInt(publicKey[i])
  y = (y << 8n) | BigInt(publicKey[i + 32])
}

// Derive address
const addr = Address.fromPublicKey(x, y)
console.log(addr.toHex())

Use Cases

Verifying Signatures

After recovering a public key from an ECDSA signature:
import { Address } from 'tevm'
import * as Secp256k1 from 'tevm/crypto/Secp256k1'

const signature = // ... ECDSA signature
const message = // ... signed message

// Recover public key from signature
const recovered = Secp256k1.recoverPublicKey(signature, message)

// Extract coordinates
const x = // ... extract from recovered
const y = // ... extract from recovered

// Derive address
const signer = Address.fromPublicKey(x, y)

Deterministic Address Generation

Generate addresses from known public keys:
const knownPublicKeys = [
  { x: 0x123n, y: 0x456n },
  { x: 0x789n, y: 0xabcn },
]

const addresses = knownPublicKeys.map(pk =>
  Address.fromPublicKey(pk.x, pk.y)
)

Performance

Cryptographic dependency: Uses keccak256 hash function internally. Bundle size impact:
  • Factory API (FromPublicKey): Tree-shakeable, only includes keccak256 if used
  • Namespace API (Address.fromPublicKey): Always includes keccak256 (~5-10 KB)
Recommendation: Use Factory API for libraries, Namespace API for applications. Alternative: For performance-critical code, consider using fromPrivateKey() directly if you have the private key, as it combines key derivation and address generation.

See Also