import { ProofStatus, SignatureProof } from "@notabene/javascript-sdk"; import { Secp256k1, Hex, Signature, Hash, Bytes, PublicKey } from "ox"; import { base58 } from "@scure/base"; export function verifyTIP191( address: string, message: string, proof: Hex.Hex, ): boolean { try { const payload = getSignPayload(Hex.fromString(message)); const signature = Signature.fromHex(proof); const publicKey = Secp256k1.recoverPublicKey({ payload, signature }); const hex: Hex.Hex = `0x41${Hash.keccak256( `0x${PublicKey.toHex(publicKey).slice(4)}`, ).substring(26)}`; const bytes = Bytes.from(hex); const checksum = Bytes.from(Hash.sha256(Hash.sha256(hex))).slice(0, 4); const checked = Bytes.concat(bytes, checksum); const b58 = base58.encode(checked); return b58 === address; // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (error) { return false; } } export async function verifyPersonalSignTIP191( proof: SignatureProof, ): Promise { const [ns, , address] = proof.address.split(/:/); if (ns !== "tron") return { ...proof, status: ProofStatus.FAILED }; const verified = verifyTIP191( address as Hex.Hex, proof.attestation, proof.proof as Hex.Hex, ); return { ...proof, status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED, }; } export function encode(data: Hex.Hex | Bytes.Bytes): Hex.Hex { const message = Hex.from(data); return Hex.concat( // Personal Sign Format: `0x19 ‖ "Ethereum Signed Message:\n" ‖ message.length ‖ message` "0x19", Hex.fromString("TRON Signed Message:\n" + Hex.size(message)), message, ); } export function getSignPayload(data: Hex.Hex | Bytes.Bytes): Hex.Hex { return Hash.keccak256(encode(data)); }