@pharosnames/address-encoder
Version:
Encodes and decodes address formats for various cryptocurrencies with Pharos network support
73 lines (60 loc) • 2.28 kB
text/typescript
import { equalBytes } from "@noble/curves/abstract/utils";
import { blake2b } from "@noble/hashes/blake2b";
import { concatBytes } from "@noble/hashes/utils";
import type { CheckedCoin } from "../types.js";
import { base32UnpaddedDecode, base32UnpaddedEncode } from "../utils/base32.js";
import { decodeLeb128, encodeLeb128 } from "../utils/leb128.js";
const name = "fil";
const coinType = 461;
const validateFilAddress = (address: string): boolean => {
if (address.length < 3) return false;
if (address[0] !== "f") return false;
if (address[1] === "0") {
if (address.length > 22) return false;
} else if (address[1] === "1" || address[1] === "2") {
if (address.length !== 41) return false;
} else if (address[1] === "3") {
if (address.length !== 86) return false;
} else {
return false;
}
return true;
};
const filChecksum = (source: Uint8Array): Uint8Array =>
blake2b(source, { dkLen: 4 });
export const encodeFilAddress = (source: Uint8Array): string => {
const payload = source.slice(1);
const protocol = source[0];
if (protocol === 0) {
const decoded = decodeLeb128(payload);
return `f${protocol}${decoded}`;
}
const checksum = blake2b(source, { dkLen: 4 });
const bytes = concatBytes(payload, checksum);
const decoded = base32UnpaddedEncode(bytes).toLowerCase();
return `f${protocol}${decoded}`;
};
export const decodeFilAddress = (source: string): Uint8Array => {
if (!validateFilAddress(source))
throw new Error("Unrecognised address format");
const protocol = parseInt(source[1], 10);
const protocolByte = new Uint8Array([protocol]);
const encoded = source.slice(2);
if (protocol === 0) {
return concatBytes(protocolByte, encodeLeb128(BigInt(encoded)));
}
const payloadWithChecksum = base32UnpaddedDecode(encoded.toUpperCase());
const payload = payloadWithChecksum.slice(0, -4);
const checksum = payloadWithChecksum.slice(-4);
const decoded = concatBytes(protocolByte, payload);
const newChecksum = filChecksum(decoded);
if (!equalBytes(checksum, newChecksum))
throw new Error("Unrecognised address format");
return decoded;
};
export const fil = {
name,
coinType,
encode: encodeFilAddress,
decode: decodeFilAddress,
} as const satisfies CheckedCoin;