@hwlt/era-connect 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +3 -9
  2. package/bch/package.json +6 -0
  3. package/dist/bch-CK6dKGQA.d.ts +74 -0
  4. package/dist/bch-CpLACx0j.cjs +139 -0
  5. package/dist/bch-CpLACx0j.cjs.map +1 -0
  6. package/dist/bch-DHsIVJqK.d.cts +74 -0
  7. package/dist/bch-dMESlQfv.js +134 -0
  8. package/dist/bch-dMESlQfv.js.map +1 -0
  9. package/dist/bch.cjs +13 -0
  10. package/dist/bch.d.cts +26 -0
  11. package/dist/bch.d.ts +26 -0
  12. package/dist/bch.js +4 -0
  13. package/dist/{btc-B_Nq2H4T.js → btc-BmVgvvBd.js} +2 -2
  14. package/dist/btc-BmVgvvBd.js.map +1 -0
  15. package/dist/{btc-ByzjzUKs.cjs → btc-BxNww6kW.cjs} +2 -2
  16. package/dist/btc-BxNww6kW.cjs.map +1 -0
  17. package/dist/{btc-B8LfSEkv.d.ts → btc-uzt0uC-Q.d.ts} +6 -4
  18. package/dist/{btc-C4dVkxHb.d.cts → btc-wogJ4INA.d.cts} +6 -4
  19. package/dist/btc.cjs +1 -1
  20. package/dist/btc.d.cts +1 -1
  21. package/dist/btc.d.ts +1 -1
  22. package/dist/btc.js +1 -1
  23. package/dist/cashaddr-BIzawizX.js +134 -0
  24. package/dist/cashaddr-BIzawizX.js.map +1 -0
  25. package/dist/cashaddr-Ci0_8ITN.cjs +151 -0
  26. package/dist/cashaddr-Ci0_8ITN.cjs.map +1 -0
  27. package/dist/{derive-CK9Nc9Jc.js → derive-00JUOkmn.js} +7 -2
  28. package/dist/derive-00JUOkmn.js.map +1 -0
  29. package/dist/{derive-BayrpfJk.cjs → derive-BuNsfWlg.cjs} +12 -1
  30. package/dist/derive-BuNsfWlg.cjs.map +1 -0
  31. package/dist/gzip-CLzDX7AZ.cjs +79 -0
  32. package/dist/gzip-CLzDX7AZ.cjs.map +1 -0
  33. package/dist/gzip-DyZLtgJJ.js +68 -0
  34. package/dist/gzip-DyZLtgJJ.js.map +1 -0
  35. package/dist/index.cjs +44 -3
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +28 -3
  38. package/dist/index.d.ts +28 -3
  39. package/dist/index.js +43 -4
  40. package/dist/index.js.map +1 -1
  41. package/dist/{messages-DbnXy3dC.js → messages--XbVef_k.js} +38 -2
  42. package/dist/messages--XbVef_k.js.map +1 -0
  43. package/dist/{messages-BZXDY29v.cjs → messages-orAQ52CI.cjs} +43 -1
  44. package/dist/messages-orAQ52CI.cjs.map +1 -0
  45. package/dist/{tron-fufBDNuQ.js → tron-BpUEYTPT.js} +4 -67
  46. package/dist/tron-BpUEYTPT.js.map +1 -0
  47. package/dist/{tron-B9wXdzlA.cjs → tron-DYPOg5cm.cjs} +5 -68
  48. package/dist/{tron-fufBDNuQ.js.map → tron-DYPOg5cm.cjs.map} +1 -1
  49. package/dist/tron.cjs +2 -2
  50. package/dist/tron.js +2 -2
  51. package/dist/verify.cjs +224 -17
  52. package/dist/verify.cjs.map +1 -1
  53. package/dist/verify.d.cts +67 -1
  54. package/dist/verify.d.ts +67 -1
  55. package/dist/verify.js +222 -18
  56. package/dist/verify.js.map +1 -1
  57. package/package.json +13 -1
  58. package/dist/btc-B_Nq2H4T.js.map +0 -1
  59. package/dist/btc-ByzjzUKs.cjs.map +0 -1
  60. package/dist/derive-BayrpfJk.cjs.map +0 -1
  61. package/dist/derive-CK9Nc9Jc.js.map +0 -1
  62. package/dist/messages-BZXDY29v.cjs.map +0 -1
  63. package/dist/messages-DbnXy3dC.js.map +0 -1
  64. package/dist/tron-B9wXdzlA.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"btc-BmVgvvBd.js","names":["UrValue"],"sources":["../src/chains/btc.ts"],"sourcesContent":["import { base64 } from '@scure/base';\nimport { cborDecode } from '../cbor/decode';\nimport { cborEncode } from '../cbor/encode';\nimport type { CborValue } from '../cbor/model';\nimport { asBytes, cbArray, cbBytes, cbMap, cbTag, cbText, cbUint, mapGet } from '../cbor/model';\nimport { asciiDecode } from '../core/bytes';\nimport { EraSdkError } from '../core/errors';\nimport { normalizeRequestId } from '../core/rand';\nimport { keypath304, normalizeXfp, parsePath } from '../registry/keypath';\nimport type { Ur } from '../ur/ur';\nimport { Ur as UrValue } from '../ur/ur';\nimport type { ChainContext, EraConnectConfig, ExpectedReply, SignRequest } from './shared';\nimport {\n makeSignRequest,\n requireReplyMap,\n requireRequestIdEcho,\n requireUrType,\n resolveContext,\n resolveRequestId,\n toUr,\n} from './shared';\n\n/** Bitcoin-family coins the PSBT path signs for (BCH rides its own FORKID envelope: `@hwlt/era-connect/bch`). */\nexport type PsbtCoin = 'btc' | 'ltc' | 'doge' | 'dash';\n\n/** `crypto-psbt-extend` coin ids (the device's own coin-type table). */\nconst PSBT_EXTEND_COIN_ID: Record<Exclude<PsbtCoin, 'btc'>, number> = {\n ltc: 2,\n doge: 3,\n dash: 5,\n};\n\nexport interface BtcPsbtSignRequestProps {\n /** Raw PSBT v0 bytes (BIP-174). The device's signer relies on the global UNSIGNED_TX. */\n readonly psbt: Uint8Array;\n /**\n * `'btc'` (default) rides plain `crypto-psbt`; Litecoin/Dogecoin/Dash ride\n * `crypto-psbt-extend` — the same PSBT plus the coin id, answered in kind.\n * Build the PSBT with the coin's own derivation paths (LTC `m/84'/2'/…`,\n * DOGE `m/44'/3'/…`, DASH `m/44'/5'/…`).\n */\n readonly coin?: PsbtCoin;\n}\n\nexport interface BtcPsbtResult {\n /** The signed, NOT finalized PSBT. Finalize + broadcast with your own stack. */\n readonly psbt: Uint8Array;\n}\n\nexport interface BtcMessageSignRequestProps {\n readonly requestId?: Uint8Array | string;\n readonly message: Uint8Array;\n /** Full signing path, e.g. `m/84'/0'/0'/0/0`. */\n readonly path: string;\n readonly xfp: string | number;\n /**\n * The signing address. Firmware 2.1.0+ signs for BIP-44/49/84 address\n * kinds (Taproot is refused — BIP-137 has no header range for it); OLDER\n * firmware signs legacy P2PKH (`1...`) only and answers a segwit address\n * with `empty-signature`.\n */\n readonly address: string;\n readonly origin?: string;\n}\n\nexport interface BtcMessageSignatureResult {\n readonly requestId: Uint8Array;\n /** Raw 65-byte BIP-137 signature (header + r + s). */\n readonly signature: Uint8Array;\n /** The base64 form dApps and verifiers expect. */\n readonly signatureBase64: string;\n readonly publicKey: Uint8Array | undefined;\n}\n\nconst PSBT_REPLY_TYPES = ['crypto-psbt'] as const;\nconst PSBT_EXTEND_REPLY_TYPES = ['crypto-psbt-extend', 'crypto-psbt'] as const;\nconst MESSAGE_REPLY_TYPES = ['btc-signature'] as const;\n\nexport class BtcChain {\n static readonly DataType = { message: 1 } as const;\n\n private readonly context: ChainContext;\n\n constructor(config?: EraConnectConfig) {\n this.context = resolveContext(config);\n }\n\n /**\n * Build a `crypto-psbt` (310) request: the UR payload is a bare CBOR byte\n * string of the raw PSBT — no map, no request id, no origin.\n *\n * BECAUSE the protocol carries no request id on this path, the reply is\n * bound to the request only by its content: after parsing, compare the\n * returned PSBT's unsigned transaction against the one you sent\n * (`verifySignedPsbt` from `@hwlt/era-connect/verify`). Skipping that\n * check re-opens replay of a stale signed PSBT.\n */\n generatePsbtSignRequest(props: BtcPsbtSignRequestProps): SignRequest<BtcPsbtResult> {\n if (props.psbt.length === 0) {\n throw new EraSdkError('invalid-props', 'psbt must not be empty');\n }\n const coin = props.coin ?? 'btc';\n const ur =\n coin === 'btc'\n ? new UrValue('crypto-psbt', cborEncode(cbBytes(props.psbt)))\n : new UrValue(\n 'crypto-psbt-extend',\n cborEncode(\n cbMap([\n [1, cbBytes(props.psbt)],\n [2, cbUint(PSBT_EXTEND_COIN_ID[coin])],\n ]),\n ),\n );\n return makeSignRequest({\n ur,\n replyTypes: coin === 'btc' ? PSBT_REPLY_TYPES : PSBT_EXTEND_REPLY_TYPES,\n context: this.context,\n parse: (reply) => this.parsePsbt(reply),\n });\n }\n\n /** Parse a `crypto-psbt` / `crypto-psbt-extend` reply: the signed (not finalized) PSBT bytes. */\n parsePsbt(input: Ur | string): BtcPsbtResult {\n const ur = toUr(input);\n requireUrType(ur, [...PSBT_EXTEND_REPLY_TYPES], 'crypto-psbt');\n let decoded: CborValue;\n try {\n decoded = cborDecode(ur.cbor);\n } catch (e) {\n throw new EraSdkError('malformed-cbor', `crypto-psbt reply: ${(e as Error).message}`);\n }\n // Plain form: a bare byte string. Extend form: {1: psbt, 2: coinId}.\n const bytes = asBytes(decoded) ?? asBytes(mapGet(decoded, 1));\n if (!bytes) {\n throw new EraSdkError('malformed-reply', 'crypto-psbt reply is not a byte string');\n }\n if (bytes.length === 0) {\n throw new EraSdkError('malformed-reply', 'crypto-psbt reply is empty');\n }\n return { psbt: bytes };\n }\n\n /** Build a `btc-sign-request` (8101) for message signing. Reply: `btc-signature` (8102). */\n generateMessageSignRequest(\n props: BtcMessageSignRequestProps,\n ): SignRequest<BtcMessageSignatureResult> {\n const requestId = resolveRequestId(this.context, props.requestId);\n const path = parsePath(props.path);\n const xfp = normalizeXfp(props.xfp);\n\n const ur = new UrValue(\n 'btc-sign-request',\n cborEncode(\n cbMap([\n // Tag-37-wrapped on this chain (per-chain firmware policy).\n [1, cbTag(37, cbBytes(requestId))],\n [2, cbBytes(props.message)],\n [3, cbUint(BtcChain.DataType.message)],\n [4, cbArray([keypath304(path, xfp)])],\n [5, cbArray([cbText(props.address)])],\n [6, cbText(props.origin ?? this.context.origin)],\n ]),\n ),\n );\n return makeSignRequest({\n ur,\n requestId,\n replyTypes: MESSAGE_REPLY_TYPES,\n context: this.context,\n parse: (reply) => parseMessageSignature(reply, requestId),\n });\n }\n\n /** Parse a `btc-signature` standalone. Prefer `SignRequest.scanner().parse()`. */\n parseMessageSignature(input: Ur | string, expect?: ExpectedReply): BtcMessageSignatureResult {\n return parseMessageSignature(\n toUr(input),\n expect?.requestId === undefined ? undefined : normalizeRequestId(expect.requestId),\n );\n }\n}\n\nfunction parseMessageSignature(\n ur: Ur,\n expectedRequestId: Uint8Array | undefined,\n): BtcMessageSignatureResult {\n requireUrType(ur, [...MESSAGE_REPLY_TYPES], 'btc-signature');\n const map = requireReplyMap(ur, 'btc-signature');\n const requestId = requireRequestIdEcho(map, 1, expectedRequestId, 'btc-signature');\n\n const sigValue = asBytes(mapGet(map, 2));\n if (sigValue === undefined) {\n throw new EraSdkError('malformed-reply', 'btc-signature is missing the signature (key 2)');\n }\n // The device answers a message request for an address its signer cannot\n // handle with an EMPTY signature. On firmware 2.1.0+ that is Taproot only;\n // older firmware refuses everything but legacy P2PKH this way.\n if (sigValue.length === 0) {\n throw new EraSdkError(\n 'empty-signature',\n 'the device returned an empty signature — the address kind is not message-signable ' +\n 'on this firmware (older firmware: legacy P2PKH only; 2.1.0+: everything but Taproot)',\n );\n }\n\n // Firmware 2.1.0+ sends the raw 65-byte signature; older firmware sends\n // the ASCII of its base64. Accept both: try the double decode first, fall\n // back to the raw bytes.\n let signature: Uint8Array;\n try {\n signature = base64.decode(asciiDecode(sigValue));\n } catch {\n signature = sigValue;\n }\n if (signature.length !== 65) {\n if (sigValue.length === 65) {\n signature = sigValue;\n } else {\n throw new EraSdkError(\n 'malformed-reply',\n `btc-signature payload does not decode to a 65-byte BIP-137 signature (${sigValue.length} bytes on the wire)`,\n );\n }\n }\n return {\n requestId,\n signature,\n signatureBase64: base64.encode(signature),\n publicKey: asBytes(mapGet(map, 3)),\n };\n}\n"],"mappings":"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"}
@@ -84,7 +84,7 @@ function parseMessageSignature(ur, expectedRequestId) {
84
84
  const requestId = require_shared.requireRequestIdEcho(map, 1, expectedRequestId, "btc-signature");
85
85
  const sigValue = require_shared.asBytes(require_shared.mapGet(map, 2));
86
86
  if (sigValue === void 0) throw new require_shared.EraSdkError("malformed-reply", "btc-signature is missing the signature (key 2)");
87
- if (sigValue.length === 0) throw new require_shared.EraSdkError("empty-signature", "the device returned an empty signature — Bitcoin message signing supports legacy P2PKH (1...) addresses only");
87
+ if (sigValue.length === 0) throw new require_shared.EraSdkError("empty-signature", "the device returned an empty signature — the address kind is not message-signable on this firmware (older firmware: legacy P2PKH only; 2.1.0+: everything but Taproot)");
88
88
  let signature;
89
89
  try {
90
90
  signature = _scure_base.base64.decode(require_shared.asciiDecode(sigValue));
@@ -110,4 +110,4 @@ Object.defineProperty(exports, "BtcChain", {
110
110
  }
111
111
  });
112
112
 
113
- //# sourceMappingURL=btc-ByzjzUKs.cjs.map
113
+ //# sourceMappingURL=btc-BxNww6kW.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"btc-BxNww6kW.cjs","names":["resolveContext","EraSdkError","UrValue","cborEncode","cbBytes","cbMap","cbUint","makeSignRequest","toUr","cborDecode","asBytes","mapGet","resolveRequestId","parsePath","normalizeXfp","cbTag","cbArray","keypath304","cbText","normalizeRequestId","requireReplyMap","requireRequestIdEcho","base64","asciiDecode"],"sources":["../src/chains/btc.ts"],"sourcesContent":["import { base64 } from '@scure/base';\nimport { cborDecode } from '../cbor/decode';\nimport { cborEncode } from '../cbor/encode';\nimport type { CborValue } from '../cbor/model';\nimport { asBytes, cbArray, cbBytes, cbMap, cbTag, cbText, cbUint, mapGet } from '../cbor/model';\nimport { asciiDecode } from '../core/bytes';\nimport { EraSdkError } from '../core/errors';\nimport { normalizeRequestId } from '../core/rand';\nimport { keypath304, normalizeXfp, parsePath } from '../registry/keypath';\nimport type { Ur } from '../ur/ur';\nimport { Ur as UrValue } from '../ur/ur';\nimport type { ChainContext, EraConnectConfig, ExpectedReply, SignRequest } from './shared';\nimport {\n makeSignRequest,\n requireReplyMap,\n requireRequestIdEcho,\n requireUrType,\n resolveContext,\n resolveRequestId,\n toUr,\n} from './shared';\n\n/** Bitcoin-family coins the PSBT path signs for (BCH rides its own FORKID envelope: `@hwlt/era-connect/bch`). */\nexport type PsbtCoin = 'btc' | 'ltc' | 'doge' | 'dash';\n\n/** `crypto-psbt-extend` coin ids (the device's own coin-type table). */\nconst PSBT_EXTEND_COIN_ID: Record<Exclude<PsbtCoin, 'btc'>, number> = {\n ltc: 2,\n doge: 3,\n dash: 5,\n};\n\nexport interface BtcPsbtSignRequestProps {\n /** Raw PSBT v0 bytes (BIP-174). The device's signer relies on the global UNSIGNED_TX. */\n readonly psbt: Uint8Array;\n /**\n * `'btc'` (default) rides plain `crypto-psbt`; Litecoin/Dogecoin/Dash ride\n * `crypto-psbt-extend` — the same PSBT plus the coin id, answered in kind.\n * Build the PSBT with the coin's own derivation paths (LTC `m/84'/2'/…`,\n * DOGE `m/44'/3'/…`, DASH `m/44'/5'/…`).\n */\n readonly coin?: PsbtCoin;\n}\n\nexport interface BtcPsbtResult {\n /** The signed, NOT finalized PSBT. Finalize + broadcast with your own stack. */\n readonly psbt: Uint8Array;\n}\n\nexport interface BtcMessageSignRequestProps {\n readonly requestId?: Uint8Array | string;\n readonly message: Uint8Array;\n /** Full signing path, e.g. `m/84'/0'/0'/0/0`. */\n readonly path: string;\n readonly xfp: string | number;\n /**\n * The signing address. Firmware 2.1.0+ signs for BIP-44/49/84 address\n * kinds (Taproot is refused — BIP-137 has no header range for it); OLDER\n * firmware signs legacy P2PKH (`1...`) only and answers a segwit address\n * with `empty-signature`.\n */\n readonly address: string;\n readonly origin?: string;\n}\n\nexport interface BtcMessageSignatureResult {\n readonly requestId: Uint8Array;\n /** Raw 65-byte BIP-137 signature (header + r + s). */\n readonly signature: Uint8Array;\n /** The base64 form dApps and verifiers expect. */\n readonly signatureBase64: string;\n readonly publicKey: Uint8Array | undefined;\n}\n\nconst PSBT_REPLY_TYPES = ['crypto-psbt'] as const;\nconst PSBT_EXTEND_REPLY_TYPES = ['crypto-psbt-extend', 'crypto-psbt'] as const;\nconst MESSAGE_REPLY_TYPES = ['btc-signature'] as const;\n\nexport class BtcChain {\n static readonly DataType = { message: 1 } as const;\n\n private readonly context: ChainContext;\n\n constructor(config?: EraConnectConfig) {\n this.context = resolveContext(config);\n }\n\n /**\n * Build a `crypto-psbt` (310) request: the UR payload is a bare CBOR byte\n * string of the raw PSBT — no map, no request id, no origin.\n *\n * BECAUSE the protocol carries no request id on this path, the reply is\n * bound to the request only by its content: after parsing, compare the\n * returned PSBT's unsigned transaction against the one you sent\n * (`verifySignedPsbt` from `@hwlt/era-connect/verify`). Skipping that\n * check re-opens replay of a stale signed PSBT.\n */\n generatePsbtSignRequest(props: BtcPsbtSignRequestProps): SignRequest<BtcPsbtResult> {\n if (props.psbt.length === 0) {\n throw new EraSdkError('invalid-props', 'psbt must not be empty');\n }\n const coin = props.coin ?? 'btc';\n const ur =\n coin === 'btc'\n ? new UrValue('crypto-psbt', cborEncode(cbBytes(props.psbt)))\n : new UrValue(\n 'crypto-psbt-extend',\n cborEncode(\n cbMap([\n [1, cbBytes(props.psbt)],\n [2, cbUint(PSBT_EXTEND_COIN_ID[coin])],\n ]),\n ),\n );\n return makeSignRequest({\n ur,\n replyTypes: coin === 'btc' ? PSBT_REPLY_TYPES : PSBT_EXTEND_REPLY_TYPES,\n context: this.context,\n parse: (reply) => this.parsePsbt(reply),\n });\n }\n\n /** Parse a `crypto-psbt` / `crypto-psbt-extend` reply: the signed (not finalized) PSBT bytes. */\n parsePsbt(input: Ur | string): BtcPsbtResult {\n const ur = toUr(input);\n requireUrType(ur, [...PSBT_EXTEND_REPLY_TYPES], 'crypto-psbt');\n let decoded: CborValue;\n try {\n decoded = cborDecode(ur.cbor);\n } catch (e) {\n throw new EraSdkError('malformed-cbor', `crypto-psbt reply: ${(e as Error).message}`);\n }\n // Plain form: a bare byte string. Extend form: {1: psbt, 2: coinId}.\n const bytes = asBytes(decoded) ?? asBytes(mapGet(decoded, 1));\n if (!bytes) {\n throw new EraSdkError('malformed-reply', 'crypto-psbt reply is not a byte string');\n }\n if (bytes.length === 0) {\n throw new EraSdkError('malformed-reply', 'crypto-psbt reply is empty');\n }\n return { psbt: bytes };\n }\n\n /** Build a `btc-sign-request` (8101) for message signing. Reply: `btc-signature` (8102). */\n generateMessageSignRequest(\n props: BtcMessageSignRequestProps,\n ): SignRequest<BtcMessageSignatureResult> {\n const requestId = resolveRequestId(this.context, props.requestId);\n const path = parsePath(props.path);\n const xfp = normalizeXfp(props.xfp);\n\n const ur = new UrValue(\n 'btc-sign-request',\n cborEncode(\n cbMap([\n // Tag-37-wrapped on this chain (per-chain firmware policy).\n [1, cbTag(37, cbBytes(requestId))],\n [2, cbBytes(props.message)],\n [3, cbUint(BtcChain.DataType.message)],\n [4, cbArray([keypath304(path, xfp)])],\n [5, cbArray([cbText(props.address)])],\n [6, cbText(props.origin ?? this.context.origin)],\n ]),\n ),\n );\n return makeSignRequest({\n ur,\n requestId,\n replyTypes: MESSAGE_REPLY_TYPES,\n context: this.context,\n parse: (reply) => parseMessageSignature(reply, requestId),\n });\n }\n\n /** Parse a `btc-signature` standalone. Prefer `SignRequest.scanner().parse()`. */\n parseMessageSignature(input: Ur | string, expect?: ExpectedReply): BtcMessageSignatureResult {\n return parseMessageSignature(\n toUr(input),\n expect?.requestId === undefined ? undefined : normalizeRequestId(expect.requestId),\n );\n }\n}\n\nfunction parseMessageSignature(\n ur: Ur,\n expectedRequestId: Uint8Array | undefined,\n): BtcMessageSignatureResult {\n requireUrType(ur, [...MESSAGE_REPLY_TYPES], 'btc-signature');\n const map = requireReplyMap(ur, 'btc-signature');\n const requestId = requireRequestIdEcho(map, 1, expectedRequestId, 'btc-signature');\n\n const sigValue = asBytes(mapGet(map, 2));\n if (sigValue === undefined) {\n throw new EraSdkError('malformed-reply', 'btc-signature is missing the signature (key 2)');\n }\n // The device answers a message request for an address its signer cannot\n // handle with an EMPTY signature. On firmware 2.1.0+ that is Taproot only;\n // older firmware refuses everything but legacy P2PKH this way.\n if (sigValue.length === 0) {\n throw new EraSdkError(\n 'empty-signature',\n 'the device returned an empty signature — the address kind is not message-signable ' +\n 'on this firmware (older firmware: legacy P2PKH only; 2.1.0+: everything but Taproot)',\n );\n }\n\n // Firmware 2.1.0+ sends the raw 65-byte signature; older firmware sends\n // the ASCII of its base64. Accept both: try the double decode first, fall\n // back to the raw bytes.\n let signature: Uint8Array;\n try {\n signature = base64.decode(asciiDecode(sigValue));\n } catch {\n signature = sigValue;\n }\n if (signature.length !== 65) {\n if (sigValue.length === 65) {\n signature = sigValue;\n } else {\n throw new EraSdkError(\n 'malformed-reply',\n `btc-signature payload does not decode to a 65-byte BIP-137 signature (${sigValue.length} bytes on the wire)`,\n );\n }\n }\n return {\n requestId,\n signature,\n signatureBase64: base64.encode(signature),\n publicKey: asBytes(mapGet(map, 3)),\n };\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { _ as Ur, a as SignRequest, i as ExpectedReply, r as EraConnectConfig } from "./shared-CI8zhQiE.js";
2
2
  //#region src/chains/btc.d.ts
3
- /** Bitcoin-family coins the PSBT path signs for (BCH needs the FORKID signer and is not offered here). */
3
+ /** Bitcoin-family coins the PSBT path signs for (BCH rides its own FORKID envelope: `@hwlt/era-connect/bch`). */
4
4
  type PsbtCoin = 'btc' | 'ltc' | 'doge' | 'dash';
5
5
  interface BtcPsbtSignRequestProps {
6
6
  /** Raw PSBT v0 bytes (BIP-174). The device's signer relies on the global UNSIGNED_TX. */
@@ -24,8 +24,10 @@ interface BtcMessageSignRequestProps {
24
24
  readonly path: string;
25
25
  readonly xfp: string | number;
26
26
  /**
27
- * The signing address. The device's message signer supports LEGACY P2PKH
28
- * (`1...`) addresses only; a segwit address yields `empty-signature`.
27
+ * The signing address. Firmware 2.1.0+ signs for BIP-44/49/84 address
28
+ * kinds (Taproot is refused BIP-137 has no header range for it); OLDER
29
+ * firmware signs legacy P2PKH (`1...`) only and answers a segwit address
30
+ * with `empty-signature`.
29
31
  */
30
32
  readonly address: string;
31
33
  readonly origin?: string;
@@ -64,4 +66,4 @@ declare class BtcChain {
64
66
  }
65
67
  //#endregion
66
68
  export { BtcPsbtSignRequestProps as a, BtcPsbtResult as i, BtcMessageSignRequestProps as n, BtcMessageSignatureResult as r, BtcChain as t };
67
- //# sourceMappingURL=btc-B8LfSEkv.d.ts.map
69
+ //# sourceMappingURL=btc-uzt0uC-Q.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import { _ as Ur, a as SignRequest, i as ExpectedReply, r as EraConnectConfig } from "./shared-CI8zhQiE.cjs";
2
2
  //#region src/chains/btc.d.ts
3
- /** Bitcoin-family coins the PSBT path signs for (BCH needs the FORKID signer and is not offered here). */
3
+ /** Bitcoin-family coins the PSBT path signs for (BCH rides its own FORKID envelope: `@hwlt/era-connect/bch`). */
4
4
  type PsbtCoin = 'btc' | 'ltc' | 'doge' | 'dash';
5
5
  interface BtcPsbtSignRequestProps {
6
6
  /** Raw PSBT v0 bytes (BIP-174). The device's signer relies on the global UNSIGNED_TX. */
@@ -24,8 +24,10 @@ interface BtcMessageSignRequestProps {
24
24
  readonly path: string;
25
25
  readonly xfp: string | number;
26
26
  /**
27
- * The signing address. The device's message signer supports LEGACY P2PKH
28
- * (`1...`) addresses only; a segwit address yields `empty-signature`.
27
+ * The signing address. Firmware 2.1.0+ signs for BIP-44/49/84 address
28
+ * kinds (Taproot is refused BIP-137 has no header range for it); OLDER
29
+ * firmware signs legacy P2PKH (`1...`) only and answers a segwit address
30
+ * with `empty-signature`.
29
31
  */
30
32
  readonly address: string;
31
33
  readonly origin?: string;
@@ -64,4 +66,4 @@ declare class BtcChain {
64
66
  }
65
67
  //#endregion
66
68
  export { BtcPsbtSignRequestProps as a, BtcPsbtResult as i, BtcMessageSignRequestProps as n, BtcMessageSignatureResult as r, BtcChain as t };
67
- //# sourceMappingURL=btc-C4dVkxHb.d.cts.map
69
+ //# sourceMappingURL=btc-wogJ4INA.d.cts.map
package/dist/btc.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_shared = require("./shared-Cu2yynP9.cjs");
3
- const require_btc = require("./btc-ByzjzUKs.cjs");
3
+ const require_btc = require("./btc-BxNww6kW.cjs");
4
4
  exports.AnimatedUr = require_shared.AnimatedUr;
5
5
  exports.BtcChain = require_btc.BtcChain;
6
6
  exports.EraSdkError = require_shared.EraSdkError;
package/dist/btc.d.cts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { _ as Ur, a as SignRequest, c as TypedUrScanner, d as EraErrorCode, f as EraSdkError, i as ExpectedReply, l as UrScanner, p as AnimatedUr, r as EraConnectConfig } from "./shared-CI8zhQiE.cjs";
2
- import { a as BtcPsbtSignRequestProps, i as BtcPsbtResult, n as BtcMessageSignRequestProps, r as BtcMessageSignatureResult, t as BtcChain } from "./btc-C4dVkxHb.cjs";
2
+ import { a as BtcPsbtSignRequestProps, i as BtcPsbtResult, n as BtcMessageSignRequestProps, r as BtcMessageSignatureResult, t as BtcChain } from "./btc-wogJ4INA.cjs";
3
3
  export { AnimatedUr, BtcChain, type BtcMessageSignRequestProps, type BtcMessageSignatureResult, type BtcPsbtResult, type BtcPsbtSignRequestProps, type EraConnectConfig, type EraErrorCode, EraSdkError, type ExpectedReply, type SignRequest, TypedUrScanner, Ur, UrScanner };
package/dist/btc.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { _ as Ur, a as SignRequest, c as TypedUrScanner, d as EraErrorCode, f as EraSdkError, i as ExpectedReply, l as UrScanner, p as AnimatedUr, r as EraConnectConfig } from "./shared-CI8zhQiE.js";
2
- import { a as BtcPsbtSignRequestProps, i as BtcPsbtResult, n as BtcMessageSignRequestProps, r as BtcMessageSignatureResult, t as BtcChain } from "./btc-B8LfSEkv.js";
2
+ import { a as BtcPsbtSignRequestProps, i as BtcPsbtResult, n as BtcMessageSignRequestProps, r as BtcMessageSignatureResult, t as BtcChain } from "./btc-uzt0uC-Q.js";
3
3
  export { AnimatedUr, BtcChain, type BtcMessageSignRequestProps, type BtcMessageSignatureResult, type BtcPsbtResult, type BtcPsbtSignRequestProps, type EraConnectConfig, type EraErrorCode, EraSdkError, type ExpectedReply, type SignRequest, TypedUrScanner, Ur, UrScanner };
package/dist/btc.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { K as EraSdkError, _ as Ur, d as UrScanner, f as AnimatedUr, u as TypedUrScanner } from "./shared-Bbgejrus.js";
2
- import { t as BtcChain } from "./btc-B_Nq2H4T.js";
2
+ import { t as BtcChain } from "./btc-BmVgvvBd.js";
3
3
  export { AnimatedUr, BtcChain, EraSdkError, TypedUrScanner, Ur, UrScanner };
@@ -0,0 +1,134 @@
1
+ import { K as EraSdkError } from "./shared-Bbgejrus.js";
2
+ //#region src/chains/cashaddr.ts
3
+ /**
4
+ * CashAddr codec (the Bitcoin Cash address format).
5
+ *
6
+ * Not bech32: the checksum is a 40-bit BCH code over its own generator set,
7
+ * and there is no separator-position rule — the payload is everything after
8
+ * the optional `prefix:`. The decoder takes every spec-legal spelling
9
+ * (bare, prefixed, all-uppercase; mixed case refused), but the DEVICE's own
10
+ * parser reads only the lowercase form — its prefix rebuild turns an
11
+ * uppercase body into a mixed-case string it then rejects, and the refusal
12
+ * fails open into a zero hash. Anything that goes on the wire must therefore
13
+ * be re-encoded through `encodeCashAddr`, never passed through verbatim;
14
+ * `BchChain.generateSignRequest` does exactly that.
15
+ */
16
+ const CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
17
+ const CHARSET_REV = new Map([...CHARSET].map((c, i) => [c, i]));
18
+ /** The default (mainnet) human-readable prefix. */
19
+ const CASHADDR_PREFIX = "bitcoincash";
20
+ const GENERATOR = [
21
+ 656907472481n,
22
+ 522768456162n,
23
+ 1044723512260n,
24
+ 748107326120n,
25
+ 130178868336n
26
+ ];
27
+ function polymod(values) {
28
+ let c = 1n;
29
+ for (const d of values) {
30
+ const c0 = c >> 35n;
31
+ c = (c & 34359738367n) << 5n ^ BigInt(d);
32
+ for (let i = 0; i < 5; i++) if (c0 >> BigInt(i) & 1n) c ^= GENERATOR[i];
33
+ }
34
+ return c ^ 1n;
35
+ }
36
+ /** Prefix expansion: the low five bits of each character, then a zero. */
37
+ function expandPrefix(prefix) {
38
+ const out = [];
39
+ for (const ch of prefix) out.push(ch.charCodeAt(0) & 31);
40
+ out.push(0);
41
+ return out;
42
+ }
43
+ function convertBits(data, from, to, pad) {
44
+ let acc = 0;
45
+ let bits = 0;
46
+ const out = [];
47
+ const maxv = (1 << to) - 1;
48
+ for (const value of data) {
49
+ if (value < 0 || value >> from !== 0) throw new EraSdkError("invalid-props", "cashaddr: value out of range");
50
+ acc = acc << from | value;
51
+ bits += from;
52
+ while (bits >= to) {
53
+ bits -= to;
54
+ out.push(acc >> bits & maxv);
55
+ }
56
+ }
57
+ if (pad) {
58
+ if (bits > 0) out.push(acc << to - bits & maxv);
59
+ } else if (bits >= from || (acc << to - bits & maxv) !== 0) throw new EraSdkError("invalid-props", "cashaddr: invalid padding");
60
+ return out;
61
+ }
62
+ /**
63
+ * Decode a CashAddr, with or without its `prefix:`. Only 20-byte P2PKH and
64
+ * P2SH payloads are accepted — those are the two script kinds the device
65
+ * builds outputs for.
66
+ */
67
+ function decodeCashAddr(address, expectedPrefix = CASHADDR_PREFIX) {
68
+ if (!/^[0-9A-Za-z:]*$/.test(address)) throw new EraSdkError("invalid-props", "cashaddr: invalid character");
69
+ const hasUpper = /[A-Z]/.test(address);
70
+ const hasLower = /[a-z]/.test(address);
71
+ if (hasUpper && hasLower) throw new EraSdkError("invalid-props", "cashaddr: mixed-case address refused");
72
+ const lower = address.toLowerCase();
73
+ const colon = lower.lastIndexOf(":");
74
+ const prefix = colon === -1 ? expectedPrefix : lower.slice(0, colon);
75
+ const payload = colon === -1 ? lower : lower.slice(colon + 1);
76
+ if (prefix !== expectedPrefix) throw new EraSdkError("invalid-props", `cashaddr: prefix "${prefix}" does not match expected "${expectedPrefix}"`);
77
+ if (payload.length < 9) throw new EraSdkError("invalid-props", "cashaddr: payload too short");
78
+ const values = [];
79
+ for (const ch of payload) {
80
+ const v = CHARSET_REV.get(ch);
81
+ if (v === void 0) throw new EraSdkError("invalid-props", `cashaddr: invalid character "${ch}"`);
82
+ values.push(v);
83
+ }
84
+ if (polymod([...expandPrefix(prefix), ...values]) !== 0n) throw new EraSdkError("invalid-props", "cashaddr: checksum mismatch");
85
+ const data = convertBits(values.slice(0, -8), 5, 8, false);
86
+ if (data.length === 0) throw new EraSdkError("invalid-props", "cashaddr: empty payload");
87
+ const version = data[0];
88
+ if (version & 128) throw new EraSdkError("invalid-props", "cashaddr: reserved version bit set");
89
+ const typeBits = version >> 3 & 15;
90
+ const hashBits = [
91
+ 160,
92
+ 192,
93
+ 224,
94
+ 256,
95
+ 320,
96
+ 384,
97
+ 448,
98
+ 512
99
+ ][version & 7];
100
+ if (data.length - 1 !== hashBits / 8) throw new EraSdkError("invalid-props", "cashaddr: hash length does not match version byte");
101
+ if (typeBits !== 0 && typeBits !== 1) throw new EraSdkError("invalid-props", `cashaddr: unsupported address type ${typeBits}`);
102
+ if (hashBits !== 160) throw new EraSdkError("invalid-props", "cashaddr: only 20-byte hashes are supported");
103
+ return {
104
+ type: typeBits === 0 ? "p2pkh" : "p2sh",
105
+ hash: Uint8Array.from(data.slice(1)),
106
+ prefix
107
+ };
108
+ }
109
+ /** Encode a 20-byte hash160 as a CashAddr. Returns the bare form by default. */
110
+ function encodeCashAddr(type, hash, options) {
111
+ if (hash.length !== 20) throw new EraSdkError("invalid-props", "cashaddr: hash must be 20 bytes");
112
+ const prefix = options?.prefix ?? "bitcoincash";
113
+ const payload = convertBits([type === "p2pkh" ? 0 : 8, ...hash], 8, 5, true);
114
+ const checksum = polymod([
115
+ ...expandPrefix(prefix),
116
+ ...payload,
117
+ 0,
118
+ 0,
119
+ 0,
120
+ 0,
121
+ 0,
122
+ 0,
123
+ 0,
124
+ 0
125
+ ]);
126
+ const suffix = [];
127
+ for (let i = 7; i >= 0; i--) suffix.push(Number(checksum >> BigInt(5 * i) & 31n));
128
+ const body = [...payload, ...suffix].map((v) => CHARSET[v]).join("");
129
+ return options?.withPrefix ? `${prefix}:${body}` : body;
130
+ }
131
+ //#endregion
132
+ export { decodeCashAddr as n, encodeCashAddr as r, CASHADDR_PREFIX as t };
133
+
134
+ //# sourceMappingURL=cashaddr-BIzawizX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cashaddr-BIzawizX.js","names":[],"sources":["../src/chains/cashaddr.ts"],"sourcesContent":["import { EraSdkError } from '../core/errors';\n\n/**\n * CashAddr codec (the Bitcoin Cash address format).\n *\n * Not bech32: the checksum is a 40-bit BCH code over its own generator set,\n * and there is no separator-position rule — the payload is everything after\n * the optional `prefix:`. The decoder takes every spec-legal spelling\n * (bare, prefixed, all-uppercase; mixed case refused), but the DEVICE's own\n * parser reads only the lowercase form — its prefix rebuild turns an\n * uppercase body into a mixed-case string it then rejects, and the refusal\n * fails open into a zero hash. Anything that goes on the wire must therefore\n * be re-encoded through `encodeCashAddr`, never passed through verbatim;\n * `BchChain.generateSignRequest` does exactly that.\n */\n\nconst CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';\n\nconst CHARSET_REV = new Map<string, number>([...CHARSET].map((c, i) => [c, i]));\n\n/** The default (mainnet) human-readable prefix. */\nexport const CASHADDR_PREFIX = 'bitcoincash';\n\nconst GENERATOR = [0x98f2bc8e61n, 0x79b76d99e2n, 0xf33e5fb3c4n, 0xae2eabe2a8n, 0x1e4f43e470n];\n\nfunction polymod(values: readonly number[]): bigint {\n let c = 1n;\n for (const d of values) {\n const c0 = c >> 35n;\n c = ((c & 0x07ffffffffn) << 5n) ^ BigInt(d);\n for (let i = 0; i < 5; i++) {\n if ((c0 >> BigInt(i)) & 1n) c ^= GENERATOR[i]!;\n }\n }\n return c ^ 1n;\n}\n\n/** Prefix expansion: the low five bits of each character, then a zero. */\nfunction expandPrefix(prefix: string): number[] {\n const out: number[] = [];\n for (const ch of prefix) out.push(ch.charCodeAt(0) & 0x1f);\n out.push(0);\n return out;\n}\n\nfunction convertBits(data: readonly number[], from: number, to: number, pad: boolean): number[] {\n let acc = 0;\n let bits = 0;\n const out: number[] = [];\n const maxv = (1 << to) - 1;\n for (const value of data) {\n if (value < 0 || value >> from !== 0) {\n throw new EraSdkError('invalid-props', 'cashaddr: value out of range');\n }\n acc = (acc << from) | value;\n bits += from;\n while (bits >= to) {\n bits -= to;\n out.push((acc >> bits) & maxv);\n }\n }\n if (pad) {\n if (bits > 0) out.push((acc << (to - bits)) & maxv);\n } else if (bits >= from || ((acc << (to - bits)) & maxv) !== 0) {\n throw new EraSdkError('invalid-props', 'cashaddr: invalid padding');\n }\n return out;\n}\n\nexport type CashAddrType = 'p2pkh' | 'p2sh';\n\nexport interface CashAddrPayload {\n readonly type: CashAddrType;\n /** The 20-byte hash160. */\n readonly hash: Uint8Array;\n readonly prefix: string;\n}\n\n/**\n * Decode a CashAddr, with or without its `prefix:`. Only 20-byte P2PKH and\n * P2SH payloads are accepted — those are the two script kinds the device\n * builds outputs for.\n */\nexport function decodeCashAddr(address: string, expectedPrefix = CASHADDR_PREFIX): CashAddrPayload {\n // ASCII only, before any case folding: the case guard below is ASCII-scoped,\n // while String.toLowerCase folds full Unicode — U+212A KELVIN SIGN would\n // otherwise slip past the guard and fold into a charset 'k'.\n if (!/^[0-9A-Za-z:]*$/.test(address)) {\n throw new EraSdkError('invalid-props', 'cashaddr: invalid character');\n }\n const hasUpper = /[A-Z]/.test(address);\n const hasLower = /[a-z]/.test(address);\n if (hasUpper && hasLower) {\n throw new EraSdkError('invalid-props', 'cashaddr: mixed-case address refused');\n }\n const lower = address.toLowerCase();\n const colon = lower.lastIndexOf(':');\n const prefix = colon === -1 ? expectedPrefix : lower.slice(0, colon);\n const payload = colon === -1 ? lower : lower.slice(colon + 1);\n if (prefix !== expectedPrefix) {\n throw new EraSdkError(\n 'invalid-props',\n `cashaddr: prefix \"${prefix}\" does not match expected \"${expectedPrefix}\"`,\n );\n }\n if (payload.length < 8 + 1) {\n throw new EraSdkError('invalid-props', 'cashaddr: payload too short');\n }\n const values: number[] = [];\n for (const ch of payload) {\n const v = CHARSET_REV.get(ch);\n if (v === undefined) {\n throw new EraSdkError('invalid-props', `cashaddr: invalid character \"${ch}\"`);\n }\n values.push(v);\n }\n if (polymod([...expandPrefix(prefix), ...values]) !== 0n) {\n throw new EraSdkError('invalid-props', 'cashaddr: checksum mismatch');\n }\n const data = convertBits(values.slice(0, -8), 5, 8, false);\n if (data.length === 0) {\n throw new EraSdkError('invalid-props', 'cashaddr: empty payload');\n }\n const version = data[0]!;\n if (version & 0x80) {\n throw new EraSdkError('invalid-props', 'cashaddr: reserved version bit set');\n }\n const typeBits = (version >> 3) & 0x0f;\n const sizeBits = version & 0x07;\n const HASH_SIZES = [160, 192, 224, 256, 320, 384, 448, 512];\n const hashBits = HASH_SIZES[sizeBits]!;\n if (data.length - 1 !== hashBits / 8) {\n throw new EraSdkError('invalid-props', 'cashaddr: hash length does not match version byte');\n }\n if (typeBits !== 0 && typeBits !== 1) {\n throw new EraSdkError('invalid-props', `cashaddr: unsupported address type ${typeBits}`);\n }\n if (hashBits !== 160) {\n throw new EraSdkError('invalid-props', 'cashaddr: only 20-byte hashes are supported');\n }\n return {\n type: typeBits === 0 ? 'p2pkh' : 'p2sh',\n hash: Uint8Array.from(data.slice(1)),\n prefix,\n };\n}\n\n/** Encode a 20-byte hash160 as a CashAddr. Returns the bare form by default. */\nexport function encodeCashAddr(\n type: CashAddrType,\n hash: Uint8Array,\n options?: { prefix?: string | undefined; withPrefix?: boolean | undefined },\n): string {\n if (hash.length !== 20) {\n throw new EraSdkError('invalid-props', 'cashaddr: hash must be 20 bytes');\n }\n const prefix = options?.prefix ?? CASHADDR_PREFIX;\n const version = type === 'p2pkh' ? 0 : 8; // typeBits<<3 | sizeBits(160 -> 0)\n const payload = convertBits([version, ...hash], 8, 5, true);\n const checksumInput = [...expandPrefix(prefix), ...payload, 0, 0, 0, 0, 0, 0, 0, 0];\n const checksum = polymod(checksumInput);\n const suffix: number[] = [];\n for (let i = 7; i >= 0; i--) suffix.push(Number((checksum >> BigInt(5 * i)) & 0x1fn));\n const body = [...payload, ...suffix].map((v) => CHARSET[v]!).join('');\n return options?.withPrefix ? `${prefix}:${body}` : body;\n}\n"],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ const require_shared = require("./shared-Cu2yynP9.cjs");
2
+ //#region src/chains/cashaddr.ts
3
+ /**
4
+ * CashAddr codec (the Bitcoin Cash address format).
5
+ *
6
+ * Not bech32: the checksum is a 40-bit BCH code over its own generator set,
7
+ * and there is no separator-position rule — the payload is everything after
8
+ * the optional `prefix:`. The decoder takes every spec-legal spelling
9
+ * (bare, prefixed, all-uppercase; mixed case refused), but the DEVICE's own
10
+ * parser reads only the lowercase form — its prefix rebuild turns an
11
+ * uppercase body into a mixed-case string it then rejects, and the refusal
12
+ * fails open into a zero hash. Anything that goes on the wire must therefore
13
+ * be re-encoded through `encodeCashAddr`, never passed through verbatim;
14
+ * `BchChain.generateSignRequest` does exactly that.
15
+ */
16
+ const CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
17
+ const CHARSET_REV = new Map([...CHARSET].map((c, i) => [c, i]));
18
+ /** The default (mainnet) human-readable prefix. */
19
+ const CASHADDR_PREFIX = "bitcoincash";
20
+ const GENERATOR = [
21
+ 656907472481n,
22
+ 522768456162n,
23
+ 1044723512260n,
24
+ 748107326120n,
25
+ 130178868336n
26
+ ];
27
+ function polymod(values) {
28
+ let c = 1n;
29
+ for (const d of values) {
30
+ const c0 = c >> 35n;
31
+ c = (c & 34359738367n) << 5n ^ BigInt(d);
32
+ for (let i = 0; i < 5; i++) if (c0 >> BigInt(i) & 1n) c ^= GENERATOR[i];
33
+ }
34
+ return c ^ 1n;
35
+ }
36
+ /** Prefix expansion: the low five bits of each character, then a zero. */
37
+ function expandPrefix(prefix) {
38
+ const out = [];
39
+ for (const ch of prefix) out.push(ch.charCodeAt(0) & 31);
40
+ out.push(0);
41
+ return out;
42
+ }
43
+ function convertBits(data, from, to, pad) {
44
+ let acc = 0;
45
+ let bits = 0;
46
+ const out = [];
47
+ const maxv = (1 << to) - 1;
48
+ for (const value of data) {
49
+ if (value < 0 || value >> from !== 0) throw new require_shared.EraSdkError("invalid-props", "cashaddr: value out of range");
50
+ acc = acc << from | value;
51
+ bits += from;
52
+ while (bits >= to) {
53
+ bits -= to;
54
+ out.push(acc >> bits & maxv);
55
+ }
56
+ }
57
+ if (pad) {
58
+ if (bits > 0) out.push(acc << to - bits & maxv);
59
+ } else if (bits >= from || (acc << to - bits & maxv) !== 0) throw new require_shared.EraSdkError("invalid-props", "cashaddr: invalid padding");
60
+ return out;
61
+ }
62
+ /**
63
+ * Decode a CashAddr, with or without its `prefix:`. Only 20-byte P2PKH and
64
+ * P2SH payloads are accepted — those are the two script kinds the device
65
+ * builds outputs for.
66
+ */
67
+ function decodeCashAddr(address, expectedPrefix = CASHADDR_PREFIX) {
68
+ if (!/^[0-9A-Za-z:]*$/.test(address)) throw new require_shared.EraSdkError("invalid-props", "cashaddr: invalid character");
69
+ const hasUpper = /[A-Z]/.test(address);
70
+ const hasLower = /[a-z]/.test(address);
71
+ if (hasUpper && hasLower) throw new require_shared.EraSdkError("invalid-props", "cashaddr: mixed-case address refused");
72
+ const lower = address.toLowerCase();
73
+ const colon = lower.lastIndexOf(":");
74
+ const prefix = colon === -1 ? expectedPrefix : lower.slice(0, colon);
75
+ const payload = colon === -1 ? lower : lower.slice(colon + 1);
76
+ if (prefix !== expectedPrefix) throw new require_shared.EraSdkError("invalid-props", `cashaddr: prefix "${prefix}" does not match expected "${expectedPrefix}"`);
77
+ if (payload.length < 9) throw new require_shared.EraSdkError("invalid-props", "cashaddr: payload too short");
78
+ const values = [];
79
+ for (const ch of payload) {
80
+ const v = CHARSET_REV.get(ch);
81
+ if (v === void 0) throw new require_shared.EraSdkError("invalid-props", `cashaddr: invalid character "${ch}"`);
82
+ values.push(v);
83
+ }
84
+ if (polymod([...expandPrefix(prefix), ...values]) !== 0n) throw new require_shared.EraSdkError("invalid-props", "cashaddr: checksum mismatch");
85
+ const data = convertBits(values.slice(0, -8), 5, 8, false);
86
+ if (data.length === 0) throw new require_shared.EraSdkError("invalid-props", "cashaddr: empty payload");
87
+ const version = data[0];
88
+ if (version & 128) throw new require_shared.EraSdkError("invalid-props", "cashaddr: reserved version bit set");
89
+ const typeBits = version >> 3 & 15;
90
+ const hashBits = [
91
+ 160,
92
+ 192,
93
+ 224,
94
+ 256,
95
+ 320,
96
+ 384,
97
+ 448,
98
+ 512
99
+ ][version & 7];
100
+ if (data.length - 1 !== hashBits / 8) throw new require_shared.EraSdkError("invalid-props", "cashaddr: hash length does not match version byte");
101
+ if (typeBits !== 0 && typeBits !== 1) throw new require_shared.EraSdkError("invalid-props", `cashaddr: unsupported address type ${typeBits}`);
102
+ if (hashBits !== 160) throw new require_shared.EraSdkError("invalid-props", "cashaddr: only 20-byte hashes are supported");
103
+ return {
104
+ type: typeBits === 0 ? "p2pkh" : "p2sh",
105
+ hash: Uint8Array.from(data.slice(1)),
106
+ prefix
107
+ };
108
+ }
109
+ /** Encode a 20-byte hash160 as a CashAddr. Returns the bare form by default. */
110
+ function encodeCashAddr(type, hash, options) {
111
+ if (hash.length !== 20) throw new require_shared.EraSdkError("invalid-props", "cashaddr: hash must be 20 bytes");
112
+ const prefix = options?.prefix ?? "bitcoincash";
113
+ const payload = convertBits([type === "p2pkh" ? 0 : 8, ...hash], 8, 5, true);
114
+ const checksum = polymod([
115
+ ...expandPrefix(prefix),
116
+ ...payload,
117
+ 0,
118
+ 0,
119
+ 0,
120
+ 0,
121
+ 0,
122
+ 0,
123
+ 0,
124
+ 0
125
+ ]);
126
+ const suffix = [];
127
+ for (let i = 7; i >= 0; i--) suffix.push(Number(checksum >> BigInt(5 * i) & 31n));
128
+ const body = [...payload, ...suffix].map((v) => CHARSET[v]).join("");
129
+ return options?.withPrefix ? `${prefix}:${body}` : body;
130
+ }
131
+ //#endregion
132
+ Object.defineProperty(exports, "CASHADDR_PREFIX", {
133
+ enumerable: true,
134
+ get: function() {
135
+ return CASHADDR_PREFIX;
136
+ }
137
+ });
138
+ Object.defineProperty(exports, "decodeCashAddr", {
139
+ enumerable: true,
140
+ get: function() {
141
+ return decodeCashAddr;
142
+ }
143
+ });
144
+ Object.defineProperty(exports, "encodeCashAddr", {
145
+ enumerable: true,
146
+ get: function() {
147
+ return encodeCashAddr;
148
+ }
149
+ });
150
+
151
+ //# sourceMappingURL=cashaddr-Ci0_8ITN.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cashaddr-Ci0_8ITN.cjs","names":["EraSdkError"],"sources":["../src/chains/cashaddr.ts"],"sourcesContent":["import { EraSdkError } from '../core/errors';\n\n/**\n * CashAddr codec (the Bitcoin Cash address format).\n *\n * Not bech32: the checksum is a 40-bit BCH code over its own generator set,\n * and there is no separator-position rule — the payload is everything after\n * the optional `prefix:`. The decoder takes every spec-legal spelling\n * (bare, prefixed, all-uppercase; mixed case refused), but the DEVICE's own\n * parser reads only the lowercase form — its prefix rebuild turns an\n * uppercase body into a mixed-case string it then rejects, and the refusal\n * fails open into a zero hash. Anything that goes on the wire must therefore\n * be re-encoded through `encodeCashAddr`, never passed through verbatim;\n * `BchChain.generateSignRequest` does exactly that.\n */\n\nconst CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';\n\nconst CHARSET_REV = new Map<string, number>([...CHARSET].map((c, i) => [c, i]));\n\n/** The default (mainnet) human-readable prefix. */\nexport const CASHADDR_PREFIX = 'bitcoincash';\n\nconst GENERATOR = [0x98f2bc8e61n, 0x79b76d99e2n, 0xf33e5fb3c4n, 0xae2eabe2a8n, 0x1e4f43e470n];\n\nfunction polymod(values: readonly number[]): bigint {\n let c = 1n;\n for (const d of values) {\n const c0 = c >> 35n;\n c = ((c & 0x07ffffffffn) << 5n) ^ BigInt(d);\n for (let i = 0; i < 5; i++) {\n if ((c0 >> BigInt(i)) & 1n) c ^= GENERATOR[i]!;\n }\n }\n return c ^ 1n;\n}\n\n/** Prefix expansion: the low five bits of each character, then a zero. */\nfunction expandPrefix(prefix: string): number[] {\n const out: number[] = [];\n for (const ch of prefix) out.push(ch.charCodeAt(0) & 0x1f);\n out.push(0);\n return out;\n}\n\nfunction convertBits(data: readonly number[], from: number, to: number, pad: boolean): number[] {\n let acc = 0;\n let bits = 0;\n const out: number[] = [];\n const maxv = (1 << to) - 1;\n for (const value of data) {\n if (value < 0 || value >> from !== 0) {\n throw new EraSdkError('invalid-props', 'cashaddr: value out of range');\n }\n acc = (acc << from) | value;\n bits += from;\n while (bits >= to) {\n bits -= to;\n out.push((acc >> bits) & maxv);\n }\n }\n if (pad) {\n if (bits > 0) out.push((acc << (to - bits)) & maxv);\n } else if (bits >= from || ((acc << (to - bits)) & maxv) !== 0) {\n throw new EraSdkError('invalid-props', 'cashaddr: invalid padding');\n }\n return out;\n}\n\nexport type CashAddrType = 'p2pkh' | 'p2sh';\n\nexport interface CashAddrPayload {\n readonly type: CashAddrType;\n /** The 20-byte hash160. */\n readonly hash: Uint8Array;\n readonly prefix: string;\n}\n\n/**\n * Decode a CashAddr, with or without its `prefix:`. Only 20-byte P2PKH and\n * P2SH payloads are accepted — those are the two script kinds the device\n * builds outputs for.\n */\nexport function decodeCashAddr(address: string, expectedPrefix = CASHADDR_PREFIX): CashAddrPayload {\n // ASCII only, before any case folding: the case guard below is ASCII-scoped,\n // while String.toLowerCase folds full Unicode — U+212A KELVIN SIGN would\n // otherwise slip past the guard and fold into a charset 'k'.\n if (!/^[0-9A-Za-z:]*$/.test(address)) {\n throw new EraSdkError('invalid-props', 'cashaddr: invalid character');\n }\n const hasUpper = /[A-Z]/.test(address);\n const hasLower = /[a-z]/.test(address);\n if (hasUpper && hasLower) {\n throw new EraSdkError('invalid-props', 'cashaddr: mixed-case address refused');\n }\n const lower = address.toLowerCase();\n const colon = lower.lastIndexOf(':');\n const prefix = colon === -1 ? expectedPrefix : lower.slice(0, colon);\n const payload = colon === -1 ? lower : lower.slice(colon + 1);\n if (prefix !== expectedPrefix) {\n throw new EraSdkError(\n 'invalid-props',\n `cashaddr: prefix \"${prefix}\" does not match expected \"${expectedPrefix}\"`,\n );\n }\n if (payload.length < 8 + 1) {\n throw new EraSdkError('invalid-props', 'cashaddr: payload too short');\n }\n const values: number[] = [];\n for (const ch of payload) {\n const v = CHARSET_REV.get(ch);\n if (v === undefined) {\n throw new EraSdkError('invalid-props', `cashaddr: invalid character \"${ch}\"`);\n }\n values.push(v);\n }\n if (polymod([...expandPrefix(prefix), ...values]) !== 0n) {\n throw new EraSdkError('invalid-props', 'cashaddr: checksum mismatch');\n }\n const data = convertBits(values.slice(0, -8), 5, 8, false);\n if (data.length === 0) {\n throw new EraSdkError('invalid-props', 'cashaddr: empty payload');\n }\n const version = data[0]!;\n if (version & 0x80) {\n throw new EraSdkError('invalid-props', 'cashaddr: reserved version bit set');\n }\n const typeBits = (version >> 3) & 0x0f;\n const sizeBits = version & 0x07;\n const HASH_SIZES = [160, 192, 224, 256, 320, 384, 448, 512];\n const hashBits = HASH_SIZES[sizeBits]!;\n if (data.length - 1 !== hashBits / 8) {\n throw new EraSdkError('invalid-props', 'cashaddr: hash length does not match version byte');\n }\n if (typeBits !== 0 && typeBits !== 1) {\n throw new EraSdkError('invalid-props', `cashaddr: unsupported address type ${typeBits}`);\n }\n if (hashBits !== 160) {\n throw new EraSdkError('invalid-props', 'cashaddr: only 20-byte hashes are supported');\n }\n return {\n type: typeBits === 0 ? 'p2pkh' : 'p2sh',\n hash: Uint8Array.from(data.slice(1)),\n prefix,\n };\n}\n\n/** Encode a 20-byte hash160 as a CashAddr. Returns the bare form by default. */\nexport function encodeCashAddr(\n type: CashAddrType,\n hash: Uint8Array,\n options?: { prefix?: string | undefined; withPrefix?: boolean | undefined },\n): string {\n if (hash.length !== 20) {\n throw new EraSdkError('invalid-props', 'cashaddr: hash must be 20 bytes');\n }\n const prefix = options?.prefix ?? CASHADDR_PREFIX;\n const version = type === 'p2pkh' ? 0 : 8; // typeBits<<3 | sizeBits(160 -> 0)\n const payload = convertBits([version, ...hash], 8, 5, true);\n const checksumInput = [...expandPrefix(prefix), ...payload, 0, 0, 0, 0, 0, 0, 0, 0];\n const checksum = polymod(checksumInput);\n const suffix: number[] = [];\n for (let i = 7; i >= 0; i--) suffix.push(Number((checksum >> BigInt(5 * i)) & 0x1fn));\n const body = [...payload, ...suffix].map((v) => CHARSET[v]!).join('');\n return options?.withPrefix ? `${prefix}:${body}` : body;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,MAAM,UAAU;AAEhB,MAAM,cAAc,IAAI,IAAoB,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;;AAG9E,MAAa,kBAAkB;AAE/B,MAAM,YAAY;CAAC;CAAe;CAAe;CAAe;CAAe;AAAa;AAE5F,SAAS,QAAQ,QAAmC;CAClD,IAAI,IAAI;CACR,KAAK,MAAM,KAAK,QAAQ;EACtB,MAAM,KAAK,KAAK;EAChB,KAAM,IAAI,iBAAkB,KAAM,OAAO,CAAC;EAC1C,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KACrB,IAAK,MAAM,OAAO,CAAC,IAAK,IAAI,KAAK,UAAU;CAE/C;CACA,OAAO,IAAI;AACb;;AAGA,SAAS,aAAa,QAA0B;CAC9C,MAAM,MAAgB,CAAC;CACvB,KAAK,MAAM,MAAM,QAAQ,IAAI,KAAK,GAAG,WAAW,CAAC,IAAI,EAAI;CACzD,IAAI,KAAK,CAAC;CACV,OAAO;AACT;AAEA,SAAS,YAAY,MAAyB,MAAc,IAAY,KAAwB;CAC9F,IAAI,MAAM;CACV,IAAI,OAAO;CACX,MAAM,MAAgB,CAAC;CACvB,MAAM,QAAQ,KAAK,MAAM;CACzB,KAAK,MAAM,SAAS,MAAM;EACxB,IAAI,QAAQ,KAAK,SAAS,SAAS,GACjC,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,8BAA8B;EAEvE,MAAO,OAAO,OAAQ;EACtB,QAAQ;EACR,OAAO,QAAQ,IAAI;GACjB,QAAQ;GACR,IAAI,KAAM,OAAO,OAAQ,IAAI;EAC/B;CACF;CACA,IAAI,KACE;MAAA,OAAO,GAAG,IAAI,KAAM,OAAQ,KAAK,OAAS,IAAI;CAAA,OAC7C,IAAI,QAAQ,SAAU,OAAQ,KAAK,OAAS,UAAU,GAC3D,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,2BAA2B;CAEpE,OAAO;AACT;;;;;;AAgBA,SAAgB,eAAe,SAAiB,iBAAiB,iBAAkC;CAIjG,IAAI,CAAC,kBAAkB,KAAK,OAAO,GACjC,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,6BAA6B;CAEtE,MAAM,WAAW,QAAQ,KAAK,OAAO;CACrC,MAAM,WAAW,QAAQ,KAAK,OAAO;CACrC,IAAI,YAAY,UACd,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,sCAAsC;CAE/E,MAAM,QAAQ,QAAQ,YAAY;CAClC,MAAM,QAAQ,MAAM,YAAY,GAAG;CACnC,MAAM,SAAS,UAAU,KAAK,iBAAiB,MAAM,MAAM,GAAG,KAAK;CACnE,MAAM,UAAU,UAAU,KAAK,QAAQ,MAAM,MAAM,QAAQ,CAAC;CAC5D,IAAI,WAAW,gBACb,MAAM,IAAIA,eAAAA,YACR,iBACA,qBAAqB,OAAO,6BAA6B,eAAe,EAC1E;CAEF,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,6BAA6B;CAEtE,MAAM,SAAmB,CAAC;CAC1B,KAAK,MAAM,MAAM,SAAS;EACxB,MAAM,IAAI,YAAY,IAAI,EAAE;EAC5B,IAAI,MAAM,KAAA,GACR,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,gCAAgC,GAAG,EAAE;EAE9E,OAAO,KAAK,CAAC;CACf;CACA,IAAI,QAAQ,CAAC,GAAG,aAAa,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,IACpD,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,6BAA6B;CAEtE,MAAM,OAAO,YAAY,OAAO,MAAM,GAAG,EAAE,GAAG,GAAG,GAAG,KAAK;CACzD,IAAI,KAAK,WAAW,GAClB,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,yBAAyB;CAElE,MAAM,UAAU,KAAK;CACrB,IAAI,UAAU,KACZ,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,oCAAoC;CAE7E,MAAM,WAAY,WAAW,IAAK;CAGlC,MAAM,WAAW;EADG;EAAK;EAAK;EAAK;EAAK;EAAK;EAAK;EAAK;CAC7B,CAAC,CAFV,UAAU;CAG3B,IAAI,KAAK,SAAS,MAAM,WAAW,GACjC,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,mDAAmD;CAE5F,IAAI,aAAa,KAAK,aAAa,GACjC,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,sCAAsC,UAAU;CAEzF,IAAI,aAAa,KACf,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,6CAA6C;CAEtF,OAAO;EACL,MAAM,aAAa,IAAI,UAAU;EACjC,MAAM,WAAW,KAAK,KAAK,MAAM,CAAC,CAAC;EACnC;CACF;AACF;;AAGA,SAAgB,eACd,MACA,MACA,SACQ;CACR,IAAI,KAAK,WAAW,IAClB,MAAM,IAAIA,eAAAA,YAAY,iBAAiB,iCAAiC;CAE1E,MAAM,SAAS,SAAS,UAAA;CAExB,MAAM,UAAU,YAAY,CADZ,SAAS,UAAU,IAAI,GACD,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI;CAE1D,MAAM,WAAW,QAAQ;EADF,GAAG,aAAa,MAAM;EAAG,GAAG;EAAS;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;CAC5C,CAAC;CACtC,MAAM,SAAmB,CAAC;CAC1B,KAAK,IAAI,IAAI,GAAG,KAAK,GAAG,KAAK,OAAO,KAAK,OAAQ,YAAY,OAAO,IAAI,CAAC,IAAK,GAAK,CAAC;CACpF,MAAM,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,CAAC,KAAK,MAAM,QAAQ,EAAG,CAAC,CAAC,KAAK,EAAE;CACpE,OAAO,SAAS,aAAa,GAAG,OAAO,GAAG,SAAS;AACrD"}
@@ -1,4 +1,5 @@
1
1
  import { K as EraSdkError, O as u32be, T as concatBytes, w as bytesToHex } from "./shared-Bbgejrus.js";
2
+ import { r as encodeCashAddr } from "./cashaddr-BIzawizX.js";
2
3
  import { secp256k1 } from "@noble/curves/secp256k1";
3
4
  import { blake2b } from "@noble/hashes/blake2b";
4
5
  import { ripemd160 } from "@noble/hashes/ripemd160";
@@ -37,6 +38,10 @@ function evmAddressFromPublicKey(publicKey33) {
37
38
  function hash160(data) {
38
39
  return ripemd160(sha256(data));
39
40
  }
41
+ /** CashAddr P2PKH address (Bitcoin Cash) from a compressed public key. */
42
+ function bchAddressFromPublicKey(publicKey33, options) {
43
+ return encodeCashAddr("p2pkh", hash160(publicKey33), { withPrefix: options?.withPrefix });
44
+ }
40
45
  /** P2WPKH (witness v0) bech32 address. */
41
46
  function btcP2wpkhAddressFromPublicKey(publicKey33, hrp = "bc") {
42
47
  return bech32.encode(hrp, [0, ...bech32.toWords(hash160(publicKey33))]);
@@ -118,6 +123,6 @@ function cardanoSoftDerivePath(publicKey, chainCode, indices) {
118
123
  return node.publicKey;
119
124
  }
120
125
  //#endregion
121
- export { cardanoSoftDerivePath as a, serializeExtendedPublicKey as c, tronAddressFromPublicKey as d, btcP2wpkhAddressFromPublicKey as i, solanaAddressFromPublicKey as l, btcNestedSegwitAddressFromPublicKey as n, derivePublicKey as o, btcP2pkhAddressFromPublicKey as r, evmAddressFromPublicKey as s, ZPUB_VERSION as t, suiAddressFromPublicKey as u };
126
+ export { btcP2wpkhAddressFromPublicKey as a, evmAddressFromPublicKey as c, suiAddressFromPublicKey as d, tronAddressFromPublicKey as f, btcP2pkhAddressFromPublicKey as i, serializeExtendedPublicKey as l, bchAddressFromPublicKey as n, cardanoSoftDerivePath as o, btcNestedSegwitAddressFromPublicKey as r, derivePublicKey as s, ZPUB_VERSION as t, solanaAddressFromPublicKey as u };
122
127
 
123
- //# sourceMappingURL=derive-CK9Nc9Jc.js.map
128
+ //# sourceMappingURL=derive-00JUOkmn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive-00JUOkmn.js","names":[],"sources":["../src/accounts/derive.ts"],"sourcesContent":["import { secp256k1 } from '@noble/curves/secp256k1';\nimport { blake2b } from '@noble/hashes/blake2b';\nimport { ripemd160 } from '@noble/hashes/ripemd160';\nimport { sha256 } from '@noble/hashes/sha2';\nimport { keccak_256 } from '@noble/hashes/sha3';\nimport { base58, bech32, createBase58check } from '@scure/base';\nimport { HDKey } from '@scure/bip32';\nimport { encodeCashAddr } from '../chains/cashaddr';\nimport { bytesToHex, concatBytes, u32be } from '../core/bytes';\nimport { EraSdkError } from '../core/errors';\n\nconst base58check = createBase58check(sha256);\n\n/** Non-hardened BIP-32 child public key from an account-level (publicKey, chainCode). */\nexport function derivePublicKey(\n publicKey: Uint8Array,\n chainCode: Uint8Array,\n change: number,\n index: number,\n): Uint8Array {\n const node = new HDKey({ publicKey, chainCode });\n const child = node.deriveChild(change).deriveChild(index);\n if (!child.publicKey) {\n throw new EraSdkError('invalid-props', 'child derivation produced no public key');\n }\n return child.publicKey;\n}\n\nfunction uncompressed(publicKey33: Uint8Array): Uint8Array {\n return secp256k1.ProjectivePoint.fromHex(publicKey33).toRawBytes(false);\n}\n\n/** EIP-55 checksummed address from a compressed secp256k1 public key. */\nexport function evmAddressFromPublicKey(publicKey33: Uint8Array): `0x${string}` {\n const hash = keccak_256(uncompressed(publicKey33).slice(1));\n const addr = bytesToHex(hash.slice(12));\n const check = keccak_256(new Uint8Array([...addr].map((c) => c.charCodeAt(0))));\n let out = '';\n for (let i = 0; i < addr.length; i++) {\n const nibble = i % 2 === 0 ? check[i >> 1]! >> 4 : check[i >> 1]! & 0x0f;\n out += nibble >= 8 ? addr[i]!.toUpperCase() : addr[i]!;\n }\n return `0x${out}`;\n}\n\nfunction hash160(data: Uint8Array): Uint8Array {\n return ripemd160(sha256(data));\n}\n\n/** CashAddr P2PKH address (Bitcoin Cash) from a compressed public key. */\nexport function bchAddressFromPublicKey(\n publicKey33: Uint8Array,\n options?: { withPrefix?: boolean },\n): string {\n return encodeCashAddr('p2pkh', hash160(publicKey33), { withPrefix: options?.withPrefix });\n}\n\n/** P2WPKH (witness v0) bech32 address. */\nexport function btcP2wpkhAddressFromPublicKey(\n publicKey33: Uint8Array,\n hrp: 'bc' | 'tb' = 'bc',\n): string {\n return bech32.encode(hrp, [0, ...bech32.toWords(hash160(publicKey33))]);\n}\n\n/** Legacy P2PKH base58check address (`1...`) — the kind the device signs messages for. */\nexport function btcP2pkhAddressFromPublicKey(publicKey33: Uint8Array, testnet = false): string {\n return base58check.encode(\n concatBytes(new Uint8Array([testnet ? 0x6f : 0x00]), hash160(publicKey33)),\n );\n}\n\n/** Nested segwit (P2SH-P2WPKH) base58check address (`3...`). */\nexport function btcNestedSegwitAddressFromPublicKey(\n publicKey33: Uint8Array,\n testnet = false,\n): string {\n const redeemScript = concatBytes(new Uint8Array([0x00, 0x14]), hash160(publicKey33));\n return base58check.encode(\n concatBytes(new Uint8Array([testnet ? 0xc4 : 0x05]), hash160(redeemScript)),\n );\n}\n\n/** Tron base58check address (0x41-prefixed keccak hash). */\nexport function tronAddressFromPublicKey(publicKey33: Uint8Array): string {\n const hash = keccak_256(uncompressed(publicKey33).slice(1));\n return base58check.encode(concatBytes(new Uint8Array([0x41]), hash.slice(12)));\n}\n\n/** `0x` Sui address: BLAKE2b-256 of the scheme flag (0x00 = Ed25519) plus the public key. */\nexport function suiAddressFromPublicKey(publicKey32: Uint8Array): string {\n const digest = blake2b(concatBytes(new Uint8Array([0x00]), publicKey32), { dkLen: 32 });\n return `0x${bytesToHex(digest)}`;\n}\n\n/** A Solana address IS the Ed25519 public key, base58. */\nexport function solanaAddressFromPublicKey(publicKey32: Uint8Array): string {\n return base58.encode(publicKey32);\n}\n\nconst XPUB_VERSION = 0x0488b21e;\nconst ZPUB_VERSION = 0x04b24746; // SLIP-132, BIP-84 P2WPKH\n\n/** BIP-32 extended public key serialization. */\nexport function serializeExtendedPublicKey(args: {\n version?: number;\n depth: number;\n parentFingerprint: number;\n childNumber: number;\n chainCode: Uint8Array;\n publicKey: Uint8Array;\n}): string {\n const {\n version = XPUB_VERSION,\n depth,\n parentFingerprint,\n childNumber,\n chainCode,\n publicKey,\n } = args;\n if (chainCode.length !== 32 || publicKey.length !== 33) {\n throw new EraSdkError(\n 'invalid-props',\n 'extended key needs a 32-byte chain code and 33-byte key',\n );\n }\n return base58check.encode(\n concatBytes(\n u32be(version),\n new Uint8Array([depth & 0xff]),\n u32be(parentFingerprint),\n u32be(childNumber),\n chainCode,\n publicKey,\n ),\n );\n}\n\nexport { XPUB_VERSION, ZPUB_VERSION };\n\n// ---------------------------------------------------------------------------\n// Cardano (BIP32-Ed25519 / CIP-3 \"V2\") soft public derivation\n// ---------------------------------------------------------------------------\n\nimport { ed25519 } from '@noble/curves/ed25519';\nimport { hmac } from '@noble/hashes/hmac';\nimport { sha512 } from '@noble/hashes/sha2';\n\nfunction u32le(value: number): Uint8Array {\n return new Uint8Array([\n value & 0xff,\n (value >> 8) & 0xff,\n (value >> 16) & 0xff,\n (value >> 24) & 0xff,\n ]);\n}\n\nfunction leBytesToBigint(bytes: Uint8Array): bigint {\n let out = 0n;\n for (let i = bytes.length - 1; i >= 0; i--) out = (out << 8n) | BigInt(bytes[i]!);\n return out;\n}\n\n/**\n * Public (soft) child of a BIP32-Ed25519 extended public key — the scheme\n * Cardano wallets share account xpubs under (CIP-3/V2):\n *\n * Z = HMAC-SHA512(chainCode, 0x02 || A || le32(index))\n * childA = A + [8 * ZL[0..28]] * B\n * childCC = HMAC-SHA512(chainCode, 0x03 || A || le32(index))[32..]\n *\n * Only non-hardened indices are derivable publicly, which is exactly what the\n * role/index tail of a CIP-1852 path uses.\n */\nexport function cardanoSoftDeriveChild(\n publicKey: Uint8Array,\n chainCode: Uint8Array,\n index: number,\n): { publicKey: Uint8Array; chainCode: Uint8Array } {\n if (publicKey.length !== 32 || chainCode.length !== 32) {\n throw new EraSdkError('invalid-props', 'Cardano derivation needs a 32-byte key and chain code');\n }\n if (!Number.isSafeInteger(index) || index < 0 || index >= 0x80000000) {\n throw new EraSdkError('invalid-props', 'Cardano public derivation is soft-index only');\n }\n const z = hmac(sha512, chainCode, concatBytes(new Uint8Array([0x02]), publicKey, u32le(index)));\n const cc = hmac(\n sha512,\n chainCode,\n concatBytes(new Uint8Array([0x03]), publicKey, u32le(index)),\n ).slice(32);\n const scalar = 8n * leBytesToBigint(z.slice(0, 28));\n const parent = ed25519.ExtendedPoint.fromHex(bytesToHex(publicKey));\n const child = scalar === 0n ? parent : parent.add(ed25519.ExtendedPoint.BASE.multiply(scalar));\n return { publicKey: child.toRawBytes(), chainCode: cc };\n}\n\n/** Soft-derive along several indices (e.g. role, then address index). */\nexport function cardanoSoftDerivePath(\n publicKey: Uint8Array,\n chainCode: Uint8Array,\n indices: readonly number[],\n): Uint8Array {\n let node = { publicKey, chainCode };\n for (const index of indices) {\n node = cardanoSoftDeriveChild(node.publicKey, node.chainCode, index);\n }\n return node.publicKey;\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  const require_shared = require("./shared-Cu2yynP9.cjs");
2
+ const require_cashaddr = require("./cashaddr-Ci0_8ITN.cjs");
2
3
  let _noble_curves_secp256k1 = require("@noble/curves/secp256k1");
3
4
  let _noble_hashes_blake2b = require("@noble/hashes/blake2b");
4
5
  let _noble_hashes_ripemd160 = require("@noble/hashes/ripemd160");
@@ -37,6 +38,10 @@ function evmAddressFromPublicKey(publicKey33) {
37
38
  function hash160(data) {
38
39
  return (0, _noble_hashes_ripemd160.ripemd160)((0, _noble_hashes_sha2.sha256)(data));
39
40
  }
41
+ /** CashAddr P2PKH address (Bitcoin Cash) from a compressed public key. */
42
+ function bchAddressFromPublicKey(publicKey33, options) {
43
+ return require_cashaddr.encodeCashAddr("p2pkh", hash160(publicKey33), { withPrefix: options?.withPrefix });
44
+ }
40
45
  /** P2WPKH (witness v0) bech32 address. */
41
46
  function btcP2wpkhAddressFromPublicKey(publicKey33, hrp = "bc") {
42
47
  return _scure_base.bech32.encode(hrp, [0, ..._scure_base.bech32.toWords(hash160(publicKey33))]);
@@ -124,6 +129,12 @@ Object.defineProperty(exports, "ZPUB_VERSION", {
124
129
  return ZPUB_VERSION;
125
130
  }
126
131
  });
132
+ Object.defineProperty(exports, "bchAddressFromPublicKey", {
133
+ enumerable: true,
134
+ get: function() {
135
+ return bchAddressFromPublicKey;
136
+ }
137
+ });
127
138
  Object.defineProperty(exports, "btcNestedSegwitAddressFromPublicKey", {
128
139
  enumerable: true,
129
140
  get: function() {
@@ -185,4 +196,4 @@ Object.defineProperty(exports, "tronAddressFromPublicKey", {
185
196
  }
186
197
  });
187
198
 
188
- //# sourceMappingURL=derive-BayrpfJk.cjs.map
199
+ //# sourceMappingURL=derive-BuNsfWlg.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive-BuNsfWlg.cjs","names":["createBase58check","sha256","HDKey","EraSdkError","secp256k1","keccak_256","bytesToHex","ripemd160","encodeCashAddr","bech32","concatBytes","blake2b","base58","u32be","hmac","sha512","ed25519"],"sources":["../src/accounts/derive.ts"],"sourcesContent":["import { secp256k1 } from '@noble/curves/secp256k1';\nimport { blake2b } from '@noble/hashes/blake2b';\nimport { ripemd160 } from '@noble/hashes/ripemd160';\nimport { sha256 } from '@noble/hashes/sha2';\nimport { keccak_256 } from '@noble/hashes/sha3';\nimport { base58, bech32, createBase58check } from '@scure/base';\nimport { HDKey } from '@scure/bip32';\nimport { encodeCashAddr } from '../chains/cashaddr';\nimport { bytesToHex, concatBytes, u32be } from '../core/bytes';\nimport { EraSdkError } from '../core/errors';\n\nconst base58check = createBase58check(sha256);\n\n/** Non-hardened BIP-32 child public key from an account-level (publicKey, chainCode). */\nexport function derivePublicKey(\n publicKey: Uint8Array,\n chainCode: Uint8Array,\n change: number,\n index: number,\n): Uint8Array {\n const node = new HDKey({ publicKey, chainCode });\n const child = node.deriveChild(change).deriveChild(index);\n if (!child.publicKey) {\n throw new EraSdkError('invalid-props', 'child derivation produced no public key');\n }\n return child.publicKey;\n}\n\nfunction uncompressed(publicKey33: Uint8Array): Uint8Array {\n return secp256k1.ProjectivePoint.fromHex(publicKey33).toRawBytes(false);\n}\n\n/** EIP-55 checksummed address from a compressed secp256k1 public key. */\nexport function evmAddressFromPublicKey(publicKey33: Uint8Array): `0x${string}` {\n const hash = keccak_256(uncompressed(publicKey33).slice(1));\n const addr = bytesToHex(hash.slice(12));\n const check = keccak_256(new Uint8Array([...addr].map((c) => c.charCodeAt(0))));\n let out = '';\n for (let i = 0; i < addr.length; i++) {\n const nibble = i % 2 === 0 ? check[i >> 1]! >> 4 : check[i >> 1]! & 0x0f;\n out += nibble >= 8 ? addr[i]!.toUpperCase() : addr[i]!;\n }\n return `0x${out}`;\n}\n\nfunction hash160(data: Uint8Array): Uint8Array {\n return ripemd160(sha256(data));\n}\n\n/** CashAddr P2PKH address (Bitcoin Cash) from a compressed public key. */\nexport function bchAddressFromPublicKey(\n publicKey33: Uint8Array,\n options?: { withPrefix?: boolean },\n): string {\n return encodeCashAddr('p2pkh', hash160(publicKey33), { withPrefix: options?.withPrefix });\n}\n\n/** P2WPKH (witness v0) bech32 address. */\nexport function btcP2wpkhAddressFromPublicKey(\n publicKey33: Uint8Array,\n hrp: 'bc' | 'tb' = 'bc',\n): string {\n return bech32.encode(hrp, [0, ...bech32.toWords(hash160(publicKey33))]);\n}\n\n/** Legacy P2PKH base58check address (`1...`) — the kind the device signs messages for. */\nexport function btcP2pkhAddressFromPublicKey(publicKey33: Uint8Array, testnet = false): string {\n return base58check.encode(\n concatBytes(new Uint8Array([testnet ? 0x6f : 0x00]), hash160(publicKey33)),\n );\n}\n\n/** Nested segwit (P2SH-P2WPKH) base58check address (`3...`). */\nexport function btcNestedSegwitAddressFromPublicKey(\n publicKey33: Uint8Array,\n testnet = false,\n): string {\n const redeemScript = concatBytes(new Uint8Array([0x00, 0x14]), hash160(publicKey33));\n return base58check.encode(\n concatBytes(new Uint8Array([testnet ? 0xc4 : 0x05]), hash160(redeemScript)),\n );\n}\n\n/** Tron base58check address (0x41-prefixed keccak hash). */\nexport function tronAddressFromPublicKey(publicKey33: Uint8Array): string {\n const hash = keccak_256(uncompressed(publicKey33).slice(1));\n return base58check.encode(concatBytes(new Uint8Array([0x41]), hash.slice(12)));\n}\n\n/** `0x` Sui address: BLAKE2b-256 of the scheme flag (0x00 = Ed25519) plus the public key. */\nexport function suiAddressFromPublicKey(publicKey32: Uint8Array): string {\n const digest = blake2b(concatBytes(new Uint8Array([0x00]), publicKey32), { dkLen: 32 });\n return `0x${bytesToHex(digest)}`;\n}\n\n/** A Solana address IS the Ed25519 public key, base58. */\nexport function solanaAddressFromPublicKey(publicKey32: Uint8Array): string {\n return base58.encode(publicKey32);\n}\n\nconst XPUB_VERSION = 0x0488b21e;\nconst ZPUB_VERSION = 0x04b24746; // SLIP-132, BIP-84 P2WPKH\n\n/** BIP-32 extended public key serialization. */\nexport function serializeExtendedPublicKey(args: {\n version?: number;\n depth: number;\n parentFingerprint: number;\n childNumber: number;\n chainCode: Uint8Array;\n publicKey: Uint8Array;\n}): string {\n const {\n version = XPUB_VERSION,\n depth,\n parentFingerprint,\n childNumber,\n chainCode,\n publicKey,\n } = args;\n if (chainCode.length !== 32 || publicKey.length !== 33) {\n throw new EraSdkError(\n 'invalid-props',\n 'extended key needs a 32-byte chain code and 33-byte key',\n );\n }\n return base58check.encode(\n concatBytes(\n u32be(version),\n new Uint8Array([depth & 0xff]),\n u32be(parentFingerprint),\n u32be(childNumber),\n chainCode,\n publicKey,\n ),\n );\n}\n\nexport { XPUB_VERSION, ZPUB_VERSION };\n\n// ---------------------------------------------------------------------------\n// Cardano (BIP32-Ed25519 / CIP-3 \"V2\") soft public derivation\n// ---------------------------------------------------------------------------\n\nimport { ed25519 } from '@noble/curves/ed25519';\nimport { hmac } from '@noble/hashes/hmac';\nimport { sha512 } from '@noble/hashes/sha2';\n\nfunction u32le(value: number): Uint8Array {\n return new Uint8Array([\n value & 0xff,\n (value >> 8) & 0xff,\n (value >> 16) & 0xff,\n (value >> 24) & 0xff,\n ]);\n}\n\nfunction leBytesToBigint(bytes: Uint8Array): bigint {\n let out = 0n;\n for (let i = bytes.length - 1; i >= 0; i--) out = (out << 8n) | BigInt(bytes[i]!);\n return out;\n}\n\n/**\n * Public (soft) child of a BIP32-Ed25519 extended public key — the scheme\n * Cardano wallets share account xpubs under (CIP-3/V2):\n *\n * Z = HMAC-SHA512(chainCode, 0x02 || A || le32(index))\n * childA = A + [8 * ZL[0..28]] * B\n * childCC = HMAC-SHA512(chainCode, 0x03 || A || le32(index))[32..]\n *\n * Only non-hardened indices are derivable publicly, which is exactly what the\n * role/index tail of a CIP-1852 path uses.\n */\nexport function cardanoSoftDeriveChild(\n publicKey: Uint8Array,\n chainCode: Uint8Array,\n index: number,\n): { publicKey: Uint8Array; chainCode: Uint8Array } {\n if (publicKey.length !== 32 || chainCode.length !== 32) {\n throw new EraSdkError('invalid-props', 'Cardano derivation needs a 32-byte key and chain code');\n }\n if (!Number.isSafeInteger(index) || index < 0 || index >= 0x80000000) {\n throw new EraSdkError('invalid-props', 'Cardano public derivation is soft-index only');\n }\n const z = hmac(sha512, chainCode, concatBytes(new Uint8Array([0x02]), publicKey, u32le(index)));\n const cc = hmac(\n sha512,\n chainCode,\n concatBytes(new Uint8Array([0x03]), publicKey, u32le(index)),\n ).slice(32);\n const scalar = 8n * leBytesToBigint(z.slice(0, 28));\n const parent = ed25519.ExtendedPoint.fromHex(bytesToHex(publicKey));\n const child = scalar === 0n ? parent : parent.add(ed25519.ExtendedPoint.BASE.multiply(scalar));\n return { publicKey: child.toRawBytes(), chainCode: cc };\n}\n\n/** Soft-derive along several indices (e.g. role, then address index). */\nexport function cardanoSoftDerivePath(\n publicKey: Uint8Array,\n chainCode: Uint8Array,\n indices: readonly number[],\n): Uint8Array {\n let node = { publicKey, chainCode };\n for (const index of indices) {\n node = cardanoSoftDeriveChild(node.publicKey, node.chainCode, index);\n }\n return node.publicKey;\n}\n"],"mappings":"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"}