Lookup performance of Numeric vs String
numeric is bigger and slower than bigint (8 bytes) in every respect. Unfortunately, like you said, bigint is just not big enough. Maximum is 2^63-1 = 9223372036854775801, 19 decimal digits.
varchar or text (same thing internally) are bigger and slower, yet. Plus, character types are encumbered with COLLATION rules unless explicitly defined without.
SELECT pg_column_size(numeric '18446744073709551616')  -- 16 bytes in RAM
     , pg_column_size(text    '18446744073709551616')  -- 24 bytes in RAM

(But 13 / 21 bytes on disk.) See:
db&lt;&gt;fiddle here
numeric saves additional space for trailing zeroes, which text cannot.
numeric disallows non-numbers from being stored and enforces some sanity out of the box.
Faced with your requirements, choose numeric and never look back.
Related:

What is the overhead for varchar(n)?


Or you could install the extension pguint by Peter Eisentraut, one of the core hackers of the Postgres project.
Be sure to read the readme first. The extension provides several additional (most of them unsigned) integer types. You might just extract uint8 (unsigned 64-bit integer) and ditch the rest to avoid over-crowding the type system.