Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/currency/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
"@requestnetwork/utils": "0.54.0",
"@ton/core": "0.61.0",
"@ton/crypto": "3.3.0",
"bech32": "2.0.0",
"multicoin-address-validator": "0.5.15",
"node-dijkstra": "2.5.0",
"starknet": "7.6.4",
Expand Down
27 changes: 15 additions & 12 deletions packages/currency/src/currencyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { utils } from 'ethers';
import { Address } from '@ton/core';
import { validateAndParseAddress } from 'starknet';
import addressValidator from 'multicoin-address-validator';
import { bech32 } from 'bech32';
import { getSupportedERC20Tokens } from './erc20';
import { getSupportedERC777Tokens } from './erc777';
import { getHash } from './getHash';
Expand Down Expand Up @@ -326,22 +325,26 @@ export class CurrencyManager<TMeta = unknown> implements CurrencyTypes.ICurrency
}

/**
* Validate an Aleo address using proper Bech32 validation with checksum verification.
* Aleo addresses use Bech32 encoding with:
* - HRP (Human Readable Part): "aleo"
* - Separator: "1"
* - Data + checksum: 58 characters
* - Total length: 63 characters
* - Strict Bech32 character set with checksum validation
* Validate an Aleo currency address (field element).
* Aleo currency addresses are field elements with 76-77 digits followed by "field".
* See https://developer.aleo.org/guides/standards/token_registry#token-registry-program-constants
* And https://developer.aleo.org/concepts/fundamentals/accounts#prime-fields
*
* See https://namespaces.chainagnostic.org/aleo/caip10 for more details.
* @param address - The address to validate
* Examples:
* - 7311977476241952331367670434347097026669181172395481678807963832961201831695field
* - 6088188135219746443092391282916151282477828391085949070550825603498725268775field
*
* @param address - The Aleo currency address to validate
* @returns True if the address is valid, false otherwise
*/
validateAleoAddress(address: string): boolean {
try {
const { prefix } = bech32.decode(address);
return prefix === 'aleo';
if (!address || typeof address !== 'string' || !address.endsWith('field')) {
return false;
}

const numericPart = address.slice(0, -5);
return (numericPart.length === 76 || numericPart.length === 77) && /^\d+$/.test(numericPart);
} catch {
return false;
}
Expand Down
69 changes: 32 additions & 37 deletions packages/currency/test/currencyManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -767,44 +767,39 @@ describe('CurrencyManager', () => {
});

describe('validateAleoAddress', () => {
it('should validate correct Aleo addresses', () => {
const validAddress = 'aleo1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8sta57j8';
expect(currencyManager.validateAleoAddress(validAddress)).toBe(true);
expect(currencyManager.validateAleoAddress(validAddress.toUpperCase())).toBe(true);
});

it('should reject invalid Aleo addresses', () => {
const invalidAddresses = [
// Empty or null inputs
'',
' ',
null,
undefined,
// Wrong prefix
'bitcoin1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8sta57j8',
'cosmos1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8sta57j8',
// Mixed case
'aleo1Qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8sta57j8',
// Wrong format
'aleo1',
'aleo1abc',
'not-an-address',
'random-string',
// Invalid characters that would pass simple regex but fail Bech32
'aleo1' + 'b'.repeat(58), // 'b' not in Bech32 alphabet
'aleo1' + 'i'.repeat(58), // 'i' not in Bech32 alphabet
'aleo1' + 'o'.repeat(58), // 'o' not in Bech32 alphabet
// Non-string inputs
123,
{},
[],
// valid address with whitespace
' aleo1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8sta57j8 ',
];
it('should validate correct Aleo field elements', () => {
// Known valid Aleo field elements
expect(
currencyManager.validateAleoAddress(
'7311977476241952331367670434347097026669181172395481678807963832961201831695field',
),
).toBe(true);
expect(
currencyManager.validateAleoAddress(
'6088188135219746443092391282916151282477828391085949070550825603498725268775field',
),
).toBe(true);
});

invalidAddresses.forEach((address) => {
expect(currencyManager.validateAleoAddress(address as any)).toBe(false);
});
it('should reject invalid addresses', () => {
expect(
currencyManager.validateAleoAddress(
'7311977476241952331367670434347097026669181172395481678807963832961201831695',
),
).toBe(false);
expect(currencyManager.validateAleoAddress('123FIELD')).toBe(false);
expect(currencyManager.validateAleoAddress('123field')).toBe(false);
expect(
currencyManager.validateAleoAddress(
'731197747624195233136767043434709702666918117239548167880796383296120183169512345field',
),
).toBe(false);
expect(
currencyManager.validateAleoAddress(
'73119774762419523313676704343470970266691811723954816788079638329612018316abfield',
),
).toBe(false);
expect(currencyManager.validateAleoAddress('')).toBe(false);
});
});
});
2 changes: 1 addition & 1 deletion packages/request-client.js/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1653,7 +1653,7 @@ describe('request-client.js', () => {
['solana', 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'],
['ton', 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs'],
['starknet', '0x028757d11c97078Dd182023B1cC7b9E7659716c631ADF94D24f1fa7Dc5943072'],
['aleo', 'aleo1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8sta57j8'],
['aleo', '7311977476241952331367670434347097026669181172395481678807963832961201831695field'],
];

it.each(cases)(
Expand Down