mirror of
				https://github.com/Xahau/xahau.js.git
				synced 2025-11-04 04:55:48 +00:00 
			
		
		
		
	- Removes need for bundlers to polyfill the `Buffer` class. `UInt8Array` are used instead which are native to the browser and node. - Reduces bundle size 7.1kb gzipped and eliminates 4 runtime dependencies: `base-x`, `base64-js`, `buffer`, and `ieee754`. BREAKING CHANGE: All methods that previously took a `Buffer` now accept a `UInt8Array`. --------- Co-authored-by: Jackson Mills <jmills@ripple.com>
		
			
				
	
	
		
			291 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			291 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import { bytesToHex, hexToBytes } from '@xrplf/isomorphic/utils'
 | 
						|
 | 
						|
import {
 | 
						|
  classicAddressToXAddress,
 | 
						|
  xAddressToClassicAddress,
 | 
						|
  isValidXAddress,
 | 
						|
  encodeXAddress,
 | 
						|
} from '../src'
 | 
						|
 | 
						|
interface AddressTestCase {
 | 
						|
  // classicAddress
 | 
						|
  [0]: string
 | 
						|
  // tag
 | 
						|
  [1]: false | number
 | 
						|
  // mainnetAddress
 | 
						|
  [2]: string
 | 
						|
  // testnetAddress
 | 
						|
  [3]: string
 | 
						|
}
 | 
						|
 | 
						|
const testCases: AddressTestCase[] = [
 | 
						|
  [
 | 
						|
    'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
 | 
						|
    false,
 | 
						|
    'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
 | 
						|
    'T719a5UwUCnEs54UsxG9CJYYDhwmFCqkr7wxCcNcfZ6p5GZ',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
 | 
						|
    1,
 | 
						|
    'X7AcgcsBL6XDcUb289X4mJ8djcdyKaGZMhc9YTE92ehJ2Fu',
 | 
						|
    'T719a5UwUCnEs54UsxG9CJYYDhwmFCvbJNZbi37gBGkRkbE',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
 | 
						|
    14,
 | 
						|
    'X7AcgcsBL6XDcUb289X4mJ8djcdyKaGo2K5VpXpmCqbV2gS',
 | 
						|
    'T719a5UwUCnEs54UsxG9CJYYDhwmFCvqXVCALUGJGSbNV3x',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
 | 
						|
    11747,
 | 
						|
    'X7AcgcsBL6XDcUb289X4mJ8djcdyKaLFuhLRuNXPrDeJd9A',
 | 
						|
    'T719a5UwUCnEs54UsxG9CJYYDhwmFCziiNHtUukubF2Mg6t',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rLczgQHxPhWtjkaQqn3Q6UM8AbRbbRvs5K',
 | 
						|
    false,
 | 
						|
    'XVZVpQj8YSVpNyiwXYSqvQoQqgBttTxAZwMcuJd4xteQHyt',
 | 
						|
    'TVVrSWtmQQssgVcmoMBcFQZKKf56QscyWLKnUyiuZW8ALU4',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
 | 
						|
    false,
 | 
						|
    'X7YenJqxv3L66CwhBSfd3N8RzGXxYqPopMGMsCcpho79rex',
 | 
						|
    'T77wVQzA8ntj9wvCTNiQpNYLT5hmhRsFyXDoMLqYC4BzQtV',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
 | 
						|
    58,
 | 
						|
    'X7YenJqxv3L66CwhBSfd3N8RzGXxYqV56ZkTCa9UCzgaao1',
 | 
						|
    'T77wVQzA8ntj9wvCTNiQpNYLT5hmhR9kej6uxm4jGcQD7rZ',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW',
 | 
						|
    23480,
 | 
						|
    'X7d3eHCXzwBeWrZec1yT24iZerQjYL8m8zCJ16ACxu1BrBY',
 | 
						|
    'T7YChPFWifjCAXLEtg5N74c7fSAYsvSokwcmBPBUZWhxH5P',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW',
 | 
						|
    11747,
 | 
						|
    'X7d3eHCXzwBeWrZec1yT24iZerQjYLo2CJf8oVC5CMWey5m',
 | 
						|
    'T7YChPFWifjCAXLEtg5N74c7fSAYsvTcc7nEfwuEEvn5Q4w',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
 | 
						|
    false,
 | 
						|
    'XVLhHMPHU98es4dbozjVtdWzVrDjtV5fdx1mHp98tDMoQXb',
 | 
						|
    'TVE26TYGhfLC7tQDno7G8dGtxSkYQn49b3qD26PK7FcGSKE',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
 | 
						|
    0,
 | 
						|
    'XVLhHMPHU98es4dbozjVtdWzVrDjtV8AqEL4xcZj5whKbmc',
 | 
						|
    'TVE26TYGhfLC7tQDno7G8dGtxSkYQnSy8RHqGHoGJ59spi2',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
 | 
						|
    1,
 | 
						|
    'XVLhHMPHU98es4dbozjVtdWzVrDjtV8xvjGQTYPiAx6gwDC',
 | 
						|
    'TVE26TYGhfLC7tQDno7G8dGtxSkYQnSz1uDimDdPYXzSpyw',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
 | 
						|
    2,
 | 
						|
    'XVLhHMPHU98es4dbozjVtdWzVrDjtV8zpDURx7DzBCkrQE7',
 | 
						|
    'TVE26TYGhfLC7tQDno7G8dGtxSkYQnTryP9tG9TW8GeMBmd',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
 | 
						|
    32,
 | 
						|
    'XVLhHMPHU98es4dbozjVtdWzVrDjtVoYiC9UvKfjKar4LJe',
 | 
						|
    'TVE26TYGhfLC7tQDno7G8dGtxSkYQnT2oqaCDzMEuCDAj1j',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
 | 
						|
    276,
 | 
						|
    'XVLhHMPHU98es4dbozjVtdWzVrDjtVoKj3MnFGMXEFMnvJV',
 | 
						|
    'TVE26TYGhfLC7tQDno7G8dGtxSkYQnTMgJJYfAbsiPsc6Zg',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
 | 
						|
    65591,
 | 
						|
    'XVLhHMPHU98es4dbozjVtdWzVrDjtVozpjdhPQVdt3ghaWw',
 | 
						|
    'TVE26TYGhfLC7tQDno7G8dGtxSkYQn7ryu2W6njw7mT1jmS',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
 | 
						|
    16781933,
 | 
						|
    'XVLhHMPHU98es4dbozjVtdWzVrDjtVqrDUk2vDpkTjPsY73',
 | 
						|
    'TVE26TYGhfLC7tQDno7G8dGtxSkYQnVsw45sDtGHhLi27Qa',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
 | 
						|
    4294967294,
 | 
						|
    'XVLhHMPHU98es4dbozjVtdWzVrDjtV1kAsixQTdMjbWi39u',
 | 
						|
    'TVE26TYGhfLC7tQDno7G8dGtxSkYQnX8tDFQ53itLNqs6vU',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
 | 
						|
    4294967295,
 | 
						|
    'XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi',
 | 
						|
    'TVE26TYGhfLC7tQDno7G8dGtxSkYQnXoy6kSDh6rZzApc69',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY',
 | 
						|
    false,
 | 
						|
    'XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD2gYsjNFQLKYW33DzBm',
 | 
						|
    'TVd2rqMkYL2AyS97NdELcpeiprNBjwLZzuUG5rZnaewsahi',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY',
 | 
						|
    0,
 | 
						|
    'XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD2m4Er6SnvjVLpMWPjR',
 | 
						|
    'TVd2rqMkYL2AyS97NdELcpeiprNBjwRQUBetPbyrvXSTuxU',
 | 
						|
  ],
 | 
						|
  [
 | 
						|
    'rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY',
 | 
						|
    13371337,
 | 
						|
    'XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD2qwGkhgc48zzcx6Gkr',
 | 
						|
    'TVd2rqMkYL2AyS97NdELcpeiprNBjwVUDvp3vhpXbNhLwJi',
 | 
						|
  ],
 | 
						|
]
 | 
						|
 | 
						|
;[false, true].forEach((isTestAddress) => {
 | 
						|
  const MAX_32_BIT_UNSIGNED_INT = 4294967295
 | 
						|
  const network = isTestAddress ? ' (test)' : ' (main)'
 | 
						|
 | 
						|
  testCases.forEach((testCase) => {
 | 
						|
    const classicAddress = testCase[0]
 | 
						|
    const tag = testCase[1] === false ? false : testCase[1]
 | 
						|
    const xAddress = isTestAddress ? testCase[3] : testCase[2]
 | 
						|
    it(`Converts ${classicAddress}${
 | 
						|
      tag ? `:${tag}` : ''
 | 
						|
    } to ${xAddress}${network}`, () => {
 | 
						|
      expect(classicAddressToXAddress(classicAddress, tag, isTestAddress)).toBe(
 | 
						|
        xAddress,
 | 
						|
      )
 | 
						|
      const myClassicAddress = xAddressToClassicAddress(xAddress)
 | 
						|
      expect(myClassicAddress).toEqual({
 | 
						|
        classicAddress,
 | 
						|
        tag,
 | 
						|
        test: isTestAddress,
 | 
						|
      })
 | 
						|
      expect(isValidXAddress(xAddress)).toBe(true)
 | 
						|
    })
 | 
						|
  })
 | 
						|
 | 
						|
  {
 | 
						|
    const classicAddress = 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf'
 | 
						|
    const tag = MAX_32_BIT_UNSIGNED_INT + 1
 | 
						|
 | 
						|
    it(`Converting ${classicAddress}:${tag}${network} throws`, () => {
 | 
						|
      expect(() => {
 | 
						|
        classicAddressToXAddress(classicAddress, tag, isTestAddress)
 | 
						|
      }).toThrow(new Error('Invalid tag'))
 | 
						|
    })
 | 
						|
  }
 | 
						|
 | 
						|
  {
 | 
						|
    const classicAddress = 'r'
 | 
						|
    it(`Invalid classic address: Converting ${classicAddress}${network} throws`, () => {
 | 
						|
      expect(() => {
 | 
						|
        classicAddressToXAddress(classicAddress, false, isTestAddress)
 | 
						|
      }).toThrow(
 | 
						|
        new Error('invalid_input_size: decoded data must have length >= 5'),
 | 
						|
      )
 | 
						|
    })
 | 
						|
  }
 | 
						|
 | 
						|
  {
 | 
						|
    const highAndLowAccounts = [
 | 
						|
      hexToBytes('00'.repeat(20)),
 | 
						|
      hexToBytes(`${'00'.repeat(19)}01`),
 | 
						|
      hexToBytes('01'.repeat(20)),
 | 
						|
      hexToBytes('FF'.repeat(20)),
 | 
						|
    ]
 | 
						|
 | 
						|
    highAndLowAccounts.forEach((accountId) => {
 | 
						|
      const tagTestCases: Array<false | number> = [
 | 
						|
        false,
 | 
						|
        0,
 | 
						|
        1,
 | 
						|
        MAX_32_BIT_UNSIGNED_INT,
 | 
						|
      ]
 | 
						|
      tagTestCases.forEach((testCase) => {
 | 
						|
        const tag = testCase || false
 | 
						|
        const xAddress = encodeXAddress(accountId, tag, isTestAddress)
 | 
						|
        it(`Encoding ${bytesToHex(accountId)}${
 | 
						|
          tag ? `:${tag}` : ''
 | 
						|
        } to ${xAddress} has expected length`, () => {
 | 
						|
          expect(xAddress.length).toBe(47)
 | 
						|
        })
 | 
						|
      })
 | 
						|
    })
 | 
						|
  }
 | 
						|
})
 | 
						|
 | 
						|
{
 | 
						|
  const xAddress = 'XVLhHMPHU98es4dbozjVtdWzVrDjtV5fdx1mHp98tDMoQXa'
 | 
						|
  it(`Invalid X-address (bad checksum): Converting ${xAddress} throws`, () => {
 | 
						|
    expect(() => {
 | 
						|
      xAddressToClassicAddress(xAddress)
 | 
						|
    }).toThrow(new Error('checksum_invalid'))
 | 
						|
  })
 | 
						|
}
 | 
						|
 | 
						|
{
 | 
						|
  const xAddress = 'dGzKGt8CVpWoa8aWL1k18tAdy9Won3PxynvbbpkAqp3V47g'
 | 
						|
  it(`Invalid X-address (bad prefix): Converting ${xAddress} throws`, () => {
 | 
						|
    expect(() => {
 | 
						|
      xAddressToClassicAddress(xAddress)
 | 
						|
    }).toThrow(new Error('Invalid X-address: bad prefix'))
 | 
						|
  })
 | 
						|
}
 | 
						|
 | 
						|
it(`Invalid X-address (64-bit tag) throws`, () => {
 | 
						|
  expect(() => {
 | 
						|
    // Encoded from:
 | 
						|
    // {
 | 
						|
    //   classicAddress: 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
 | 
						|
    //   tag: MAX_32_BIT_UNSIGNED_INT + 1
 | 
						|
    // }
 | 
						|
    xAddressToClassicAddress('XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8zeUygYrCgrPh')
 | 
						|
  }).toThrow(new Error('Unsupported X-address'))
 | 
						|
})
 | 
						|
 | 
						|
it(`Invalid Account ID throws`, () => {
 | 
						|
  expect(() => {
 | 
						|
    encodeXAddress(hexToBytes('00'.repeat(19)), false, false)
 | 
						|
  }).toThrowError('Account ID must be 20 bytes')
 | 
						|
})
 | 
						|
 | 
						|
it(`isValidXAddress returns false for invalid X-address`, () => {
 | 
						|
  expect(
 | 
						|
    isValidXAddress('XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8zeUygYrCgrPh'),
 | 
						|
  ).toBe(false)
 | 
						|
})
 | 
						|
 | 
						|
it(`Converts X7AcgcsBL6XDcUb... to r9cZA1mLK5R5A... and tag: false`, () => {
 | 
						|
  const classicAddress = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59'
 | 
						|
  // eslint-disable-next-line @typescript-eslint/naming-convention -- tag can be false or a number
 | 
						|
  const tag = false
 | 
						|
  const xAddress = 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ'
 | 
						|
  const isTestAddress = false
 | 
						|
  expect(classicAddressToXAddress(classicAddress, tag, isTestAddress)).toBe(
 | 
						|
    xAddress,
 | 
						|
  )
 | 
						|
  const myClassicAddress = xAddressToClassicAddress(xAddress)
 | 
						|
  expect(myClassicAddress).toEqual({
 | 
						|
    classicAddress,
 | 
						|
    tag,
 | 
						|
    test: isTestAddress,
 | 
						|
  })
 | 
						|
  expect(isValidXAddress(xAddress)).toBe(true)
 | 
						|
 | 
						|
  // Notice that converting an X-address to a classic address has `result.tag === false` (not undefined)
 | 
						|
  expect(myClassicAddress.tag).toEqual(false)
 | 
						|
})
 |