├── bun.lockb ├── .gitignore ├── .vscode └── settings.json ├── tsconfig.json ├── tsconfig.build.json ├── src ├── utils │ ├── near.ts │ ├── base32.ts │ ├── leb128.ts │ ├── evm.test.ts │ ├── hex.test.ts │ ├── eosio.ts │ ├── byron.ts │ ├── zcash.ts │ ├── dot.ts │ ├── bitcoin.ts │ ├── near.test.ts │ ├── evm.ts │ └── index.ts ├── coin │ ├── xmr.ts │ ├── ar.ts │ ├── bdx.ts │ ├── xhv.ts │ ├── mrx.ts │ ├── neo.ts │ ├── nmc.ts │ ├── trx.ts │ ├── qtum.ts │ ├── zil.ts │ ├── egld.ts │ ├── atom.ts │ ├── dcr.ts │ ├── srm.ts │ ├── ela.ts │ ├── vlx.ts │ ├── bts.ts │ ├── eos.ts │ ├── fio.ts │ ├── gxc.ts │ ├── iost.ts │ ├── bnb.ts │ ├── ckb.ts │ ├── hive.ts │ ├── one.ts │ ├── abbc.ts │ ├── grin.ts │ ├── iotx.ts │ ├── iris.ts │ ├── kava.ts │ ├── rune.ts │ ├── eth.ts │ ├── luna.ts │ ├── steem.ts │ ├── vet.ts │ ├── nostr.ts │ ├── tfuel.ts │ ├── vlxLegacy.ts │ ├── btm.ts │ ├── dot.ts │ ├── ksm.ts │ ├── xch.ts │ ├── goLegacy.ts │ ├── ttLegacy.ts │ ├── cloLegacy.ts │ ├── etcLegacy.ts │ ├── ewtLegacy.ts │ ├── ftmLegacy.ts │ ├── gnoLegacy.ts │ ├── nrgLegacy.ts │ ├── poaLegacy.ts │ ├── tomoLegacy.ts │ ├── celoLegacy.ts │ ├── rbtc.ts │ ├── thetaLegacy.ts │ ├── ardr.test.ts │ ├── ark.test.ts │ ├── mrx.test.ts │ ├── neo.test.ts │ ├── aib.test.ts │ ├── bnb.test.ts │ ├── ela.test.ts │ ├── hns.test.ts │ ├── kmd.test.ts │ ├── nim.test.ts │ ├── one.test.ts │ ├── ppc.test.ts │ ├── qtum.test.ts │ ├── vet.test.ts │ ├── vlx.test.ts │ ├── wan.test.ts │ ├── xvg.test.ts │ ├── zil.test.ts │ ├── ark.ts │ ├── ckb.test.ts │ ├── dcr.test.ts │ ├── iotx.test.ts │ ├── iris.test.ts │ ├── kava.test.ts │ ├── rbtc.test.ts │ ├── vlxLegacy.test.ts │ ├── xrp.ts │ ├── atom.test.ts │ ├── luna.test.ts │ ├── ar.test.ts │ ├── tfuel.test.ts │ ├── waves.test.ts │ ├── srm.test.ts │ ├── ae.test.ts │ ├── dot.test.ts │ ├── iost.test.ts │ ├── bts.test.ts │ ├── fio.test.ts │ ├── goLegacy.test.ts │ ├── gxc.test.ts │ ├── ttLegacy.test.ts │ ├── xlm.test.ts │ ├── cloLegacy.test.ts │ ├── etcLegacy.test.ts │ ├── ewtLegacy.test.ts │ ├── ftmLegacy.test.ts │ ├── gnoLegacy.test.ts │ ├── nrgLegacy.test.ts │ ├── hive.test.ts │ ├── xch.test.ts │ ├── egld.test.ts │ ├── grin.test.ts │ ├── sc.test.ts │ ├── steem.test.ts │ ├── aion.test.ts │ ├── strk.test.ts │ ├── lsk.test.ts │ ├── sero.ts │ ├── celoLegacy.test.ts │ ├── thetaLegacy.test.ts │ ├── aib.ts │ ├── bps.ts │ ├── bsv.test.ts │ ├── cca.ts │ ├── kmd.ts │ ├── lrg.ts │ ├── ppc.ts │ ├── rdd.ts │ ├── rvn.ts │ ├── via.ts │ ├── xvg.ts │ ├── zec.ts │ ├── dash.ts │ ├── doge.ts │ ├── hbar.test.ts │ ├── nmc.test.ts │ ├── trx.test.ts │ ├── ae.ts │ ├── divi.ts │ ├── firo.ts │ ├── flux.ts │ ├── nas.test.ts │ ├── wicc.ts │ ├── zen.test.ts │ ├── bcd.test.ts │ ├── bps.test.ts │ ├── btc.ts │ ├── lrg.test.ts │ ├── rdd.test.ts │ ├── strat.ts │ ├── via.test.ts │ ├── bcd.ts │ ├── btg.ts │ ├── cca.test.ts │ ├── ccxx.test.ts │ ├── dgb.ts │ ├── icx.test.ts │ ├── sys.ts │ ├── dash.test.ts │ ├── doge.test.ts │ ├── flux.test.ts │ ├── rune.test.ts │ ├── divi.test.ts │ ├── stx.test.ts │ ├── wicc.test.ts │ ├── bdx.test.ts │ ├── rvn.test.ts │ ├── strat.test.ts │ ├── xem.test.ts │ ├── xhv.test.ts │ ├── ltc.ts │ ├── xrp.test.ts │ ├── lcc.ts │ ├── ccxx.ts │ ├── mona.ts │ ├── poaLegacy.test.ts │ ├── btm.test.ts │ ├── avax.ts │ ├── lsk.ts │ ├── tomoLegacy.test.ts │ ├── eos.test.ts │ ├── hnt.test.ts │ ├── vsys.test.ts │ ├── abbc.test.ts │ ├── algo.test.ts │ ├── iota.test.ts │ ├── nano.test.ts │ ├── nostr.test.ts │ ├── near.ts │ ├── ont.test.ts │ ├── flow.test.ts │ ├── hnt.ts │ ├── mona.test.ts │ ├── dgb.test.ts │ ├── lcc.test.ts │ ├── ltc.test.ts │ ├── ont.ts │ ├── firo.test.ts │ ├── iota.ts │ ├── bsv.ts │ ├── aion.ts │ ├── ksm.test.ts │ ├── algo.ts │ ├── near.test.ts │ ├── ada.ts │ ├── etn.test.ts │ ├── nuls.test.ts │ ├── sys.test.ts │ ├── bcn.test.ts │ ├── xem.ts │ ├── sol.ts │ ├── nas.ts │ ├── btg.test.ts │ ├── zec.test.ts │ ├── flow.ts │ ├── avax.test.ts │ ├── eth.test.ts │ ├── etn.ts │ ├── xtz.test.ts │ ├── hns.ts │ ├── sui.ts │ ├── waves.ts │ ├── zen.ts │ ├── icx.ts │ ├── bcn.ts │ ├── hbar.ts │ ├── sc.ts │ ├── vsys.ts │ ├── sero.test.ts │ ├── wan.ts │ ├── strk.ts │ ├── sol.test.ts │ ├── btc.test.ts │ ├── bch.test.ts │ └── nim.ts ├── coins.test.ts ├── coders.test.ts └── consts │ └── coinNameToTypeMap.ts ├── tsconfig.bun.json ├── .github └── workflows │ ├── main.yml │ └── publish.yml └── LICENSE /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ensdomains/address-encoder/HEAD/bun.lockb -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | .DS_Store 4 | *.swp 5 | /old 6 | *.tsbuildinfo -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This configuration is used for local development and type checking. 3 | "extends": "./tsconfig.base.json", 4 | "include": ["src"], 5 | "exclude": [], 6 | "references": [{ "path": "./tsconfig.bun.json" }] 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["./src"], 4 | "exclude": ["src/**/*.test.ts", "src/**/*.bench.ts"], 5 | "compilerOptions": { 6 | "sourceMap": true, 7 | "rootDir": "./src", 8 | "moduleResolution": "Node" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/near.ts: -------------------------------------------------------------------------------- 1 | const nearAddressRegex = 2 | /^(([a-z\d]+[\-_])*[a-z\d]+\.)*([a-z\d]+[\-_])*[a-z\d]+$/; 3 | 4 | export const validateNearAddress = (address: string): boolean => { 5 | if (address.length < 2 || address.length > 64) return false; 6 | if (!nearAddressRegex.test(address)) return false; 7 | return true; 8 | }; 9 | -------------------------------------------------------------------------------- /tsconfig.bun.json: -------------------------------------------------------------------------------- 1 | { 2 | // This configuration is used for local development and type checking of configuration and script files that are not part of the build. 3 | "include": ["scripts", "src/**/*.test.ts"], 4 | "compilerOptions": { 5 | "composite": true, 6 | "module": "NodeNext", 7 | "resolveJsonModule": true, 8 | "types": ["bun-types"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/coin/xmr.ts: -------------------------------------------------------------------------------- 1 | import { base58xmr } from "@scure/base"; 2 | import type { CheckedCoin } from "../types.js"; 3 | 4 | const name = "xmr"; 5 | const coinType = 128; 6 | 7 | export const encodeXmrAddress = base58xmr.encode; 8 | export const decodeXmrAddress = base58xmr.decode; 9 | 10 | export const xmr = { 11 | name, 12 | coinType, 13 | encode: encodeXmrAddress, 14 | decode: decodeXmrAddress, 15 | } as const satisfies CheckedCoin; 16 | -------------------------------------------------------------------------------- /src/coin/ar.ts: -------------------------------------------------------------------------------- 1 | import { base64urlnopad } from "@scure/base"; 2 | import type { CheckedCoin } from "../types.js"; 3 | 4 | const name = "ar"; 5 | const coinType = 472; 6 | 7 | export const encodeArAddress = base64urlnopad.encode; 8 | export const decodeArAddress = base64urlnopad.decode; 9 | 10 | export const ar = { 11 | name, 12 | coinType, 13 | encode: encodeArAddress, 14 | decode: decodeArAddress, 15 | } as const satisfies CheckedCoin; 16 | -------------------------------------------------------------------------------- /src/coin/bdx.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { decodeXmrAddress, encodeXmrAddress } from "./xmr.js"; 3 | 4 | const name = "bdx"; 5 | const coinType = 570; 6 | 7 | export const encodeBdxAddress = encodeXmrAddress; 8 | export const decodeBdxAddress = decodeXmrAddress; 9 | 10 | export const bdx = { 11 | name, 12 | coinType, 13 | encode: encodeBdxAddress, 14 | decode: decodeBdxAddress, 15 | } as const satisfies CheckedCoin; 16 | -------------------------------------------------------------------------------- /src/coin/xhv.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { decodeXmrAddress, encodeXmrAddress } from "./xmr.js"; 3 | 4 | const name = "xhv"; 5 | const coinType = 535; 6 | 7 | export const encodeXhvAddress = encodeXmrAddress; 8 | export const decodeXhvAddress = decodeXmrAddress; 9 | 10 | export const xhv = { 11 | name, 12 | coinType, 13 | encode: encodeXhvAddress, 14 | decode: decodeXhvAddress, 15 | } as const satisfies CheckedCoin; 16 | -------------------------------------------------------------------------------- /src/coin/mrx.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { base58CheckDecode, base58CheckEncode } from "../utils/base58.js"; 3 | 4 | const name = "mrx"; 5 | const coinType = 326; 6 | 7 | export const encodeMrxAddress = base58CheckEncode; 8 | export const decodeMrxAddress = base58CheckDecode; 9 | 10 | export const mrx = { 11 | name, 12 | coinType, 13 | encode: encodeMrxAddress, 14 | decode: decodeMrxAddress, 15 | } as const satisfies CheckedCoin; 16 | -------------------------------------------------------------------------------- /src/coin/neo.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { base58CheckDecode, base58CheckEncode } from "../utils/base58.js"; 3 | 4 | const name = "neo"; 5 | const coinType = 888; 6 | 7 | export const encodeNeoAddress = base58CheckEncode; 8 | export const decodeNeoAddress = base58CheckDecode; 9 | 10 | export const neo = { 11 | name, 12 | coinType, 13 | encode: encodeNeoAddress, 14 | decode: decodeNeoAddress, 15 | } as const satisfies CheckedCoin; 16 | -------------------------------------------------------------------------------- /src/coin/nmc.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { base58CheckDecode, base58CheckEncode } from "../utils/base58.js"; 3 | 4 | const name = "nmc"; 5 | const coinType = 7; 6 | 7 | export const encodeNmcAddress = base58CheckEncode; 8 | export const decodeNmcAddress = base58CheckDecode; 9 | 10 | export const nmc = { 11 | name, 12 | coinType, 13 | encode: encodeNmcAddress, 14 | decode: decodeNmcAddress, 15 | } as const satisfies CheckedCoin; 16 | -------------------------------------------------------------------------------- /src/coin/trx.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { base58CheckDecode, base58CheckEncode } from "../utils/base58.js"; 3 | 4 | const name = "trx"; 5 | const coinType = 195; 6 | 7 | export const encodeTrxAddress = base58CheckEncode; 8 | export const decodeTrxAddress = base58CheckDecode; 9 | 10 | export const trx = { 11 | name, 12 | coinType, 13 | encode: encodeTrxAddress, 14 | decode: decodeTrxAddress, 15 | } as const satisfies CheckedCoin; 16 | -------------------------------------------------------------------------------- /src/coin/qtum.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { base58CheckDecode, base58CheckEncode } from "../utils/base58.js"; 3 | 4 | const name = "qtum"; 5 | const coinType = 2301; 6 | 7 | export const encodeQtumAddress = base58CheckEncode; 8 | export const decodeQtumAddress = base58CheckDecode; 9 | 10 | export const qtum = { 11 | name, 12 | coinType, 13 | encode: encodeQtumAddress, 14 | decode: decodeQtumAddress, 15 | } as const satisfies CheckedCoin; 16 | -------------------------------------------------------------------------------- /src/coin/zil.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "zil"; 5 | const coinType = 313; 6 | 7 | export const encodeZilAddress = createBech32Encoder("zil"); 8 | export const decodeZilAddress = createBech32Decoder("zil"); 9 | 10 | export const zil = { 11 | name, 12 | coinType, 13 | encode: encodeZilAddress, 14 | decode: decodeZilAddress, 15 | } as const satisfies CheckedCoin; 16 | -------------------------------------------------------------------------------- /src/coin/egld.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "egld"; 5 | const coinType = 508; 6 | 7 | export const encodeEgldAddress = createBech32Encoder("erd"); 8 | export const decodeEgldAddress = createBech32Decoder("erd"); 9 | 10 | export const egld = { 11 | name, 12 | coinType, 13 | encode: encodeEgldAddress, 14 | decode: decodeEgldAddress, 15 | } as const satisfies CheckedCoin; 16 | -------------------------------------------------------------------------------- /src/coin/atom.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "atom"; 5 | const coinType = 118; 6 | 7 | export const encodeAtomAddress = createBech32Encoder("cosmos"); 8 | export const decodeAtomAddress = createBech32Decoder("cosmos"); 9 | 10 | export const atom = { 11 | name, 12 | coinType, 13 | encode: encodeAtomAddress, 14 | decode: decodeAtomAddress, 15 | } as const satisfies CheckedCoin; 16 | -------------------------------------------------------------------------------- /src/coin/dcr.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | base58UncheckedDecode, 4 | base58UncheckedEncode, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "dcr"; 8 | const coinType = 42; 9 | 10 | export const encodeDcrAddress = base58UncheckedEncode; 11 | export const decodeDcrAddress = base58UncheckedDecode; 12 | 13 | export const dcr = { 14 | name, 15 | coinType, 16 | encode: encodeDcrAddress, 17 | decode: decodeDcrAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/srm.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | base58UncheckedDecode, 4 | base58UncheckedEncode, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "srm"; 8 | const coinType = 573; 9 | 10 | export const encodeSrmAddress = base58UncheckedEncode; 11 | export const decodeSrmAddress = base58UncheckedDecode; 12 | 13 | export const srm = { 14 | name, 15 | coinType, 16 | encode: encodeSrmAddress, 17 | decode: decodeSrmAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/ela.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | base58UncheckedDecode, 4 | base58UncheckedEncode, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "ela"; 8 | const coinType = 2305; 9 | 10 | export const encodeElaAddress = base58UncheckedEncode; 11 | export const decodeElaAddress = base58UncheckedDecode; 12 | 13 | export const ela = { 14 | name, 15 | coinType, 16 | encode: encodeElaAddress, 17 | decode: decodeElaAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/vlx.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | base58UncheckedDecode, 4 | base58UncheckedEncode, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "vlx"; 8 | const coinType = 5655640; 9 | 10 | export const encodeVlxAddress = base58UncheckedEncode; 11 | export const decodeVlxAddress = base58UncheckedDecode; 12 | 13 | export const vlx = { 14 | name, 15 | coinType, 16 | encode: encodeVlxAddress, 17 | decode: decodeVlxAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/bts.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createEosDecoder, createEosEncoder } from "../utils/eosio.js"; 3 | 4 | const name = "bts"; 5 | const coinType = 308; 6 | 7 | const prefix = "BTS"; 8 | 9 | export const encodeBtsAddress = createEosEncoder(prefix); 10 | export const decodeBtsAddress = createEosDecoder(prefix); 11 | 12 | export const bts = { 13 | name, 14 | coinType, 15 | encode: encodeBtsAddress, 16 | decode: decodeBtsAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/eos.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createEosDecoder, createEosEncoder } from "../utils/eosio.js"; 3 | 4 | const name = "eos"; 5 | const coinType = 194; 6 | 7 | const prefix = "EOS"; 8 | 9 | export const encodeEosAddress = createEosEncoder(prefix); 10 | export const decodeEosAddress = createEosDecoder(prefix); 11 | 12 | export const eos = { 13 | name, 14 | coinType, 15 | encode: encodeEosAddress, 16 | decode: decodeEosAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/fio.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createEosDecoder, createEosEncoder } from "../utils/eosio.js"; 3 | 4 | const name = "fio"; 5 | const coinType = 235; 6 | 7 | const prefix = "FIO"; 8 | 9 | export const encodeFioAddress = createEosEncoder(prefix); 10 | export const decodeFioAddress = createEosDecoder(prefix); 11 | 12 | export const fio = { 13 | name, 14 | coinType, 15 | encode: encodeFioAddress, 16 | decode: decodeFioAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/gxc.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createEosDecoder, createEosEncoder } from "../utils/eosio.js"; 3 | 4 | const name = "gxc"; 5 | const coinType = 2303; 6 | 7 | const prefix = "GXC"; 8 | 9 | export const encodeGxcAddress = createEosEncoder(prefix); 10 | export const decodeGxcAddress = createEosDecoder(prefix); 11 | 12 | export const gxc = { 13 | name, 14 | coinType, 15 | encode: encodeGxcAddress, 16 | decode: decodeGxcAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/iost.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | base58UncheckedDecode, 4 | base58UncheckedEncode, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "iost"; 8 | const coinType = 291; 9 | 10 | export const encodeIostAddress = base58UncheckedEncode; 11 | export const decodeIostAddress = base58UncheckedDecode; 12 | 13 | export const iost = { 14 | name, 15 | coinType, 16 | encode: encodeIostAddress, 17 | decode: decodeIostAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/bnb.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "bnb"; 5 | const coinType = 714; 6 | 7 | const hrp = "bnb"; 8 | 9 | export const encodeBnbAddress = createBech32Encoder(hrp); 10 | export const decodeBnbAddress = createBech32Decoder(hrp); 11 | 12 | export const bnb = { 13 | name, 14 | coinType, 15 | encode: encodeBnbAddress, 16 | decode: decodeBnbAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/ckb.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "ckb"; 5 | const coinType = 309; 6 | 7 | const hrp = "ckb"; 8 | 9 | export const encodeCkbAddress = createBech32Encoder(hrp); 10 | export const decodeCkbAddress = createBech32Decoder(hrp); 11 | 12 | export const ckb = { 13 | name, 14 | coinType, 15 | encode: encodeCkbAddress, 16 | decode: decodeCkbAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/hive.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createEosDecoder, createEosEncoder } from "../utils/eosio.js"; 3 | 4 | const name = "hive"; 5 | const coinType = 825; 6 | 7 | const prefix = "STM"; 8 | 9 | export const encodeHiveAddress = createEosEncoder(prefix); 10 | export const decodeHiveAddress = createEosDecoder(prefix); 11 | 12 | export const hive = { 13 | name, 14 | coinType, 15 | encode: encodeHiveAddress, 16 | decode: decodeHiveAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/one.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "one"; 5 | const coinType = 1023; 6 | 7 | const hrp = "one"; 8 | 9 | export const encodeOneAddress = createBech32Encoder(hrp); 10 | export const decodeOneAddress = createBech32Decoder(hrp); 11 | 12 | export const one = { 13 | name, 14 | coinType, 15 | encode: encodeOneAddress, 16 | decode: decodeOneAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/abbc.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createEosDecoder, createEosEncoder } from "../utils/eosio.js"; 3 | 4 | const name = "abbc"; 5 | const coinType = 367; 6 | 7 | const prefix = "ABBC"; 8 | 9 | export const encodeAbbcAddress = createEosEncoder(prefix); 10 | export const decodeAbbcAddress = createEosDecoder(prefix); 11 | 12 | export const abbc = { 13 | name, 14 | coinType, 15 | encode: encodeAbbcAddress, 16 | decode: decodeAbbcAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/grin.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "grin"; 5 | const coinType = 592; 6 | 7 | const hrp = "grin"; 8 | 9 | export const encodeGrinAddress = createBech32Encoder(hrp); 10 | export const decodeGrinAddress = createBech32Decoder(hrp); 11 | 12 | export const grin = { 13 | name, 14 | coinType, 15 | encode: encodeGrinAddress, 16 | decode: decodeGrinAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/iotx.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "iotx"; 5 | const coinType = 304; 6 | 7 | const hrp = "io"; 8 | 9 | export const encodeIotxAddress = createBech32Encoder(hrp); 10 | export const decodeIotxAddress = createBech32Decoder(hrp); 11 | 12 | export const iotx = { 13 | name, 14 | coinType, 15 | encode: encodeIotxAddress, 16 | decode: decodeIotxAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/iris.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "iris"; 5 | const coinType = 566; 6 | 7 | const hrp = "iaa"; 8 | 9 | export const encodeIrisAddress = createBech32Encoder(hrp); 10 | export const decodeIrisAddress = createBech32Decoder(hrp); 11 | 12 | export const iris = { 13 | name, 14 | coinType, 15 | encode: encodeIrisAddress, 16 | decode: decodeIrisAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/kava.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "kava"; 5 | const coinType = 459; 6 | 7 | const hrp = "kava"; 8 | 9 | export const encodeKavaAddress = createBech32Encoder(hrp); 10 | export const decodeKavaAddress = createBech32Decoder(hrp); 11 | 12 | export const kava = { 13 | name, 14 | coinType, 15 | encode: encodeKavaAddress, 16 | decode: decodeKavaAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/rune.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "rune"; 5 | const coinType = 931; 6 | 7 | const hrp = "thor"; 8 | 9 | export const encodeRuneAddress = createBech32Encoder(hrp); 10 | export const decodeRuneAddress = createBech32Decoder(hrp); 11 | 12 | export const rune = { 13 | name, 14 | coinType, 15 | encode: encodeRuneAddress, 16 | decode: decodeRuneAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/eth.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "eth"; 8 | const coinType = 60; 9 | 10 | export const encodeEthAddress = createHexChecksummedEncoder(); 11 | export const decodeEthAddress = createHexChecksummedDecoder(); 12 | 13 | export const eth = { 14 | name, 15 | coinType, 16 | encode: encodeEthAddress, 17 | decode: decodeEthAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/luna.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "luna"; 5 | const coinType = 330; 6 | 7 | const hrp = "terra"; 8 | 9 | export const encodeLunaAddress = createBech32Encoder(hrp); 10 | export const decodeLunaAddress = createBech32Decoder(hrp); 11 | 12 | export const luna = { 13 | name, 14 | coinType, 15 | encode: encodeLunaAddress, 16 | decode: decodeLunaAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/steem.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createEosDecoder, createEosEncoder } from "../utils/eosio.js"; 3 | 4 | const name = "steem"; 5 | const coinType = 135; 6 | 7 | const prefix = "STM"; 8 | 9 | export const encodeSteemAddress = createEosEncoder(prefix); 10 | export const decodeSteemAddress = createEosDecoder(prefix); 11 | 12 | export const steem = { 13 | name, 14 | coinType, 15 | encode: encodeSteemAddress, 16 | decode: decodeSteemAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/vet.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "vet"; 8 | const coinType = 818; 9 | 10 | export const encodeVetAddress = createHexChecksummedEncoder(); 11 | export const decodeVetAddress = createHexChecksummedDecoder(); 12 | 13 | export const vet = { 14 | name, 15 | coinType, 16 | encode: encodeVetAddress, 17 | decode: decodeVetAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/nostr.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "nostr"; 5 | const coinType = 1237; 6 | 7 | const hrp = "npub"; 8 | 9 | export const encodeNostrAddress = createBech32Encoder(hrp); 10 | export const decodeNostrAddress = createBech32Decoder(hrp); 11 | 12 | export const nostr = { 13 | name, 14 | coinType, 15 | encode: encodeNostrAddress, 16 | decode: decodeNostrAddress, 17 | } as const satisfies CheckedCoin; 18 | -------------------------------------------------------------------------------- /src/coin/tfuel.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "tfuel"; 8 | const coinType = 589; 9 | 10 | export const encodeTfuelAddress = createHexChecksummedEncoder(); 11 | export const decodeTfuelAddress = createHexChecksummedDecoder(); 12 | 13 | export const tfuel = { 14 | name, 15 | coinType, 16 | encode: encodeTfuelAddress, 17 | decode: decodeTfuelAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/vlxLegacy.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | base58UncheckedDecode, 4 | base58UncheckedEncode, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "vlxLegacy"; 8 | const coinType = 574; 9 | 10 | export const encodeVlxLegacyAddress = base58UncheckedEncode; 11 | export const decodeVlxLegacyAddress = base58UncheckedDecode; 12 | 13 | export const vlxLegacy = { 14 | name, 15 | coinType, 16 | encode: encodeVlxLegacyAddress, 17 | decode: decodeVlxLegacyAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/btm.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBech32SegwitDecoder, 4 | createBech32SegwitEncoder, 5 | } from "../utils/bech32.js"; 6 | 7 | const name = "btm"; 8 | const coinType = 153; 9 | 10 | const hrp = "bm"; 11 | 12 | export const encodeBtmAddress = createBech32SegwitEncoder(hrp); 13 | export const decodeBtmAddress = createBech32SegwitDecoder(hrp); 14 | 15 | export const btm = { 16 | name, 17 | coinType, 18 | encode: encodeBtmAddress, 19 | decode: decodeBtmAddress, 20 | } as const satisfies CheckedCoin; 21 | -------------------------------------------------------------------------------- /src/coin/dot.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createDotAddressDecoder, 4 | createDotAddressEncoder, 5 | } from "../utils/dot.js"; 6 | 7 | const name = "dot"; 8 | const coinType = 354; 9 | 10 | const dotType = 0; 11 | 12 | export const encodeDotAddress = createDotAddressEncoder(dotType); 13 | export const decodeDotAddress = createDotAddressDecoder(dotType); 14 | 15 | export const dot = { 16 | name, 17 | coinType, 18 | encode: encodeDotAddress, 19 | decode: decodeDotAddress, 20 | } as const satisfies CheckedCoin; 21 | -------------------------------------------------------------------------------- /src/coin/ksm.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createDotAddressDecoder, 4 | createDotAddressEncoder, 5 | } from "../utils/dot.js"; 6 | 7 | const name = "ksm"; 8 | const coinType = 434; 9 | 10 | const dotType = 2; 11 | 12 | export const encodeKsmAddress = createDotAddressEncoder(dotType); 13 | export const decodeKsmAddress = createDotAddressDecoder(dotType); 14 | 15 | export const ksm = { 16 | name, 17 | coinType, 18 | encode: encodeKsmAddress, 19 | decode: decodeKsmAddress, 20 | } as const satisfies CheckedCoin; 21 | -------------------------------------------------------------------------------- /src/coin/xch.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32mDecoder, createBech32mEncoder } from "../utils/bech32.js"; 3 | 4 | const name = "xch"; 5 | const coinType = 8444; 6 | 7 | const hrp = "xch"; 8 | const limit = 90; 9 | 10 | export const encodeXchAddress = createBech32mEncoder(hrp, limit); 11 | export const decodeXchAddress = createBech32mDecoder(hrp, limit); 12 | 13 | export const xch = { 14 | name, 15 | coinType, 16 | encode: encodeXchAddress, 17 | decode: decodeXchAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/goLegacy.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "goLegacy"; 8 | const coinType = 6060; 9 | 10 | export const encodeGoLegacyAddress = createHexChecksummedEncoder(); 11 | export const decodeGoLegacyAddress = createHexChecksummedDecoder(); 12 | 13 | export const goLegacy = { 14 | name, 15 | coinType, 16 | encode: encodeGoLegacyAddress, 17 | decode: decodeGoLegacyAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/ttLegacy.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "ttLegacy"; 8 | const coinType = 1001; 9 | 10 | export const encodeTtLegacyAddress = createHexChecksummedEncoder(); 11 | export const decodeTtLegacyAddress = createHexChecksummedDecoder(); 12 | 13 | export const ttLegacy = { 14 | name, 15 | coinType, 16 | encode: encodeTtLegacyAddress, 17 | decode: decodeTtLegacyAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/cloLegacy.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "cloLegacy"; 8 | const coinType = 820; 9 | 10 | export const encodeCloLegacyAddress = createHexChecksummedEncoder(); 11 | export const decodeCloLegacyAddress = createHexChecksummedDecoder(); 12 | 13 | export const cloLegacy = { 14 | name, 15 | coinType, 16 | encode: encodeCloLegacyAddress, 17 | decode: decodeCloLegacyAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/etcLegacy.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "etcLegacy"; 8 | const coinType = 61; 9 | 10 | export const encodeEtcLegacyAddress = createHexChecksummedEncoder(); 11 | export const decodeEtcLegacyAddress = createHexChecksummedDecoder(); 12 | 13 | export const etcLegacy = { 14 | name, 15 | coinType, 16 | encode: encodeEtcLegacyAddress, 17 | decode: decodeEtcLegacyAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/ewtLegacy.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "ewtLegacy"; 8 | const coinType = 246; 9 | 10 | export const encodeEwtLegacyAddress = createHexChecksummedEncoder(); 11 | export const decodeEwtLegacyAddress = createHexChecksummedDecoder(); 12 | 13 | export const ewtLegacy = { 14 | name, 15 | coinType, 16 | encode: encodeEwtLegacyAddress, 17 | decode: decodeEwtLegacyAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/ftmLegacy.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "ftmLegacy"; 8 | const coinType = 1007; 9 | 10 | export const encodeFtmLegacyAddress = createHexChecksummedEncoder(); 11 | export const decodeFtmLegacyAddress = createHexChecksummedDecoder(); 12 | 13 | export const ftmLegacy = { 14 | name, 15 | coinType, 16 | encode: encodeFtmLegacyAddress, 17 | decode: decodeFtmLegacyAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/gnoLegacy.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "gnoLegacy"; 8 | const coinType = 700; 9 | 10 | export const encodeGnoLegacyAddress = createHexChecksummedEncoder(); 11 | export const decodeGnoLegacyAddress = createHexChecksummedDecoder(); 12 | 13 | export const gnoLegacy = { 14 | name, 15 | coinType, 16 | encode: encodeGnoLegacyAddress, 17 | decode: decodeGnoLegacyAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/nrgLegacy.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "nrgLegacy"; 8 | const coinType = 9797; 9 | 10 | export const encodeNrgLegacyAddress = createHexChecksummedEncoder(); 11 | export const decodeNrgLegacyAddress = createHexChecksummedDecoder(); 12 | 13 | export const nrgLegacy = { 14 | name, 15 | coinType, 16 | encode: encodeNrgLegacyAddress, 17 | decode: decodeNrgLegacyAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/poaLegacy.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "poaLegacy"; 8 | const coinType = 178; 9 | 10 | export const encodePoaLegacyAddress = createHexChecksummedEncoder(); 11 | export const decodePoaLegacyAddress = createHexChecksummedDecoder(); 12 | 13 | export const poaLegacy = { 14 | name, 15 | coinType, 16 | encode: encodePoaLegacyAddress, 17 | decode: decodePoaLegacyAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coins.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "bun:test"; 2 | import * as coins from "./coins.js"; 3 | import { nonEvmCoinNameToTypeMap } from "./consts/coinNameToTypeMap.js"; 4 | 5 | const coinNames = Object.keys(nonEvmCoinNameToTypeMap); 6 | 7 | test.each(coinNames)("coins.ts export - %s", (coinName) => { 8 | const obj = coins[coinName]; 9 | expect(obj).toBeObject(); 10 | expect(obj.name).toBe(coinName); 11 | expect(obj.coinType).toBe(nonEvmCoinNameToTypeMap[coinName]); 12 | expect(obj.encode).toBeFunction(); 13 | expect(obj.decode).toBeFunction(); 14 | }); 15 | -------------------------------------------------------------------------------- /src/coin/tomoLegacy.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "tomoLegacy"; 8 | const coinType = 889; 9 | 10 | export const encodeTomoLegacyAddress = createHexChecksummedEncoder(); 11 | export const decodeTomoLegacyAddress = createHexChecksummedDecoder(); 12 | 13 | export const tomoLegacy = { 14 | name, 15 | coinType, 16 | encode: encodeTomoLegacyAddress, 17 | decode: decodeTomoLegacyAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/utils/base32.ts: -------------------------------------------------------------------------------- 1 | import { base32, utils } from "@scure/base"; 2 | 3 | export const base32Encode = base32.encode; 4 | export const base32Decode = base32.decode; 5 | 6 | const base32Unpadded = utils.chain( 7 | utils.radix2(5), 8 | utils.alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), 9 | utils.join("") 10 | ); 11 | export const base32UnpaddedEncode = base32Unpadded.encode; 12 | export const base32UnpaddedDecode = base32Unpadded.decode; 13 | 14 | export const base32CrockfordNormalise = (source: string) => 15 | source.toUpperCase().replace(/O/g, "0").replace(/[IL]/g, "1"); 16 | -------------------------------------------------------------------------------- /src/coin/celoLegacy.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "celoLegacy"; 8 | const coinType = 52752; 9 | 10 | export const encodeCeloLegacyAddress = createHexChecksummedEncoder(); 11 | export const decodeCeloLegacyAddress = createHexChecksummedDecoder(); 12 | 13 | export const celoLegacy = { 14 | name, 15 | coinType, 16 | encode: encodeCeloLegacyAddress, 17 | decode: decodeCeloLegacyAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/rbtc.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "rbtc"; 8 | const coinType = 137; 9 | 10 | const chainId = 30; 11 | 12 | export const encodeRbtcAddress = createHexChecksummedEncoder(chainId); 13 | export const decodeRbtcAddress = createHexChecksummedDecoder(chainId); 14 | 15 | export const rbtc = { 16 | name, 17 | coinType, 18 | encode: encodeRbtcAddress, 19 | decode: decodeRbtcAddress, 20 | } as const satisfies CheckedCoin; 21 | -------------------------------------------------------------------------------- /src/coin/thetaLegacy.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createHexChecksummedDecoder, 4 | createHexChecksummedEncoder, 5 | } from "../utils/hex.js"; 6 | 7 | const name = "thetaLegacy"; 8 | const coinType = 500; 9 | 10 | export const encodeThetaLegacyAddress = createHexChecksummedEncoder(); 11 | export const decodeThetaLegacyAddress = createHexChecksummedDecoder(); 12 | 13 | export const thetaLegacy = { 14 | name, 15 | coinType, 16 | encode: encodeThetaLegacyAddress, 17 | decode: decodeThetaLegacyAddress, 18 | } as const satisfies CheckedCoin; 19 | -------------------------------------------------------------------------------- /src/coin/ardr.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeArdrAddress, encodeArdrAddress } from "./ardr.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "ARDOR-MT4P-AHG4-A4NA-CCMM2", 8 | hex: "15021913020e0f080a1313000a08021408", 9 | }, 10 | ])("ardr address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeArdrAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeArdrAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/ark.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeArkAddress, encodeArkAddress } from "./ark.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "AKkCgA5To85YSAgJgxUw8dKJsHkCzsu2dy", 8 | hex: "172b8f8e3490db00c6cc0dda2d2b9626e681500e29", 9 | }, 10 | ])("ark address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeArkAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeArkAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/mrx.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeMrxAddress, encodeMrxAddress } from "./mrx.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "MPYAKTYDaEMEXWFSxHeMtpXNNiSjK4TVch", 8 | hex: "32ab8959869ee2579028abdf6a199b049bfae6dc3b", 9 | }, 10 | ])("mrx address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeMrxAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeMrxAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/neo.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeNeoAddress, encodeNeoAddress } from "./neo.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "AXaXZjZGA3qhQRTCsyG5uFKr9HeShgVhTF", 8 | hex: "17ad5cac596a1ef6c18ac1746dfd304f93964354b5", 9 | }, 10 | ])("neo address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeNeoAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeNeoAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/aib.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeAibAddress, encodeAibAddress } from "./aib.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "AJc4bPnvyvdUhFqaGLB8hhiAPyJdcZvs4Z", 8 | hex: "76a9141f0d5afac97c916cdaccc0dd1c41cb03fde8452f88ac", 9 | }, 10 | ])("aib address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeAibAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeAibAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/bnb.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeBnbAddress, encodeBnbAddress } from "./bnb.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", 8 | hex: "40c2979694bbc961023d1d27be6fc4d21a9febe6", 9 | }, 10 | ])("bnb address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeBnbAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeBnbAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/ela.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeElaAddress, encodeElaAddress } from "./ela.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "EQDZ4T6YyVkg9mb2cAuLEu8iBKbajQAywF", 8 | hex: "214d797cc92303dac242b17026e79bbea28eb642f29f0d3582", 9 | }, 10 | ])("ela address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeElaAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeElaAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/hns.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeHnsAddress, encodeHnsAddress } from "./hns.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "hs1qd42hrldu5yqee58se4uj6xctm7nk28r70e84vx", 8 | hex: "6d5571fdbca1019cd0f0cd792d1b0bdfa7651c7e", 9 | }, 10 | ])("hns address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeHnsAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeHnsAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/kmd.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeKmdAddress, encodeKmdAddress } from "./kmd.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "RDNC9mLrN48pVGDQ5jSoPb2nRsUPJ5t2R7", 8 | hex: "76a9142cd2a4e3d1c2738ee4fce61e73ea822dcaacb9b488ac", 9 | }, 10 | ])("kmd address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeKmdAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeKmdAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/nim.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeNimAddress, encodeNimAddress } from "./nim.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "NQ18 GAL5 Y1FC 66VV PE1X J82Q 0A2F LYPB 2EY7", 8 | hex: "82a85f85ec31bbdbb83e920580284fa7eeb13be7", 9 | }, 10 | ])("nim address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeNimAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeNimAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/one.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeOneAddress, encodeOneAddress } from "./one.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7", 8 | hex: "7c41e0668b551f4f902cfaec05b5bdca68b124ce", 9 | }, 10 | ])("one address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeOneAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeOneAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/ppc.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodePpcAddress, encodePpcAddress } from "./ppc.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "PRL8bojUujzDGA6HRapzprXWFxMyhpS7Za", 8 | hex: "76a914b7a1c4349e794ee3484b8f433a7063eb614dfdc788ac", 9 | }, 10 | ])("ppc address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodePpcAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodePpcAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/qtum.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeQtumAddress, encodeQtumAddress } from "./qtum.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "Qc6iYCZWn4BauKXGYirRG8pMtgdHMk2dzn", 8 | hex: "3aa9f8f3b055324f6b2d6bcac328ec2d7e3cd22d8b", 9 | }, 10 | ])("qtum address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeQtumAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeQtumAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/vet.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeVetAddress, encodeVetAddress } from "./vet.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0x9760b32C0A515F6C8c4E6B7B89AF8964DDaCB985", 8 | hex: "9760b32c0a515f6c8c4e6b7b89af8964ddacb985", 9 | }, 10 | ])("vet address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeVetAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeVetAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/vlx.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeVlxAddress, encodeVlxAddress } from "./vlx.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "VDTHiswjSTkLFbfh2S5XFsqkLzC11HoBD6", 8 | hex: "461ea68e5e13c72abf1bd2f0bcae4650521712cdb76276f0d5", 9 | }, 10 | ])("vlx address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeVlxAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeVlxAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/wan.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeWanAddress, encodeWanAddress } from "./wan.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0x2eF088E183231C9bEA30d8430937D3A57b7327D4", 8 | hex: "2ef088e183231c9bea30d8430937d3a57b7327d4", 9 | }, 10 | ])("wan address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeWanAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeWanAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/xvg.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeXvgAddress, encodeXvgAddress } from "./xvg.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "D7MKQnLxXEqn84PN42jWAVhvrXEuULLV9r", 8 | hex: "76a914183ffcc41f3095bea7ff324e52a65b46c74126e188ac", 9 | }, 10 | ])("xvg address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeXvgAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeXvgAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/zil.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeZilAddress, encodeZilAddress } from "./zil.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "zil139tkqvc8rw92e6jrs40gawwc3mmdmmauv3x3yz", 8 | hex: "89576033071b8aacea43855e8eb9d88ef6ddefbc", 9 | }, 10 | ])("zil address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeZilAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeZilAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/ark.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { base58CheckDecode, base58CheckEncode } from "../utils/base58.js"; 3 | 4 | const name = "ark"; 5 | const coinType = 111; 6 | 7 | export const encodeArkAddress = base58CheckEncode; 8 | export const decodeArkAddress = (source: string): Uint8Array => { 9 | const decoded = base58CheckDecode(source); 10 | if (decoded[0] !== 23) throw new Error("Invalid address"); 11 | return decoded; 12 | }; 13 | 14 | export const ark = { 15 | name, 16 | coinType, 17 | encode: encodeArkAddress, 18 | decode: decodeArkAddress, 19 | } as const satisfies CheckedCoin; 20 | -------------------------------------------------------------------------------- /src/coin/ckb.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeCkbAddress, encodeCkbAddress } from "./ckb.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "ckb1qyqt8xaupvm8837nv3gtc9x0ekkj64vud3jqfwyw5v", 8 | hex: "0100b39bbc0b3673c7d36450bc14cfcdad2d559c6c64", 9 | }, 10 | ])("ckb address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeCkbAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeCkbAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/dcr.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeDcrAddress, encodeDcrAddress } from "./dcr.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "DsnBFk2BdqYP3WEmChpL7TSonhpxUAi8wiA", 8 | hex: "073fe8b089c48ba23c60c64c5226d47acfb26565e313934d5d73", 9 | }, 10 | ])("dcr address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeDcrAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeDcrAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/iotx.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeIotxAddress, encodeIotxAddress } from "./iotx.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "io1nyjs526mnqcsx4twa7nptkg08eclsw5c2dywp4", 8 | hex: "99250a2b5b983103556eefa615d90f3e71f83a98", 9 | }, 10 | ])("iotx address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeIotxAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeIotxAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/iris.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeIrisAddress, encodeIrisAddress } from "./iris.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "iaa1k5y45px87c42ttxgk8x4y6w0y9gzgcwvvunht5", 8 | hex: "b5095a04c7f62aa5acc8b1cd5269cf21502461cc", 9 | }, 10 | ])("iris address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeIrisAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeIrisAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/kava.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeKavaAddress, encodeKavaAddress } from "./kava.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "kava1r4v2zdhdalfj2ydazallqvrus9fkphmglhn6u6", 8 | hex: "1d58a136edefd32511bd177ff0307c815360df68", 9 | }, 10 | ])("kava address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeKavaAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeKavaAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/rbtc.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeRbtcAddress, encodeRbtcAddress } from "./rbtc.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD", 8 | hex: "5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD", 9 | }, 10 | ])("rsk address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeRbtcAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeRbtcAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/vlxLegacy.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeVlxAddress, encodeVlxAddress } from "./vlx.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "VDTHiswjSTkLFbfh2S5XFsqkLzC11HoBD6", 8 | hex: "461ea68e5e13c72abf1bd2f0bcae4650521712cdb76276f0d5", 9 | }, 10 | ])("vlx address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeVlxAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeVlxAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/xrp.ts: -------------------------------------------------------------------------------- 1 | import { sha256 } from "@noble/hashes/sha256"; 2 | import { base58xrp, utils } from "@scure/base"; 3 | import type { CheckedCoin } from "../types.js"; 4 | 5 | const name = "xrp"; 6 | const coinType = 144; 7 | 8 | const base58XrpCheck = utils.chain( 9 | utils.checksum(4, (data) => sha256(sha256(data))), 10 | base58xrp 11 | ); 12 | 13 | export const encodeXrpAddress = base58XrpCheck.encode; 14 | export const decodeXrpAddress = base58XrpCheck.decode; 15 | 16 | export const xrp = { 17 | name, 18 | coinType, 19 | encode: encodeXrpAddress, 20 | decode: decodeXrpAddress, 21 | } as const satisfies CheckedCoin; 22 | -------------------------------------------------------------------------------- /src/coin/atom.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeAtomAddress, encodeAtomAddress } from "./atom.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27", 8 | hex: "6e436a571cec916167ba105160474b9c9cd132bd", 9 | }, 10 | ])("atom address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeAtomAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeAtomAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/luna.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeLunaAddress, encodeLunaAddress } from "./luna.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "terra1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0tmam9", 8 | hex: "0b4d529c6fb8f0a4ca3a84af03b397188b76ab0e", 9 | }, 10 | ])("luna address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeLunaAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeLunaAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coders.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "bun:test"; 2 | import * as coders from "./coders.js"; 3 | import { nonEvmCoinNameToTypeMap } from "./consts/coinNameToTypeMap.js"; 4 | 5 | const coinNames = Object.keys(nonEvmCoinNameToTypeMap); 6 | 7 | const capitalise = (s: string) => s[0].toUpperCase() + s.slice(1); 8 | 9 | test.each(coinNames)("coders.ts exports - %s", (coinName) => { 10 | const coderSuffix = `${capitalise(coinName)}Address`; 11 | const encoder = coders[`encode${coderSuffix}`]; 12 | const decoder = coders[`decode${coderSuffix}`]; 13 | expect(encoder).toBeFunction(); 14 | expect(decoder).toBeFunction(); 15 | }); 16 | -------------------------------------------------------------------------------- /src/coin/ar.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeArAddress, encodeArAddress } from "./ar.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "GRQ7swQO1AMyFgnuAPI7AvGQlW3lzuQuwlJbIpWV7xk", 8 | hex: "19143bb3040ed403321609ee00f23b02f190956de5cee42ec2525b229595ef19", 9 | }, 10 | ])("ar address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeArAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeArAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/tfuel.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeTfuelAddress, encodeTfuelAddress } from "./tfuel.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0x3599CF49e80A01BCb879A19599C8a6cd8C8d9aa6", 8 | hex: "3599cf49e80a01bcb879a19599c8a6cd8c8d9aa6", 9 | }, 10 | ])("tfuel address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeTfuelAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeTfuelAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/waves.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeWavesAddress, encodeWavesAddress } from "./waves.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "3PAP3wkgbGjdd1FuBLn9ajXvo6edBMCa115", 8 | hex: "01575cb3839cef68f8b5650461fe707311e2919c73b945cf1edc", 9 | }, 10 | ])("waves address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeWavesAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeWavesAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/srm.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeSrmAddress, encodeSrmAddress } from "./srm.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "6ZRCB7AAqGre6c72PRz3MHLC73VMYvJ8bi9KHf1HFpNk", 8 | hex: "52986010573739df4b58ba50e39cf3f335b89cc7d1cb1d32b5de04efa068c939", 9 | }, 10 | ])("srm address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeSrmAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeSrmAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/ae.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeAeAddress, encodeAeAddress } from "./ae.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "ak_Gd6iMVsoonGuTF8LeswwDDN2NF5wYHAoTRtzwdEcfS32LWoxm", 8 | hex: "30782378f892b7cc82c2d2739e994ec9953aa36461f1eb5a4a49a5b0de17b3d23ae8", 9 | }, 10 | ])("ae address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeAeAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeAeAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/dot.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeDotAddress, encodeDotAddress } from "./dot.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg", 8 | hex: "0aff6865635ae11013a83835c019d44ec3f865145943f487ae82a8e7bed3a66b", 9 | }, 10 | ])("dot address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeDotAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeDotAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/iost.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeIostAddress, encodeIostAddress } from "./iost.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "BkHuWzs6x2wUcuDwcodwQSaWUfZHiN7SfF3vBKy1U2Qg", 8 | hex: "9fabf5897177aabbd3c3d6052b351fe6c6c36d603dba257eb5bad3a17930ca39", 9 | }, 10 | ])("iost address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeIostAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeIostAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/utils/leb128.ts: -------------------------------------------------------------------------------- 1 | export const encodeLeb128 = (source: bigint): Uint8Array => { 2 | const bytes: number[] = []; 3 | do { 4 | let byte = Number(source & 127n); 5 | source >>= 7n; 6 | if (source != 0n) { 7 | byte = byte | 128; 8 | } 9 | bytes.push(byte); 10 | } while (source != 0n); 11 | return Uint8Array.from(bytes); 12 | }; 13 | 14 | export const decodeLeb128 = (source: Uint8Array): bigint => { 15 | let result = 0n; 16 | let shift = 0n; 17 | for (const byte of source) { 18 | result |= BigInt(byte & 127) << shift; 19 | if ((byte & 128) === 0) break; 20 | shift += 7n; 21 | } 22 | return result; 23 | }; 24 | -------------------------------------------------------------------------------- /src/coin/bts.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeBtsAddress, encodeBtsAddress } from "./bts.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "BTS8QykigLRi9ZUcNy1iXGY3KjRuCiLM8Ga49LHti1F8hgawKFc3K", 8 | hex: "03d0519ddad62bd2a833bee5dc04011c08f77f66338c38d99c685dee1f454cd1b8", 9 | }, 10 | ])("bts address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeBtsAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeBtsAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/fio.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeFioAddress, encodeFioAddress } from "./fio.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "FIO7tkpmicyK2YWShSKef6B9XXqBN6LpDJo69oRDfhn67CEnj3L2G", 8 | hex: "038bb1a68d19eb9139734d0f38da55cfcea955ed8f0baf42f12502e244293c08eb", 9 | }, 10 | ])("fio address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeFioAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeFioAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/goLegacy.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeGoLegacyAddress, encodeGoLegacyAddress } from "./goLegacy.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0x314159265dD8dbb310642f98f50C066173C1259b", 8 | hex: "314159265dd8dbb310642f98f50c066173c1259b", 9 | }, 10 | ])("goLegacy address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeGoLegacyAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeGoLegacyAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/gxc.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeGxcAddress, encodeGxcAddress } from "./gxc.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "GXC6UKk9URcsCuGxLuRDqEuGzAqDkgKbG8AuWXFXsyzc2r9z7A1kw", 8 | hex: "02d085655f8060a79a4b12b14e442b8a554ba867bdadce3c2dc39e1a42a01827c0", 9 | }, 10 | ])("gxc address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeGxcAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeGxcAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/ttLegacy.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeTtLegacyAddress, encodeTtLegacyAddress } from "./ttLegacy.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0x1001EEc06f2aDff074fC2A9492e132c33d6bd54d", 8 | hex: "1001eec06f2adff074fc2a9492e132c33d6bd54d", 9 | }, 10 | ])("ttLegacy address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeTtLegacyAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeTtLegacyAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/xlm.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeXlmAddress, encodeXlmAddress } from "./xlm.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "GAI3GJ2Q3B35AOZJ36C4ANE3HSS4NK7WI6DNO4ZSHRAX6NG7BMX6VJER", 8 | hex: "11b32750d877d03b29df85c0349b3ca5c6abf64786d773323c417f34df0b2fea", 9 | }, 10 | ])("xlm address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeXlmAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeXlmAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: oven-sh/setup-bun@v1 15 | with: 16 | bun-version: latest 17 | - run: bun install --frozen-lockfile 18 | - run: bun test 19 | build: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v3 23 | - uses: oven-sh/setup-bun@v1 24 | with: 25 | bun-version: latest 26 | - run: bun install --frozen-lockfile 27 | - run: bun run build 28 | -------------------------------------------------------------------------------- /src/coin/cloLegacy.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeCloLegacyAddress, encodeCloLegacyAddress } from "./cloLegacy.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", 8 | hex: "5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", 9 | }, 10 | ])("cloLegacy address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeCloLegacyAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeCloLegacyAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/etcLegacy.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeEtcLegacyAddress, encodeEtcLegacyAddress } from "./etcLegacy.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0x314159265dD8dbb310642f98f50C066173C1259b", 8 | hex: "314159265dd8dbb310642f98f50c066173c1259b", 9 | }, 10 | ])("etcLegacy address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeEtcLegacyAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeEtcLegacyAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/ewtLegacy.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeEwtLegacyAddress, encodeEwtLegacyAddress } from "./ewtLegacy.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0x2ce42c2B3aCff7eddcfd32DCB0703F1870b0eBe1", 8 | hex: "2ce42c2b3acff7eddcfd32dcb0703f1870b0ebe1", 9 | }, 10 | ])("ewtLegacy address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeEwtLegacyAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeEwtLegacyAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/ftmLegacy.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeFtmLegacyAddress, encodeFtmLegacyAddress } from "./ftmLegacy.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0x314159265dD8dbb310642f98f50C066173C1259b", 8 | hex: "314159265dd8dbb310642f98f50c066173c1259b", 9 | }, 10 | ])("ftmLegacy address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeFtmLegacyAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeFtmLegacyAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/gnoLegacy.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeGnoLegacyAddress, encodeGnoLegacyAddress } from "./gnoLegacy.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0x314159265dD8dbb310642f98f50C066173C1259b", 8 | hex: "314159265dd8dbb310642f98f50c066173c1259b", 9 | }, 10 | ])("gnoLegacy address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeGnoLegacyAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeGnoLegacyAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/nrgLegacy.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeNrgLegacyAddress, encodeNrgLegacyAddress } from "./nrgLegacy.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0x7e534bc64A80e56dB3eEDBd1b54639C3A9a7CDEA", 8 | hex: "7e534bc64a80e56db3eedbd1b54639c3a9a7cdea", 9 | }, 10 | ])("nrgLegacy address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeNrgLegacyAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeNrgLegacyAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/hive.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeHiveAddress, encodeHiveAddress } from "./hive.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "STM8QykigLRi9ZUcNy1iXGY3KjRuCiLM8Ga49LHti1F8hgawKFc3K", 8 | hex: "03d0519ddad62bd2a833bee5dc04011c08f77f66338c38d99c685dee1f454cd1b8", 9 | }, 10 | ])("hive address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeHiveAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeHiveAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/xch.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeXchAddress, encodeXchAddress } from "./xch.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "xch1f0ryxk6qn096hefcwrdwpuph2hm24w69jnzezhkfswk0z2jar7aq5zzpfj", 8 | hex: "4bc6435b409bcbabe53870dae0f03755f6aabb4594c5915ec983acf12a5d1fba", 9 | }, 10 | ])("xch address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeXchAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeXchAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/egld.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeEgldAddress, encodeEgldAddress } from "./egld.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "erd1qdzvfpa7gqjsnfhdxhvcp2mlysc80uz60yjhxre3lwl00q0jd4nqgauy9q", 8 | hex: "0344c487be402509a6ed35d980ab7f243077f05a7925730f31fbbef781f26d66", 9 | }, 10 | ])("egld address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeEgldAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeEgldAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/grin.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeGrinAddress, encodeGrinAddress } from "./grin.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "grin1k6m6sjpwc047zdhsdj9r77v5nnxm33hx7wxqvw5dhd9vl0d7t4fsaqt0lg", 8 | hex: "b6b7a8482ec3ebe136f06c8a3f79949ccdb8c6e6f38c063a8dbb4acfbdbe5d53", 9 | }, 10 | ])("grin address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeGrinAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeGrinAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/sc.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeScAddress, encodeScAddress } from "./sc.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "dfb563d6ec6ff876a059fcc96380a6d6718e1a7237e81580123070976243b77988cf8d0b7398", 8 | hex: "dfb563d6ec6ff876a059fcc96380a6d6718e1a7237e81580123070976243b779", 9 | }, 10 | ])("sc address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeScAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeScAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/steem.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeSteemAddress, encodeSteemAddress } from "./steem.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "STM8QykigLRi9ZUcNy1iXGY3KjRuCiLM8Ga49LHti1F8hgawKFc3K", 8 | hex: "03d0519ddad62bd2a833bee5dc04011c08f77f66338c38d99c685dee1f454cd1b8", 9 | }, 10 | ])("steem address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeSteemAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeSteemAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/aion.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeAionAddress, encodeAionAddress } from "./aion.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0xa0c24fbbecf42184d1ca8e9401ddaa2a99f69f3560e3d6c673de3c8a0be2a8eb", 8 | hex: "a0c24fbbecf42184d1ca8e9401ddaa2a99f69f3560e3d6c673de3c8a0be2a8eb", 9 | }, 10 | ])("aion address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeAionAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeAionAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/strk.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeStrkAddress, encodeStrkAddress } from "./strk.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0x02Fd23d9182193775423497fc0c472E156C57C69E4089A1967fb288A2d84e914", 8 | hex: "02fd23d9182193775423497fc0c472e156c57c69e4089a1967fb288a2d84e914", 9 | }, 10 | ])("strk address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeStrkAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeStrkAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/lsk.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeLskAddress, encodeLskAddress } from "./lsk.js"; 4 | 5 | describe.each([ 6 | { text: "5506432865724830000L", hex: "4c6ac7845d109130" }, 7 | { text: "10588416556841527004L", hex: "92f19cc2346766dc" }, 8 | { text: "4980451641598555896L", hex: "451e1e61667e36f8" }, 9 | ])("lsk address", ({ text, hex }) => { 10 | test(`encode: ${text}`, () => { 11 | expect(encodeLskAddress(hexToBytes(hex))).toEqual(text); 12 | }); 13 | test(`decode: ${text}`, () => { 14 | expect(decodeLskAddress(text)).toEqual(hexToBytes(hex)); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/coin/sero.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | base58UncheckedDecode, 4 | base58UncheckedEncode, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "sero"; 8 | const coinType = 569; 9 | 10 | export const encodeSeroAddress = base58UncheckedEncode; 11 | export const decodeSeroAddress = (source: string): Uint8Array => { 12 | const decoded = base58UncheckedDecode(source); 13 | if (decoded.length !== 96) throw new Error("Unrecognised address format"); 14 | return decoded; 15 | }; 16 | 17 | export const sero = { 18 | name, 19 | coinType, 20 | encode: encodeSeroAddress, 21 | decode: decodeSeroAddress, 22 | } as const satisfies CheckedCoin; 23 | -------------------------------------------------------------------------------- /src/coin/celoLegacy.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { 4 | decodeCeloLegacyAddress, 5 | encodeCeloLegacyAddress, 6 | } from "./celoLegacy.js"; 7 | 8 | describe.each([ 9 | { 10 | text: "0x67316300f17f063085Ca8bCa4bd3f7a5a3C66275", 11 | hex: "67316300f17f063085ca8bca4bd3f7a5a3c66275", 12 | }, 13 | ])("celoLegacy address", ({ text, hex }) => { 14 | test(`encode: ${text}`, () => { 15 | expect(encodeCeloLegacyAddress(hexToBytes(hex))).toEqual(text); 16 | }); 17 | test(`decode: ${text}`, () => { 18 | expect(decodeCeloLegacyAddress(text)).toEqual(hexToBytes(hex)); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/coin/thetaLegacy.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { 4 | decodeThetaLegacyAddress, 5 | encodeThetaLegacyAddress, 6 | } from "./thetaLegacy.js"; 7 | 8 | describe.each([ 9 | { 10 | text: "0x314159265dD8dbb310642f98f50C066173C1259b", 11 | hex: "314159265dd8dbb310642f98f50c066173c1259b", 12 | }, 13 | ])("thetaLegacy address", ({ text, hex }) => { 14 | test(`encode: ${text}`, () => { 15 | expect(encodeThetaLegacyAddress(hexToBytes(hex))).toEqual(text); 16 | }); 17 | test(`decode: ${text}`, () => { 18 | expect(decodeThetaLegacyAddress(text)).toEqual(hexToBytes(hex)); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/coin/aib.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "aib"; 8 | const coinType = 55; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x17])]; 11 | const p2shVersions = [new Uint8Array([0x05])]; 12 | 13 | export const encodeAibAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeAibAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const aib = { 23 | name, 24 | coinType, 25 | encode: encodeAibAddress, 26 | decode: decodeAibAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/bps.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "bps"; 8 | const coinType = 576; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x00])]; 11 | const p2shVersions = [new Uint8Array([0x05])]; 12 | 13 | export const encodeBpsAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeBpsAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const bps = { 23 | name, 24 | coinType, 25 | encode: encodeBpsAddress, 26 | decode: decodeBpsAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/bsv.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeBsvAddress, encodeBsvAddress } from "./bsv.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", 8 | hex: "65a16059864a2fdbc7c99a4723a8395bc6f188eb", 9 | }, 10 | { 11 | text: "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", 12 | hex: "6d23156cbbdcc82a5a47eee4c2c7c583c18b6bf4", 13 | }, 14 | ])("bsv address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeBsvAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeBsvAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/cca.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "cca"; 8 | const coinType = 489; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x0b])]; 11 | const p2shVersions = [new Uint8Array([0x05])]; 12 | 13 | export const encodeCcaAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeCcaAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const cca = { 23 | name, 24 | coinType, 25 | encode: encodeCcaAddress, 26 | decode: decodeCcaAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/kmd.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "kmd"; 8 | const coinType = 141; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x3c])]; 11 | const p2shVersions = [new Uint8Array([0x55])]; 12 | 13 | export const encodeKmdAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeKmdAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const kmd = { 23 | name, 24 | coinType, 25 | encode: encodeKmdAddress, 26 | decode: decodeKmdAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/lrg.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "lrg"; 8 | const coinType = 568; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x1e])]; 11 | const p2shVersions = [new Uint8Array([0x0d])]; 12 | 13 | export const encodeLrgAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeLrgAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const lrg = { 23 | name, 24 | coinType, 25 | encode: encodeLrgAddress, 26 | decode: decodeLrgAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/ppc.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "ppc"; 8 | const coinType = 6; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x37])]; 11 | const p2shVersions = [new Uint8Array([0x75])]; 12 | 13 | export const encodePpcAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodePpcAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const ppc = { 23 | name, 24 | coinType, 25 | encode: encodePpcAddress, 26 | decode: decodePpcAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/rdd.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "rdd"; 8 | const coinType = 4; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x3d])]; 11 | const p2shVersions = [new Uint8Array([0x05])]; 12 | 13 | export const encodeRddAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeRddAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const rdd = { 23 | name, 24 | coinType, 25 | encode: encodeRddAddress, 26 | decode: decodeRddAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/rvn.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "rvn"; 8 | const coinType = 175; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x3c])]; 11 | const p2shVersions = [new Uint8Array([0x7a])]; 12 | 13 | export const encodeRvnAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeRvnAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const rvn = { 23 | name, 24 | coinType, 25 | encode: encodeRvnAddress, 26 | decode: decodeRvnAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/via.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "via"; 8 | const coinType = 14; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x47])]; 11 | const p2shVersions = [new Uint8Array([0x21])]; 12 | 13 | export const encodeViaAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeViaAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const via = { 23 | name, 24 | coinType, 25 | encode: encodeViaAddress, 26 | decode: decodeViaAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/xvg.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "xvg"; 8 | const coinType = 77; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x1e])]; 11 | const p2shVersions = [new Uint8Array([0x21])]; 12 | 13 | export const encodeXvgAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeXvgAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const xvg = { 23 | name, 24 | coinType, 25 | encode: encodeXvgAddress, 26 | decode: decodeXvgAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/zec.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createZcashDecoder, createZcashEncoder } from "../utils/zcash.js"; 3 | 4 | const name = "zec"; 5 | const coinType = 133; 6 | 7 | const hrp = "zs"; 8 | const p2pkhVersions = [new Uint8Array([0x1c, 0xb8])]; 9 | const p2shVersions = [new Uint8Array([0x1c, 0xbd])]; 10 | 11 | export const encodeZecAddress = createZcashEncoder({ 12 | hrp, 13 | p2pkhVersions, 14 | p2shVersions, 15 | }); 16 | export const decodeZecAddress = createZcashDecoder({ 17 | hrp, 18 | p2pkhVersions, 19 | p2shVersions, 20 | }); 21 | 22 | export const zec = { 23 | name, 24 | coinType, 25 | encode: encodeZecAddress, 26 | decode: decodeZecAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/dash.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "dash"; 8 | const coinType = 5; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x4c])]; 11 | const p2shVersions = [new Uint8Array([0x10])]; 12 | 13 | export const encodeDashAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeDashAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const dash = { 23 | name, 24 | coinType, 25 | encode: encodeDashAddress, 26 | decode: decodeDashAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/doge.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "doge"; 8 | const coinType = 3; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x1e])]; 11 | const p2shVersions = [new Uint8Array([0x16])]; 12 | 13 | export const encodeDogeAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeDogeAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const doge = { 23 | name, 24 | coinType, 25 | encode: encodeDogeAddress, 26 | decode: decodeDogeAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/hbar.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeHbarAddress, encodeHbarAddress } from "./hbar.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "255.255.1024", 8 | hex: "000000ff00000000000000ff0000000000000400", 9 | }, 10 | { 11 | text: `${2n ** 32n - 1n}.${2n ** 64n - 1n}.${2n ** 64n - 1n}`, 12 | hex: "ffffffffffffffffffffffffffffffffffffffff", 13 | }, 14 | ])("hbar address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeHbarAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeHbarAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/nmc.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeNmcAddress, encodeNmcAddress } from "./nmc.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "TUrMmF9Gd4rzrXsQ34ui3Wou94E7HFuJQh", 8 | hex: "41cf1ecacaf90a04bb0297f9991ae1262d0a3399e1", 9 | }, 10 | { 11 | text: "TJCnKsPa7y5okkXvQAidZBzqx3QyQ6sxMW", 12 | hex: "415a523b449890854c8fc460ab602df9f31fe4293f", 13 | }, 14 | ])("nmc address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeNmcAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeNmcAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/trx.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeTrxAddress, encodeTrxAddress } from "./trx.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "TUrMmF9Gd4rzrXsQ34ui3Wou94E7HFuJQh", 8 | hex: "41cf1ecacaf90a04bb0297f9991ae1262d0a3399e1", 9 | }, 10 | { 11 | text: "TJCnKsPa7y5okkXvQAidZBzqx3QyQ6sxMW", 12 | hex: "415a523b449890854c8fc460ab602df9f31fe4293f", 13 | }, 14 | ])("trx address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeTrxAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeTrxAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/ae.ts: -------------------------------------------------------------------------------- 1 | import { concatBytes } from "@noble/hashes/utils"; 2 | import type { CheckedCoin } from "../types.js"; 3 | import { base58CheckDecode, base58CheckEncode } from "../utils/base58.js"; 4 | 5 | const name = "ae"; 6 | const coinType = 457; 7 | 8 | export const encodeAeAddress = (source: Uint8Array): string => { 9 | return `ak_${base58CheckEncode(source.slice(2))}`; 10 | }; 11 | export const decodeAeAddress = (source: string): Uint8Array => { 12 | return concatBytes( 13 | new Uint8Array([0x30, 0x78] /* 0x string */), 14 | base58CheckDecode(source.slice(3)) 15 | ); 16 | }; 17 | 18 | export const ae = { 19 | name, 20 | coinType, 21 | encode: encodeAeAddress, 22 | decode: decodeAeAddress, 23 | } as const satisfies CheckedCoin; 24 | -------------------------------------------------------------------------------- /src/coin/divi.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "divi"; 8 | const coinType = 301; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x1e])]; 11 | const p2shVersions = [new Uint8Array([0xd])]; 12 | 13 | export const encodeDiviAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeDiviAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const divi = { 23 | name, 24 | coinType, 25 | encode: encodeDiviAddress, 26 | decode: decodeDiviAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/firo.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "firo"; 8 | const coinType = 136; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x52])]; 11 | const p2shVersions = [new Uint8Array([0x07])]; 12 | 13 | export const encodeFiroAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeFiroAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const firo = { 23 | name, 24 | coinType, 25 | encode: encodeFiroAddress, 26 | decode: decodeFiroAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/flux.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createZcashDecoder, createZcashEncoder } from "../utils/zcash.js"; 3 | 4 | const name = "flux"; 5 | const coinType = 19167; 6 | 7 | const hrp = "za"; 8 | const p2pkhVersions = [new Uint8Array([0x1c, 0xb8])]; 9 | const p2shVersions = [new Uint8Array([0x1c, 0xbd])]; 10 | 11 | export const encodeFluxAddress = createZcashEncoder({ 12 | hrp, 13 | p2pkhVersions, 14 | p2shVersions, 15 | }); 16 | export const decodeFluxAddress = createZcashDecoder({ 17 | hrp, 18 | p2pkhVersions, 19 | p2shVersions, 20 | }); 21 | 22 | export const flux = { 23 | name, 24 | coinType, 25 | encode: encodeFluxAddress, 26 | decode: decodeFluxAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/nas.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeNasAddress, encodeNasAddress } from "./nas.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE", 8 | hex: "195707f964ff495324635f22c7b486e05d7e67c7af5c", 9 | }, 10 | { 11 | text: "n1sLnoc7j57YfzAVP8tJ3yK5a2i56QrTDdK", 12 | hex: "195893f59359e3de8ddb7b4e8e9fe51afcf27c59a4c1", 13 | }, 14 | ])("nas address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeNasAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeNasAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/wicc.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "wicc"; 8 | const coinType = 99999; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x49])]; 11 | const p2shVersions = [new Uint8Array([0x33])]; 12 | 13 | export const encodeWiccAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeWiccAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const wicc = { 23 | name, 24 | coinType, 25 | encode: encodeWiccAddress, 26 | decode: decodeWiccAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/zen.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeZenAddress, encodeZenAddress } from "./zen.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "znc3p7CFNTsz1s6CceskrTxKevQLPoDK4cK", 8 | hex: "20897843a3fcc6ab7d02d40946360c070b13cf7b9795", 9 | }, 10 | { 11 | text: "zswRHzwXtwKVmP8ffKKgWz6A7TB97Fuzx7w", 12 | hex: "2096b9d286b397a019f3a41ea6495dbce88d753f28a3", 13 | }, 14 | ])("zen address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeZenAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeZenAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/bcd.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeBcdAddress, encodeBcdAddress } from "./bcd.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", 8 | hex: "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac", 9 | }, 10 | { 11 | text: "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", 12 | hex: "a91474f209f6ea907e2ea48f74fae05782ae8a66525787", 13 | }, 14 | ])("bcd address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeBcdAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeBcdAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/bps.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeBpsAddress, encodeBpsAddress } from "./bps.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", 8 | hex: "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac", 9 | }, 10 | { 11 | text: "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", 12 | hex: "a91474f209f6ea907e2ea48f74fae05782ae8a66525787", 13 | }, 14 | ])("bps address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeBpsAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeBpsAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/btc.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBitcoinDecoder, 4 | createBitcoinEncoder, 5 | } from "../utils/bitcoin.js"; 6 | 7 | const name = "btc"; 8 | const coinType = 0; 9 | 10 | const hrp = "bc"; 11 | const p2pkhVersions = [new Uint8Array([0x00])]; 12 | const p2shVersions = [new Uint8Array([0x05])]; 13 | 14 | export const encodeBtcAddress = createBitcoinEncoder({ 15 | hrp, 16 | p2pkhVersions, 17 | p2shVersions, 18 | }); 19 | export const decodeBtcAddress = createBitcoinDecoder({ 20 | hrp, 21 | p2pkhVersions, 22 | p2shVersions, 23 | }); 24 | 25 | export const btc = { 26 | name, 27 | coinType, 28 | encode: encodeBtcAddress, 29 | decode: decodeBtcAddress, 30 | } as const satisfies CheckedCoin; 31 | -------------------------------------------------------------------------------- /src/coin/lrg.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeLrgAddress, encodeLrgAddress } from "./lrg.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "DM8Zwin2rJczpjy2TXY5UZbZQLkUhYBH61", 8 | hex: "76a914af687904a4e15a2f1cac37dfb6cbceb9dba8afb788ac", 9 | }, 10 | { 11 | text: "6bNNutYQz11WrkVCrj1nUS1dBGyoVZjdEg", 12 | hex: "a914e613c7be9b53e1a47fd4edb3ea9777cf29dce30f87", 13 | }, 14 | ])("lrg address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeLrgAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeLrgAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/rdd.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeRddAddress, encodeRddAddress } from "./rdd.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "RkQDYcqiv7mzQfNYMc8FfYv3dtQ8wuSGoM", 8 | hex: "76a914814089fb909f05918d54e530f0ad8e339a4edffe88ac", 9 | }, 10 | { 11 | text: "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC", 12 | hex: "a914f815b036d9bbbce5e9f2a00abd1bf3dc91e9551087", 13 | }, 14 | ])("rdd address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeRddAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeRddAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/strat.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBase58VersionedDecoder, 4 | createBase58VersionedEncoder, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "strat"; 8 | const coinType = 105; 9 | 10 | const p2pkhVersions = [new Uint8Array([0x3f])]; 11 | const p2shVersions = [new Uint8Array([0x7d])]; 12 | 13 | export const encodeStratAddress = createBase58VersionedEncoder( 14 | p2pkhVersions[0], 15 | p2shVersions[0] 16 | ); 17 | export const decodeStratAddress = createBase58VersionedDecoder( 18 | p2pkhVersions, 19 | p2shVersions 20 | ); 21 | 22 | export const strat = { 23 | name, 24 | coinType, 25 | encode: encodeStratAddress, 26 | decode: decodeStratAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/via.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeViaAddress, encodeViaAddress } from "./via.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "Vxgc5PCLkzNkDLkuduQEcrUBF1Z1UUHnav", 8 | hex: "76a914f8d8b16d9409898a976b66bad157b91b71dc18ca88ac", 9 | }, 10 | { 11 | text: "EYg9j8ieF6BQzS9doHnjg3Faj7SdAhfqnV", 12 | hex: "a914aa423f4ab9ea252abc360ec1dada62ef2527245987", 13 | }, 14 | ])("via address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeViaAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeViaAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/bcd.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBitcoinDecoder, 4 | createBitcoinEncoder, 5 | } from "../utils/bitcoin.js"; 6 | 7 | const name = "bcd"; 8 | const coinType = 999; 9 | 10 | const hrp = "bcd"; 11 | const p2pkhVersions = [new Uint8Array([0x00])]; 12 | const p2shVersions = [new Uint8Array([0x05])]; 13 | 14 | export const encodeBcdAddress = createBitcoinEncoder({ 15 | hrp, 16 | p2pkhVersions, 17 | p2shVersions, 18 | }); 19 | export const decodeBcdAddress = createBitcoinDecoder({ 20 | hrp, 21 | p2pkhVersions, 22 | p2shVersions, 23 | }); 24 | 25 | export const bcd = { 26 | name, 27 | coinType, 28 | encode: encodeBcdAddress, 29 | decode: decodeBcdAddress, 30 | } as const satisfies CheckedCoin; 31 | -------------------------------------------------------------------------------- /src/coin/btg.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBitcoinDecoder, 4 | createBitcoinEncoder, 5 | } from "../utils/bitcoin.js"; 6 | 7 | const name = "btg"; 8 | const coinType = 156; 9 | 10 | const hrp = "btg"; 11 | const p2pkhVersions = [new Uint8Array([0x26])]; 12 | const p2shVersions = [new Uint8Array([0x17])]; 13 | 14 | export const encodeBtgAddress = createBitcoinEncoder({ 15 | hrp, 16 | p2pkhVersions, 17 | p2shVersions, 18 | }); 19 | export const decodeBtgAddress = createBitcoinDecoder({ 20 | hrp, 21 | p2pkhVersions, 22 | p2shVersions, 23 | }); 24 | 25 | export const btg = { 26 | name, 27 | coinType, 28 | encode: encodeBtgAddress, 29 | decode: decodeBtgAddress, 30 | } as const satisfies CheckedCoin; 31 | -------------------------------------------------------------------------------- /src/coin/cca.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeCcaAddress, encodeCcaAddress } from "./cca.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "5jZrpsZVkNhDKEuNcYZ1kk2wNWJRbaKy22", 8 | hex: "76a914c3c95e1effb0f6ebde0ac0751d6bfd69ad98511c88ac", 9 | }, 10 | { 11 | text: "5mi7oAoMVL7cVJhXsmWxnTDxTUiBUkR996", 12 | hex: "76a914db49719be13e8221f6d568a01f9d14adc4f887ff88ac", 13 | }, 14 | ])("cca address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeCcaAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeCcaAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/ccxx.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeCcxxAddress, encodeCcxxAddress } from "./ccxx.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "XVVxhJAGNXP32xAcfCm1mVDLs5dCeodLjL", 8 | hex: "a914c7188637dfd328e6911d63da67cdbea52507dd3087", 9 | }, 10 | { 11 | text: "XKcgJ1jyjwbGCE7wT6GRMKZGjFrkNs2sLb", 12 | hex: "a9145aac7ca95006faf9244907af1e2b873a6a58e1af87", 13 | }, 14 | ])("ccxx address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeCcxxAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeCcxxAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/dgb.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBitcoinDecoder, 4 | createBitcoinEncoder, 5 | } from "../utils/bitcoin.js"; 6 | 7 | const name = "dgb"; 8 | const coinType = 20; 9 | 10 | const hrp = "dgb"; 11 | const p2pkhVersions = [new Uint8Array([0x1e])]; 12 | const p2shVersions = [new Uint8Array([0x3f])]; 13 | 14 | export const encodeDgbAddress = createBitcoinEncoder({ 15 | hrp, 16 | p2pkhVersions, 17 | p2shVersions, 18 | }); 19 | export const decodeDgbAddress = createBitcoinDecoder({ 20 | hrp, 21 | p2pkhVersions, 22 | p2shVersions, 23 | }); 24 | 25 | export const dgb = { 26 | name, 27 | coinType, 28 | encode: encodeDgbAddress, 29 | decode: decodeDgbAddress, 30 | } as const satisfies CheckedCoin; 31 | -------------------------------------------------------------------------------- /src/coin/icx.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeIcxAddress, encodeIcxAddress } from "./icx.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "hx6b38701ddc411e6f4e84a04f6abade7661a207e2", 8 | hex: "006b38701ddc411e6f4e84a04f6abade7661a207e2", 9 | }, 10 | { 11 | text: "cxa4524257b3511fb9574009785c1f1e73cf4097e7", 12 | hex: "01a4524257b3511fb9574009785c1f1e73cf4097e7", 13 | }, 14 | ])("icx address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeIcxAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeIcxAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/sys.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBitcoinDecoder, 4 | createBitcoinEncoder, 5 | } from "../utils/bitcoin.js"; 6 | 7 | const name = "sys"; 8 | const coinType = 57; 9 | 10 | const hrp = "sys"; 11 | const p2pkhVersions = [new Uint8Array([0x3f])]; 12 | const p2shVersions = [new Uint8Array([0x05])]; 13 | 14 | export const encodeSysAddress = createBitcoinEncoder({ 15 | hrp, 16 | p2pkhVersions, 17 | p2shVersions, 18 | }); 19 | export const decodeSysAddress = createBitcoinDecoder({ 20 | hrp, 21 | p2pkhVersions, 22 | p2shVersions, 23 | }); 24 | 25 | export const sys = { 26 | name, 27 | coinType, 28 | encode: encodeSysAddress, 29 | decode: decodeSysAddress, 30 | } as const satisfies CheckedCoin; 31 | -------------------------------------------------------------------------------- /src/coin/dash.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeDashAddress, encodeDashAddress } from "./dash.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "XtAG1982HcYJVibHxRZrBmdzL5YTzj4cA1", 8 | hex: "76a914bfa98bb8a919330c432e4ff16563c5ab449604ad88ac", 9 | }, 10 | { 11 | text: "7gks9gWVmGeir7m4MhsSxMzXC2eXXAuuRD", 12 | hex: "a9149d646d71f0815c0cfd8cd08aa9d391cd127f378687", 13 | }, 14 | ])("dash address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeDashAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeDashAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/doge.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeDogeAddress, encodeDogeAddress } from "./doge.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "DBXu2kgc3xtvCUWFcxFE3r9hEYgmuaaCyD", 8 | hex: "76a9144620b70031f0e9437e374a2100934fba4911046088ac", 9 | }, 10 | { 11 | text: "AF8ekvSf6eiSBRspJjnfzK6d1EM6pnPq3G", 12 | hex: "a914f8f5d99a9fc21aa676e74d15e7b8134557615bda87", 13 | }, 14 | ])("doge address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeDogeAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeDogeAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/flux.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeFluxAddress, encodeFluxAddress } from "./flux.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "t1XWTigDqS5Dy9McwQc752ShtZV1ffTMJB3", 8 | hex: "76a91495921ba2fc5277d8a35b0e2d339987d51681c51d88ac", 9 | }, 10 | { 11 | text: "t3c51GjrkUg7pUiS8bzNdTnW2hD25egWUih", 12 | hex: "a914c008da0bbc92b35ff71f613ca10ff11e2a6ae2fe87", 13 | }, 14 | ])("zel address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeFluxAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeFluxAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/rune.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeRuneAddress, encodeRuneAddress } from "./rune.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "thor1kljxxccrheghavaw97u78le6yy3sdj7h696nl4", 8 | hex: "b7e4636303be517eb3ae2fb9e3ff3a212306cbd7", 9 | }, 10 | { 11 | text: "thor1yv0mrrygnjs03zsrwrgqz4sa36evfw2a049l5p", 12 | hex: "231fb18c889ca0f88a0370d001561d8eb2c4b95d", 13 | }, 14 | ])("rune address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeRuneAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeRuneAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/divi.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeDiviAddress, encodeDiviAddress } from "./divi.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "D8gBQyHPm7A673utQQwBaQcX2Kz91wJovR", 8 | hex: "76a91426c95750c1afe443b3351ea5923d5bae09c2a74b88ac", 9 | }, 10 | { 11 | text: "DSQvV5yKP5m2tR6uShpt8zmeM8UavPhwfH", 12 | hex: "76a914e958e753703fa13eb63b39a92d1f17f06abead5e88ac", 13 | }, 14 | ])("divi address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeDiviAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeDiviAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/stx.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeStxAddress, encodeStxAddress } from "./stx.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7", 8 | hex: "a46ff88886c2ef9762d970b4d2c63678835bd39d71b4ba47", 9 | }, 10 | { 11 | text: "SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G", 12 | hex: "a46ff88886c2ef9762d970b4d2c63678835bd39df7d47410", 13 | }, 14 | ])("stx address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeStxAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeStxAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/wicc.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeWiccAddress, encodeWiccAddress } from "./wicc.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "WPCCQwJafaApw6482EkDR6V84arfa47VmT", 8 | hex: "76a91405b4701f113f51576fd7f6422dfe6ab00f41739488ac", 9 | }, 10 | { 11 | text: "WV116oEVxKUzrafgcRZXCNdDN7r7hjt4xV", 12 | hex: "76a91445672c77361c4f90f95b7c4c721f375a6a99766888ac", 13 | }, 14 | ])("wicc address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeWiccAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeWiccAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/bdx.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeBdxAddress, encodeBdxAddress } from "./bdx.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "bxdBHRJaUhrFjfHLVESP2KQ7j56LVXhgxBCiJB2fdKvuauVSUpxAqVF3gTvEx9fcd4MditoVxumV3VYFyY35S9TK19JAmCMXz", 8 | hex: "d101a272642ddf45581910432620975c8a3385df68e1bb3d3cfe4ce1c97b4c5ecab46cca3eca869e100670ba171e59a77b5b8543ecdabc9aaa9f861374856e3e10a8dd024d2d", 9 | }, 10 | ])("bdx address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeBdxAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeBdxAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/rvn.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeRvnAddress, encodeRvnAddress } from "./rvn.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "RJYZeWxr1Ly8YgcvJU1qD5MR9jUtk14HkN", 8 | hex: "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac", 9 | }, // p2pk 10 | { 11 | text: "rGtwTfEisPQ7k8KNggmT4kq2vHpbEV6evU", 12 | hex: "a91474f209f6ea907e2ea48f74fae05782ae8a66525787", 13 | }, // p2sh 14 | ])("rvn address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeRvnAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeRvnAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/strat.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeStratAddress, encodeStratAddress } from "./strat.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "SdMCMmLjD6NK8ssWt5nH2gtv6XkQXErBRs", 8 | hex: "76a914b01cb711ec63be7441c350907682a73d00bf7d2888ac", 9 | }, 10 | { 11 | text: "STrATiSwHPf36VbqWMUaduaN57A791YP9c", 12 | hex: "76a91447e5efb0d23a8ffa492d33df862a93e039ab622088ac", 13 | }, 14 | ])("strat address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeStratAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeStratAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/xem.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeXemAddress, encodeXemAddress } from "./xem.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "NAPRILC6USCTAY7NNXB4COVKQJL427NPCEERGKS6", 8 | hex: "681f142c5ea4853063ed6dc3c13aaa8257cd7daf1109132a5e", 9 | }, 10 | { 11 | text: "NAMOAVHFVPJ6FP32YP2GCM64WSRMKXA5KKYWWHPY", 12 | hex: "6818e054e5abd3e2bf7ac3f46133dcb4a2c55c1d52b16b1df8", 13 | }, 14 | ])("xem address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeXemAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeXemAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/xhv.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeXhvAddress, encodeXhvAddress } from "./xhv.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "hvs1VkXQ7qvBzrCuTofumZ52HNBhriXWP5kWcqZAG2VDXKuLwcCN5YaF2A4wmUXrZMGiz97eT9jXQBPp6vmRyTsk2ttY8z6YRU", 8 | hex: "f4b24b708551a04541bfc33b74edddf8180bee188a01b7581c66452619634bf0b54e866dc481be8f53d1d99a470080185e01c7760aac8c4b3e2336b6b1c53da731ff047530a5df", 9 | }, 10 | ])("xhv address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeXhvAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeXhvAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/coin/ltc.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBitcoinDecoder, 4 | createBitcoinEncoder, 5 | } from "../utils/bitcoin.js"; 6 | 7 | const name = "ltc"; 8 | const coinType = 2; 9 | 10 | const hrp = "ltc"; 11 | const p2pkhVersions = [new Uint8Array([0x30])]; 12 | const p2shVersions = [new Uint8Array([0x32]), new Uint8Array([0x05])]; 13 | 14 | export const encodeLtcAddress = createBitcoinEncoder({ 15 | hrp, 16 | p2pkhVersions, 17 | p2shVersions, 18 | }); 19 | export const decodeLtcAddress = createBitcoinDecoder({ 20 | hrp, 21 | p2pkhVersions, 22 | p2shVersions, 23 | }); 24 | 25 | export const ltc = { 26 | name, 27 | coinType, 28 | encode: encodeLtcAddress, 29 | decode: decodeLtcAddress, 30 | } as const satisfies CheckedCoin; 31 | -------------------------------------------------------------------------------- /src/coin/xrp.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeXrpAddress, encodeXrpAddress } from "./xrp.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", 8 | hex: "004b4e9c06f24296074f7bc48f92a97916c6dc5ea9", 9 | }, 10 | { 11 | text: "X7qvLs7gSnNoKvZzNWUT2e8st17QPY64PPe7zriLNuJszeg", 12 | hex: "05444b4e9c06f24296074f7bc48f92a97916c6dc5ea9000000000000000000", 13 | }, 14 | ])("xrp address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeXrpAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeXrpAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/lcc.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBitcoinDecoder, 4 | createBitcoinEncoder, 5 | } from "../utils/bitcoin.js"; 6 | 7 | const name = "lcc"; 8 | const coinType = 192; 9 | 10 | const hrp = "lcc"; 11 | const p2pkhVersions = [new Uint8Array([0x1c])]; 12 | const p2shVersions = [new Uint8Array([0x32]), new Uint8Array([0x05])]; 13 | 14 | export const encodeLccAddress = createBitcoinEncoder({ 15 | hrp, 16 | p2pkhVersions, 17 | p2shVersions, 18 | }); 19 | export const decodeLccAddress = createBitcoinDecoder({ 20 | hrp, 21 | p2pkhVersions, 22 | p2shVersions, 23 | }); 24 | 25 | export const lcc = { 26 | name, 27 | coinType, 28 | encode: encodeLccAddress, 29 | decode: decodeLccAddress, 30 | } as const satisfies CheckedCoin; 31 | -------------------------------------------------------------------------------- /src/coin/ccxx.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBitcoinDecoder, 4 | createBitcoinEncoder, 5 | } from "../utils/bitcoin.js"; 6 | 7 | const name = "ccxx"; 8 | const coinType = 571; 9 | 10 | const hrp = "ccx"; 11 | const p2pkhVersions = [new Uint8Array([0x89])]; 12 | const p2shVersions = [new Uint8Array([0x4b]), new Uint8Array([0x05])]; 13 | 14 | export const encodeCcxxAddress = createBitcoinEncoder({ 15 | hrp, 16 | p2pkhVersions, 17 | p2shVersions, 18 | }); 19 | export const decodeCcxxAddress = createBitcoinDecoder({ 20 | hrp, 21 | p2pkhVersions, 22 | p2shVersions, 23 | }); 24 | 25 | export const ccxx = { 26 | name, 27 | coinType, 28 | encode: encodeCcxxAddress, 29 | decode: decodeCcxxAddress, 30 | } as const satisfies CheckedCoin; 31 | -------------------------------------------------------------------------------- /src/coin/mona.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | createBitcoinDecoder, 4 | createBitcoinEncoder, 5 | } from "../utils/bitcoin.js"; 6 | 7 | const name = "mona"; 8 | const coinType = 22; 9 | 10 | const hrp = "mona"; 11 | const p2pkhVersions = [new Uint8Array([0x32])]; 12 | const p2shVersions = [new Uint8Array([0x37]), new Uint8Array([0x05])]; 13 | 14 | export const encodeMonaAddress = createBitcoinEncoder({ 15 | hrp, 16 | p2pkhVersions, 17 | p2shVersions, 18 | }); 19 | export const decodeMonaAddress = createBitcoinDecoder({ 20 | hrp, 21 | p2pkhVersions, 22 | p2shVersions, 23 | }); 24 | 25 | export const mona = { 26 | name, 27 | coinType, 28 | encode: encodeMonaAddress, 29 | decode: decodeMonaAddress, 30 | } as const satisfies CheckedCoin; 31 | -------------------------------------------------------------------------------- /src/coin/poaLegacy.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodePoaLegacyAddress, encodePoaLegacyAddress } from "./poaLegacy.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "0xF977814e90dA44bFA03b6295A0616a897441aceC", 8 | hex: "f977814e90da44bfa03b6295a0616a897441acec", 9 | }, 10 | { 11 | text: "0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8", 12 | hex: "be0eb53f46cd790cd13851d5eff43d12404d33e8", 13 | }, 14 | ])("poaLegacy address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodePoaLegacyAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodePoaLegacyAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/btm.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeBtmAddress, encodeBtmAddress } from "./btm.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "bm1qw508d6qejxtdg4y5r3zarvary0c5xw7k23gyyf", 8 | hex: "0014751e76e8199196d454941c45d1b3a323f1433bd6", 9 | }, 10 | { 11 | text: "bm1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qk5egtg", 12 | hex: "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", 13 | }, 14 | ])("btm address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeBtmAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeBtmAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/avax.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | 4 | const name = "avax"; 5 | const coinType = 9000; 6 | 7 | const hrp = "avax"; 8 | 9 | const decodeBech32 = createBech32Decoder(hrp); 10 | 11 | export const encodeAvaxAddress = createBech32Encoder(hrp); 12 | export const decodeAvaxAddress = (source: string): Uint8Array => { 13 | let address; 14 | const [id, possibleAddr] = source.split("-"); 15 | if (!possibleAddr) { 16 | address = id; 17 | } else { 18 | address = possibleAddr; 19 | } 20 | 21 | return decodeBech32(address); 22 | }; 23 | 24 | export const avax = { 25 | name, 26 | coinType, 27 | encode: encodeAvaxAddress, 28 | decode: decodeAvaxAddress, 29 | } as const satisfies CheckedCoin; 30 | -------------------------------------------------------------------------------- /src/coin/lsk.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { base10ToBytes, bytesToBase10 } from "../utils/bytes.js"; 3 | 4 | const name = "lsk"; 5 | const coinType = 134; 6 | 7 | export const encodeLskAddress = (source: Uint8Array): string => { 8 | return `${bytesToBase10(source)}L`; 9 | }; 10 | export const decodeLskAddress = (source: string): Uint8Array => { 11 | if (source.length < 2 || source.length > 22) 12 | throw new Error("Invalid address length"); 13 | 14 | if (!source.endsWith("L") || source.includes(".")) 15 | throw new Error("Invalid address format"); 16 | 17 | return base10ToBytes(source.slice(0, -1)); 18 | }; 19 | 20 | export const lsk = { 21 | name, 22 | coinType, 23 | encode: encodeLskAddress, 24 | decode: decodeLskAddress, 25 | } as const satisfies CheckedCoin; 26 | -------------------------------------------------------------------------------- /src/coin/tomoLegacy.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { 4 | decodeTomoLegacyAddress, 5 | encodeTomoLegacyAddress, 6 | } from "./tomoLegacy.js"; 7 | 8 | describe.each([ 9 | { 10 | text: "0xf5C9206843DAe847DdFd551ef7b850895430EcA3", 11 | hex: "f5c9206843dae847ddfd551ef7b850895430eca3", 12 | }, 13 | { 14 | text: "0x15813DAE07E373DC800690031A1385eB7faDe49F", 15 | hex: "15813dae07e373dc800690031a1385eb7fade49f", 16 | }, 17 | ])("tomoLegacy address", ({ text, hex }) => { 18 | test(`encode: ${text}`, () => { 19 | expect(encodeTomoLegacyAddress(hexToBytes(hex))).toEqual(text); 20 | }); 21 | test(`decode: ${text}`, () => { 22 | expect(decodeTomoLegacyAddress(text)).toEqual(hexToBytes(hex)); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/coin/eos.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeEosAddress, encodeEosAddress } from "./eos.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "EOS7pyZLEjxhkSBYnPJf585vcZrqdQoA4KsRHDej6i3vsnV7aseh9", 8 | hex: "03831c26f94b3af1a5f73ec3b961bc617b35bd99afe74bc1fe2c15d6d09bd4a416", 9 | }, 10 | { 11 | text: "EOS51imoRdUT7THtgrrVxPfYwRk3V5jVmrj18D7hbk1FQFexNmCv1", 12 | hex: "02106b727b87e01b0a298253655e7b0848ce3f4ec152ae6574643c0400ec3d1816", 13 | }, 14 | ])("eos address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeEosAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeEosAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/hnt.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeHntAddress, encodeHntAddress } from "./hnt.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "13M8dUbxymE3xtiAXszRkGMmezMhBS8Li7wEsMojLdb4Sdxc4wc", 8 | hex: "01351a71c22fefec2231936ad2826b217ece39d9f77fc6c49639926299c3869295", 9 | }, 10 | { 11 | text: "112qB3YaH5bZkCnKA5uRH7tBtGNv2Y5B4smv1jsmvGUzgKT71QpE", 12 | hex: "00f11444921875e2ef7435513a1d1f1b0fa49e3242956a24383912ec5d4f194077", 13 | }, 14 | ])("hnt address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeHntAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeHntAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/vsys.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeVsysAddress, encodeVsysAddress } from "./vsys.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "ARF12jvtjz9caUFmiwBeRe1SPRGQhUWKrtd", 8 | hex: "054d878288c4d4e2dd250560e303476b2152703557a0d3aa3396", 9 | }, 10 | ])("vsys address", ({ text, hex }) => { 11 | test(`encode: ${text}`, () => { 12 | expect(encodeVsysAddress(hexToBytes(hex))).toEqual(text); 13 | }); 14 | test(`decode: ${text}`, () => { 15 | expect(decodeVsysAddress(text)).toEqual(hexToBytes(hex)); 16 | }); 17 | test(`decode -> encode: ${text}`, () => { 18 | const decoded = decodeVsysAddress(text); 19 | const encoded = encodeVsysAddress(decoded); 20 | expect(encoded).toEqual(text); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/coin/abbc.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeAbbcAddress, encodeAbbcAddress } from "./abbc.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "ABBC5i3zbGsuyexJc6NaHv81yPh2WeaqrtYMMVaEqcYLz9guAAV74A", 8 | hex: "026bff3fc4dc3cde1dcb2068bef16624a260c6f0e330addb54f894bce7fa353de6", 9 | }, 10 | { 11 | text: "ABBC5MTKdW6dFqEjYqQYMmLohCsALWcBAx2xRapzDTKAtz3XwKJcaf", 12 | hex: "023d3a2e33a90f8f5bcbda1ec129ba1eee5e5f2ab6a77d652cbb0517f2b49669e8", 13 | }, 14 | ])("abbc address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeAbbcAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeAbbcAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/algo.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeAlgoAddress, encodeAlgoAddress } from "./algo.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "7777777777777777777777777777777777777777777777777774MSJUVU", 8 | hex: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 9 | }, 10 | { 11 | text: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ", 12 | hex: "0000000000000000000000000000000000000000000000000000000000000000", 13 | }, 14 | ])("algo address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeAlgoAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeAlgoAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/iota.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeIotaAddress, encodeIotaAddress } from "./iota.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "iota1qpw6k49dedaxrt854rau02talgfshgt0jlm5w8x9nk5ts6f5x5m759nh2ml", 8 | hex: "5dab54adcb7a61acf4a8fbc7a97dfa130ba16f97f7471cc59da8b869343537ea", 9 | }, 10 | { 11 | text: "iota1qrhacyfwlcnzkvzteumekfkrrwks98mpdm37cj4xx3drvmjvnep6xqgyzyx", 12 | hex: "efdc112efe262b304bcf379b26c31bad029f616ee3ec4aa6345a366e4c9e43a3", 13 | }, 14 | ])("iota address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeIotaAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeIotaAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/nano.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeNanoAddress, encodeNanoAddress } from "./nano.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "nano_15dng9kx49xfumkm4q6qpaxneie6oynebiwpums3ktdd6t3f3dhp69nxgb38", 8 | hex: "0d7471e5d11faddce5315c97b23b464184afa8c4c396dcf219696b2682d0adf6", 9 | }, 10 | { 11 | text: "nano_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs", 12 | hex: "2298fab7c61058e77ea554cb93edeeda0692cbfcc540ab213b2836b29029e23a", 13 | }, 14 | ])("nano address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeNanoAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeNanoAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/nostr.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeNostrAddress, encodeNostrAddress } from "./nostr.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6", 8 | hex: "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", 9 | }, 10 | { 11 | text: "npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg", 12 | hex: "7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e", 13 | }, 14 | ])("nostr address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeNostrAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeNostrAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/near.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { bytesToString, stringToBytes } from "../utils/bytes.js"; 3 | import { validateNearAddress } from "../utils/near.js"; 4 | 5 | const name = "near"; 6 | const coinType = 397; 7 | 8 | export const encodeNearAddress = (source: Uint8Array): string => { 9 | const encoded = bytesToString(source); 10 | if (!validateNearAddress(encoded)) 11 | throw new Error("Unrecognised address format"); 12 | return encoded; 13 | }; 14 | export const decodeNearAddress = (source: string): Uint8Array => { 15 | if (!validateNearAddress(source)) 16 | throw new Error("Unrecognised address format"); 17 | return stringToBytes(source); 18 | }; 19 | 20 | export const near = { 21 | name, 22 | coinType, 23 | encode: encodeNearAddress, 24 | decode: decodeNearAddress, 25 | } as const satisfies CheckedCoin; 26 | -------------------------------------------------------------------------------- /src/coin/ont.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeOntAddress, encodeOntAddress } from "./ont.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "ALvmTSEjNREwcRNJiLcTkxCnsXBfbZEUFK", 8 | hex: "3887346ea0b83129ff21f1ef3e6008a80373d1b3", 9 | }, 10 | { 11 | text: "AavjHwiNfkr7xKGHBpNEQYSL5QiKgRjZf1", 12 | hex: "d21728df85b2b457908bd33def8ff493d47f184a", 13 | }, 14 | { 15 | text: "AGmV3oHqzfAs3VFiqmn6cecxCXVNyg6tNh", 16 | hex: "0ae542fee226c044dc19b036db7cec939777596f", 17 | }, 18 | ])("ont address", ({ text, hex }) => { 19 | test(`encode: ${text}`, () => { 20 | expect(encodeOntAddress(hexToBytes(hex))).toEqual(text); 21 | }); 22 | test(`decode: ${text}`, () => { 23 | expect(decodeOntAddress(text)).toEqual(hexToBytes(hex)); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/coin/flow.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeFlowAddress, encodeFlowAddress } from "./flow.js"; 4 | 5 | describe.each([{ text: "0xf233dcee88fe0abe", hex: "f233dcee88fe0abe" }])( 6 | "flow address", 7 | ({ text, hex }) => { 8 | test(`encode: ${text}`, () => { 9 | expect(encodeFlowAddress(hexToBytes(hex))).toEqual(text); 10 | }); 11 | test(`decode: ${text}`, () => { 12 | expect(decodeFlowAddress(text)).toEqual(hexToBytes(hex)); 13 | }); 14 | } 15 | ); 16 | 17 | test("left crop", () => { 18 | expect(encodeFlowAddress(hexToBytes("aaaaaaaaaaaaaaaaaaaadeadbeef"))).toEqual( 19 | "0xaaaaaaaadeadbeef" 20 | ); 21 | }); 22 | 23 | test("left pad", () => { 24 | expect(encodeFlowAddress(hexToBytes("beef"))).toEqual("0x000000000000beef"); 25 | }); 26 | -------------------------------------------------------------------------------- /src/coin/hnt.ts: -------------------------------------------------------------------------------- 1 | import { concatBytes } from "@noble/hashes/utils"; 2 | import type { CheckedCoin } from "../types.js"; 3 | import { base58CheckDecode, base58CheckEncode } from "../utils/base58.js"; 4 | 5 | const name = "hnt"; 6 | const coinType = 904; 7 | 8 | export const encodeHntAddress = (source: Uint8Array): string => { 9 | const sourceWithVersion = concatBytes(new Uint8Array([0x00]), source); 10 | return base58CheckEncode(sourceWithVersion); 11 | }; 12 | export const decodeHntAddress = (source: string): Uint8Array => { 13 | const decoded = base58CheckDecode(source); 14 | 15 | const version = decoded[0]; 16 | if (version !== 0) throw new Error("Unrecognised address format"); 17 | 18 | return decoded.slice(1); 19 | }; 20 | 21 | export const hnt = { 22 | name, 23 | coinType, 24 | encode: encodeHntAddress, 25 | decode: decodeHntAddress, 26 | } as const satisfies CheckedCoin; 27 | -------------------------------------------------------------------------------- /src/coin/mona.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeMonaAddress, encodeMonaAddress } from "./mona.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "MHxgS2XMXjeJ4if2PRRbWYcdwZPWfdwaDT", 8 | hex: "76a9146e5bb7226a337fe8307b4192ae5c3fab9fa9edf588ac", 9 | }, 10 | { 11 | text: "PHjTKtgYLTJ9D2Bzw2f6xBB41KBm2HeGfg", 12 | hex: "a9146449f568c9cd2378138f2636e1567112a184a9e887", 13 | }, 14 | { 15 | text: "mona1zw508d6qejxtdg4y5r3zarvaryvz8pq8u", 16 | hex: "5210751e76e8199196d454941c45d1b3a323", 17 | }, 18 | ])("mona address", ({ text, hex }) => { 19 | test(`encode: ${text}`, () => { 20 | expect(encodeMonaAddress(hexToBytes(hex))).toEqual(text); 21 | }); 22 | test(`decode: ${text}`, () => { 23 | expect(decodeMonaAddress(text)).toEqual(hexToBytes(hex)); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/coin/dgb.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeDgbAddress, encodeDgbAddress } from "./dgb.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "dgb1q6fdfum8w0052aqmqjhpcpjzuyg4jlwjy9jrwz9", 8 | hex: "0014d25a9e6cee7be8ae836095c380c85c222b2fba44", 9 | }, 10 | { 11 | text: "DPPWe2aK4aYj3rt3yvw9zstCDXrN6frS7a", 12 | hex: "76a914c82c346ddb007e70fbb73edcbe104ecceea97bd188ac", 13 | }, 14 | { 15 | text: "SRFLzWuizzCPQDc5qLM2L8pZkvFws6We3j", 16 | hex: "a9142b5feabcb3feb6c45f9b623a7f1bc16be7377db787", 17 | }, 18 | ])("dgb address", ({ text, hex }) => { 19 | test(`encode: ${text}`, () => { 20 | expect(encodeDgbAddress(hexToBytes(hex))).toEqual(text); 21 | }); 22 | test(`decode: ${text}`, () => { 23 | expect(decodeDgbAddress(text)).toEqual(hexToBytes(hex)); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/coin/lcc.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeLccAddress, encodeLccAddress } from "./lcc.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "CJkeBGuySxGcdY1wupo7FXT1h8bbv4zFHt", 8 | hex: "76a914191b4f0395e2e66c9c1d9ab5e77a3455acf2c67188ac", 9 | }, 10 | { 11 | text: "MV5hqZU1rNDZ4fubL3Jpc7GMDGHmaVtreg", 12 | hex: "a914e8592f26abbbc754209ae58b131d54312a313b5787", 13 | }, 14 | { 15 | text: "lcc1q45yjegxencjtxslypllvyqfz0xk77mdklxzrcr", 16 | hex: "0014ad092ca0d99e24b343e40ffec2012279adef6db6", 17 | }, 18 | ])("lcc address", ({ text, hex }) => { 19 | test(`encode: ${text}`, () => { 20 | expect(encodeLccAddress(hexToBytes(hex))).toEqual(text); 21 | }); 22 | test(`decode: ${text}`, () => { 23 | expect(decodeLccAddress(text)).toEqual(hexToBytes(hex)); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/coin/ltc.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeLtcAddress, encodeLtcAddress } from "./ltc.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "LaMT348PWRnrqeeWArpwQPbuanpXDZGEUz", 8 | hex: "76a914a5f4d12ce3685781b227c1f39548ddef429e978388ac", 9 | }, 10 | { 11 | text: "MQMcJhpWHYVeQArcZR3sBgyPZxxRtnH441", 12 | hex: "a914b48297bff5dadecc5f36145cec6a5f20d57c8f9b87", 13 | }, 14 | { 15 | text: "ltc1qdp7p2rpx4a2f80h7a4crvppczgg4egmv5c78w8", 16 | hex: "0014687c150c26af5493befeed7036043812115ca36c", 17 | }, 18 | ])("ltc address", ({ text, hex }) => { 19 | test(`encode: ${text}`, () => { 20 | expect(encodeLtcAddress(hexToBytes(hex))).toEqual(text); 21 | }); 22 | test(`decode: ${text}`, () => { 23 | expect(decodeLtcAddress(text)).toEqual(hexToBytes(hex)); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/coin/ont.ts: -------------------------------------------------------------------------------- 1 | import { concatBytes } from "@noble/hashes/utils"; 2 | import type { CheckedCoin } from "../types.js"; 3 | import { base58CheckDecode, base58CheckEncode } from "../utils/base58.js"; 4 | 5 | const name = "ont"; 6 | const coinType = 1024; 7 | 8 | export const encodeOntAddress = (source: Uint8Array): string => { 9 | const sourceWithVersion = concatBytes(new Uint8Array([0x17]), source); 10 | return base58CheckEncode(sourceWithVersion); 11 | }; 12 | export const decodeOntAddress = (source: string): Uint8Array => { 13 | const decoded = base58CheckDecode(source); 14 | 15 | const version = decoded[0]; 16 | if (version !== 0x17) throw new Error("Unrecognised address format"); 17 | 18 | return decoded.slice(1); 19 | }; 20 | 21 | export const ont = { 22 | name, 23 | coinType, 24 | encode: encodeOntAddress, 25 | decode: decodeOntAddress, 26 | } as const satisfies CheckedCoin; 27 | -------------------------------------------------------------------------------- /src/coin/firo.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeFiroAddress, encodeFiroAddress } from "./firo.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "aJpBLBFFkxY1iGfBmZCTWQQABPqakQwWZ3", 8 | hex: "76a914c6870ff00109a0aaca255e609de7d40d245aa61788ac", 9 | }, 10 | { 11 | text: "a4roLhCKc2m3RtG7ucoxyJrCk2JqayqdSr", 12 | hex: "76a9142d743121ff929299be3c4488ce64e22634d58d5f88ac", 13 | }, 14 | { 15 | text: "Zzn3ivpQZ3XoTnEBUuqPuVCMJ3JBGoxmsi", 16 | hex: "76a91400ad9d984a8217ffe6548ef5c91b12e6c8d2c10788ac", 17 | }, 18 | ])("firo address", ({ text, hex }) => { 19 | test(`encode: ${text}`, () => { 20 | expect(encodeFiroAddress(hexToBytes(hex))).toEqual(text); 21 | }); 22 | test(`decode: ${text}`, () => { 23 | expect(decodeFiroAddress(text)).toEqual(hexToBytes(hex)); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/coin/iota.ts: -------------------------------------------------------------------------------- 1 | import { concatBytes } from "@noble/hashes/utils"; 2 | import type { CheckedCoin } from "../types.js"; 3 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 4 | 5 | const name = "iota"; 6 | const coinType = 4218; 7 | 8 | const hrp = "iota"; 9 | const version = new Uint8Array([0x00]); 10 | 11 | const iotaBech32Encode = createBech32Encoder(hrp); 12 | const iotaBech32Decode = createBech32Decoder(hrp); 13 | 14 | export const encodeIotaAddress = (source: Uint8Array): string => { 15 | return iotaBech32Encode(concatBytes(version, source)); 16 | }; 17 | export const decodeIotaAddress = (source: string): Uint8Array => { 18 | const decoded = iotaBech32Decode(source); 19 | return decoded.slice(1); 20 | }; 21 | 22 | export const iota = { 23 | name, 24 | coinType, 25 | encode: encodeIotaAddress, 26 | decode: decodeIotaAddress, 27 | } as const satisfies CheckedCoin; 28 | -------------------------------------------------------------------------------- /src/coin/bsv.ts: -------------------------------------------------------------------------------- 1 | import { concatBytes } from "@noble/hashes/utils"; 2 | import type { CheckedCoin } from "../types.js"; 3 | import { base58CheckDecode, base58CheckEncode } from "../utils/base58.js"; 4 | 5 | const name = "bsv"; 6 | const coinType = 236; 7 | 8 | export const encodeBsvAddress = (source: Uint8Array): string => 9 | base58CheckEncode(concatBytes(new Uint8Array([0x00]), source)); 10 | export const decodeBsvAddress = (source: string): Uint8Array => { 11 | const decoded = base58CheckDecode(source); 12 | 13 | if (decoded.length !== 21) throw new Error("Unrecognised address format"); 14 | 15 | const version = decoded[0]; 16 | if (version !== 0x00) throw new Error("Unrecognised address format"); 17 | 18 | return decoded.slice(1); 19 | }; 20 | 21 | export const bsv = { 22 | name, 23 | coinType, 24 | encode: encodeBsvAddress, 25 | decode: decodeBsvAddress, 26 | } as const satisfies CheckedCoin; 27 | -------------------------------------------------------------------------------- /src/consts/coinNameToTypeMap.ts: -------------------------------------------------------------------------------- 1 | import type { ParseInt } from "../types.js"; 2 | import { 3 | evmCoinTypeToNameMap, 4 | nonEvmCoinTypeToNameMap, 5 | } from "./coinTypeToNameMap.js"; 6 | 7 | export const evmCoinNameToTypeMap = Object.freeze( 8 | Object.fromEntries( 9 | Object.entries(evmCoinTypeToNameMap).map(([k, [v]]) => [v, parseInt(k)]) 10 | ) as { 11 | readonly [key in keyof typeof evmCoinTypeToNameMap as (typeof evmCoinTypeToNameMap)[key][0]]: ParseInt; 12 | } 13 | ); 14 | 15 | export const nonEvmCoinNameToTypeMap = Object.freeze( 16 | Object.fromEntries( 17 | Object.entries(nonEvmCoinTypeToNameMap).map(([k, [v]]) => [v, parseInt(k)]) 18 | ) as { 19 | readonly [key in keyof typeof nonEvmCoinTypeToNameMap as (typeof nonEvmCoinTypeToNameMap)[key][0]]: ParseInt; 20 | } 21 | ); 22 | 23 | export const coinNameToTypeMap = Object.freeze({ 24 | ...evmCoinNameToTypeMap, 25 | ...nonEvmCoinNameToTypeMap, 26 | } as const); 27 | -------------------------------------------------------------------------------- /src/coin/aion.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { bytesToHex, hexWithoutPrefixToBytes } from "../utils/bytes.js"; 3 | 4 | const name = "aion"; 5 | const coinType = 425; 6 | 7 | const hexRegex = /^[0-9A-Fa-f]{64}$/g; 8 | 9 | export const encodeAionAddress = (source: Uint8Array): string => { 10 | if (source.length !== 32) throw new Error("Unrecognised address format"); 11 | return bytesToHex(source); 12 | }; 13 | export const decodeAionAddress = (source: string): Uint8Array => { 14 | const noPrefix = source.startsWith("0x") ? source.slice(2) : source; 15 | if (noPrefix.length !== 64) throw new Error("Unrecognised address format"); 16 | if (!hexRegex.test(noPrefix)) throw new Error("Unrecognised address format"); 17 | return hexWithoutPrefixToBytes(noPrefix); 18 | }; 19 | 20 | export const aion = { 21 | name, 22 | coinType, 23 | encode: encodeAionAddress, 24 | decode: decodeAionAddress, 25 | } as const satisfies CheckedCoin; 26 | -------------------------------------------------------------------------------- /src/coin/ksm.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeKsmAddress, encodeKsmAddress } from "./ksm.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "CpjsLDC1JFyrhm3ftC9Gs4QoyrkHKhZKtK7YqGTRFtTafgp", 8 | hex: "0aff6865635ae11013a83835c019d44ec3f865145943f487ae82a8e7bed3a66b", 9 | }, 10 | { 11 | text: "DDioZ6gLeKMc5xUCeSXRHZ5U43MH1Tsrmh8T3Gcg9Vxr6DY", 12 | hex: "1c86776eda34405584e710a7363650afd1f2b38ef72836317b11ef1303a0ae72", 13 | }, 14 | { 15 | text: "EDNfVHuNHrXsVTLMMNbp6Con5zESZJa3fkRc93AgahuMm99", 16 | hex: "487ee7e677203b4209af2ffaec0f5068033c870c97fee18b31b4aee524089943", 17 | }, 18 | ])("ksm address", ({ text, hex }) => { 19 | test(`encode: ${text}`, () => { 20 | expect(encodeKsmAddress(hexToBytes(hex))).toEqual(text); 21 | }); 22 | test(`decode: ${text}`, () => { 23 | expect(decodeKsmAddress(text)).toEqual(hexToBytes(hex)); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/coin/algo.ts: -------------------------------------------------------------------------------- 1 | import { sha512_256 } from "@noble/hashes/sha512"; 2 | import { utils } from "@scure/base"; 3 | import type { CheckedCoin } from "../types.js"; 4 | import { base32UnpaddedDecode, base32UnpaddedEncode } from "../utils/base32.js"; 5 | 6 | const name = "algo"; 7 | const coinType = 283; 8 | 9 | const algoChecksum = utils.checksum(4, (data) => sha512_256(data).slice(-4)); 10 | 11 | export const encodeAlgoAddress = (source: Uint8Array): string => { 12 | const checksum = algoChecksum.encode(source); 13 | return base32UnpaddedEncode(checksum); 14 | }; 15 | export const decodeAlgoAddress = (source: string): Uint8Array => { 16 | const decoded = base32UnpaddedDecode(source); 17 | 18 | if (decoded.length !== 36) throw new Error("Unrecognised address format"); 19 | 20 | return algoChecksum.decode(decoded); 21 | }; 22 | 23 | export const algo = { 24 | name, 25 | coinType, 26 | encode: encodeAlgoAddress, 27 | decode: decodeAlgoAddress, 28 | } as const satisfies CheckedCoin; 29 | -------------------------------------------------------------------------------- /src/coin/near.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeNearAddress, encodeNearAddress } from "./near.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "9902c136629fc630416e50d4f2fef6aff867ea7e.lockup.near", 8 | hex: "393930326331333636323966633633303431366535306434663266656636616666383637656137652e6c6f636b75702e6e656172", 9 | }, 10 | { text: "blah.com", hex: "626c61682e636f6d" }, 11 | { 12 | text: "9685af3fe2dc231e5069ccff8ec6950eb961d42ebb9116a8ab9c0d38f9e45249", 13 | hex: "39363835616633666532646332333165353036396363666638656336393530656239363164343265626239313136613861623963306433386639653435323439", 14 | }, 15 | ])("near address", ({ text, hex }) => { 16 | test(`encode: ${text}`, () => { 17 | expect(encodeNearAddress(hexToBytes(hex))).toEqual(text); 18 | }); 19 | test(`decode: ${text}`, () => { 20 | expect(decodeNearAddress(text)).toEqual(hexToBytes(hex)); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/coin/ada.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { createBech32Decoder, createBech32Encoder } from "../utils/bech32.js"; 3 | import { byronDecode, byronEncode } from "../utils/byron.js"; 4 | 5 | const name = "ada"; 6 | const coinType = 1815; 7 | 8 | const hrp = "addr"; 9 | const limit = 104; 10 | 11 | const cardanoBech32Encode = createBech32Encoder(hrp, limit); 12 | const cardanoBech32Decode = createBech32Decoder(hrp, limit); 13 | 14 | export const encodeAdaAddress = (source: Uint8Array): string => { 15 | try { 16 | return byronEncode(source); 17 | } catch { 18 | return cardanoBech32Encode(source); 19 | } 20 | }; 21 | export const decodeAdaAddress = (source: string): Uint8Array => { 22 | if (source.toLowerCase().startsWith(hrp)) { 23 | return cardanoBech32Decode(source); 24 | } 25 | return byronDecode(source); 26 | }; 27 | 28 | export const ada = { 29 | name, 30 | coinType, 31 | encode: encodeAdaAddress, 32 | decode: decodeAdaAddress, 33 | } as const satisfies CheckedCoin; 34 | -------------------------------------------------------------------------------- /src/coin/etn.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeEtnAddress, encodeEtnAddress } from "./etn.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "45Jmf8PnJKziGyrLouJMeBFw2yVyX1QB52sKEQ4S1VSU2NVsaVGPNu4bWKkaHaeZ6tWCepP6iceZk8XhTLzDaEVa72QrtVh", 8 | hex: "6135cf83f4b3f9f6c52cb0dc0f91245945346c1ece03640b2a3c5eb9acdf650831d0b00f0a1ddfce4b9bf7df1df46dae94ac6229e492c72d03b94e3609159535", 9 | }, 10 | { 11 | text: "46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em", 12 | hex: "785b9309dd604860fa86133edbabfae7f89d216b1676de44025e98dc13429f398265b4c125b0c061663e76939027cd22e83520282b05ee2d4803049aefaefe01", 13 | }, 14 | ])("etn address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeEtnAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeEtnAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/nuls.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeNulsAddress, encodeNulsAddress } from "./nuls.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "NULSd6HgXY3zLvEoCRUa6yFXwpnF8gqrDeToT", 8 | hex: "0100013ba3e3c56062266262514c74fafb01852af99fec", 9 | }, 10 | { 11 | text: "tNULSeBaMvEtDfvZuukDf2mVyfGo3DdiN8KLRG", 12 | hex: "020001f7ec6473df12e751d64cf20a8baa7edd50810f81", 13 | }, 14 | { 15 | text: "AHUcC84FN4CWrhuMgvvGPy6UacBvcutgQ4rAR", 16 | hex: "79ff019fe3eff24409addcefe8f5115e17e5dfdd5f04c2", 17 | }, 18 | { 19 | text: "APNcCm4yik6XXquTHUNbHqfPhGrfcSoGoMudc", 20 | hex: "80ff01acfa253cc35655e300061e2563f813c5e4b9589c", 21 | }, 22 | ])("nuls address", ({ text, hex }) => { 23 | test(`encode: ${text}`, () => { 24 | expect(encodeNulsAddress(hexToBytes(hex))).toEqual(text); 25 | }); 26 | test(`decode: ${text}`, () => { 27 | expect(decodeNulsAddress(text)).toEqual(hexToBytes(hex)); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/coin/sys.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeSysAddress, encodeSysAddress } from "./sys.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "SVoQzrfQpoiYsrHMXvwbgeJZooqw8zPF9Q", 8 | hex: "76a9145d5113254a2fb792d209b2731b7c05ee9441aa9088ac", 9 | }, 10 | { 11 | text: "SdQRVkLTiYCA75o4hE4TMjMCJL8CytF31G", 12 | hex: "76a914b0b8ee03d302db1bd6ef689a73de764e3157909588ac", 13 | }, 14 | { 15 | text: "sys1q42jdpqq4369ze73rskkrncplcv7mtejhdkxj90", 16 | hex: "0014aaa4d080158e8a2cfa2385ac39e03fc33db5e657", 17 | }, 18 | { 19 | text: "sys1qlfz9tcds52ajh25v2a85ur22rt2mm488twvs5l", 20 | hex: "0014fa4455e1b0a2bb2baa8c574f4e0d4a1ad5bdd4e7", 21 | }, 22 | ])("sys address", ({ text, hex }) => { 23 | test(`encode: ${text}`, () => { 24 | expect(encodeSysAddress(hexToBytes(hex))).toEqual(text); 25 | }); 26 | test(`decode: ${text}`, () => { 27 | expect(decodeSysAddress(text)).toEqual(hexToBytes(hex)); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/coin/bcn.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeBcnAddress, encodeBcnAddress } from "./bcn.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "21UQFLdH7WvPZEd8HNwXncHtDwFvv4GRqaN3R4cWyuw2TRZxRtRPb7FFTxfcwwQsqYSD2EqhgVCLsGdRdejAoHFHAHJrxxo", 8 | hex: "0606fd971eb1513f86da272c0e64700d64f013286f1bd024c7768bbfc24b36bd9df9f02985759782567ac26311e9637f96b452da1cb5e15c5d6f0c15cdd107bc52", 9 | }, 10 | { 11 | text: "bcnZ6VSM78fQNL5js7VnCybbs3ojLbdAD4DfbdJkUqghYWLqXeEgdyo9UyiAZKnB548DK1ofu8wed3jYCPT62zpf2R97SejoT7", 12 | hex: "cef6222d354172048bb4e38b4b62d78aceddca4ea16a5b66133dcaec3bc71346bc5c87f5891aa07632b67a864ef9393b2b1e8620181e27b610578d4a899c2d88255f0c", 13 | }, 14 | ])("bcn address", ({ text, hex }) => { 15 | test(`encode: ${text}`, () => { 16 | expect(encodeBcnAddress(hexToBytes(hex))).toEqual(text); 17 | }); 18 | test(`decode: ${text}`, () => { 19 | expect(decodeBcnAddress(text)).toEqual(hexToBytes(hex)); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/coin/xem.ts: -------------------------------------------------------------------------------- 1 | import { keccak_256 } from "@noble/hashes/sha3"; 2 | import type { CheckedCoin } from "../types.js"; 3 | import { base32Decode, base32Encode } from "../utils/base32.js"; 4 | import { bytesToHexWithoutPrefix } from "../utils/bytes.js"; 5 | 6 | const name = "xem"; 7 | const coinType = 43; 8 | 9 | export const encodeXemAddress = base32Encode; 10 | 11 | export const decodeXemAddress = (source: string): Uint8Array => { 12 | const address = source.toUpperCase().replace(/-/g, ""); 13 | 14 | if (!address || address.length !== 40) throw new Error("Invalid address"); 15 | 16 | const decoded = base32Decode(address); 17 | 18 | let checksum = bytesToHexWithoutPrefix( 19 | keccak_256(decoded.slice(0, 21)) 20 | ).slice(0, 8); 21 | 22 | if (checksum !== bytesToHexWithoutPrefix(decoded.slice(21))) 23 | throw new Error("Invalid address"); 24 | 25 | return decoded; 26 | }; 27 | 28 | export const xem = { 29 | name, 30 | coinType, 31 | encode: encodeXemAddress, 32 | decode: decodeXemAddress, 33 | } as const satisfies CheckedCoin; 34 | -------------------------------------------------------------------------------- /src/utils/evm.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "bun:test"; 2 | import { 3 | coinTypeToEvmChainId, 4 | evmChainIdToCoinType, 5 | isEvmCoinType, 6 | } from "./evm.js"; 7 | 8 | describe("isEvmCoinType()", () => { 9 | test("non evm coin type", () => { 10 | expect(isEvmCoinType(1000)).toBeFalse(); 11 | }); 12 | test("evm coin type", () => { 13 | expect(isEvmCoinType(2147483658)).toBeTrue(); 14 | }); 15 | }); 16 | 17 | describe("evmChainIdToCoinType()", () => { 18 | test("normal chainId", () => { 19 | expect(evmChainIdToCoinType(10)).toBe(2147483658); 20 | }); 21 | test("chainId too large", () => { 22 | expect(() => evmChainIdToCoinType(2147483648)).toThrow("Invalid chainId"); 23 | }); 24 | }); 25 | 26 | describe("coinTypeToEvmChainId()", () => { 27 | test("non evm coin type", () => { 28 | expect(() => coinTypeToEvmChainId(1000)).toThrow( 29 | "Coin type is not an EVM chain" 30 | ); 31 | }); 32 | test("evm coin type", () => { 33 | expect(coinTypeToEvmChainId(2147483658)).toBe(10); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/utils/hex.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "bun:test"; 2 | import { isValidChecksumAddress } from "./hex.js"; 3 | 4 | const prefixlessAddress = "314159265dD8dbb310642f98f50C066173C1259b"; 5 | 6 | describe("isValidChecksumAddress()", () => { 7 | test("valid checksum address", () => { 8 | expect(isValidChecksumAddress(`0x${prefixlessAddress}`)).toBeTrue(); 9 | }); 10 | test("all lowercased address", () => { 11 | expect( 12 | isValidChecksumAddress(`0x${prefixlessAddress.toLowerCase()}`) 13 | ).toBeTrue(); 14 | }); 15 | test("all uppercased address", () => { 16 | expect( 17 | isValidChecksumAddress(`0x${prefixlessAddress.toUpperCase()}`) 18 | ).toBeTrue(); 19 | }); 20 | test("invalid checksum address", () => { 21 | expect( 22 | isValidChecksumAddress("0x314159265Dd8Dbb310642f98F50C066173C1259b") 23 | ).toBeFalse(); 24 | }); 25 | test("non-hex", () => { 26 | expect( 27 | isValidChecksumAddress("0x1234567890abcdefghijklmnopqrstuvwxyz0123") 28 | ).toBeFalse(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/coin/sol.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | base58UncheckedDecode, 4 | base58UncheckedEncode, 5 | } from "../utils/base58.js"; 6 | 7 | const name = "sol"; 8 | const coinType = 501; 9 | 10 | export const encodeSolAddress = (source: Uint8Array) => { 11 | if (source.length !== 32) throw new Error("Unrecognised address format"); 12 | 13 | const encoded = base58UncheckedEncode(source); 14 | if (encoded.length < 32 || encoded.length > 44) 15 | throw new Error("Unrecognised address format"); 16 | 17 | return encoded; 18 | }; 19 | export const decodeSolAddress = (source: string) => { 20 | if (source.length < 32 || source.length > 44) 21 | throw new Error("Unrecognised address format"); 22 | 23 | const decoded = base58UncheckedDecode(source); 24 | if (decoded.length !== 32) throw new Error("Unrecognised address format"); 25 | 26 | return decoded; 27 | }; 28 | 29 | export const sol = { 30 | name, 31 | coinType, 32 | encode: encodeSolAddress, 33 | decode: decodeSolAddress, 34 | } as const satisfies CheckedCoin; 35 | -------------------------------------------------------------------------------- /src/utils/eosio.ts: -------------------------------------------------------------------------------- 1 | import { secp256k1 } from "@noble/curves/secp256k1"; 2 | import { ripemd160 } from "@noble/hashes/ripemd160"; 3 | import { utils } from "@scure/base"; 4 | import { base58UncheckedDecode, base58UncheckedEncode } from "./base58.js"; 5 | 6 | const eosChecksum = utils.checksum(4, ripemd160); 7 | 8 | export const createEosEncoder = 9 | (prefix: string) => 10 | (source: Uint8Array): string => { 11 | const point = secp256k1.ProjectivePoint.fromHex(source); 12 | const checksummed = eosChecksum.encode(point.toRawBytes(true)); 13 | const encoded = base58UncheckedEncode(checksummed); 14 | return `${prefix}${encoded}`; 15 | }; 16 | 17 | export const createEosDecoder = 18 | (prefix: string) => 19 | (source: string): Uint8Array => { 20 | if (!source.startsWith(prefix)) 21 | throw new Error("Unrecognised address format"); 22 | const prefixStripped = source.slice(prefix.length); 23 | const decoded = base58UncheckedDecode(prefixStripped); 24 | const checksummed = eosChecksum.decode(decoded); 25 | return checksummed; 26 | }; 27 | -------------------------------------------------------------------------------- /src/coin/nas.ts: -------------------------------------------------------------------------------- 1 | import { sha3_256 } from "@noble/hashes/sha3"; 2 | import { utils } from "@scure/base"; 3 | import type { CheckedCoin } from "../types.js"; 4 | import { 5 | base58UncheckedDecode, 6 | base58UncheckedEncode, 7 | } from "../utils/base58.js"; 8 | 9 | const name = "nas"; 10 | const coinType = 2718; 11 | 12 | const nasChecksum = utils.checksum(4, sha3_256); 13 | 14 | export const encodeNasAddress = (source: Uint8Array): string => { 15 | const checksummed = nasChecksum.encode(source); 16 | return base58UncheckedEncode(checksummed); 17 | }; 18 | export const decodeNasAddress = (source: string): Uint8Array => { 19 | const decoded = base58UncheckedDecode(source); 20 | 21 | if ( 22 | decoded.length !== 26 || 23 | decoded[0] !== 25 || 24 | (decoded[1] !== 87 && decoded[1] !== 88) 25 | ) 26 | throw new Error("Unrecognised address format"); 27 | 28 | return nasChecksum.decode(decoded); 29 | }; 30 | 31 | export const nas = { 32 | name, 33 | coinType, 34 | encode: encodeNasAddress, 35 | decode: decodeNasAddress, 36 | } as const satisfies CheckedCoin; 37 | -------------------------------------------------------------------------------- /src/coin/btg.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeBtgAddress, encodeBtgAddress } from "./btg.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "GT7Hz8QWPNmrZ9Z1mEgpYKN7Jdp97eoQjN", 8 | hex: "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac", 9 | }, 10 | { 11 | text: "AQRA16uKrFpxzR17yidYtJDn2t287dc1XY", 12 | hex: "a9145ece0cadddc415b1980f001785947120acdb36fc87", 13 | }, 14 | { 15 | text: "btg1zw508d6qejxtdg4y5r3zarvaryvl9f8z2", 16 | hex: "5210751e76e8199196d454941c45d1b3a323", 17 | }, 18 | { 19 | text: "btg1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kdd2qs6", 20 | hex: "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6", 21 | }, 22 | ])("btg address", ({ text, hex }) => { 23 | test(`encode: ${text}`, () => { 24 | expect(encodeBtgAddress(hexToBytes(hex))).toEqual(text); 25 | }); 26 | test(`decode: ${text}`, () => { 27 | expect(decodeBtgAddress(text)).toEqual(hexToBytes(hex)); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/coin/zec.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeZecAddress, encodeZecAddress } from "./zec.js"; 4 | 5 | describe.each([ 6 | { 7 | // P2PKH Transparent Address 8 | text: "t1b2ArRwLq6KbdJFzJVYPxgUVT1d9QuBzTf", 9 | hex: "76a914bc18e286d40706de62928155d6167bf30719857888ac", 10 | }, 11 | { 12 | // P2SH Transparent Address 13 | text: "t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd", 14 | hex: "a9147d46a730d31f97b1930d3368a967c309bd4d136a87", 15 | }, 16 | { 17 | // Sapling Payment Address (shielded address) 18 | text: "zs1wkejr23wqa9ptpvv73ch3wr96lh8gnyx3689skmyttljy4nyfj69eyclukwkxrhr3rrkgxvnur0", 19 | hex: "75b321aa2e074a15858cf47178b865d7ee744c868e8e585b645aff2256644cb45c931fe59d630ee388c764", 20 | }, 21 | ])("zec address", ({ text, hex }) => { 22 | test(`encode: ${text}`, () => { 23 | expect(encodeZecAddress(hexToBytes(hex))).toEqual(text); 24 | }); 25 | test(`decode: ${text}`, () => { 26 | expect(decodeZecAddress(text)).toEqual(hexToBytes(hex)); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/coin/flow.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { bytesToHex, hexWithoutPrefixToBytes } from "../utils/bytes.js"; 3 | import { validateFlowAddress } from "../utils/flow.js"; 4 | 5 | const name = "flow"; 6 | const coinType = 539; 7 | 8 | const addressLength = 8; 9 | 10 | export const encodeFlowAddress = (source: Uint8Array): string => { 11 | let bytes = new Uint8Array(addressLength); 12 | if (source.length > addressLength) { 13 | bytes.set(source.slice(source.length - addressLength)); 14 | } else { 15 | bytes.set(source, addressLength - source.length); 16 | } 17 | 18 | return bytesToHex(bytes).toLowerCase(); 19 | }; 20 | export const decodeFlowAddress = (source: string): Uint8Array => { 21 | if (!validateFlowAddress(BigInt(source))) 22 | throw new Error("Unrecognised address format"); 23 | return hexWithoutPrefixToBytes( 24 | source.startsWith("0x") ? source.slice(2) : source 25 | ); 26 | }; 27 | 28 | export const flow = { 29 | name, 30 | coinType, 31 | encode: encodeFlowAddress, 32 | decode: decodeFlowAddress, 33 | } as const satisfies CheckedCoin; 34 | -------------------------------------------------------------------------------- /src/coin/avax.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeAvaxAddress, encodeAvaxAddress } from "./avax.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "avax1a5h6v9weng8guuah6aamagea0xhsd04mvs2zun", 8 | hex: "ed2fa615d99a0e8e73b7d77bbea33d79af06bebb", 9 | canonical: "avax1a5h6v9weng8guuah6aamagea0xhsd04mvs2zun", 10 | }, 11 | { 12 | text: "P-avax1a5h6v9weng8guuah6aamagea0xhsd04mvs2zun", 13 | hex: "ed2fa615d99a0e8e73b7d77bbea33d79af06bebb", 14 | canonical: "avax1a5h6v9weng8guuah6aamagea0xhsd04mvs2zun", 15 | }, 16 | { 17 | text: "X-avax1a5h6v9weng8guuah6aamagea0xhsd04mvs2zun", 18 | hex: "ed2fa615d99a0e8e73b7d77bbea33d79af06bebb", 19 | canonical: "avax1a5h6v9weng8guuah6aamagea0xhsd04mvs2zun", 20 | }, 21 | ])("avax address", ({ text, hex, canonical }) => { 22 | test(`encode: ${text}`, () => { 23 | expect(encodeAvaxAddress(hexToBytes(hex))).toEqual(canonical); 24 | }); 25 | test(`decode: ${text}`, () => { 26 | expect(decodeAvaxAddress(text)).toEqual(hexToBytes(hex)); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/coin/eth.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeEthAddress, encodeEthAddress } from "./eth.js"; 4 | 5 | const prefixlessAddress = "314159265dD8dbb310642f98f50C066173C1259b"; 6 | const hex = "314159265dd8dbb310642f98f50c066173c1259b"; 7 | 8 | test(`eth address: encode 0x${prefixlessAddress}`, () => { 9 | expect(encodeEthAddress(hexToBytes(hex))).toEqual(`0x${prefixlessAddress}`); 10 | }); 11 | test("eth address: invalid checksum", () => { 12 | expect(() => 13 | decodeEthAddress("0x314159265Dd8Dbb310642f98F50C066173C1259b") 14 | ).toThrow(); 15 | }); 16 | 17 | describe.each([ 18 | { 19 | // checksummed address 20 | text: `0x${prefixlessAddress}`, 21 | }, 22 | { 23 | // all lowercased address 24 | text: `0x${prefixlessAddress.toLowerCase()}`, 25 | }, 26 | { 27 | // all uppercased address 28 | text: `0x${prefixlessAddress.toUpperCase()}`, 29 | }, 30 | ])("eth address", ({ text }) => { 31 | test(`decode: ${text}`, () => { 32 | expect(decodeEthAddress(text)).toEqual(hexToBytes(hex)); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 ENS Labs Limited 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/coin/etn.ts: -------------------------------------------------------------------------------- 1 | import { keccak_256 } from "@noble/hashes/sha3"; 2 | import { concatBytes } from "@noble/hashes/utils"; 3 | import { utils } from "@scure/base"; 4 | import type { CheckedCoin } from "../types.js"; 5 | import { decodeXmrAddress, encodeXmrAddress } from "./xmr.js"; 6 | 7 | const name = "etn"; 8 | const coinType = 415; 9 | 10 | const type = 18; 11 | 12 | const etnChecksum = utils.checksum(4, keccak_256); 13 | 14 | export const encodeEtnAddress = (source: Uint8Array): string => { 15 | const sourceWithType = concatBytes(new Uint8Array([type]), source); 16 | const checksummed = etnChecksum.encode(sourceWithType); 17 | return encodeXmrAddress(checksummed); 18 | }; 19 | export const decodeEtnAddress = (source: string): Uint8Array => { 20 | const decoded = decodeXmrAddress(source); 21 | 22 | if (decoded[0] !== 18) throw new Error("Unrecognised address format"); 23 | 24 | const checksummed = etnChecksum.decode(decoded); 25 | 26 | return checksummed.slice(1); 27 | }; 28 | 29 | export const etn = { 30 | name, 31 | coinType, 32 | encode: encodeEtnAddress, 33 | decode: decodeEtnAddress, 34 | } as const satisfies CheckedCoin; 35 | -------------------------------------------------------------------------------- /src/coin/xtz.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeXtzAddress, encodeXtzAddress } from "./xtz.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "KT1BDEn6wobs7tDReKkGheXAhoq278TGaNn5", 8 | hex: "011cd5f135e80fd8ebb6e43335b24ca6116edeba6900", 9 | }, 10 | { 11 | text: "KT1BDEn6wobs7tDReKkGheXAhoq278TGaNn5", 12 | hex: "011cd5f135e80fd8ebb6e43335b24ca6116edeba6900", 13 | }, 14 | { 15 | text: "tz1XdRrrqrMfsFKA8iuw53xHzug9ipr6MuHq", 16 | hex: "000083846eddd5d3c5ed96e962506253958649c84a74", 17 | }, 18 | { 19 | text: "tz2Cfwk4ortcaqAGcVJKSxLiAdcFxXBLBoyY", 20 | hex: "00012fcb1d9307f0b1f94c048ff586c09f46614c7e90", 21 | }, 22 | { 23 | text: "tz3NdTPb3Ax2rVW2Kq9QEdzfYFkRwhrQRPhX", 24 | hex: "0002193b2b3f6b8f8e1e6b39b4d442fc2b432f6427a8", 25 | }, 26 | ])("xtz address", ({ text, hex }) => { 27 | test(`encode: ${text}`, () => { 28 | expect(encodeXtzAddress(hexToBytes(hex))).toEqual(text); 29 | }); 30 | test(`decode: ${text}`, () => { 31 | expect(decodeXtzAddress(text)).toEqual(hexToBytes(hex)); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /src/coin/hns.ts: -------------------------------------------------------------------------------- 1 | import { bech32 } from "@scure/base"; 2 | import type { CheckedCoin } from "../types.js"; 3 | 4 | const name = "hns"; 5 | const coinType = 5353; 6 | 7 | const hrp = "hs"; 8 | const versionBytes = new Uint8Array([0x00]); 9 | 10 | export const encodeHnsAddress = (source: Uint8Array): string => { 11 | if (source.length !== 20) throw new Error("Unrecognised address format"); 12 | return bech32.encode(hrp, [versionBytes[0], ...bech32.toWords(source)]); 13 | }; 14 | export const decodeHnsAddress = (source: string): Uint8Array => { 15 | const { prefix, words } = bech32.decode(source); 16 | 17 | if (prefix !== hrp) throw new Error("Unrecognised address format"); 18 | 19 | const version = words[0]; 20 | const bytes = bech32.fromWords(words.slice(1)); 21 | 22 | if (version !== versionBytes[0]) 23 | throw new Error("Unrecognised address format"); 24 | if (bytes.length !== 20) throw new Error("Unrecognised address format"); 25 | 26 | return new Uint8Array(bytes); 27 | }; 28 | 29 | export const hns = { 30 | name, 31 | coinType, 32 | encode: encodeHnsAddress, 33 | decode: decodeHnsAddress, 34 | } as const satisfies CheckedCoin; 35 | -------------------------------------------------------------------------------- /src/coin/sui.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | import { 3 | bytesToHex, 4 | hexToBytes, 5 | } from "../utils/bytes.js"; 6 | import { stripHexPrefix } from "../utils/hex.js"; 7 | 8 | const name = "sui"; 9 | const coinType = 784; 10 | 11 | export const encodeSuiAddress = (source: Uint8Array) => { 12 | if (source.length !== 32) throw new Error("Unrecognised address format"); 13 | 14 | const encoded = bytesToHex(source); 15 | return encoded.toLowerCase(); 16 | }; 17 | 18 | export const decodeSuiAddress = (source: string) => { 19 | const stripped = stripHexPrefix(source); 20 | 21 | // Check if the address has the correct format (64 hex chars) 22 | if (!/^0x[a-fA-F0-9]{64}$/.test(source) && !/^[a-fA-F0-9]{64}$/.test(stripped)) { 23 | throw new Error("Unrecognised address format"); 24 | } 25 | 26 | const decoded = hexToBytes(`0x${stripped}` as const); 27 | if (decoded.length !== 32) throw new Error("Unrecognised address format"); 28 | 29 | return decoded; 30 | }; 31 | 32 | export const sui = { 33 | name, 34 | coinType, 35 | encode: encodeSuiAddress, 36 | decode: decodeSuiAddress, 37 | } as const satisfies CheckedCoin; -------------------------------------------------------------------------------- /src/coin/waves.ts: -------------------------------------------------------------------------------- 1 | import { blake2b } from "@noble/hashes/blake2b"; 2 | import { keccak_256 } from "@noble/hashes/sha3"; 3 | import { utils } from "@scure/base"; 4 | import type { CheckedCoin } from "../types.js"; 5 | import { 6 | base58UncheckedDecode, 7 | base58UncheckedEncode, 8 | } from "../utils/base58.js"; 9 | 10 | const name = "waves"; 11 | const coinType = 5741564; 12 | 13 | const checksumFn = (source: Uint8Array): Uint8Array => 14 | keccak_256(blake2b(source, { dkLen: 32 })); 15 | const checksumLength = 4; 16 | const wavesChecksum = utils.checksum(checksumLength, checksumFn); 17 | 18 | export const encodeWavesAddress = base58UncheckedEncode; 19 | export const decodeWavesAddress = (source: string): Uint8Array => { 20 | const decoded = base58UncheckedDecode(source); 21 | 22 | if (decoded[0] !== 1) throw new Error("Invalid address format"); 23 | 24 | if (decoded[1] !== 87 || decoded.length !== 26) 25 | throw new Error("Invalid address format"); 26 | 27 | wavesChecksum.decode(decoded); 28 | 29 | return decoded; 30 | }; 31 | 32 | export const waves = { 33 | name, 34 | coinType, 35 | encode: encodeWavesAddress, 36 | decode: decodeWavesAddress, 37 | } as const satisfies CheckedCoin; 38 | -------------------------------------------------------------------------------- /src/coin/zen.ts: -------------------------------------------------------------------------------- 1 | import { equalBytes } from "@noble/curves/abstract/utils"; 2 | import type { CheckedCoin } from "../types.js"; 3 | import { base58CheckDecode, base58CheckEncode } from "../utils/base58.js"; 4 | 5 | const name = "zen"; 6 | const coinType = 121; 7 | 8 | const validPrefixes = [ 9 | new Uint8Array([0x20, 0x89]), // zn 10 | new Uint8Array([0x1c, 0xb8]), // t1 11 | new Uint8Array([0x20, 0x96]), // zs 12 | new Uint8Array([0x1c, 0xbd]), // t3 13 | new Uint8Array([0x16, 0x9a]), // zc 14 | ]; 15 | 16 | export const encodeZenAddress = (source: Uint8Array): string => { 17 | const prefix = source.slice(0, 2); 18 | if (!validPrefixes.some((x) => equalBytes(x, prefix))) 19 | throw new Error("Invalid prefix"); 20 | 21 | return base58CheckEncode(source); 22 | }; 23 | export const decodeZenAddress = (source: string): Uint8Array => { 24 | const decoded = base58CheckDecode(source); 25 | const prefix = decoded.slice(0, 2); 26 | 27 | if (!validPrefixes.some((x) => equalBytes(x, prefix))) 28 | throw new Error("Invalid prefix"); 29 | 30 | return decoded; 31 | }; 32 | 33 | export const zen = { 34 | name, 35 | coinType, 36 | encode: encodeZenAddress, 37 | decode: decodeZenAddress, 38 | } as const satisfies CheckedCoin; 39 | -------------------------------------------------------------------------------- /src/utils/byron.ts: -------------------------------------------------------------------------------- 1 | import { base58UncheckedDecode, base58UncheckedEncode } from "./base58.js"; 2 | import { TaggedValue, cborDecode, cborEncode } from "./cbor.js"; 3 | import { crc32 } from "./crc32.js"; 4 | 5 | export const byronEncode = (source: Uint8Array): string => { 6 | const checksum = crc32(source); 7 | const taggedValue = new TaggedValue(source.buffer, 24); 8 | 9 | const cborEncodedAddress = cborEncode([taggedValue, checksum]); 10 | 11 | const address = base58UncheckedEncode(new Uint8Array(cborEncodedAddress)); 12 | 13 | if (!address.startsWith("Ae2") && !address.startsWith("Ddz")) 14 | throw new Error("Unrecognised address format"); 15 | 16 | return address; 17 | }; 18 | 19 | export const byronDecode = (source: string): Uint8Array => { 20 | const bytes = base58UncheckedDecode(source); 21 | 22 | const cborDecoded = cborDecode(bytes.buffer); 23 | 24 | const taggedAddress = cborDecoded[0]; 25 | if (taggedAddress === undefined) 26 | throw new Error("Unrecognised address format"); 27 | 28 | const checksum = cborDecoded[1]; 29 | const newChecksum = crc32(taggedAddress.value); 30 | 31 | if (checksum !== newChecksum) throw new Error("Unrecognised address format"); 32 | 33 | return taggedAddress.value; 34 | }; 35 | -------------------------------------------------------------------------------- /src/coin/icx.ts: -------------------------------------------------------------------------------- 1 | import { concatBytes } from "@noble/hashes/utils"; 2 | import type { CheckedCoin } from "../types.js"; 3 | import { 4 | bytesToHexWithoutPrefix, 5 | hexWithoutPrefixToBytes, 6 | } from "../utils/bytes.js"; 7 | 8 | const name = "icx"; 9 | const coinType = 74; 10 | 11 | export const encodeIcxAddress = (source: Uint8Array): string => { 12 | if (source.length !== 21) throw new Error("Invalid address length"); 13 | 14 | const type = source[0]; 15 | if (type === 0x00) return `hx${bytesToHexWithoutPrefix(source.slice(1))}`; 16 | if (type === 0x01) return `cx${bytesToHexWithoutPrefix(source.slice(1))}`; 17 | throw new Error("Invalid address type"); 18 | }; 19 | export const decodeIcxAddress = (source: string): Uint8Array => { 20 | const prefix = source.slice(0, 2); 21 | const body = source.slice(2); 22 | if (prefix === "hx") 23 | return concatBytes(new Uint8Array([0x00]), hexWithoutPrefixToBytes(body)); 24 | if (prefix === "cx") 25 | return concatBytes(new Uint8Array([0x01]), hexWithoutPrefixToBytes(body)); 26 | throw new Error("Invalid address prefix"); 27 | }; 28 | 29 | export const icx = { 30 | name, 31 | coinType, 32 | encode: encodeIcxAddress, 33 | decode: decodeIcxAddress, 34 | } as const satisfies CheckedCoin; 35 | -------------------------------------------------------------------------------- /src/coin/bcn.ts: -------------------------------------------------------------------------------- 1 | import { equalBytes } from "@noble/curves/abstract/utils"; 2 | import { keccak_256 } from "@noble/hashes/sha3"; 3 | import { concatBytes } from "@noble/hashes/utils"; 4 | import { utils } from "@scure/base"; 5 | import type { CheckedCoin } from "../types.js"; 6 | import { decodeXmrAddress, encodeXmrAddress } from "./xmr.js"; 7 | 8 | const name = "bcn"; 9 | const coinType = 204; 10 | 11 | const bcnChecksum = utils.checksum(4, keccak_256); 12 | 13 | export const encodeBcnAddress = (source: Uint8Array): string => { 14 | const checksum = keccak_256(source).slice(0, 4); 15 | return encodeXmrAddress(concatBytes(source, checksum)); 16 | }; 17 | export const decodeBcnAddress = (source: string): Uint8Array => { 18 | const decoded = decodeXmrAddress(source); 19 | 20 | const tag = decoded.slice(0, -68); 21 | 22 | if ( 23 | decoded.length < 68 || 24 | (!equalBytes(tag, new Uint8Array([0x06])) && 25 | !equalBytes(tag, new Uint8Array([0xce, 0xf6, 0x22]))) 26 | ) 27 | throw new Error("Unrecognised address format"); 28 | 29 | return bcnChecksum.decode(decoded); 30 | }; 31 | 32 | export const bcn = { 33 | name, 34 | coinType, 35 | encode: encodeBcnAddress, 36 | decode: decodeBcnAddress, 37 | } as const satisfies CheckedCoin; 38 | -------------------------------------------------------------------------------- /src/coin/hbar.ts: -------------------------------------------------------------------------------- 1 | import type { CheckedCoin } from "../types.js"; 2 | 3 | const name = "hbar"; 4 | const coinType = 3030; 5 | 6 | export const encodeHbarAddress = (source: Uint8Array): string => { 7 | if (source.length !== 20) throw new Error("Unrecognised address format"); 8 | 9 | const view = new DataView(source.buffer, 0, 20); 10 | 11 | const shard = view.getUint32(0); 12 | const realm = view.getBigUint64(4); 13 | const account = view.getBigUint64(12, false); 14 | 15 | return [shard, realm, account].join("."); 16 | }; 17 | export const decodeHbarAddress = (source: string): Uint8Array => { 18 | const view = new DataView(new ArrayBuffer(20), 0, 20); 19 | 20 | const components = source.split("."); 21 | if (components.length !== 3) throw new Error("Unrecognised address format"); 22 | 23 | const shard = Number(components[0]); 24 | const realm = BigInt(components[1]); 25 | const account = BigInt(components[2]); 26 | 27 | view.setUint32(0, shard); 28 | view.setBigUint64(4, realm); 29 | view.setBigUint64(12, account); 30 | 31 | return new Uint8Array(view.buffer, 0, 20); 32 | }; 33 | 34 | export const hbar = { 35 | name, 36 | coinType, 37 | encode: encodeHbarAddress, 38 | decode: decodeHbarAddress, 39 | } as const satisfies CheckedCoin; 40 | -------------------------------------------------------------------------------- /src/utils/zcash.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createBase58VersionedDecoder, 3 | createBase58VersionedEncoder, 4 | } from "./base58.js"; 5 | import { createBech32Decoder, createBech32Encoder } from "./bech32.js"; 6 | import type { BitcoinCoderParameters } from "./bitcoin.js"; 7 | 8 | // changes from bitcoin.ts: 9 | // - no hrp suffix (hrp + "1") 10 | // - no segwit 11 | 12 | export const createZcashDecoder = ({ 13 | hrp, 14 | p2pkhVersions, 15 | p2shVersions, 16 | }: BitcoinCoderParameters) => { 17 | const decodeBech32 = createBech32Decoder(hrp); 18 | const decodeBase58 = createBase58VersionedDecoder( 19 | p2pkhVersions, 20 | p2shVersions 21 | ); 22 | return (source: string): Uint8Array => { 23 | if (source.toLowerCase().startsWith(hrp)) { 24 | return decodeBech32(source); 25 | } 26 | return decodeBase58(source); 27 | }; 28 | }; 29 | 30 | export const createZcashEncoder = ({ 31 | hrp, 32 | p2pkhVersions, 33 | p2shVersions, 34 | }: BitcoinCoderParameters) => { 35 | const encodeBech32 = createBech32Encoder(hrp); 36 | const encodeBase58 = createBase58VersionedEncoder( 37 | p2pkhVersions[0], 38 | p2shVersions[0] 39 | ); 40 | return (source: Uint8Array): string => { 41 | try { 42 | return encodeBase58(source); 43 | } catch { 44 | return encodeBech32(source); 45 | } 46 | }; 47 | }; 48 | -------------------------------------------------------------------------------- /src/coin/sc.ts: -------------------------------------------------------------------------------- 1 | import { equalBytes } from "@noble/curves/abstract/utils"; 2 | import { blake2b } from "@noble/hashes/blake2b"; 3 | import { concatBytes } from "@noble/hashes/utils"; 4 | import type { CheckedCoin } from "../types.js"; 5 | import { 6 | bytesToHexWithoutPrefix, 7 | hexWithoutPrefixToBytes, 8 | } from "../utils/bytes.js"; 9 | 10 | const name = "sc"; 11 | const coinType = 1991; 12 | 13 | const length = 32; 14 | const checksumLength = 6; 15 | 16 | const scChecksum = (source: Uint8Array) => 17 | blake2b(source, { dkLen: length }).slice(0, checksumLength); 18 | 19 | export const encodeScAddress = (source: Uint8Array): string => { 20 | const checksum = scChecksum(source); 21 | return bytesToHexWithoutPrefix(concatBytes(source, checksum)); 22 | }; 23 | export const decodeScAddress = (source: string): Uint8Array => { 24 | if (source.length !== 76) throw new Error("Unrecognised address format"); 25 | 26 | const decoded = hexWithoutPrefixToBytes(source); 27 | const payload = decoded.slice(0, -checksumLength); 28 | const checksum = decoded.slice(-checksumLength); 29 | const newChecksum = scChecksum(payload); 30 | 31 | if (!equalBytes(checksum, newChecksum)) 32 | throw new Error("Unrecognised address format"); 33 | 34 | return payload; 35 | }; 36 | 37 | export const sc = { 38 | name, 39 | coinType, 40 | encode: encodeScAddress, 41 | decode: decodeScAddress, 42 | } as const satisfies CheckedCoin; 43 | -------------------------------------------------------------------------------- /src/utils/dot.ts: -------------------------------------------------------------------------------- 1 | import { equalBytes } from "@noble/curves/abstract/utils"; 2 | import { blake2b } from "@noble/hashes/blake2b"; 3 | import { concatBytes } from "@noble/hashes/utils"; 4 | import { base58UncheckedDecode, base58UncheckedEncode } from "./base58.js"; 5 | 6 | const prefixStringBytes = new Uint8Array([ 7 | 0x53, 0x53, 0x35, 0x38, 0x50, 0x52, 0x45, 8 | ]); 9 | 10 | const dotChecksum = (sourceWithTypePrefix: Uint8Array): Uint8Array => 11 | blake2b(concatBytes(prefixStringBytes, sourceWithTypePrefix)).slice(0, 2); 12 | 13 | export const createDotAddressEncoder = 14 | (type: number) => 15 | (source: Uint8Array): string => { 16 | const typePrefix = new Uint8Array([type]); 17 | const sourceWithTypePrefix = concatBytes(typePrefix, source); 18 | const checksum = dotChecksum(sourceWithTypePrefix); 19 | return base58UncheckedEncode(concatBytes(sourceWithTypePrefix, checksum)); 20 | }; 21 | 22 | export const createDotAddressDecoder = 23 | (type: number) => 24 | (source: string): Uint8Array => { 25 | const decoded = base58UncheckedDecode(source); 26 | if (decoded[0] !== type) throw new Error("Unrecognized address format"); 27 | 28 | const checksum = decoded.slice(33, 35); 29 | const newChecksum = dotChecksum(decoded.slice(0, 33)); 30 | if (!equalBytes(checksum, newChecksum)) 31 | throw new Error("Unrecognized address format"); 32 | 33 | return decoded.slice(1, 33); 34 | }; 35 | -------------------------------------------------------------------------------- /src/utils/bitcoin.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createBase58VersionedDecoder, 3 | createBase58VersionedEncoder, 4 | type Base58CheckVersion, 5 | } from "./base58.js"; 6 | import { 7 | createBech32SegwitDecoder, 8 | createBech32SegwitEncoder, 9 | } from "./bech32.js"; 10 | 11 | export type BitcoinCoderParameters = { 12 | hrp: string; 13 | p2pkhVersions: Base58CheckVersion[]; 14 | p2shVersions: Base58CheckVersion[]; 15 | }; 16 | 17 | export const createBitcoinDecoder = ({ 18 | hrp, 19 | p2pkhVersions, 20 | p2shVersions, 21 | }: BitcoinCoderParameters) => { 22 | const decodeBech32 = createBech32SegwitDecoder(hrp); 23 | const decodeBase58 = createBase58VersionedDecoder( 24 | p2pkhVersions, 25 | p2shVersions 26 | ); 27 | return (source: string): Uint8Array => { 28 | if (source.toLowerCase().startsWith(hrp + "1")) { 29 | return decodeBech32(source); 30 | } 31 | return decodeBase58(source); 32 | }; 33 | }; 34 | 35 | export const createBitcoinEncoder = ({ 36 | hrp, 37 | p2pkhVersions, 38 | p2shVersions, 39 | }: BitcoinCoderParameters) => { 40 | const encodeBech32 = createBech32SegwitEncoder(hrp); 41 | const encodeBase58 = createBase58VersionedEncoder( 42 | p2pkhVersions[0], 43 | p2shVersions[0] 44 | ); 45 | return (source: Uint8Array): string => { 46 | try { 47 | return encodeBase58(source); 48 | } catch { 49 | return encodeBech32(source); 50 | } 51 | }; 52 | }; 53 | -------------------------------------------------------------------------------- /src/utils/near.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "bun:test"; 2 | import { validateNearAddress } from "./near.js"; 3 | 4 | describe("validateNearAddress", () => { 5 | test("root name", () => { 6 | expect(validateNearAddress("near")).toBeTrue(); 7 | }); 8 | test("2ld", () => { 9 | expect(validateNearAddress("alice.near")).toBeTrue(); 10 | }); 11 | test("3ld", () => { 12 | expect(validateNearAddress("alice.bob.near")).toBeTrue(); 13 | }); 14 | test("64 char account id", () => { 15 | expect(validateNearAddress("a".repeat(64))).toBeTrue(); 16 | }); 17 | test("larger than 64 chars", () => { 18 | expect(validateNearAddress("a".repeat(65))).toBeFalse(); 19 | }); 20 | test("smaller than 2 chars", () => { 21 | expect(validateNearAddress("a")).toBeFalse(); 22 | }); 23 | test("non alphanumeric", () => { 24 | expect(validateNearAddress("ƒelicia.near")).toBeFalse(); 25 | }); 26 | describe.each([".", "-", "_"])("separator", (separator) => { 27 | test(`starting separator: ${separator}`, () => { 28 | expect(validateNearAddress(`${separator}near`)).toBeFalse(); 29 | }); 30 | test(`ending separator: ${separator}`, () => { 31 | expect(validateNearAddress(`near${separator}`)).toBeFalse(); 32 | }); 33 | test(`consecutive separators: ${separator}`, () => { 34 | expect( 35 | validateNearAddress(`alice${separator}${separator}near`) 36 | ).toBeFalse(); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /src/coin/vsys.ts: -------------------------------------------------------------------------------- 1 | import { equalBytes } from "@noble/curves/abstract/utils"; 2 | import { blake2b } from "@noble/hashes/blake2b"; 3 | import { keccak_256 } from "@noble/hashes/sha3"; 4 | import type { CheckedCoin } from "../types.js"; 5 | import { 6 | base58UncheckedDecode, 7 | base58UncheckedEncode, 8 | } from "../utils/base58.js"; 9 | 10 | const name = "vsys"; 11 | const coinType = 360; 12 | 13 | const vsysChecksum = (source: Uint8Array): boolean => { 14 | if (source[0] !== 5 || source[1] !== 77 /* M */ || source.length !== 26) 15 | return false; 16 | 17 | const checksum = source.slice(-4); 18 | const newChecksum = keccak_256( 19 | blake2b(source.slice(0, -4), { dkLen: 32 }) 20 | ).slice(0, 4); 21 | if (!equalBytes(checksum, newChecksum)) return false; 22 | return true; 23 | }; 24 | 25 | export const encodeVsysAddress = (source: Uint8Array): string => { 26 | if (!vsysChecksum(source)) throw new Error("Unrecognised address format"); 27 | return base58UncheckedEncode(source); 28 | }; 29 | export const decodeVsysAddress = (source: string): Uint8Array => { 30 | const encoded = source.startsWith("address:") ? source.slice(8) : source; 31 | if (encoded.length > 36) throw new Error("Unrecognised address format"); 32 | 33 | const decoded = base58UncheckedDecode(encoded); 34 | if (!vsysChecksum(decoded)) throw new Error("Unrecognised address format"); 35 | 36 | return decoded; 37 | }; 38 | 39 | export const vsys = { 40 | name, 41 | coinType, 42 | encode: encodeVsysAddress, 43 | decode: decodeVsysAddress, 44 | } as const satisfies CheckedCoin; 45 | -------------------------------------------------------------------------------- /src/coin/sero.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeSeroAddress, encodeSeroAddress } from "./sero.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "p2ya5oepEuHAdcPrn5pfNZTpLMv15ub6vmCmMyqbavPCdxpQ7BVQomCnR2Gv2YQrGUKXYEzstzyUkP7WYef2Kb6ciRcp6eazH8LhjX5cTyJc9Zs2sR7SCSNGJJuJ5K5ihUT", 8 | hex: "8889d697ff9f3bc140d6df1679edaf9be975bf41d655c7db7ba8d5d5f7d86b17434b9660b7ccd9f8df866c698d81cf7ae8ae10f6a4c0a17aa36db766fee3ca9ecc7af78b9506a414c2acfbba2adaf5fa811612bd8cc534ea3e083eac6464c340", 9 | }, 10 | { 11 | text: "SPpb3UAuzAarWVDgyU6FTtS3X5dBucTQXb89ydjqXjABiNg8x3v8jPTrGWqo974BtPvuWt9zqBKJXYyEFYjZ3CS5D2jUhncTGmYQCHFWHSAFRyM4kuBhqWcgekPm5z3Jdwj", 12 | hex: "49b767bca9137dbbf58eafde5936ddeb843747f450252fbe6c178094cbf76290f575a84bae4565774a0b0098df0fafae874dafc5b628c621b8ccb0ef7372562f10ed5a7343d295c1ca53235d97e7dd9c5ce957e747cbfa2d0c34af1ccacb23de", 13 | }, 14 | { 15 | text: "Gwdm8LqXkvmSuhS6MAP1XQEedzNJ6msxcUAFCfRi2vS8hxXGkpcxUmtBaPVS8bY6yuacyS4FD2ZfGGoP2ioYcUU4pXmxJQYa8XphWffmtsAiVbreRRWZdzFUjWsZUb8WXar", 16 | hex: "2e476e68ec4f9dc8b7e4d41dddd07e84ecf7a777fa9229ac7886d5c1499d4c81fe5075976c6025bbd1081caabd587543ce3ce9a2a6a11b4972c951f99f6a36a535c608d00bd7cc5f5314151b7270e820ecff823f0a55e1563a3816cd6b39ffdb", 17 | }, 18 | ])("sero address", ({ text, hex }) => { 19 | test(`encode: ${text}`, () => { 20 | expect(encodeSeroAddress(hexToBytes(hex))).toEqual(text); 21 | }); 22 | test(`decode: ${text}`, () => { 23 | expect(decodeSeroAddress(text)).toEqual(hexToBytes(hex)); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/utils/evm.ts: -------------------------------------------------------------------------------- 1 | import type { Add, GtOrEq, Lt, Subtract } from "ts-arithmetic"; 2 | import type { EvmChainId, EvmCoinType } from "../types.js"; 3 | 4 | export const SLIP44_MSB = 0x80000000; 5 | 6 | export const isEvmCoinType = < 7 | TCoinType extends EvmCoinType | number = EvmCoinType | number 8 | >( 9 | coinType: TCoinType 10 | ) => 11 | ((coinType & SLIP44_MSB) !== 0) as GtOrEq< 12 | TCoinType, 13 | typeof SLIP44_MSB 14 | > extends 1 15 | ? true 16 | : false; 17 | 18 | type EvmChainIdToCoinType< 19 | TChainId extends EvmChainId | number = EvmChainId | number 20 | > = Lt extends 1 21 | ? Add 22 | : never; 23 | 24 | export const evmChainIdToCoinType = < 25 | TChainId extends EvmChainId | number = EvmChainId | number 26 | >( 27 | chainId: TChainId 28 | ): EvmChainIdToCoinType => { 29 | if (chainId >= SLIP44_MSB) throw new Error("Invalid chainId"); 30 | return ((SLIP44_MSB | chainId) >>> 0) as EvmChainIdToCoinType; 31 | }; 32 | 33 | type CoinTypeToEvmChainId< 34 | TCoinType extends EvmCoinType | number = EvmCoinType | number 35 | > = Lt extends 1 36 | ? never 37 | : Subtract; 38 | 39 | export const coinTypeToEvmChainId = < 40 | TCoinType extends EvmCoinType | number = EvmCoinType | number 41 | >( 42 | coinType: TCoinType 43 | ): CoinTypeToEvmChainId => { 44 | if ((coinType & SLIP44_MSB) === 0) 45 | throw new Error("Coin type is not an EVM chain"); 46 | return (((SLIP44_MSB - 1) & coinType) >> 47 | 0) as CoinTypeToEvmChainId; 48 | }; 49 | -------------------------------------------------------------------------------- /src/coin/wan.ts: -------------------------------------------------------------------------------- 1 | import { keccak_256 } from "@noble/hashes/sha3"; 2 | import type { CheckedCoin } from "../types.js"; 3 | import { 4 | bytesToHexWithoutPrefix, 5 | hexToBytes, 6 | stringToBytes, 7 | type Hex, 8 | } from "../utils/bytes.js"; 9 | import { isAddress } from "../utils/hex.js"; 10 | 11 | const name = "wan"; 12 | const coinType = 5718350; 13 | 14 | const wanChecksum = (addressBytes: Uint8Array): string => { 15 | const addressStr = bytesToHexWithoutPrefix(addressBytes); 16 | const address = addressStr.split(""); 17 | // hash of string representation of hex address 18 | // this is intentional 19 | const hash = keccak_256(stringToBytes(addressStr)); 20 | 21 | let hashByte: number; 22 | for (let i = 0; i < 40; i++) { 23 | hashByte = hash[Math.floor(i / 2)]; 24 | 25 | if (i % 2 === 0) hashByte = hashByte >> 4; 26 | else hashByte &= 0xf; 27 | 28 | if (address[i] > "9" && hashByte <= 7) 29 | address[i] = address[i].toUpperCase(); 30 | } 31 | 32 | return `0x${address.join("")}`; 33 | }; 34 | 35 | export const encodeWanAddress = (source: Uint8Array): string => { 36 | return wanChecksum(source); 37 | }; 38 | export const decodeWanAddress = (source: string): Uint8Array => { 39 | if (!isAddress(source)) throw new Error("Unrecognised address format"); 40 | 41 | const bytes = hexToBytes(source as Hex); 42 | 43 | if (wanChecksum(bytes) !== source) 44 | throw new Error("Unrecognised address format"); 45 | 46 | return bytes; 47 | }; 48 | 49 | export const wan = { 50 | name, 51 | coinType, 52 | encode: encodeWanAddress, 53 | decode: decodeWanAddress, 54 | } as const satisfies CheckedCoin; 55 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | permissions: 4 | contents: write 5 | deployments: write 6 | 7 | on: 8 | release: 9 | types: [published] 10 | 11 | jobs: 12 | publish: 13 | name: Publish 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | ref: ${{ github.event.release.target_commitish }} 19 | 20 | - uses: oven-sh/setup-bun@v1 21 | with: 22 | bun-version: latest 23 | 24 | - name: Install dependencies 25 | run: bun install --frozen-lockfile 26 | 27 | - name: Install Node.js 28 | uses: actions/setup-node@v3 29 | with: 30 | node-version: 18 31 | 32 | - name: Set up git 33 | run: | 34 | git config --local user.email '41898282+github-actions[bot]@users.noreply.github.com' 35 | git config --local user.name 'github-actions[bot]' 36 | 37 | - name: Bump version to ${{ github.event.release.tag_name }} 38 | run: npm version ${{ github.event.release.tag_name }} 39 | 40 | - name: Build 41 | run: bun run build 42 | 43 | - name: Generate Publish Structure 44 | run: bun run generatePublishStructure 45 | 46 | - name: Publish 47 | env: 48 | NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 49 | NPM_TAG: ${{ github.event.release.prerelease == true && 'next' || 'latest' }} 50 | run: | 51 | npm config set //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} 52 | npm publish --tag $NPM_TAG 53 | 54 | - name: Push changes 55 | run: git push 56 | env: 57 | github-token: ${{ secrets.GITHUB_TOKEN }} 58 | -------------------------------------------------------------------------------- /src/coin/strk.ts: -------------------------------------------------------------------------------- 1 | import { keccak_256 } from "@noble/hashes/sha3"; 2 | import type { CheckedCoin } from "../types.js"; 3 | import { 4 | bytesToHexWithoutPrefix, 5 | hexToBytes, 6 | type Hex, 7 | } from "../utils/bytes.js"; 8 | import { rawChecksumAddress } from "../utils/hex.js"; 9 | 10 | const name = "strk"; 11 | const coinType = 9004; 12 | 13 | const addressLength = 32; 14 | 15 | const addressRegex = /^0x[a-fA-F0-9]{64}$/; 16 | 17 | const strkChecksum = (source: Uint8Array): string => { 18 | const chars = bytesToHexWithoutPrefix(source).toLowerCase().split(""); 19 | const sourceLeadingZeroIndex = source.findIndex((byte) => byte !== 0x00); 20 | const leadingZeroStripped = 21 | sourceLeadingZeroIndex > 0 ? source.slice(sourceLeadingZeroIndex) : source; 22 | const hash = new Uint8Array(32); 23 | hash.set(keccak_256(leadingZeroStripped), sourceLeadingZeroIndex); 24 | return rawChecksumAddress({ address: chars, hash, length: 64 }); 25 | }; 26 | 27 | export const encodeStrkAddress = (source: Uint8Array): string => { 28 | if (source.length !== addressLength) 29 | throw new Error("Unrecognised address format"); 30 | 31 | return strkChecksum(source); 32 | }; 33 | export const decodeStrkAddress = (source: string): Uint8Array => { 34 | if (!addressRegex.test(source)) 35 | throw new Error("Unrecognised address format"); 36 | 37 | const decoded = hexToBytes(source as Hex); 38 | 39 | if (strkChecksum(decoded) !== source) 40 | throw new Error("Unrecognised address format"); 41 | 42 | return decoded; 43 | }; 44 | 45 | export const strk = { 46 | name, 47 | coinType, 48 | encode: encodeStrkAddress, 49 | decode: decodeStrkAddress, 50 | } as const satisfies CheckedCoin; 51 | -------------------------------------------------------------------------------- /src/coin/sol.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeSolAddress, encodeSolAddress } from "./sol.js"; 4 | 5 | describe.each([ 6 | // The address reportetd by Trust Wallet team that it is having problem. 7 | { 8 | text: "AHy6YZA8BsHgQfVkk7MbwpAN94iyN7Nf1zN4nPqUN32Q", 9 | hex: "8a11e71b96cabbe3216e3153b09694f39fc85022cbc076f79846a3ab4d8c1991", 10 | }, 11 | // https://github.com/trustwallet/wallet-core/blob/8d3100f61e36d1e928ed1dea60ff7554bba0db16/tests/Solana/AddressTests.cpp#L26 12 | { 13 | text: "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST", 14 | hex: "18f9d8d877393bbbe8d697a8a2e52879cc7e84f467656d1cce6bab5a8d2637ec", 15 | }, 16 | // https://explorer.solana.com/address/CNR8RPMxjY28VsPA6KFq3B8PUdZnrTSC5HSFwKPBR29Z 17 | { 18 | text: "CNR8RPMxjY28VsPA6KFq3B8PUdZnrTSC5HSFwKPBR29Z", 19 | hex: "a8ed08e3e8fe204de45e7295cc1ad53db096621b878f8c546e5c09f5e48f70b4", 20 | }, 21 | ])("sol address", ({ text, hex }) => { 22 | test(`encode: ${text}`, () => { 23 | expect(encodeSolAddress(hexToBytes(hex))).toEqual(text); 24 | }); 25 | test(`decode: ${text}`, () => { 26 | expect(decodeSolAddress(text)).toEqual(hexToBytes(hex)); 27 | }); 28 | }); 29 | 30 | test("SOL decoding - incorrect length", () => { 31 | expect(() => 32 | decodeSolAddress("CNR8RPMxjY28VsPA6KFq3B8PUdZnrTSC5HSFwKPBR2") 33 | ).toThrow("Unrecognised address format"); 34 | }); 35 | 36 | test("SOL encoding - incorrect length", () => { 37 | expect(() => 38 | encodeSolAddress( 39 | hexToBytes("a8ed08e3e8fe204de45e7295cc1ad53db096621b878f8c546e5c09f5e48f") 40 | ) 41 | ).toThrow("Unrecognised address format"); 42 | }); 43 | -------------------------------------------------------------------------------- /src/coin/btc.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeBtcAddress, encodeBtcAddress } from "./btc.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", 8 | hex: "76a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac", 9 | }, 10 | { 11 | text: "3Ai1JZ8pdJb2ksieUV8FsxSNVJCpoPi8W6", 12 | hex: "a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1887", 13 | }, 14 | { 15 | text: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", 16 | hex: "0014751e76e8199196d454941c45d1b3a323f1433bd6", 17 | }, 18 | { 19 | text: "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", 20 | hex: "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6", 21 | }, 22 | { text: "bc1sw50qgdz25j", hex: "6002751e" }, 23 | { 24 | text: "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", 25 | hex: "5210751e76e8199196d454941c45d1b3a323", 26 | }, 27 | { 28 | text: "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", 29 | hex: "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", 30 | }, 31 | { 32 | text: "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", 33 | hex: "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", 34 | }, 35 | { 36 | text: "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", 37 | hex: "512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 38 | } 39 | ])("btc address", ({ text, hex }) => { 40 | test(`encode: ${text}`, () => { 41 | expect(encodeBtcAddress(hexToBytes(hex))).toEqual(text); 42 | }); 43 | test(`decode: ${text}`, () => { 44 | expect(decodeBtcAddress(text)).toEqual(hexToBytes(hex)); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | base32CrockfordNormalise, 3 | base32Decode, 4 | base32Encode, 5 | base32UnpaddedDecode, 6 | base32UnpaddedEncode, 7 | } from "./base32.js"; 8 | export { 9 | base58CheckDecode, 10 | base58CheckEncode, 11 | base58UncheckedDecode, 12 | base58UncheckedEncode, 13 | createBase58VersionedDecoder, 14 | createBase58VersionedEncoder, 15 | type Base58CheckVersion, 16 | } from "./base58.js"; 17 | export { 18 | decodeBchAddressToTypeAndHash, 19 | encodeBchAddressWithVersion, 20 | } from "./bch.js"; 21 | export { 22 | createBech32Decoder, 23 | createBech32Encoder, 24 | createBech32SegwitDecoder, 25 | createBech32SegwitEncoder, 26 | createBech32mDecoder, 27 | createBech32mEncoder, 28 | } from "./bech32.js"; 29 | export { 30 | createBitcoinDecoder, 31 | createBitcoinEncoder, 32 | type BitcoinCoderParameters, 33 | } from "./bitcoin.js"; 34 | export { byronDecode, byronEncode } from "./byron.js"; 35 | export { bytesToHex, hexToBytes } from "./bytes.js"; 36 | export { SimpleValue, TaggedValue, cborDecode, cborEncode } from "./cbor.js"; 37 | export { crc32 } from "./crc32.js"; 38 | export { createDotAddressDecoder, createDotAddressEncoder } from "./dot.js"; 39 | export { createEosDecoder, createEosEncoder } from "./eosio.js"; 40 | export { 41 | SLIP44_MSB, 42 | coinTypeToEvmChainId, 43 | evmChainIdToCoinType, 44 | isEvmCoinType, 45 | } from "./evm.js"; 46 | export { validateFlowAddress } from "./flow.js"; 47 | export { 48 | checksumAddress, 49 | createHexChecksummedDecoder, 50 | createHexChecksummedEncoder, 51 | isAddress, 52 | isValidChecksumAddress, 53 | rawChecksumAddress, 54 | stripHexPrefix, 55 | } from "./hex.js"; 56 | export { decodeLeb128, encodeLeb128 } from "./leb128.js"; 57 | export { validateNearAddress } from "./near.js"; 58 | export { createZcashDecoder, createZcashEncoder } from "./zcash.js"; 59 | -------------------------------------------------------------------------------- /src/coin/bch.test.ts: -------------------------------------------------------------------------------- 1 | import { hexToBytes } from "@noble/hashes/utils"; 2 | import { describe, expect, test } from "bun:test"; 3 | import { decodeBchAddress, encodeBchAddress } from "./bch.js"; 4 | 5 | describe.each([ 6 | { 7 | text: "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu", 8 | hex: "76a91476a04053bda0a88bda5177b86a15c3b29f55987388ac", 9 | canonical: "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a", 10 | }, 11 | { 12 | text: "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR", 13 | hex: "76a914cb481232299cd5743151ac4b2d63ae198e7bb0a988ac", 14 | canonical: "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy", 15 | }, 16 | { 17 | text: "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb", 18 | hex: "76a914011f28e473c95f4013d7d53ec5fbc3b42df8ed1088ac", 19 | canonical: "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r", 20 | }, 21 | { 22 | text: "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC", 23 | hex: "a91476a04053bda0a88bda5177b86a15c3b29f55987387", 24 | canonical: "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq", 25 | }, 26 | { 27 | text: "3LDsS579y7sruadqu11beEJoTjdFiFCdX4", 28 | hex: "a914cb481232299cd5743151ac4b2d63ae198e7bb0a987", 29 | canonical: "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e", 30 | }, 31 | { 32 | text: "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw", 33 | hex: "a914011f28e473c95f4013d7d53ec5fbc3b42df8ed1087", 34 | canonical: "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37", 35 | }, 36 | ])("bch address", ({ text, hex, canonical }) => { 37 | test(`encode: ${canonical}`, () => { 38 | expect(encodeBchAddress(hexToBytes(hex))).toEqual(canonical); 39 | }); 40 | test(`decode: ${text}`, () => { 41 | expect(decodeBchAddress(text)).toEqual(hexToBytes(hex)); 42 | }); 43 | test(`decode: ${canonical}`, () => { 44 | expect(decodeBchAddress(canonical)).toEqual(hexToBytes(hex)); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/coin/nim.ts: -------------------------------------------------------------------------------- 1 | import { utils } from "@scure/base"; 2 | import type { CheckedCoin } from "../types.js"; 3 | 4 | const name = "nim"; 5 | const coinType = 242; 6 | 7 | const CCODE = "NQ"; 8 | 9 | const ibanCheck = (data: string): number => { 10 | const num = data 11 | .toUpperCase() 12 | .split("") 13 | .map((c) => { 14 | const code = c.charCodeAt(0); 15 | if (code >= 48 && code <= 57) { 16 | return c; 17 | } else { 18 | return (code - 55).toString(); 19 | } 20 | }) 21 | .join(""); 22 | 23 | let tmp = ""; 24 | for (let i = 0; i < Math.ceil(num.length / 6); i++) { 25 | const a = num.slice(i * 6, i * 6 + 6); 26 | tmp = (parseInt(tmp + a, 10) % 97).toString(); 27 | } 28 | 29 | return parseInt(tmp, 10); 30 | }; 31 | 32 | const nimChecksum = (source: string): string => { 33 | return ("00" + (98 - ibanCheck(source + CCODE + "00"))).slice(-2); 34 | }; 35 | 36 | const base32Nim = utils.chain( 37 | utils.radix2(5), 38 | utils.alphabet("0123456789ABCDEFGHJKLMNPQRSTUVXY"), 39 | utils.padding(5), 40 | utils.join("") 41 | ); 42 | 43 | export const encodeNimAddress = (source: Uint8Array): string => { 44 | const base32Part = base32Nim.encode(source); 45 | const checksummed = nimChecksum(base32Part); 46 | return `${CCODE}${checksummed}${base32Part}`.replace(/.{4}/g, "$& ").trim(); 47 | }; 48 | export const decodeNimAddress = (source: string): Uint8Array => { 49 | if (!source.startsWith(CCODE)) throw new Error("Unrecognised address format"); 50 | 51 | const noWhitespace = source.replace(/ /g, ""); 52 | const checksum = noWhitespace.slice(2, 4); 53 | const base32Part = noWhitespace.slice(4); 54 | 55 | if (checksum !== nimChecksum(base32Part)) 56 | throw new Error("Unrecognised address format"); 57 | 58 | return base32Nim.decode(base32Part); 59 | }; 60 | 61 | export const nim = { 62 | name, 63 | coinType, 64 | encode: encodeNimAddress, 65 | decode: decodeNimAddress, 66 | } as const satisfies CheckedCoin; 67 | --------------------------------------------------------------------------------