Files
salvium-rs/test/address.test.js
T
Matt Hess ea09516981 ● Add proper error handling - eliminate silent failures
- Add ParseError class with context (offset, field, expected/actual)
  - transaction.js: parseRctSigPrunable throws instead of returning null
  - address.js: createAddress, toIntegratedAddress, toStandardAddress throw on invalid input
  - base58.js: decode functions throw descriptive errors
  - scanning.js: validate inputs and throw on failures
  - carrot-scanning.js: validate required fields
  - signature.js: remove debug console.log and testEd25519
  - Update tests to expect throws for error cases
2026-01-24 20:56:24 +00:00

754 lines
23 KiB
JavaScript

/**
* Address Generation Tests
*
* Tests for address creation, integrated addresses, and high-level subaddress generation.
*/
import {
generateSeed,
deriveKeys,
deriveCarrotKeys,
createAddress,
parseAddress,
toIntegratedAddress,
toStandardAddress,
generateRandomPaymentId,
createIntegratedAddressWithRandomId,
generateCNSubaddress,
generateCarrotSubaddress,
isValidAddress,
isMainnet,
isTestnet,
isStagenet,
isCarrot,
isLegacy,
isStandard,
isIntegrated,
isSubaddress,
bytesToHex,
hexToBytes,
seedToMnemonic,
mnemonicToSeed,
NETWORK,
ADDRESS_TYPE,
ADDRESS_FORMAT
} from '../src/index.js';
let passed = 0;
let failed = 0;
function test(name, fn) {
try {
fn();
console.log(`${name}`);
passed++;
} catch (error) {
console.log(`${name}`);
console.log(` Error: ${error.message}`);
failed++;
}
}
function assertEqual(actual, expected, message = '') {
if (actual !== expected) {
throw new Error(`${message} Expected "${expected}", got "${actual}"`);
}
}
function assertNotEqual(actual, expected, message = '') {
if (actual === expected) {
throw new Error(`${message} Values should not be equal: ${actual}`);
}
}
function assertTrue(value, message = '') {
if (!value) {
throw new Error(`${message} Expected true, got ${value}`);
}
}
function assertFalse(value, message = '') {
if (value) {
throw new Error(`${message} Expected false, got ${value}`);
}
}
function assertLength(value, length, message = '') {
if (value.length !== length) {
throw new Error(`${message} Expected length ${length}, got ${value.length}`);
}
}
function assertThrows(fn, message = '') {
try {
fn();
throw new Error(`${message} Expected function to throw, but it did not`);
} catch (e) {
if (e.message.includes('Expected function to throw')) {
throw e;
}
// Function threw as expected
}
}
// ============================================================
// Seed Generation Tests
// ============================================================
console.log('\n--- Seed Generation Tests ---');
test('generateSeed returns 32 bytes', () => {
const seed = generateSeed();
assertLength(seed, 32);
});
test('generateSeed returns Uint8Array', () => {
const seed = generateSeed();
assertTrue(seed instanceof Uint8Array);
});
test('generateSeed produces different seeds each time', () => {
const seed1 = generateSeed();
const seed2 = generateSeed();
assertNotEqual(bytesToHex(seed1), bytesToHex(seed2));
});
test('generateSeed produces non-zero seeds', () => {
const seed = generateSeed();
let hasNonZero = false;
for (let i = 0; i < seed.length; i++) {
if (seed[i] !== 0) {
hasNonZero = true;
break;
}
}
assertTrue(hasNonZero, 'Seed should have non-zero bytes');
});
// ============================================================
// Address Creation Tests
// ============================================================
console.log('\n--- Address Creation Tests ---');
const testSeed = generateSeed();
const testKeys = deriveKeys(testSeed);
test('createAddress creates valid mainnet legacy standard address', () => {
const addr = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: testKeys.spendPublicKey,
viewPublicKey: testKeys.viewPublicKey
});
assertTrue(addr !== null, 'Address should not be null');
assertTrue(isValidAddress(addr), 'Address should be valid');
assertTrue(isMainnet(addr), 'Should be mainnet');
assertTrue(isLegacy(addr), 'Should be legacy');
assertTrue(isStandard(addr), 'Should be standard');
});
test('createAddress creates valid mainnet CARROT standard address', () => {
const addr = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.CARROT,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: testKeys.spendPublicKey,
viewPublicKey: testKeys.viewPublicKey
});
assertTrue(addr !== null);
assertTrue(isValidAddress(addr));
assertTrue(isMainnet(addr));
assertTrue(isCarrot(addr));
assertTrue(isStandard(addr));
});
test('createAddress creates valid testnet address', () => {
const addr = createAddress({
network: NETWORK.TESTNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: testKeys.spendPublicKey,
viewPublicKey: testKeys.viewPublicKey
});
assertTrue(addr !== null);
assertTrue(isValidAddress(addr));
assertTrue(isTestnet(addr));
});
test('createAddress creates valid stagenet address', () => {
const addr = createAddress({
network: NETWORK.STAGENET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: testKeys.spendPublicKey,
viewPublicKey: testKeys.viewPublicKey
});
assertTrue(addr !== null);
assertTrue(isValidAddress(addr));
assertTrue(isStagenet(addr));
});
test('createAddress throws for invalid keys', () => {
assertThrows(() => {
createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: new Uint8Array(31), // Wrong length
viewPublicKey: testKeys.viewPublicKey
});
});
});
test('createAddress round-trips correctly', () => {
const addr = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: testKeys.spendPublicKey,
viewPublicKey: testKeys.viewPublicKey
});
const parsed = parseAddress(addr);
assertTrue(parsed.valid);
assertEqual(bytesToHex(parsed.spendPublicKey), bytesToHex(testKeys.spendPublicKey));
assertEqual(bytesToHex(parsed.viewPublicKey), bytesToHex(testKeys.viewPublicKey));
});
// ============================================================
// Integrated Address Tests
// ============================================================
console.log('\n--- Integrated Address Tests ---');
test('toIntegratedAddress creates valid integrated address', () => {
const standardAddr = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: testKeys.spendPublicKey,
viewPublicKey: testKeys.viewPublicKey
});
const paymentId = generateRandomPaymentId();
const integrated = toIntegratedAddress(standardAddr, paymentId);
assertTrue(integrated !== null);
assertTrue(isValidAddress(integrated));
assertTrue(isIntegrated(integrated));
assertTrue(isMainnet(integrated));
});
test('toIntegratedAddress accepts hex string payment ID', () => {
const standardAddr = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: testKeys.spendPublicKey,
viewPublicKey: testKeys.viewPublicKey
});
const integrated = toIntegratedAddress(standardAddr, 'deadbeef12345678');
assertTrue(integrated !== null);
assertTrue(isIntegrated(integrated));
});
test('toIntegratedAddress preserves payment ID', () => {
const standardAddr = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: testKeys.spendPublicKey,
viewPublicKey: testKeys.viewPublicKey
});
const paymentIdHex = 'deadbeef12345678';
const integrated = toIntegratedAddress(standardAddr, paymentIdHex);
const parsed = parseAddress(integrated);
assertEqual(bytesToHex(parsed.paymentId), paymentIdHex);
});
test('toIntegratedAddress throws for integrated address input', () => {
const standardAddr = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: testKeys.spendPublicKey,
viewPublicKey: testKeys.viewPublicKey
});
const integrated = toIntegratedAddress(standardAddr, 'deadbeef12345678');
assertThrows(() => {
toIntegratedAddress(integrated, 'abcdef0123456789');
});
});
test('toStandardAddress extracts standard from integrated', () => {
const standardAddr = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: testKeys.spendPublicKey,
viewPublicKey: testKeys.viewPublicKey
});
const integrated = toIntegratedAddress(standardAddr, 'deadbeef12345678');
const extracted = toStandardAddress(integrated);
assertEqual(extracted, standardAddr);
});
test('toStandardAddress throws for non-integrated', () => {
const standardAddr = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: testKeys.spendPublicKey,
viewPublicKey: testKeys.viewPublicKey
});
assertThrows(() => {
toStandardAddress(standardAddr);
});
});
test('createIntegratedAddressWithRandomId works', () => {
const standardAddr = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: testKeys.spendPublicKey,
viewPublicKey: testKeys.viewPublicKey
});
const result = createIntegratedAddressWithRandomId(standardAddr);
assertTrue(result !== null);
assertTrue(result.address !== null);
assertTrue(isIntegrated(result.address));
assertLength(result.paymentId, 8);
assertLength(result.paymentIdHex, 16);
});
test('createIntegratedAddressWithRandomId generates unique payment IDs', () => {
const standardAddr = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: testKeys.spendPublicKey,
viewPublicKey: testKeys.viewPublicKey
});
const result1 = createIntegratedAddressWithRandomId(standardAddr);
const result2 = createIntegratedAddressWithRandomId(standardAddr);
assertNotEqual(result1.paymentIdHex, result2.paymentIdHex);
});
// ============================================================
// High-Level Subaddress Generation Tests
// ============================================================
console.log('\n--- High-Level Subaddress Generation Tests ---');
test('generateCNSubaddress creates valid subaddress', () => {
const result = generateCNSubaddress({
network: NETWORK.MAINNET,
spendPublicKey: testKeys.spendPublicKey,
viewSecretKey: testKeys.viewSecretKey,
major: 0,
minor: 1
});
assertTrue(result !== null);
assertTrue(result.address !== null);
assertTrue(isValidAddress(result.address));
assertTrue(isSubaddress(result.address));
assertTrue(isLegacy(result.address));
assertLength(result.spendPublicKey, 32);
assertLength(result.viewPublicKey, 32);
assertEqual(result.major, 0);
assertEqual(result.minor, 1);
});
test('generateCNSubaddress (0,0) creates standard address', () => {
const result = generateCNSubaddress({
network: NETWORK.MAINNET,
spendPublicKey: testKeys.spendPublicKey,
viewSecretKey: testKeys.viewSecretKey,
major: 0,
minor: 0
});
// (0,0) should return original keys, but as subaddress type
assertEqual(bytesToHex(result.spendPublicKey), bytesToHex(testKeys.spendPublicKey));
});
test('generateCNSubaddress creates different addresses for different indices', () => {
const result1 = generateCNSubaddress({
network: NETWORK.MAINNET,
spendPublicKey: testKeys.spendPublicKey,
viewSecretKey: testKeys.viewSecretKey,
major: 0,
minor: 1
});
const result2 = generateCNSubaddress({
network: NETWORK.MAINNET,
spendPublicKey: testKeys.spendPublicKey,
viewSecretKey: testKeys.viewSecretKey,
major: 0,
minor: 2
});
assertNotEqual(result1.address, result2.address);
});
test('generateCarrotSubaddress creates valid CARROT subaddress', () => {
const carrotKeys = deriveCarrotKeys(testSeed);
const result = generateCarrotSubaddress({
network: NETWORK.MAINNET,
accountSpendPubkey: testKeys.spendPublicKey,
accountViewPubkey: testKeys.viewPublicKey,
generateAddressSecret: hexToBytes(carrotKeys.generateAddressSecret),
major: 0,
minor: 1
});
assertTrue(result !== null);
assertTrue(result.address !== null);
assertTrue(isValidAddress(result.address));
assertTrue(isCarrot(result.address));
assertFalse(result.isMainAddress);
});
test('generateCarrotSubaddress (0,0) returns main address', () => {
const carrotKeys = deriveCarrotKeys(testSeed);
const result = generateCarrotSubaddress({
network: NETWORK.MAINNET,
accountSpendPubkey: testKeys.spendPublicKey,
accountViewPubkey: testKeys.viewPublicKey,
generateAddressSecret: hexToBytes(carrotKeys.generateAddressSecret),
major: 0,
minor: 0
});
assertTrue(result.isMainAddress);
assertTrue(isStandard(result.address));
});
// ============================================================
// Full Wallet Generation Flow Test
// ============================================================
console.log('\n--- Full Wallet Generation Flow ---');
test('Complete wallet generation flow works', () => {
// 1. Generate seed
const seed = generateSeed();
assertLength(seed, 32);
// 2. Derive keys
const keys = deriveKeys(seed);
assertLength(keys.spendSecretKey, 32);
assertLength(keys.spendPublicKey, 32);
assertLength(keys.viewSecretKey, 32);
assertLength(keys.viewPublicKey, 32);
// 3. Create main address
const mainAddress = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: keys.spendPublicKey,
viewPublicKey: keys.viewPublicKey
});
assertTrue(isValidAddress(mainAddress));
assertTrue(isStandard(mainAddress));
// 4. Generate subaddress
const subaddress = generateCNSubaddress({
network: NETWORK.MAINNET,
spendPublicKey: keys.spendPublicKey,
viewSecretKey: keys.viewSecretKey,
major: 0,
minor: 1
});
assertTrue(isValidAddress(subaddress.address));
assertTrue(isSubaddress(subaddress.address));
// 5. Create integrated address
const integrated = createIntegratedAddressWithRandomId(mainAddress);
assertTrue(isValidAddress(integrated.address));
assertTrue(isIntegrated(integrated.address));
// 6. Verify addresses are on same network
assertTrue(isMainnet(mainAddress));
assertTrue(isMainnet(subaddress.address));
assertTrue(isMainnet(integrated.address));
});
test('Complete CARROT wallet generation flow works', () => {
// 1. Generate seed
const seed = generateSeed();
// 2. Derive keys
const keys = deriveKeys(seed);
const carrotKeys = deriveCarrotKeys(seed);
// 3. Create CARROT main address
const mainAddress = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.CARROT,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: keys.spendPublicKey,
viewPublicKey: keys.viewPublicKey
});
assertTrue(isValidAddress(mainAddress));
assertTrue(isCarrot(mainAddress));
// 4. Generate CARROT subaddress
const subaddress = generateCarrotSubaddress({
network: NETWORK.MAINNET,
accountSpendPubkey: keys.spendPublicKey,
accountViewPubkey: keys.viewPublicKey,
generateAddressSecret: hexToBytes(carrotKeys.generateAddressSecret),
major: 0,
minor: 1
});
assertTrue(isValidAddress(subaddress.address));
assertTrue(isCarrot(subaddress.address));
});
// ============================================================
// Recovery & Round-Trip Tests
// ============================================================
console.log('\n--- Recovery & Round-Trip Tests ---');
test('Seed -> Mnemonic -> Seed round-trip produces identical seed', () => {
const originalSeed = generateSeed();
const mnemonic = seedToMnemonic(originalSeed, { language: 'english' });
const result = mnemonicToSeed(mnemonic, { language: 'english' });
assertTrue(result.valid, 'Mnemonic should be valid');
assertEqual(bytesToHex(result.seed), bytesToHex(originalSeed), 'Recovered seed should match original');
});
test('Seed -> Mnemonic -> Seed -> Keys produces identical keys', () => {
const originalSeed = generateSeed();
const originalKeys = deriveKeys(originalSeed);
// Convert to mnemonic and back
const mnemonic = seedToMnemonic(originalSeed, { language: 'english' });
const result = mnemonicToSeed(mnemonic, { language: 'english' });
const recoveredKeys = deriveKeys(result.seed);
assertEqual(bytesToHex(recoveredKeys.spendSecretKey), bytesToHex(originalKeys.spendSecretKey));
assertEqual(bytesToHex(recoveredKeys.spendPublicKey), bytesToHex(originalKeys.spendPublicKey));
assertEqual(bytesToHex(recoveredKeys.viewSecretKey), bytesToHex(originalKeys.viewSecretKey));
assertEqual(bytesToHex(recoveredKeys.viewPublicKey), bytesToHex(originalKeys.viewPublicKey));
});
test('Seed -> Mnemonic -> Seed -> Address produces identical address', () => {
const originalSeed = generateSeed();
const originalKeys = deriveKeys(originalSeed);
const originalAddress = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: originalKeys.spendPublicKey,
viewPublicKey: originalKeys.viewPublicKey
});
// Recover from mnemonic
const mnemonic = seedToMnemonic(originalSeed, { language: 'english' });
const result = mnemonicToSeed(mnemonic, { language: 'english' });
const recoveredKeys = deriveKeys(result.seed);
const recoveredAddress = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: recoveredKeys.spendPublicKey,
viewPublicKey: recoveredKeys.viewPublicKey
});
assertEqual(recoveredAddress, originalAddress, 'Recovered address should match original');
});
test('Full wallet recovery flow with subaddresses', () => {
// Original wallet
const originalSeed = generateSeed();
const originalKeys = deriveKeys(originalSeed);
const originalMain = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: originalKeys.spendPublicKey,
viewPublicKey: originalKeys.viewPublicKey
});
const originalSub = generateCNSubaddress({
network: NETWORK.MAINNET,
spendPublicKey: originalKeys.spendPublicKey,
viewSecretKey: originalKeys.viewSecretKey,
major: 0,
minor: 5
});
// Backup and recover
const mnemonic = seedToMnemonic(originalSeed, { language: 'english' });
// Simulate recovery on different device
const recoveryResult = mnemonicToSeed(mnemonic, { language: 'english' });
assertTrue(recoveryResult.valid);
const recoveredKeys = deriveKeys(recoveryResult.seed);
const recoveredMain = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: recoveredKeys.spendPublicKey,
viewPublicKey: recoveredKeys.viewPublicKey
});
const recoveredSub = generateCNSubaddress({
network: NETWORK.MAINNET,
spendPublicKey: recoveredKeys.spendPublicKey,
viewSecretKey: recoveredKeys.viewSecretKey,
major: 0,
minor: 5
});
assertEqual(recoveredMain, originalMain, 'Main address should match');
assertEqual(recoveredSub.address, originalSub.address, 'Subaddress should match');
});
test('CARROT wallet recovery flow', () => {
// Original CARROT wallet
const originalSeed = generateSeed();
const originalKeys = deriveKeys(originalSeed);
const originalCarrotKeys = deriveCarrotKeys(originalSeed);
const originalAddress = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.CARROT,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: originalKeys.spendPublicKey,
viewPublicKey: originalKeys.viewPublicKey
});
// Backup and recover
const mnemonic = seedToMnemonic(originalSeed, { language: 'english' });
const recoveryResult = mnemonicToSeed(mnemonic, { language: 'english' });
const recoveredKeys = deriveKeys(recoveryResult.seed);
const recoveredCarrotKeys = deriveCarrotKeys(recoveryResult.seed);
const recoveredAddress = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.CARROT,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: recoveredKeys.spendPublicKey,
viewPublicKey: recoveredKeys.viewPublicKey
});
assertEqual(recoveredAddress, originalAddress, 'CARROT address should match');
assertEqual(recoveredCarrotKeys.proveSpendKey, originalCarrotKeys.proveSpendKey);
assertEqual(recoveredCarrotKeys.viewBalanceSecret, originalCarrotKeys.viewBalanceSecret);
assertEqual(recoveredCarrotKeys.generateAddressSecret, originalCarrotKeys.generateAddressSecret);
});
test('Mnemonic recovery works across all supported languages', () => {
const originalSeed = generateSeed();
const originalKeys = deriveKeys(originalSeed);
const originalAddress = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: originalKeys.spendPublicKey,
viewPublicKey: originalKeys.viewPublicKey
});
// Test a selection of languages
const testLanguages = ['english', 'spanish', 'french', 'italian', 'portuguese'];
for (const lang of testLanguages) {
const mnemonic = seedToMnemonic(originalSeed, { language: lang });
const result = mnemonicToSeed(mnemonic, { language: lang });
assertTrue(result.valid, `${lang} mnemonic should be valid`);
const recoveredKeys = deriveKeys(result.seed);
const recoveredAddress = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: recoveredKeys.spendPublicKey,
viewPublicKey: recoveredKeys.viewPublicKey
});
assertEqual(recoveredAddress, originalAddress, `${lang} recovery should produce same address`);
}
});
test('Address parsing and recreation produces identical address', () => {
const seed = generateSeed();
const keys = deriveKeys(seed);
const original = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: keys.spendPublicKey,
viewPublicKey: keys.viewPublicKey
});
// Parse it
const parsed = parseAddress(original);
assertTrue(parsed.valid);
// Recreate it from parsed components
const recreated = createAddress({
network: parsed.network,
format: parsed.format,
type: parsed.type,
spendPublicKey: parsed.spendPublicKey,
viewPublicKey: parsed.viewPublicKey
});
assertEqual(recreated, original);
});
test('Integrated address round-trip preserves all data', () => {
const seed = generateSeed();
const keys = deriveKeys(seed);
const standard = createAddress({
network: NETWORK.MAINNET,
format: ADDRESS_FORMAT.LEGACY,
type: ADDRESS_TYPE.STANDARD,
spendPublicKey: keys.spendPublicKey,
viewPublicKey: keys.viewPublicKey
});
const paymentId = 'abcdef0123456789';
const integrated = toIntegratedAddress(standard, paymentId);
// Parse integrated
const parsed = parseAddress(integrated);
assertTrue(parsed.valid);
assertEqual(parsed.type, ADDRESS_TYPE.INTEGRATED);
assertEqual(bytesToHex(parsed.paymentId), paymentId);
// Extract standard and verify
const extractedStandard = toStandardAddress(integrated);
assertEqual(extractedStandard, standard);
});
// ============================================================
// Summary
// ============================================================
console.log('\n--- Address Test Summary ---');
console.log(`Passed: ${passed}`);
console.log(`Failed: ${failed}`);
console.log(`Total: ${passed + failed}`);
if (failed > 0) {
console.log('\n⚠️ Some tests failed!');
process.exit(1);
} else {
console.log('\n✓ All address tests passed!');
}