From b8de48ebbca460a7bf4e699f2761df9b592d1498 Mon Sep 17 00:00:00 2001 From: Julien Eluard Date: Sat, 3 Feb 2024 11:42:20 +0100 Subject: [PATCH] fix: cleanup --- packages/types/src/utils/ExecutionAddress.ts | 48 ++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 packages/types/src/utils/ExecutionAddress.ts diff --git a/packages/types/src/utils/ExecutionAddress.ts b/packages/types/src/utils/ExecutionAddress.ts new file mode 100644 index 000000000000..bfda06dc3579 --- /dev/null +++ b/packages/types/src/utils/ExecutionAddress.ts @@ -0,0 +1,48 @@ +import {keccak256} from "ethereum-cryptography/keccak.js"; +import {ByteVectorType} from "@chainsafe/ssz"; + +export type ByteVector = Uint8Array; + +export class ExecutionAddressType extends ByteVectorType { + constructor() { + super(20, {typeName: "ExecutionAddress"}); + } + toJson(value: ByteVector): unknown { + const str = super.toJson(value) as string; + return toChecksumAddress(str); + } +} + +function isAddressValid(address: string): boolean { + return /^(0x)?[0-9a-f]{40}$/i.test(address); +} + +/** + * Formats an address according to [ERC55](https://eips.ethereum.org/EIPS/eip-55) + */ +export function toChecksumAddress(address: string): string { + if (!isAddressValid(address)) { + throw Error(`Invalid address: ${address}`); + } + + const rawAddress = address.toLowerCase().startsWith("0x") ? address.slice(2) : address; + const chars = rawAddress.split(""); + + // Inspired by https://github.com/ethers-io/ethers.js/blob/cac1da1f912c2ae9ba20f25aa51a91766673cd76/src.ts/address/address.ts#L8 + const expanded = new Uint8Array(chars.length); + for (let i = 0; i < expanded.length; i++) { + expanded[i] = rawAddress[i].charCodeAt(0); + } + + const hashed = keccak256(expanded); + for (let i = 0; i < chars.length; i += 2) { + if (hashed[i >> 1] >> 4 >= 8) { + chars[i] = chars[i].toUpperCase(); + } + if ((hashed[i >> 1] & 0x0f) >= 8) { + chars[i + 1] = chars[i + 1].toUpperCase(); + } + } + + return "0x" + chars.join(""); +}