1+ import { convertStringToHex , convertHexToString } from './stringConversion'
2+
3+ /**
4+ * Convert a currency name to a properly formatted currency code for XRPL.
5+ *
6+ * For currency names of 3 characters or less, returns the name as-is (ASCII).
7+ * For longer names, converts to a 40-character hex-encoded string.
8+ *
9+ * @param currencyName - The human-readable currency name (e.g., "USD", "MyCustomToken")
10+ * @returns The properly formatted currency code for use in XRPL transactions
11+ * @throws Error if the currency name is invalid or too long
12+ *
13+ * @example
14+ * ```typescript
15+ * currencyNameToCode("USD") // Returns: "USD"
16+ * currencyNameToCode("EUR") // Returns: "EUR"
17+ * currencyNameToCode("MyCustomToken") // Returns: "4D79437573746F6D546F6B656E000000000000000000000000"
18+ * ```
19+ */
20+ export function currencyNameToCode ( currencyName : string ) : string {
21+ if ( typeof currencyName !== 'string' ) {
22+ throw new Error ( 'Currency name must be a string' )
23+ }
24+
25+ if ( currencyName . length === 0 ) {
26+ throw new Error ( 'Currency name cannot be empty' )
27+ }
28+
29+ if ( currencyName === 'XRP' ) {
30+ throw new Error ( 'XRP cannot be used as a currency code' )
31+ }
32+
33+ // For names 3 characters or less, use as-is (standard ASCII codes like USD, EUR)
34+ if ( currencyName . length <= 3 ) {
35+ return currencyName . toUpperCase ( )
36+ }
37+
38+ // For longer names, convert to hex string
39+ const hexString = convertStringToHex ( currencyName ) . toUpperCase ( )
40+
41+ // Check if the hex string is too long (more than 40 characters = 20 bytes)
42+ if ( hexString . length > 40 ) {
43+ throw new Error ( `Currency name "${ currencyName } " is too long. Maximum length is 20 bytes when UTF-8 encoded.` )
44+ }
45+
46+ // Pad to exactly 40 characters (20 bytes) with zeros
47+ return hexString . padEnd ( 40 , '0' )
48+ }
49+
50+ /**
51+ * Convert a currency code back to a human-readable currency name.
52+ *
53+ * For 3-character ASCII codes, returns as-is.
54+ * For 40-character hex codes, converts back to the original string.
55+ *
56+ * @param currencyCode - The currency code from XRPL (e.g., "USD" or hex string)
57+ * @returns The human-readable currency name
58+ * @throws Error if the currency code is invalid
59+ *
60+ * @example
61+ * ```typescript
62+ * currencyCodeToName("USD") // Returns: "USD"
63+ * currencyCodeToName("4D79437573746F6D546F6B656E000000000000000000000000") // Returns: "MyCustomToken"
64+ * ```
65+ */
66+ export function currencyCodeToName ( currencyCode : string ) : string {
67+ if ( typeof currencyCode !== 'string' ) {
68+ throw new Error ( 'Currency code must be a string' )
69+ }
70+
71+ if ( currencyCode . length === 0 ) {
72+ throw new Error ( 'Currency code cannot be empty' )
73+ }
74+
75+ if ( currencyCode === 'XRP' ) {
76+ return 'XRP'
77+ }
78+
79+ // If it's a short code (3 characters or less), return as-is
80+ if ( currencyCode . length <= 3 ) {
81+ return currencyCode
82+ }
83+
84+ // If it's a 40-character hex string, convert back to string
85+ if ( currencyCode . length === 40 ) {
86+ // Check if it's valid hex
87+ if ( ! / ^ [ 0 - 9 A - F a - f ] + $ / u. test ( currencyCode ) ) {
88+ throw new Error ( 'Invalid currency code: not valid hexadecimal' )
89+ }
90+
91+ try {
92+ // Remove trailing zeros and convert from hex
93+ const trimmedHex = currencyCode . replace ( / 0 + $ / u, '' )
94+ if ( trimmedHex . length === 0 ) {
95+ throw new Error ( 'Invalid currency code: empty after removing padding' )
96+ }
97+
98+ return convertHexToString ( trimmedHex )
99+ } catch ( error ) {
100+ throw new Error ( `Invalid currency code: ${ error instanceof Error ? error . message : 'conversion failed' } ` )
101+ }
102+ }
103+
104+ throw new Error ( 'Invalid currency code: must be 3 characters or less, or exactly 40 characters hex' )
105+ }
106+
107+ /**
108+ * Check if a currency code is in standard 3-character ASCII format.
109+ *
110+ * @param currencyCode - The currency code to check
111+ * @returns True if the code is a standard 3-character ASCII format
112+ *
113+ * @example
114+ * ```typescript
115+ * isStandardCurrencyCode("USD") // Returns: true
116+ * isStandardCurrencyCode("EUR") // Returns: true
117+ * isStandardCurrencyCode("4D79437573746F6D546F6B656E000000000000000000000000") // Returns: false
118+ * ```
119+ */
120+ export function isStandardCurrencyCode ( currencyCode : string ) : boolean {
121+ return typeof currencyCode === 'string' &&
122+ currencyCode . length <= 3 &&
123+ currencyCode . length > 0 &&
124+ currencyCode !== 'XRP'
125+ }
126+
127+ /**
128+ * Check if a currency code is in hex format (40-character string).
129+ *
130+ * @param currencyCode - The currency code to check
131+ * @returns True if the code is a valid 40-character hex format
132+ *
133+ * @example
134+ * ```typescript
135+ * isHexCurrencyCode("USD") // Returns: false
136+ * isHexCurrencyCode("4D79437573746F6D546F6B656E000000000000000000000000") // Returns: true
137+ * ```
138+ */
139+ export function isHexCurrencyCode ( currencyCode : string ) : boolean {
140+ return typeof currencyCode === 'string' &&
141+ currencyCode . length === 40 &&
142+ / ^ [ 0 - 9 A - F a - f ] + $ / u. test ( currencyCode )
143+ }
0 commit comments