Skip to content

Commit

Permalink
feat: add checksum utils for address (#3308)
Browse files Browse the repository at this point in the history
* feat: add checksum for address

* chore: add changesets

* Update .changeset/hip-files-protect.md

* chore: release PR

* unrelease PR

---------

Co-authored-by: Peter Smith <peter@blueoceancomputing.co.uk>
Co-authored-by: Sérgio Torres <30977845+Torres-ssf@users.noreply.github.com>
  • Loading branch information
3 people authored and Dhaiwat10 committed Nov 19, 2024
1 parent c2bf271 commit 72a13c3
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .changeset/hip-files-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fuel-ts/interfaces": patch
"@fuel-ts/address": patch
---

feat: add checksum utils for address
21 changes: 21 additions & 0 deletions packages/address/src/address.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,4 +377,25 @@ describe('Address class', () => {
);
await expectToThrowFuelError(() => Address.fromEvmAddress(address), expectedError);
});

test('validate checksum address', () => {
const address = Address.fromRandom();
const addressMock = '0x9cFB2Cad509d417eC40B70eBe1DD72a3624d46fDD1EA5420DbD755Ce7f4dC897';
expect(Address.isChecksumValid(address.toChecksum())).toBeTruthy();
expect(Address.fromB256(addressMock).toChecksum()).toBe(addressMock);
expect(Address.isChecksumValid(addressMock)).toBeTruthy();
expect(Address.isChecksumValid(addressMock.slice(2))).toBeTruthy();
});

test('validate checksum address for invalid types', () => {
const address = Address.fromRandom();
expect(Address.isChecksumValid(address.toB256())).toBeFalsy();
expect(
Address.isChecksumValid('0x9cFB2Cad509d417eC40B70eBe1DD72a3624d46fDD1EA5420DbD755Ce7f4dc897')
).toBeFalsy();
expect(
Address.isChecksumValid('9cFB2Cad509d417eC40B70eBe1DD72a3624d46fDD1EA5420DbD755Ce7f4dc897')
).toBeFalsy();
expect(Address.isChecksumValid('9cFB2Cad509d417eC40B70eBe1DD72')).toBeFalsy();
});
});
53 changes: 52 additions & 1 deletion packages/address/src/address.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { FuelError } from '@fuel-ts/errors';
import { AbstractAddress } from '@fuel-ts/interfaces';
import type { Bech32Address, B256Address, EvmAddress, AssetId } from '@fuel-ts/interfaces';
import type {
Bech32Address,
B256Address,
EvmAddress,
AssetId,
ChecksumAddress,
} from '@fuel-ts/interfaces';
import { arrayify, hexlify } from '@fuel-ts/utils';
import { sha256 } from '@noble/hashes/sha256';

Expand Down Expand Up @@ -42,6 +48,16 @@ export default class Address extends AbstractAddress {
}
}

/**
* Takes an B256 Address and returns back an checksum address.
* The implementation follows the ERC-55 https://github.com/ethereum/ercs/blob/master/ERCS/erc-55.md.
*
* @returns A new `ChecksumAddress` instance
*/
toChecksum(): ChecksumAddress {
return Address.toChecksum(this.toB256());
}

/**
* Returns the `bech32Address` property
*
Expand Down Expand Up @@ -252,4 +268,39 @@ export default class Address extends AbstractAddress {

return new Address(toBech32(paddedAddress));
}

/**
* Takes an ChecksumAddress and validates if it is a valid checksum address.
*
* @returns A `boolean` instance indicating if the address is valid.
*/
static isChecksumValid(address: ChecksumAddress): boolean {
let addressParsed = address;

if (!address.startsWith('0x')) {
addressParsed = `0x${address}`;
}
if (addressParsed.trim().length !== 66) {
return false;
}

return Address.toChecksum(hexlify(addressParsed)) === addressParsed;
}

/** @hidden */
private static toChecksum(address: string) {
const addressHex = hexlify(address).toLowerCase().slice(2);
const checksum = sha256(address);

let ret = '0x';
for (let i = 0; i < 32; ++i) {
const byte = checksum[i];
const ha = addressHex.charAt(i * 2);
const hb = addressHex.charAt(i * 2 + 1);
ret += (byte & 0xf0) >= 0x80 ? ha.toUpperCase() : ha;
ret += (byte & 0x0f) >= 0x08 ? hb.toUpperCase() : hb;
}

return ret;
}
}
2 changes: 2 additions & 0 deletions packages/interfaces/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export type Bech32Address = `fuel${string}`;
// #endregion bech32-1
export type B256Address = string;

export type ChecksumAddress = string;

export type B256AddressEvm = `0x000000000000000000000000${string}`;

export type Bytes = Uint8Array | number[];
Expand Down

0 comments on commit 72a13c3

Please sign in to comment.