diff --git a/src/utils.spec.js b/src/utils.spec.js index 4d2581b4c..20fd121c2 100644 --- a/src/utils.spec.js +++ b/src/utils.spec.js @@ -505,66 +505,267 @@ describe('utils', () => { describe('getFormattedAddress', () => { describe('when explicitly passing an address type', () => { - test('returns a checksummed EVM address', () => { - const address = '0x91fd2c8d24767db4ece7069aa27832ffaf8590f3'; - expect(getFormattedAddress(address, 'evm')).toEqual( - '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3' - ); - }); + describe('EVM type parsing', () => { + test('should return checksummed EVM address when given checksummed input', () => { + const address = '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3'; + expect(getFormattedAddress(address, 'evm')).toEqual( + '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3' + ); + }); - test('returns a padded and lowercased starknet address', () => { - const address = - '0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0'; - expect(getFormattedAddress(address, 'starknet')).toEqual( - '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0' - ); - }); + test('should return checksummed EVM address when given lowercase input', () => { + const address = '0x91fd2c8d24767db4ece7069aa27832ffaf8590f3'; + expect(getFormattedAddress(address, 'evm')).toEqual( + '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3' + ); + }); - test('returns an EVM address as starknet address', () => { - const address = '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3'; - expect(getFormattedAddress(address, 'starknet')).toEqual( - '0x00000000000000000000000091fd2c8d24767db4ece7069aa27832ffaf8590f3' - ); - }); + test('should return checksummed EVM address when given uppercase input', () => { + const uppercaseAddress = '0x91FD2C8D24767DB4ECE7069AA27832FFAF8590F3'; + expect(getFormattedAddress(uppercaseAddress, 'evm')).toEqual( + '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3' + ); + }); + + test('should throw error when forcing EVM parsing on address with uppercase 0X prefix', () => { + const uppercaseHexPrefix = + '0X91FD2C8D24767DB4ECE7069AA27832FFAF8590F3'; + expect(() => getFormattedAddress(uppercaseHexPrefix, 'evm')).toThrow( + 'Invalid evm address: 0X91FD2C8D24767DB4ECE7069AA27832FFAF8590F3' + ); + }); + + test('should throw error when forcing EVM parsing on invalid mixed case address', () => { + const invalidMixedCaseAddress = + '0x91Fd2C8d24767Db4eCe7069aA27832FfaF8590F3'; + expect(() => + getFormattedAddress(invalidMixedCaseAddress, 'evm') + ).toThrow( + 'Invalid evm address: 0x91Fd2C8d24767Db4eCe7069aA27832FfaF8590F3' + ); + }); - test('throws an error when the address is not a starknet address', () => { - const address = 'hello'; - expect(() => getFormattedAddress(address, 'starknet')).toThrow(); + test('should throw error when address is not an EVM address', () => { + const address = + '0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0'; + expect(() => getFormattedAddress(address, 'evm')).toThrow( + 'Invalid evm address: 0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0' + ); + }); }); - test('throws an error when the address is not an EVM address', () => { - const address = - '0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0'; - expect(() => getFormattedAddress(address, 'evm')).toThrow(); + describe('Starknet type parsing', () => { + test('should return padded and lowercased starknet address when given unpadded input', () => { + const address = + '0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0'; + expect(getFormattedAddress(address, 'starknet')).toEqual( + '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0' + ); + }); + + test('should return padded and lowercased starknet address when given lowercase input', () => { + const address = + '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0'; + expect(getFormattedAddress(address, 'starknet')).toEqual( + '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0' + ); + }); + + test('should return padded and lowercased starknet address when given uppercase Starknet input', () => { + const uppercaseAddress = + '0x02A0A8F3B6097E7A6BD7649DEB30715323072A159C0E6B71B689BD245C146CC0'; + expect(getFormattedAddress(uppercaseAddress, 'starknet')).toEqual( + '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0' + ); + }); + + test('should return padded and lowercased starknet address when given checksum Starknet input', () => { + const checksumAddress = + '0x02a0a8F3B6097e7A6bd7649DEB30715323072A159c0E6B71B689Bd245c146cC0'; + expect(getFormattedAddress(checksumAddress, 'starknet')).toEqual( + '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0' + ); + }); + + test('should return padded and lowercased starknet address when given mixed case Starknet input', () => { + const mixedCaseAddress = + '0x02A0a8F3B6097e7A6bD7649DEB30715323072a159C0e6b71B689BD245c146Cc0'; + expect(getFormattedAddress(mixedCaseAddress, 'starknet')).toEqual( + '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0' + ); + }); + + test('should return EVM address as starknet address when explicitly formatted', () => { + const address = '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3'; + expect(getFormattedAddress(address, 'starknet')).toEqual( + '0x00000000000000000000000091fd2c8d24767db4ece7069aa27832ffaf8590f3' + ); + }); + + test('should throw error when given invalid Starknet address with explicit format', () => { + const invalidStarknetAddress = '0xinvalidstarknetaddresshere'; + expect(() => + getFormattedAddress(invalidStarknetAddress, 'starknet') + ).toThrow('Invalid starknet address: 0xinvalidstarknetaddresshere'); + }); }); }); describe('when not passing an address type', () => { - test('returns a checksummed EVM address for an EVM input', () => { - const address = '0x91fd2c8d24767db4ece7069aa27832ffaf8590f3'; - expect(getFormattedAddress(address)).toEqual( - '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3' - ); - }); + describe('EVM address auto-detection', () => { + test('should auto-detect and format valid 42-char lowercase EVM address', () => { + const address = '0x91fd2c8d24767db4ece7069aa27832ffaf8590f3'; + expect(getFormattedAddress(address)).toEqual( + '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3' + ); + }); - test('returns a padded and lowercased starknet address for a Starknet input', () => { - const address = - '0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0'; - expect(getFormattedAddress(address)).toEqual( - '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0' - ); + test('should auto-detect and format valid 42-char uppercase EVM address', () => { + const address = '0x91FD2C8D24767DB4ECE7069AA27832FFAF8590F3'; + expect(getFormattedAddress(address)).toEqual( + '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3' + ); + }); + + test('should auto-detect and format valid 42-char checksummed EVM address', () => { + const address = '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3'; + expect(getFormattedAddress(address)).toEqual( + '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3' + ); + }); + + test('should throw error when auto-detecting invalid mixed case EVM address', () => { + const invalidMixedCaseAddress = + '0x91Fd2C8d24767Db4eCe7069aA27832FfaF8590F3'; + expect(() => getFormattedAddress(invalidMixedCaseAddress)).toThrow( + 'Invalid evm address: 0x91Fd2C8d24767Db4eCe7069aA27832FfaF8590F3' + ); + }); + + test('should throw error when auto-detecting 42-char invalid hex address', () => { + const invalidHexAddress = + '0xgggggggggggggggggggggggggggggggggggggggg'; + expect(() => getFormattedAddress(invalidHexAddress)).toThrow( + 'Invalid evm address: 0xgggggggggggggggggggggggggggggggggggggggg' + ); + }); + + test('should throw error when auto-detecting EVM address with uppercase 0X prefix', () => { + const uppercaseHexPrefix = + '0X91FD2C8D24767DB4ECE7069AA27832FFAF8590F3'; + expect(() => getFormattedAddress(uppercaseHexPrefix)).toThrow( + 'Invalid evm address: 0X91FD2C8D24767DB4ECE7069AA27832FFAF8590F3' + ); + }); }); - test('throws an error when the input is not address-like', () => { - const address = 'hello'; - expect(() => getFormattedAddress(address)).toThrow(); + describe('Starknet address auto-detection', () => { + test('should auto-detect and format valid unpadded Starknet address', () => { + const address = + '0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0'; + expect(getFormattedAddress(address)).toEqual( + '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0' + ); + }); + + test('should auto-detect and format valid padded Starknet address', () => { + const address = + '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0'; + expect(getFormattedAddress(address)).toEqual( + '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0' + ); + }); + + test('should auto-detect and format uppercase Starknet address', () => { + const address = + '0x02A0A8F3B6097E7A6BD7649DEB30715323072A159C0E6B71B689BD245C146CC0'; + expect(getFormattedAddress(address)).toEqual( + '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0' + ); + }); + + test('should return padded and lowercased address when input has uppercase 0X prefix', () => { + const fullyUppercaseAddress = + '0X02A0A8F3B6097E7A6BD7649DEB30715323072A159C0E6B71B689BD245C146CC0'; + expect(getFormattedAddress(fullyUppercaseAddress)).toEqual( + '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0' + ); + }); + + test('should return padded and lowercased address when given short input', () => { + const address = '0x1'; + expect(getFormattedAddress(address)).toEqual( + '0x0000000000000000000000000000000000000000000000000000000000000001' + ); + }); + + test('should auto-detect actual 41-char address as Starknet', () => { + const address = '0x123456789012345678901234567890123456789'; + expect(getFormattedAddress(address)).toEqual( + '0x0000000000000000000000000123456789012345678901234567890123456789' + ); + }); + + test('should auto-detect 43+ char address as Starknet', () => { + const address = '0x123456789012345678901234567890123456789012'; + expect(getFormattedAddress(address)).toEqual( + '0x0000000000000000000000123456789012345678901234567890123456789012' + ); + }); }); - test('returns a padded and lowercased starknet address for any non-EVM like address input', () => { - const address = '0x1'; - expect(getFormattedAddress(address)).toEqual( - '0x0000000000000000000000000000000000000000000000000000000000000001' - ); + describe('Invalid address format', () => { + test('should throw error when passing invalid format argument', () => { + const validAddress = '0x91fd2c8d24767db4ece7069aa27832ffaf8590f3'; + expect(() => getFormattedAddress(validAddress, 'invalid')).toThrow( + 'Invalid invalid address: 0x91fd2c8d24767db4ece7069aa27832ffaf8590f3' + ); + }); + + test('should treat undefined format parameter as auto-detection', () => { + const evmAddress = '0x91fd2c8d24767db4ece7069aa27832ffaf8590f3'; + expect(getFormattedAddress(evmAddress, undefined)).toEqual( + '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3' + ); + }); + + test('should throw error when parsing invalid string', () => { + const invalidString = 'hello'; + expect(() => getFormattedAddress(invalidString)).toThrow( + 'Invalid address: hello' + ); + }); + + test('should throw error when parsing empty string', () => { + const emptyString = ''; + expect(() => getFormattedAddress(emptyString)).toThrow( + 'Invalid address: ' + ); + }); + + test('should throw error when parsing null input', () => { + expect(() => getFormattedAddress(null)).toThrow( + 'Invalid address: null' + ); + }); + + test('should throw error when parsing undefined input', () => { + expect(() => getFormattedAddress(undefined)).toThrow( + 'Invalid address: undefined' + ); + }); + + test('should throw error when parsing number input', () => { + expect(() => getFormattedAddress(123)).toThrow( + 'Invalid address: 123' + ); + }); + + test('should throw error when parsing object input', () => { + expect(() => getFormattedAddress({})).toThrow( + 'Invalid address: [object Object]' + ); + }); }); }); }); diff --git a/src/utils.ts b/src/utils.ts index b5c05e4a1..3e99936db 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -830,16 +830,18 @@ export function getFormattedAddress( address: string, format?: 'evm' | 'starknet' ): string { - // Consider non-evm addresses as Starknet by default - // as there's no other way to differentiate them - const addressType = format ?? (isEvmAddress(address) ? 'evm' : 'starknet'); + if (typeof address !== 'string' || !/^0[xX]/.test(address)) { + throw new Error(`Invalid address: ${address}`); + } + + const addressType = format ?? (address.length === 42 ? 'evm' : 'starknet'); if (addressType === 'evm' && isEvmAddress(address)) return getAddress(address); if (addressType === 'starknet' && isStarknetAddress(address)) return validateAndParseAddress(address); - throw new Error(`Invalid address: ${address}`); + throw new Error(`Invalid ${addressType} address: ${address}`); } function inputError(message: string) {