Creates a new Address instance.
If mldsaPublicKey is omitted, a zeroed 32-byte address is created. If the raw ML-DSA public key is provided (not the hash), the class stores the original and computes the SHA256 hash internally.
Represents a quantum-resistant address in OP_NET's hybrid cryptographic architecture. An Address internally stores the SHA256 hash of an ML-DSA (quantum) public key as its primary 32-byte content and maintains a classical Bitcoin tweaked public key separately. This dual-key design enables quantum-resistant addressing while preserving full compatibility with traditional Bitcoin address formats (P2TR, P2WPKH, P2PKH, P2SH-P2WPKH, P2WDA, and P2OP).
The Address class extends Uint8Array (always 32 bytes) and implements Disposable for secure memory cleanup.
The full source code is available on GitHub at Address.ts.
Address.fromString() does NOT accept bech32 addresses. You cannot pass bc1q... or bc1p... strings to this method. It requires two hexadecimal public key parameters: the first is the 32-byte SHA256 hash of the ML-DSA public key, and the second is the 33-byte (or 65-byte) Bitcoin compressed (or uncompressed) public key. Both must be 0x-prefixed hex strings.
If you have a bech32 address and need to create an Address object, you must first resolve it to its public keys using a provider (e.g., await provider.getPublicKeyInfo("bc1q...")).
The Address class uses lazy evaluation for expensive EC operations. When a classical public key is provided, it is stored but not processed until a method that requires it is called (e.g., p2tr(), p2wpkh(), tweakedPublicKeyToBuffer()). This means:
This design is critical for performance in hot paths like block parsing, where thousands of addresses may be created but only a fraction require classical key operations.
import { Address } from '@btc-vision/transaction';new Address(mldsaPublicKey?: ArrayLike<number>, publicKeyOrTweak?: ArrayLike<number>)Creates a new Address instance.
If mldsaPublicKey is omitted, a zeroed 32-byte address is created. If the raw ML-DSA public key is provided (not the hash), the class stores the original and computes the SHA256 hash internally.
| Name | Type | Required | Description |
|---|---|---|---|
| mldsaPublicKey | ArrayLike<number> | No | Either a 32-byte SHA256 hash of the ML-DSA public key, or the raw ML-DSA public key (1312, 1952, or 2592 bytes). If raw, it will be hashed with SHA256 automatically. |
| publicKeyOrTweak | ArrayLike<number> | No | The classical Bitcoin public key. Accepts 32-byte x-only, 33-byte compressed, or 65-byte uncompressed formats. Expensive EC operations are deferred until needed. |
| Type | Description |
|---|---|
| Error | Thrown if publicKeyOrTweak has an invalid length (not 32, 33, or 65 bytes). |
| Error | Thrown if mldsaPublicKey is not 32 bytes and not a valid ML-DSA public key length (1312, 1952, or 2592 bytes). |
static fromString(mldsaPublicKey: string, legacyPublicKey?: string): AddressCreates an Address from hex-encoded public key strings. This is the primary factory method for constructing addresses from string representations.
| Name | Type | Required | Description |
|---|---|---|---|
| mldsaPublicKey | string | Yes | The 32-byte SHA256 hash of the ML-DSA public key in hex format. 0x prefix is optional. |
| legacyPublicKey | string | No | The classical Bitcoin public key in hex format (33 bytes compressed or 65 bytes uncompressed). 0x prefix is optional. |
| Type | Description |
|---|---|
| Address | The new Address object. |
| Type | Description |
|---|---|
| Error | Thrown if either parameter is not valid hexadecimal. The error message explicitly states that bech32 addresses are not accepted. |
This method takes hex-encoded public keys, NOT bech32 addresses. Passing bc1q... or bc1p... will throw an error. If you only have an address string, resolve it to a public key first via provider.getPublicKeyInfo().
// Correct usage with two hex public keys
const address = Address.fromString(
'0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890',
'0x020373626d317ae8788ce3280b491068610d840c23ecb64c14075bbb9f670af52c',
);
// WRONG - this will throw an error
const bad = Address.fromString('bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh');static dead(): AddressReturns a dead/burn address. The ML-DSA hash is all zeros and the classical key is the Bitcoin genesis block coinbase public key. Useful as a placeholder or for burning tokens.
| Type | Description |
|---|---|
| Address | A dead address with a zeroed ML-DSA hash. |
const burnAddress = Address.dead();
console.log(burnAddress.isDead()); // truestatic wrap(bytes: ArrayLike<number>): AddressCreates an Address from raw bytes, treating them as the ML-DSA public key (or its hash). No classical public key is set.
| Name | Type | Required | Description |
|---|---|---|---|
| bytes | ArrayLike<number> | Yes | The raw bytes to use as the ML-DSA key portion. |
| Type | Description |
|---|---|
| Address | The new Address object. |
static fromBigInt(value: bigint, tweakedValue?: bigint): AddressCreates an Address from a 256-bit unsigned integer. The inverse of toBigInt().
| Name | Type | Required | Description |
|---|---|---|---|
| value | bigint | Yes | The 256-bit unsigned integer representing the ML-DSA hash (0 to 2^256-1). |
| tweakedValue | bigint | No | Optional tweaked public key as a 256-bit unsigned integer. |
| Type | Description |
|---|---|
| Address | The new Address object. |
const addr = Address.fromBigInt(12345678901234567890n);
console.log(addr.toBigInt()); // 12345678901234567890nstatic fromUint64Array(w0: bigint, w1: bigint, w2: bigint, w3: bigint): AddressCreates an Address from four 64-bit big-endian unsigned integers. The inverse of toUint64Array(). Useful for efficient serialization or interfacing with word-aligned storage.
| Name | Type | Required | Description |
|---|---|---|---|
| w0 | bigint | Yes | Most significant 64 bits (bytes 0-7). |
| w1 | bigint | Yes | Second 64 bits (bytes 8-15). |
| w2 | bigint | Yes | Third 64 bits (bytes 16-23). |
| w3 | bigint | Yes | Least significant 64 bits (bytes 24-31). |
| Type | Description |
|---|---|
| Address | The new Address object. |
static uncompressedToCompressed(publicKey: ArrayLike<number>): Uint8ArrayConverts a 65-byte uncompressed public key to a 33-byte compressed public key.
| Name | Type | Required | Description |
|---|---|---|---|
| publicKey | ArrayLike<number> | Yes | The 65-byte uncompressed public key (starting with 0x04). |
| Type | Description |
|---|---|
| Uint8Array | The 33-byte compressed public key. |
toHex(): stringReturns the SHA256 hash of the ML-DSA public key as a 0x-prefixed hex string. This 32-byte value is the universal OP_NET identifier for the address.
| Type | Description |
|---|---|
| string | 0x-prefixed 64-character hex string. |
const address = Address.fromString('0xabcd...', '0x02...');
console.log(address.toHex()); // '0xabcd...'tweakedToHex(): stringReturns the classical tweaked x-only public key as a 0x-prefixed hex string.
| Type | Description |
|---|---|
| string | 0x-prefixed hex classical tweaked x-only public key. |
| Type | Description |
|---|---|
| Error | Thrown if the legacy public key was not set. |
toBuffer(): MLDSAHashedPublicKeyReturns the address content (SHA256 hash of the ML-DSA public key) as a new Uint8Array.
| Type | Description |
|---|---|
| Uint8Array | Branded as MLDSAHashedPublicKey. |
originalPublicKeyBuffer(): PublicKeyReturns the original compressed classical public key (33 bytes) as a new Uint8Array. This is a copy of the key that was provided to the constructor.
| Type | Description |
|---|---|
| Uint8Array | The 33-byte compressed public key, branded as PublicKey. |
| Type | Description |
|---|---|
| Error | Thrown if the legacy public key was not set. |
tweakedPublicKeyToBuffer(): XOnlyPublicKeyReturns the classical tweaked x-only public key (32 bytes) as a new Uint8Array.
| Type | Description |
|---|---|
| Uint8Array | Branded as XOnlyPublicKey. |
| Type | Description |
|---|---|
| Error | Thrown if the legacy public key was not set. |
toUncompressedHex(): stringReturns the uncompressed classical public key (65 bytes, starting with 0x04) as a 0x-prefixed hex string.
| Type | Description |
|---|---|
| string | The uncompressed hex classical public key. |
| Type | Description |
|---|---|
| Error | Thrown if the legacy public key was not set. |
toUncompressedBuffer(): PublicKeyReturns the uncompressed classical public key (65 bytes) as a Uint8Array.
| Type | Description |
|---|---|
| Uint8Array | Branded as PublicKey. |
| Type | Description |
|---|---|
| Error | Thrown if the legacy public key was not set. |
toHybridPublicKeyHex(): stringReturns the hybrid public key (derived from decompressing the classical public key) as a 0x-prefixed hex string.
| Type | Description |
|---|---|
| string | 0x-prefixed hex hybrid public key. |
| Type | Description |
|---|---|
| Error | Thrown if the legacy public key was not set. |
toHybridPublicKeyBuffer(): HybridPublicKeyReturns the hybrid public key as a Uint8Array.
| Type | Description |
|---|---|
| Uint8Array | Branded as HybridPublicKey. |
| Type | Description |
|---|---|
| Error | Thrown if the legacy public key was not set. |
toTweakedHybridPublicKeyHex(): stringReturns the tweaked hybrid public key (derived from the Taproot-tweaked classical public key) as a 0x-prefixed hex string.
| Type | Description |
|---|---|
| string | 0x-prefixed hex tweaked hybrid public key. |
| Type | Description |
|---|---|
| Error | Thrown if the legacy public key was not set. |
toTweakedHybridPublicKeyBuffer(): Uint8ArrayReturns the tweaked hybrid public key as a Uint8Array.
| Type | Description |
|---|---|
| Uint8Array | The tweaked hybrid public key as a Uint8Array. |
| Type | Description |
|---|---|
| Error | Thrown if the legacy public key was not set. |
p2tr(network: Network): stringReturns the Taproot (P2TR) address derived from the classical tweaked public key.
| Name | Type | Required | Description |
|---|---|---|---|
| network | Network | Yes | The Bitcoin network. |
| Type | Description |
|---|---|
| string | A bech32m Taproot address (e.g., bc1p...). |
| Type | Description |
|---|---|
| Error | Thrown if the legacy public key was not set. |
import { networks } from '@btc-vision/bitcoin';
const taprootAddr = address.p2tr(networks.bitcoin);
console.log(taprootAddr); // 'bc1p...'p2wpkh(network: Network): stringReturns the native SegWit (P2WPKH) address derived from the classical public key.
| Name | Type | Required | Description |
|---|---|---|---|
| network | Network | Yes | The Bitcoin network. |
| Type | Description |
|---|---|
| string | A bech32 SegWit address (e.g., bc1q...). |
p2pkh(network: Network): stringReturns the legacy (P2PKH) address derived from the classical public key.
| Name | Type | Required | Description |
|---|---|---|---|
| network | Network | Yes | The Bitcoin network. |
| Type | Description |
|---|---|
| string | A legacy address (e.g., 1...). |
p2shp2wpkh(network: Network): stringReturns the nested SegWit (P2SH-P2WPKH) address derived from the classical public key.
| Name | Type | Required | Description |
|---|---|---|---|
| network | Network | Yes | The Bitcoin network. |
| Type | Description |
|---|---|
| string | A nested SegWit address (e.g., 3...). |
p2wda(network: Network): IP2WSHAddressGenerates a Pay-to-Witness-Data-Authentication (P2WDA) address. P2WDA addresses are special P2WSH addresses that embed authenticated data in the witness field, achieving 75% cost reduction through the SegWit witness discount.
| Name | Type | Required | Description |
|---|---|---|---|
| network | Network | Yes | The Bitcoin network. |
| Type | Description |
|---|---|
| IP2WSHAddress | An object containing address (the P2WSH address string) and witnessScript (the serialized witness script). |
| Type | Description |
|---|---|
| Error | Thrown if the public key is not set or if generation fails. |
const p2wdaInfo = address.p2wda(networks.bitcoin);
console.log(p2wdaInfo.address); // 'bc1q...' (P2WSH format)
console.log(p2wdaInfo.witnessScript); // Uint8Array of witness scriptp2op(network: Network): stringReturns the P2OP (Pay-to-OPNet) address encoded in bech32m format with witness version 16. This address is derived from the ML-DSA hash only and does not require the classical public key.
| Name | Type | Required | Description |
|---|---|---|---|
| network | Network | Yes | The Bitcoin network. |
| Type | Description |
|---|---|
| string | A bech32m P2OP address. |
p2pk(): stringReturns the hex-encoded ML-DSA hash (equivalent to toHex()). This is the public-key representation used for Pay-to-Public-Key style outputs.
| Type | Description |
|---|---|
| string | The hex-encoded ML-DSA hash. |
override set(mldsaPublicKey: ArrayLike<number>): voidOverrides Uint8Array.set() to set the ML-DSA public key content. If the provided bytes are a raw ML-DSA public key (1312, 1952, or 2592 bytes), it will be hashed with SHA256 and the hash stored. If it is already 32 bytes, it is stored directly.
| Name | Type | Required | Description |
|---|---|---|---|
| mldsaPublicKey | ArrayLike<number> | Yes | The ML-DSA public key (raw or hashed). |
equals(other: Address): booleanCompares two addresses byte-by-byte for equality. Comparison is based on the ML-DSA hash content only (the 32-byte Uint8Array content).
| Name | Type | Required | Description |
|---|---|---|---|
| other | Address | Yes | The address to compare against. |
| Type | Description |
|---|---|
| boolean | true if the two addresses are equal, false otherwise. |
const a = Address.fromString('0xabcd...', '0x02...');
const b = Address.fromString('0xabcd...', '0x03...');
console.log(a.equals(b)); // true (same ML-DSA hash)lessThan(other: Address): booleanByte-by-byte comparison of two addresses, treating them as big-endian 256-bit unsigned integers. Useful for sorting or ordering addresses.
| Name | Type | Required | Description |
|---|---|---|---|
| other | Address | Yes | The address to compare against. |
| Type | Description |
|---|---|
| boolean | true if this address is less than the other address, false otherwise. |
greaterThan(other: Address): booleanByte-by-byte comparison of two addresses, treating them as big-endian 256-bit unsigned integers. Useful for sorting or ordering addresses.
| Name | Type | Required | Description |
|---|---|---|---|
| other | Address | Yes | The address to compare against. |
| Type | Description |
|---|---|
| boolean | true if this address is greater than the other address, false otherwise. |
toBigInt(): bigintConverts the 32-byte ML-DSA hash to a single bigint value. Uses an optimized DataView approach (10-20x faster than string conversion). The result is cached after the first call.
| Type | Description |
|---|---|
| bigint | ML-DSA hash as a 256-bit unsigned integer. |
tweakedToBigInt(): bigintConverts the 32-byte tweaked classical public key to a single bigint value. Cached after the first call.
| Type | Description |
|---|---|
| bigint | Tweaked classical public key as a 256-bit unsigned integer. |
| Type | Description |
|---|---|
| Error | Thrown if the legacy public key was not set. |
toUint64Array(): [bigint, bigint, bigint, bigint]Splits the 32-byte address into four 64-bit big-endian unsigned integers. The result is cached.
| Type | Description |
|---|---|
| [bigint, bigint, bigint, bigint] | [w0, w1, w2, w3] from most significant to least significant. |
isDead(): booleanChecks if every byte of the ML-DSA hash is zero (i.e., the dead/burn address).
| Type | Description |
|---|---|
| boolean | true if the address is the dead address, false otherwise. |
isValidLegacyPublicKey(network: Network): booleanValidates the classical public key against the given network.
| Name | Type | Required | Description |
|---|---|---|---|
| network | Network | Yes | The Bitcoin network. |
| Type | Description |
|---|---|
| boolean | true if valid, false otherwise. |
| Type | Description |
|---|---|
| Error | Thrown if the legacy key was not set. |
toString(): stringReturns the 0x-prefixed hex string of the ML-DSA hash. Equivalent to toHex().
| Type | Description |
|---|---|
| string | The 0x-prefixed hex string of the ML-DSA hash. |
toJSON(): stringReturns the 0x-prefixed hex string of the ML-DSA hash. Used for JSON serialization.
| Type | Description |
|---|---|
| string | The 0x-prefixed hex string of the ML-DSA hash. |
toCSV(duration: bigint | number | string, network: Network): IP2WSHAddressGenerates a P2WSH address with a CheckSequenceVerify (CSV) time lock. The resulting address can only be spent after the specified number of blocks.
| Name | Type | Required | Description |
|---|---|---|---|
| duration | bigint | number | string | Yes | Number of blocks before spending is allowed (1-65535). |
| network | Network | Yes | The Bitcoin network. |
| Type | Description |
|---|---|
| IP2WSHAddress | The time-locked address and its witness script. |
| Type | Description |
|---|---|
| Error | Thrown if the duration is out of range or the public key is not set. |
toCSVTweaked(duration: bigint | number | string, network: Network): stringGenerates a P2TR address with a CSV time lock using the tweaked public key.
| Name | Type | Required | Description |
|---|---|---|---|
| duration | bigint | number | string | Yes | Number of blocks before spending is allowed (1-65535). |
| network | Network | Yes | The Bitcoin network. |
| Type | Description |
|---|---|
| string | The time-locked Taproot address. |
toCSVP2MR(duration: bigint | number | string, network: Network): stringGenerates a P2MR address with a CSV time lock. The CSV script uses the tweaked public key for OP_CHECKSIG, but the P2MR output itself commits directly to the Merkle root without exposing an internal public key, providing quantum resistance.
| Name | Type | Required | Description |
|---|---|---|---|
| duration | bigint | number | string | Yes | Number of blocks before spending is allowed (1-65535). |
| network | Network | Yes | The Bitcoin network. |
| Type | Description |
|---|---|
| string | The time-locked P2MR address (e.g., bc1z...). |
| Type | Description |
|---|---|
| Error | Thrown if the duration is out of range or the public key is not set. |
const csvP2MR = address.toCSVP2MR(144, networks.bitcoin);
// Returns a bc1z... address with a 144-block CSV timelock[Symbol.dispose](): voidSecurely disposes the address by zeroing all internal buffers and clearing cached state. Use this when you are done with the address to minimize secret material exposure.
{
using address = new Address(mldsaKey, classicalKey);
// ... use address
} // automatically disposed hereget originalPublicKey(): PublicKey | undefinedReturn the original compressed classical public key (33 bytes), if set. Triggers lazy legacy processing.
| Name | Type | Description |
|---|---|---|
| - | PublicKey | Original compressed classical public key. |
| - | undefined | When not defined. |
get mldsaPublicKey(): MLDSAHashedPublicKey | undefinedReturn the original (unhashed) ML-DSA public key, if the raw key was provided to the constructor.
| Name | Type | Description |
|---|---|---|
| - | MLDSAHashedPublicKey | Original (unhashed) ML-DSA public key. |
| - | undefined | When not defined. |
get originalMDLSAPublicKey(): Uint8Array | undefined
set originalMDLSAPublicKey(key: Uint8Array | undefined)Get/set the original ML-DSA public key bytes.
| Name | Type | Required | Description |
|---|---|---|---|
| key | Uint8Array | undefined | - | The original ML-DSA public key bytes or undefined. |
| Name | Type | Description |
|---|---|---|
| - | Uint8Array | Original ML-DSA public key bytes. |
| - | undefined | When not defined. |
get mldsaLevel(): MLDSASecurityLevel | undefined
set mldsaLevel(level: MLDSASecurityLevel | undefined)Get/set the ML-DSA security level associated with this address.
| Name | Type | Required | Description |
|---|---|---|---|
| level | MLDSASecurityLevel | undefined | - | The ML-DSA security level or undefined. |
| Name | Type | Description |
|---|---|---|
| - | MLDSASecurityLevel | The ML-DSA security level. |
| - | undefined | When not defined. |