Skip to content

Commit 8b8d675

Browse files
authored
Merge pull request #346 from ene-rizzles/feat/stellar-key-validation
Add Stellar public key validation utility (#244)
2 parents 2609807 + 0b119b9 commit 8b8d675

3 files changed

Lines changed: 69 additions & 1 deletion

File tree

src/components/TokenSelectModal.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import React, { useState, useMemo } from 'react';
44
import { X, AlertCircle } from 'lucide-react';
55
import Icon from './ui/Icon';
6+
import { isValidStellarPublicKey } from '@/lib/validation';
67

78
interface Token {
89
symbol: string;
@@ -35,7 +36,7 @@ export const TokenSelectModal: React.FC<TokenSelectModalProps> = ({
3536
);
3637
}, [tokens, searchQuery]);
3738

38-
const isStellarAddress = searchQuery.length === 56;
39+
const isStellarAddress = isValidStellarPublicKey(searchQuery);
3940
const showImportUI = isStellarAddress && filteredTokens.length === 0;
4041

4142
if (!isOpen) return null;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { isValidStellarPublicKey } from '../validation';
2+
3+
describe('isValidStellarPublicKey', () => {
4+
const VALID_KEY = 'GBBHPLX4LBHS5JPC4FBDHD4YDZSZJZG7VQMIY6RDZT6HRJ5QJ5N6KFGH';
5+
6+
test('returns true for valid Stellar public key', () => {
7+
expect(isValidStellarPublicKey(VALID_KEY)).toBe(true);
8+
});
9+
10+
test('returns false for string not starting with G', () => {
11+
expect(isValidStellarPublicKey('A' + VALID_KEY.slice(1))).toBe(false);
12+
});
13+
14+
test('returns false for string shorter than 56 characters', () => {
15+
expect(isValidStellarPublicKey('GBBHPLX4LBHS5JPC4FBDHD4YDZSZJZG7VQMIY6RDZT6HRJ5QJ5N6KF')).toBe(
16+
false
17+
);
18+
});
19+
20+
test('returns false for string longer than 56 characters', () => {
21+
expect(isValidStellarPublicKey(VALID_KEY + 'A')).toBe(false);
22+
});
23+
24+
test('returns false for empty string', () => {
25+
expect(isValidStellarPublicKey('')).toBe(false);
26+
});
27+
28+
test('returns false for non-string input', () => {
29+
expect(isValidStellarPublicKey(null as any)).toBe(false);
30+
expect(isValidStellarPublicKey(undefined as any)).toBe(false);
31+
});
32+
33+
test('returns false for key with invalid base32 characters', () => {
34+
const invalidKey = 'GBBHPLX4LBHS5JPC4FBDHD4YDZSZJZG7VQMIY6RDZT6HRJ5QJ5N6KFGI';
35+
expect(isValidStellarPublicKey(invalidKey)).toBe(false);
36+
});
37+
38+
test('returns true for another valid Stellar public key', () => {
39+
expect(
40+
isValidStellarPublicKey('GBBD67IF633ZHJ2CCYBT6RILOY7Y6S6M5SOW2S2ZQRAGI7XRYB2TOC6S')
41+
).toBe(true);
42+
});
43+
});

src/lib/validation.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Validates whether a given string is a properly formatted Stellar public key.
3+
*
4+
* A valid Stellar ed25519 public key:
5+
* - Starts with the letter 'G'
6+
* - Is exactly 56 characters long
7+
* - Uses the Stellar base32 alphabet (A-Z, 2-7)
8+
*
9+
* For bulletproof validation, consider using `@stellar/stellar-sdk`'s
10+
* `StrKey.isValidEd25519PublicKey()` which performs additional checksum verification.
11+
*
12+
* @param key - The string to validate
13+
* @returns True if the string is a valid Stellar public key
14+
*/
15+
export function isValidStellarPublicKey(key: string): boolean {
16+
if (!key || typeof key !== 'string') return false;
17+
18+
if (key.length !== 56 || key[0] !== 'G') return false;
19+
20+
const stellarKeyRegex = /^G[A-Z2-7]{55}$/;
21+
if (!stellarKeyRegex.test(key)) return false;
22+
23+
return true;
24+
}

0 commit comments

Comments
 (0)