Skip to content

Commit 3dd92e1

Browse files
authored
Merge pull request #403 from ensdomains/fix/fixed-case-hex
fix: single case hex address validation
2 parents 4bb847b + 14f66fa commit 3dd92e1

File tree

4 files changed

+73
-7
lines changed

4 files changed

+73
-7
lines changed

src/coin/eth.test.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,32 @@ import { hexToBytes } from "@noble/hashes/utils";
22
import { describe, expect, test } from "bun:test";
33
import { decodeEthAddress, encodeEthAddress } from "./eth.js";
44

5+
const prefixlessAddress = "314159265dD8dbb310642f98f50C066173C1259b";
6+
const hex = "314159265dd8dbb310642f98f50c066173c1259b";
7+
8+
test(`eth address: encode 0x${prefixlessAddress}`, () => {
9+
expect(encodeEthAddress(hexToBytes(hex))).toEqual(`0x${prefixlessAddress}`);
10+
});
11+
test("eth address: invalid checksum", () => {
12+
expect(() =>
13+
decodeEthAddress("0x314159265Dd8Dbb310642f98F50C066173C1259b")
14+
).toThrow();
15+
});
16+
517
describe.each([
618
{
7-
text: "0x314159265dD8dbb310642f98f50C066173C1259b",
8-
hex: "314159265dd8dbb310642f98f50c066173c1259b",
19+
// checksummed address
20+
text: `0x${prefixlessAddress}`,
921
},
10-
])("eth address", ({ text, hex }) => {
11-
test(`encode: ${text}`, () => {
12-
expect(encodeEthAddress(hexToBytes(hex))).toEqual(text);
13-
});
22+
{
23+
// all lowercased address
24+
text: `0x${prefixlessAddress.toLowerCase()}`,
25+
},
26+
{
27+
// all uppercased address
28+
text: `0x${prefixlessAddress.toUpperCase()}`,
29+
},
30+
])("eth address", ({ text }) => {
1431
test(`decode: ${text}`, () => {
1532
expect(decodeEthAddress(text)).toEqual(hexToBytes(hex));
1633
});

src/utils/hex.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { describe, expect, test } from "bun:test";
2+
import { isValidChecksumAddress } from "./hex.js";
3+
4+
const prefixlessAddress = "314159265dD8dbb310642f98f50C066173C1259b";
5+
6+
describe("isValidChecksumAddress()", () => {
7+
test("valid checksum address", () => {
8+
expect(isValidChecksumAddress(`0x${prefixlessAddress}`)).toBeTrue();
9+
});
10+
test("all lowercased address", () => {
11+
expect(
12+
isValidChecksumAddress(`0x${prefixlessAddress.toLowerCase()}`)
13+
).toBeTrue();
14+
});
15+
test("all uppercased address", () => {
16+
expect(
17+
isValidChecksumAddress(`0x${prefixlessAddress.toUpperCase()}`)
18+
).toBeTrue();
19+
});
20+
test("invalid checksum address", () => {
21+
expect(
22+
isValidChecksumAddress("0x314159265Dd8Dbb310642f98F50C066173C1259b")
23+
).toBeFalse();
24+
});
25+
test("non-hex", () => {
26+
expect(
27+
isValidChecksumAddress("0x1234567890abcdefghijklmnopqrstuvwxyz0123")
28+
).toBeFalse();
29+
});
30+
});

src/utils/hex.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,17 @@ export function isValidChecksumAddress(
4545
address: string,
4646
chainId?: number
4747
): boolean {
48-
return isAddress(address) && checksumAddress(address, chainId) === address;
48+
if (!isAddress(address)) return false;
49+
50+
if (address === checksumAddress(address, chainId)) return true;
51+
52+
const prefixlessAddress = stripHexPrefix(address);
53+
// all lowercase
54+
if (prefixlessAddress.toLowerCase() === prefixlessAddress) return true;
55+
// all uppercase
56+
if (prefixlessAddress.toUpperCase() === prefixlessAddress) return true;
57+
58+
return false;
4959
}
5060

5161
export const createHexChecksummedEncoder =

src/utils/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ export {
4444
isEvmCoinType,
4545
} from "./evm.js";
4646
export { validateFlowAddress } from "./flow.js";
47+
export {
48+
checksumAddress,
49+
createHexChecksummedDecoder,
50+
createHexChecksummedEncoder,
51+
isAddress,
52+
isValidChecksumAddress,
53+
rawChecksumAddress,
54+
stripHexPrefix,
55+
} from "./hex.js";
4756
export { decodeLeb128, encodeLeb128 } from "./leb128.js";
4857
export { validateNearAddress } from "./near.js";
4958
export { createZcashDecoder, createZcashEncoder } from "./zcash.js";

0 commit comments

Comments
 (0)