Skip to content

Commit

Permalink
Add support for eip1191
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmillr committed Mar 13, 2024
1 parent 37db4df commit c8a86a6
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 25 deletions.
22 changes: 10 additions & 12 deletions src/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { astr, add0x, ethHex, strip0x } from './utils.js';

export const addr = {
RE: /^(0[xX])?([0-9a-fA-F]{40})?$/,
// Not much support, only RSK for now
EIP1991_CHAINS: [30n, 31n],

parse(address: string) {
astr(address);
Expand Down Expand Up @@ -34,9 +36,13 @@ export const addr = {
* @param nonChecksummedAddress
* @returns checksummed address
*/
addChecksum(nonChecksummedAddress: string): string {
addChecksum(nonChecksummedAddress: string, chainId = 1n): string {
if (typeof chainId !== 'bigint')
throw new Error(`address.addChecksum wrong chainId=${chainId}`);
const hasEIP1191 = addr.EIP1991_CHAINS.includes(chainId);
const low = addr.parse(nonChecksummedAddress).data.toLowerCase();
const hash = bytesToHex(keccak_256(low));
const hashInput = hasEIP1191 ? `${chainId}0x${low}` : low;
const hash = bytesToHex(keccak_256(hashInput));
let checksummed = '';
for (let i = 0; i < low.length; i++) {
const hi = Number.parseInt(hash[i], 16);
Expand Down Expand Up @@ -77,19 +83,11 @@ export const addr = {
* Verifies checksum if the address is checksummed.
* Always returns true when the address is not checksummed.
*/
verifyChecksum(checksummedAddress: string): boolean {
verifyChecksum(checksummedAddress: string, chainId = 1n): boolean {
const { data: address } = addr.parse(checksummedAddress);
const low = address.toLowerCase();
const upp = address.toUpperCase();
if (address === low || address === upp) return true;
const hash = bytesToHex(keccak_256(low));
for (let i = 0; i < 40; i++) {
// the nth letter should be uppercase if the nth digit of casemap is 1
const hi = Number.parseInt(hash[i]!, 16);
const char = address[i];
if ((hi <= 7 && char.toLowerCase() !== char) || (hi > 7 && char.toUpperCase() !== char))
return false;
}
return true;
return addr.addChecksum(low, chainId) === checksummedAddress;
},
};
41 changes: 28 additions & 13 deletions src/tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,8 @@ function assertYParityValid(elm: number) {
if (elm === undefined) elm = 0;
if (elm !== 0 && elm !== 1) throw new Error(`yParity wrong value=${elm} (${typeof elm})`);
}

const addrCoder = {
decode: (data: string): Uint8Array => {
if (addr.verifyChecksum(data)) return ethHex.decode(data);
else throw new Error(`invalid address checksum: ${data}`);
},
encode: (data: Uint8Array): string => addr.addChecksum(ethHex.encode(data)),
} as P.Coder<Uint8Array, string>;
// We don't know chainId when specific field coded yet.
const addrCoder = ethHex;

// Parses eip2930 access lists:
// ["0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae", [
Expand Down Expand Up @@ -430,7 +424,21 @@ export const TxVersions = {
eip4844, // 0x03
};

export const RawTx = createTxMap(TxVersions);
export const RawTx = P.apply(createTxMap(TxVersions), {
// NOTE: we apply checksum to addresses here, since chainId is not available inside coders
// By construction 'to' field is decoded before anything about chainId is known
encode: (data) => {
data.data.to = addr.addChecksum(data.data.to, data.data.chainId);
if (data.type !== 'legacy' && data.data.accessList) {
for (const item of data.data.accessList) {
item[0] = addr.addChecksum(item[0], data.data.chainId);
}
}
return data;
},
// Nothing to check here, is validated in validator
decode: (data) => data,
});

/**
* Unchecked TX for debugging. Returns raw Uint8Array-s.
Expand Down Expand Up @@ -494,8 +502,9 @@ const validators: Record<string, (num: any, { strict, type, data }: ValidationOp
if (strict) minmax(num, amounts.minGasLimit, amounts.maxGasLimit);
else minmax(num, 0n, amounts.maxUint64);
},
to(address: string) {
if (!addr.verifyChecksum(address)) throw new Error('address checksum does not match');
to(address: string, { data }: ValidationOpts) {
const chainId = typeof data.chainId === 'bigint' ? data.chainId : undefined;
if (!addr.verifyChecksum(address, chainId)) throw new Error('address checksum does not match');
},
value(num: bigint) {
abig(num);
Expand All @@ -513,8 +522,14 @@ const validators: Record<string, (num: any, { strict, type, data }: ValidationOp
abig(num);
if (strict) minmax(num, 1n, amounts.maxChainId, '>= 1 and <= 2**32-1');
},
// handled in decoder/encoder
accessList() {},
accessList(list: [string, string[]][], { data }: ValidationOpts) {
// NOTE: we cannot handle this validation in coder, since it requires chainId to calculate correct checksum
const chainId = typeof data.chainId === 'bigint' ? data.chainId : undefined;
for (const [address, _] of list) {
if (!addr.verifyChecksum(address, chainId))
throw new Error('address checksum does not match');
}
},
};

// Validation
Expand Down

0 comments on commit c8a86a6

Please sign in to comment.