@hwlt/era-connect 0.2.0 → 0.4.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 (112) hide show
  1. package/README.md +6 -6
  2. package/cardano/package.json +6 -0
  3. package/cosmos/package.json +6 -0
  4. package/dist/{btc-DfQ0q79G.d.ts → btc-B8LfSEkv.d.ts} +11 -2
  5. package/dist/{btc-VyJKXq7d.js → btc-B_Nq2H4T.js} +16 -7
  6. package/dist/btc-B_Nq2H4T.js.map +1 -0
  7. package/dist/{btc-Fr6Jz4Fi.cjs → btc-ByzjzUKs.cjs} +19 -10
  8. package/dist/btc-ByzjzUKs.cjs.map +1 -0
  9. package/dist/{btc-Dn6_Y12c.d.cts → btc-C4dVkxHb.d.cts} +11 -2
  10. package/dist/btc.cjs +2 -2
  11. package/dist/btc.d.cts +1 -1
  12. package/dist/btc.d.ts +1 -1
  13. package/dist/btc.js +2 -2
  14. package/dist/cardano-BCT55gwf.js +95 -0
  15. package/dist/cardano-BCT55gwf.js.map +1 -0
  16. package/dist/cardano-CzYswGzK.cjs +106 -0
  17. package/dist/cardano-CzYswGzK.cjs.map +1 -0
  18. package/dist/cardano-Do1ahUlK.d.cts +62 -0
  19. package/dist/cardano-OzjWZHh6.d.ts +62 -0
  20. package/dist/cardano.cjs +10 -0
  21. package/dist/cardano.d.cts +3 -0
  22. package/dist/cardano.d.ts +3 -0
  23. package/dist/cardano.js +3 -0
  24. package/dist/cosmos-BYrhIqRk.d.cts +77 -0
  25. package/dist/cosmos-DDsgtUki.cjs +113 -0
  26. package/dist/cosmos-DDsgtUki.cjs.map +1 -0
  27. package/dist/cosmos-DqyjA6lg.d.ts +77 -0
  28. package/dist/cosmos-jUHNxgWC.js +102 -0
  29. package/dist/cosmos-jUHNxgWC.js.map +1 -0
  30. package/dist/cosmos.cjs +10 -0
  31. package/dist/cosmos.d.cts +3 -0
  32. package/dist/cosmos.d.ts +3 -0
  33. package/dist/cosmos.js +3 -0
  34. package/dist/derive-BayrpfJk.cjs +188 -0
  35. package/dist/derive-BayrpfJk.cjs.map +1 -0
  36. package/dist/derive-CK9Nc9Jc.js +123 -0
  37. package/dist/derive-CK9Nc9Jc.js.map +1 -0
  38. package/dist/{evm-kJb3-oiR.js → evm-CI1cuZsp.js} +3 -2
  39. package/dist/{evm-kJb3-oiR.js.map → evm-CI1cuZsp.js.map} +1 -1
  40. package/dist/{evm-BHj_2OTG.cjs → evm-yfKOc2dy.cjs} +6 -5
  41. package/dist/{evm-BHj_2OTG.cjs.map → evm-yfKOc2dy.cjs.map} +1 -1
  42. package/dist/evm.cjs +2 -2
  43. package/dist/evm.js +2 -2
  44. package/dist/index.cjs +156 -116
  45. package/dist/index.cjs.map +1 -1
  46. package/dist/index.d.cts +68 -9
  47. package/dist/index.d.ts +68 -9
  48. package/dist/index.js +125 -94
  49. package/dist/index.js.map +1 -1
  50. package/dist/keypath-Bvv_cfHt.js +75 -0
  51. package/dist/keypath-Bvv_cfHt.js.map +1 -0
  52. package/dist/keypath-pzm_YIfN.cjs +116 -0
  53. package/dist/keypath-pzm_YIfN.cjs.map +1 -0
  54. package/dist/{messages-BzcdWlg1.cjs → messages-BZXDY29v.cjs} +2 -2
  55. package/dist/{messages-BzcdWlg1.cjs.map → messages-BZXDY29v.cjs.map} +1 -1
  56. package/dist/{messages-17Oup0-Z.js → messages-DbnXy3dC.js} +2 -2
  57. package/dist/{messages-17Oup0-Z.js.map → messages-DbnXy3dC.js.map} +1 -1
  58. package/dist/{shared-B8Wc7ViU.js → shared-Bbgejrus.js} +2 -73
  59. package/dist/shared-Bbgejrus.js.map +1 -0
  60. package/dist/{shared-nISrEktU.cjs → shared-Cu2yynP9.cjs} +13 -114
  61. package/dist/shared-Cu2yynP9.cjs.map +1 -0
  62. package/dist/{solana-DdEn24X4.cjs → solana-BoQBy0hu.cjs} +6 -5
  63. package/dist/{solana-DdEn24X4.cjs.map → solana-BoQBy0hu.cjs.map} +1 -1
  64. package/dist/{solana-C7JQEAec.js → solana-BukH3AI3.js} +3 -2
  65. package/dist/{solana-C7JQEAec.js.map → solana-BukH3AI3.js.map} +1 -1
  66. package/dist/solana.cjs +2 -2
  67. package/dist/solana.js +2 -2
  68. package/dist/sui-6WX6VPlW.d.cts +49 -0
  69. package/dist/sui-BtbPG6Xt.js +71 -0
  70. package/dist/sui-BtbPG6Xt.js.map +1 -0
  71. package/dist/sui-CYRn331O.cjs +82 -0
  72. package/dist/sui-CYRn331O.cjs.map +1 -0
  73. package/dist/sui-DI0ruW7E.d.ts +49 -0
  74. package/dist/sui.cjs +10 -0
  75. package/dist/sui.d.cts +3 -0
  76. package/dist/sui.d.ts +3 -0
  77. package/dist/sui.js +3 -0
  78. package/dist/{ton-5Uvi5ifg.js → ton-BSPKU5B9.js} +3 -2
  79. package/dist/{ton-5Uvi5ifg.js.map → ton-BSPKU5B9.js.map} +1 -1
  80. package/dist/{ton-DUJGXdGa.cjs → ton-CI2s9puH.cjs} +6 -5
  81. package/dist/{ton-DUJGXdGa.cjs.map → ton-CI2s9puH.cjs.map} +1 -1
  82. package/dist/ton.cjs +2 -2
  83. package/dist/ton.js +2 -2
  84. package/dist/{tron-CQIEqjCA.cjs → tron-B9wXdzlA.cjs} +7 -6
  85. package/dist/{tron-CQIEqjCA.cjs.map → tron-B9wXdzlA.cjs.map} +1 -1
  86. package/dist/{tron-Bo9HnvVE.js → tron-fufBDNuQ.js} +4 -3
  87. package/dist/{tron-Bo9HnvVE.js.map → tron-fufBDNuQ.js.map} +1 -1
  88. package/dist/tron.cjs +3 -3
  89. package/dist/tron.js +3 -3
  90. package/dist/verify.cjs +335 -4
  91. package/dist/verify.cjs.map +1 -1
  92. package/dist/verify.d.cts +86 -1
  93. package/dist/verify.d.ts +86 -1
  94. package/dist/verify.js +333 -6
  95. package/dist/verify.js.map +1 -1
  96. package/dist/xrp-BXAeK4iH.d.cts +32 -0
  97. package/dist/xrp-BrHXUnX5.cjs +52 -0
  98. package/dist/xrp-BrHXUnX5.cjs.map +1 -0
  99. package/dist/xrp-C8LJO5b-.js +47 -0
  100. package/dist/xrp-C8LJO5b-.js.map +1 -0
  101. package/dist/xrp-DqCXGYEk.d.ts +32 -0
  102. package/dist/xrp.cjs +9 -0
  103. package/dist/xrp.d.cts +3 -0
  104. package/dist/xrp.d.ts +3 -0
  105. package/dist/xrp.js +3 -0
  106. package/package.json +49 -1
  107. package/sui/package.json +6 -0
  108. package/xrp/package.json +6 -0
  109. package/dist/btc-Fr6Jz4Fi.cjs.map +0 -1
  110. package/dist/btc-VyJKXq7d.js.map +0 -1
  111. package/dist/shared-B8Wc7ViU.js.map +0 -1
  112. package/dist/shared-nISrEktU.cjs.map +0 -1
package/README.md CHANGED
@@ -78,6 +78,11 @@ today and speaks the rest through the same primitives.
78
78
  | Solana | `sol-sign-request` | off-chain messages | `@hwlt/era-connect/solana` |
79
79
  | Tron | structured envelope (any contract via `rawData`) | UTF-8 in `rawData` | `@hwlt/era-connect/tron` |
80
80
  | TON | `ton-sign-request` (BoC root-hash signing) | TON Connect proof | `@hwlt/era-connect/ton` |
81
+ | Cardano | `cardano-sign-request` (witness-set replies, soft-derived vkey binding) | — | `@hwlt/era-connect/cardano` |
82
+ | Sui | `sui-sign-request` / hash variant | — | `@hwlt/era-connect/sui` |
83
+ | Cosmos (~35 networks incl. Ethermint) | `cosmos-sign-request` / `evm-sign-request` | ADR-036 | `@hwlt/era-connect/cosmos` |
84
+ | XRP | `ur:bytes` (XRP Toolkit convention) | — | `@hwlt/era-connect/xrp` |
85
+ | Litecoin, Dogecoin, Dash | `crypto-psbt-extend` (same flow as Bitcoin) | — | `@hwlt/era-connect/btc` |
81
86
 
82
87
  **Also supported by the device** — usable today via `era.raw` +
83
88
  `era.scanner({expectedTypes})`, dedicated modules on the roadmap (order driven
@@ -85,12 +90,7 @@ by integrator demand):
85
90
 
86
91
  | Chain | Wire type | Module status |
87
92
  |---|---|---|
88
- | Cosmos (~35 networks, incl. Ethermint: INJ/EVMOS/DYM) | `cosmos-sign-request` / `evm-sign-request` | planned |
89
- | Cardano | `cardano-sign-request` | planned |
90
- | Sui | `sui-sign-request` / `sui-sign-hash-request` | planned |
91
- | Dogecoin | `crypto-psbt-extend` | planned |
92
- | Litecoin, Dash, Bitcoin Cash | structured envelope (as Tron) | planned |
93
- | XRP | `ur:bytes` (XRP Toolkit convention) | planned |
93
+ | Bitcoin Cash | structured envelope (FORKID signing) | planned |
94
94
 
95
95
  Two things make the gap workable now: **linking already surfaces every account
96
96
  the device exports** (unknown chains still carry their path, fingerprint and
@@ -0,0 +1,6 @@
1
+ {
2
+ "main": "../dist/cardano.cjs",
3
+ "module": "../dist/cardano.js",
4
+ "types": "../dist/cardano.d.ts",
5
+ "react-native": "../dist/cardano.js"
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "main": "../dist/cosmos.cjs",
3
+ "module": "../dist/cosmos.js",
4
+ "types": "../dist/cosmos.d.ts",
5
+ "react-native": "../dist/cosmos.js"
6
+ }
@@ -1,8 +1,17 @@
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). */
4
+ type PsbtCoin = 'btc' | 'ltc' | 'doge' | 'dash';
3
5
  interface BtcPsbtSignRequestProps {
4
6
  /** Raw PSBT v0 bytes (BIP-174). The device's signer relies on the global UNSIGNED_TX. */
5
7
  readonly psbt: Uint8Array;
8
+ /**
9
+ * `'btc'` (default) rides plain `crypto-psbt`; Litecoin/Dogecoin/Dash ride
10
+ * `crypto-psbt-extend` — the same PSBT plus the coin id, answered in kind.
11
+ * Build the PSBT with the coin's own derivation paths (LTC `m/84'/2'/…`,
12
+ * DOGE `m/44'/3'/…`, DASH `m/44'/5'/…`).
13
+ */
14
+ readonly coin?: PsbtCoin;
6
15
  }
7
16
  interface BtcPsbtResult {
8
17
  /** The signed, NOT finalized PSBT. Finalize + broadcast with your own stack. */
@@ -46,7 +55,7 @@ declare class BtcChain {
46
55
  * check re-opens replay of a stale signed PSBT.
47
56
  */
48
57
  generatePsbtSignRequest(props: BtcPsbtSignRequestProps): SignRequest<BtcPsbtResult>;
49
- /** Parse a `crypto-psbt` reply: the signed (not finalized) PSBT bytes. */
58
+ /** Parse a `crypto-psbt` / `crypto-psbt-extend` reply: the signed (not finalized) PSBT bytes. */
50
59
  parsePsbt(input: Ur | string): BtcPsbtResult;
51
60
  /** Build a `btc-sign-request` (8101) for message signing. Reply: `btc-signature` (8102). */
52
61
  generateMessageSignRequest(props: BtcMessageSignRequestProps): SignRequest<BtcMessageSignatureResult>;
@@ -55,4 +64,4 @@ declare class BtcChain {
55
64
  }
56
65
  //#endregion
57
66
  export { BtcPsbtSignRequestProps as a, BtcPsbtResult as i, BtcMessageSignRequestProps as n, BtcMessageSignatureResult as r, BtcChain as t };
58
- //# sourceMappingURL=btc-DfQ0q79G.d.ts.map
67
+ //# sourceMappingURL=btc-B8LfSEkv.d.ts.map
@@ -1,7 +1,15 @@
1
- import { G as cbMap, J as cbUint, K as cbTag, M as keypath304, N as normalizeXfp, P as parsePath, S as asciiDecode, U as cbArray, W as cbBytes, Y as mapGet, Z as EraSdkError, _ as Ur, b as cborDecode, c as resolveRequestId, h as normalizeRequestId, i as requireRequestIdEcho, l as toUr, n as makeSignRequest, o as requireUrType, q as cbText, r as requireReplyMap, s as resolveContext, x as cborEncode, z as asBytes } from "./shared-B8Wc7ViU.js";
1
+ import { B as cbMap, H as cbText, K as EraSdkError, L as cbArray, N as asBytes, S as asciiDecode, U as cbUint, V as cbTag, W as mapGet, _ as Ur, b as cborDecode, c as resolveRequestId, h as normalizeRequestId, i as requireRequestIdEcho, l as toUr, n as makeSignRequest, o as requireUrType, r as requireReplyMap, s as resolveContext, x as cborEncode, z as cbBytes } from "./shared-Bbgejrus.js";
2
+ import { i as parsePath, n as keypath304, r as normalizeXfp } from "./keypath-Bvv_cfHt.js";
2
3
  import { base64 } from "@scure/base";
3
4
  //#region src/chains/btc.ts
5
+ /** `crypto-psbt-extend` coin ids (the device's own coin-type table). */
6
+ const PSBT_EXTEND_COIN_ID = {
7
+ ltc: 2,
8
+ doge: 3,
9
+ dash: 5
10
+ };
4
11
  const PSBT_REPLY_TYPES = ["crypto-psbt"];
12
+ const PSBT_EXTEND_REPLY_TYPES = ["crypto-psbt-extend", "crypto-psbt"];
5
13
  const MESSAGE_REPLY_TYPES = ["btc-signature"];
6
14
  var BtcChain = class BtcChain {
7
15
  constructor(config) {
@@ -19,25 +27,26 @@ var BtcChain = class BtcChain {
19
27
  */
20
28
  generatePsbtSignRequest(props) {
21
29
  if (props.psbt.length === 0) throw new EraSdkError("invalid-props", "psbt must not be empty");
22
- const ur = new Ur("crypto-psbt", cborEncode(cbBytes(props.psbt)));
30
+ const coin = props.coin ?? "btc";
31
+ const ur = coin === "btc" ? new Ur("crypto-psbt", cborEncode(cbBytes(props.psbt))) : new Ur("crypto-psbt-extend", cborEncode(cbMap([[1, cbBytes(props.psbt)], [2, cbUint(PSBT_EXTEND_COIN_ID[coin])]])));
23
32
  return makeSignRequest({
24
33
  ur,
25
- replyTypes: PSBT_REPLY_TYPES,
34
+ replyTypes: coin === "btc" ? PSBT_REPLY_TYPES : PSBT_EXTEND_REPLY_TYPES,
26
35
  context: this.context,
27
36
  parse: (reply) => this.parsePsbt(reply)
28
37
  });
29
38
  }
30
- /** Parse a `crypto-psbt` reply: the signed (not finalized) PSBT bytes. */
39
+ /** Parse a `crypto-psbt` / `crypto-psbt-extend` reply: the signed (not finalized) PSBT bytes. */
31
40
  parsePsbt(input) {
32
41
  const ur = toUr(input);
33
- requireUrType(ur, [...PSBT_REPLY_TYPES], "crypto-psbt");
42
+ requireUrType(ur, [...PSBT_EXTEND_REPLY_TYPES], "crypto-psbt");
34
43
  let decoded;
35
44
  try {
36
45
  decoded = cborDecode(ur.cbor);
37
46
  } catch (e) {
38
47
  throw new EraSdkError("malformed-cbor", `crypto-psbt reply: ${e.message}`);
39
48
  }
40
- const bytes = asBytes(decoded);
49
+ const bytes = asBytes(decoded) ?? asBytes(mapGet(decoded, 1));
41
50
  if (!bytes) throw new EraSdkError("malformed-reply", "crypto-psbt reply is not a byte string");
42
51
  if (bytes.length === 0) throw new EraSdkError("malformed-reply", "crypto-psbt reply is empty");
43
52
  return { psbt: bytes };
@@ -96,4 +105,4 @@ function parseMessageSignature(ur, expectedRequestId) {
96
105
  //#endregion
97
106
  export { BtcChain as t };
98
107
 
99
- //# sourceMappingURL=btc-VyJKXq7d.js.map
108
+ //# sourceMappingURL=btc-B_Nq2H4T.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"btc-B_Nq2H4T.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 needs the FORKID signer and is not offered here). */\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. The device's message signer supports LEGACY P2PKH\n * (`1...`) addresses only; a segwit address yields `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 (any non-legacy script type) with an EMPTY signature.\n if (sigValue.length === 0) {\n throw new EraSdkError(\n 'empty-signature',\n 'the device returned an empty signature — Bitcoin message signing supports legacy P2PKH (1...) addresses only',\n );\n }\n\n // Device quirk: the reply bytes are the ASCII of a base64 string, not raw\n // signature bytes. Decode twice; tolerate a firmware that starts sending\n // raw 65-byte signatures directly.\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,7 +1,15 @@
1
- const require_shared = require("./shared-nISrEktU.cjs");
1
+ const require_shared = require("./shared-Cu2yynP9.cjs");
2
+ const require_keypath = require("./keypath-pzm_YIfN.cjs");
2
3
  let _scure_base = require("@scure/base");
3
4
  //#region src/chains/btc.ts
5
+ /** `crypto-psbt-extend` coin ids (the device's own coin-type table). */
6
+ const PSBT_EXTEND_COIN_ID = {
7
+ ltc: 2,
8
+ doge: 3,
9
+ dash: 5
10
+ };
4
11
  const PSBT_REPLY_TYPES = ["crypto-psbt"];
12
+ const PSBT_EXTEND_REPLY_TYPES = ["crypto-psbt-extend", "crypto-psbt"];
5
13
  const MESSAGE_REPLY_TYPES = ["btc-signature"];
6
14
  var BtcChain = class BtcChain {
7
15
  constructor(config) {
@@ -19,25 +27,26 @@ var BtcChain = class BtcChain {
19
27
  */
20
28
  generatePsbtSignRequest(props) {
21
29
  if (props.psbt.length === 0) throw new require_shared.EraSdkError("invalid-props", "psbt must not be empty");
22
- const ur = new require_shared.Ur("crypto-psbt", require_shared.cborEncode(require_shared.cbBytes(props.psbt)));
30
+ const coin = props.coin ?? "btc";
31
+ const ur = coin === "btc" ? new require_shared.Ur("crypto-psbt", require_shared.cborEncode(require_shared.cbBytes(props.psbt))) : new require_shared.Ur("crypto-psbt-extend", require_shared.cborEncode(require_shared.cbMap([[1, require_shared.cbBytes(props.psbt)], [2, require_shared.cbUint(PSBT_EXTEND_COIN_ID[coin])]])));
23
32
  return require_shared.makeSignRequest({
24
33
  ur,
25
- replyTypes: PSBT_REPLY_TYPES,
34
+ replyTypes: coin === "btc" ? PSBT_REPLY_TYPES : PSBT_EXTEND_REPLY_TYPES,
26
35
  context: this.context,
27
36
  parse: (reply) => this.parsePsbt(reply)
28
37
  });
29
38
  }
30
- /** Parse a `crypto-psbt` reply: the signed (not finalized) PSBT bytes. */
39
+ /** Parse a `crypto-psbt` / `crypto-psbt-extend` reply: the signed (not finalized) PSBT bytes. */
31
40
  parsePsbt(input) {
32
41
  const ur = require_shared.toUr(input);
33
- require_shared.requireUrType(ur, [...PSBT_REPLY_TYPES], "crypto-psbt");
42
+ require_shared.requireUrType(ur, [...PSBT_EXTEND_REPLY_TYPES], "crypto-psbt");
34
43
  let decoded;
35
44
  try {
36
45
  decoded = require_shared.cborDecode(ur.cbor);
37
46
  } catch (e) {
38
47
  throw new require_shared.EraSdkError("malformed-cbor", `crypto-psbt reply: ${e.message}`);
39
48
  }
40
- const bytes = require_shared.asBytes(decoded);
49
+ const bytes = require_shared.asBytes(decoded) ?? require_shared.asBytes(require_shared.mapGet(decoded, 1));
41
50
  if (!bytes) throw new require_shared.EraSdkError("malformed-reply", "crypto-psbt reply is not a byte string");
42
51
  if (bytes.length === 0) throw new require_shared.EraSdkError("malformed-reply", "crypto-psbt reply is empty");
43
52
  return { psbt: bytes };
@@ -45,13 +54,13 @@ var BtcChain = class BtcChain {
45
54
  /** Build a `btc-sign-request` (8101) for message signing. Reply: `btc-signature` (8102). */
46
55
  generateMessageSignRequest(props) {
47
56
  const requestId = require_shared.resolveRequestId(this.context, props.requestId);
48
- const path = require_shared.parsePath(props.path);
49
- const xfp = require_shared.normalizeXfp(props.xfp);
57
+ const path = require_keypath.parsePath(props.path);
58
+ const xfp = require_keypath.normalizeXfp(props.xfp);
50
59
  const ur = new require_shared.Ur("btc-sign-request", require_shared.cborEncode(require_shared.cbMap([
51
60
  [1, require_shared.cbTag(37, require_shared.cbBytes(requestId))],
52
61
  [2, require_shared.cbBytes(props.message)],
53
62
  [3, require_shared.cbUint(BtcChain.DataType.message)],
54
- [4, require_shared.cbArray([require_shared.keypath304(path, xfp)])],
63
+ [4, require_shared.cbArray([require_keypath.keypath304(path, xfp)])],
55
64
  [5, require_shared.cbArray([require_shared.cbText(props.address)])],
56
65
  [6, require_shared.cbText(props.origin ?? this.context.origin)]
57
66
  ])));
@@ -101,4 +110,4 @@ Object.defineProperty(exports, "BtcChain", {
101
110
  }
102
111
  });
103
112
 
104
- //# sourceMappingURL=btc-Fr6Jz4Fi.cjs.map
113
+ //# sourceMappingURL=btc-ByzjzUKs.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"btc-ByzjzUKs.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 needs the FORKID signer and is not offered here). */\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. The device's message signer supports LEGACY P2PKH\n * (`1...`) addresses only; a segwit address yields `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 (any non-legacy script type) with an EMPTY signature.\n if (sigValue.length === 0) {\n throw new EraSdkError(\n 'empty-signature',\n 'the device returned an empty signature — Bitcoin message signing supports legacy P2PKH (1...) addresses only',\n );\n }\n\n // Device quirk: the reply bytes are the ASCII of a base64 string, not raw\n // signature bytes. Decode twice; tolerate a firmware that starts sending\n // raw 65-byte signatures directly.\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,8 +1,17 @@
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). */
4
+ type PsbtCoin = 'btc' | 'ltc' | 'doge' | 'dash';
3
5
  interface BtcPsbtSignRequestProps {
4
6
  /** Raw PSBT v0 bytes (BIP-174). The device's signer relies on the global UNSIGNED_TX. */
5
7
  readonly psbt: Uint8Array;
8
+ /**
9
+ * `'btc'` (default) rides plain `crypto-psbt`; Litecoin/Dogecoin/Dash ride
10
+ * `crypto-psbt-extend` — the same PSBT plus the coin id, answered in kind.
11
+ * Build the PSBT with the coin's own derivation paths (LTC `m/84'/2'/…`,
12
+ * DOGE `m/44'/3'/…`, DASH `m/44'/5'/…`).
13
+ */
14
+ readonly coin?: PsbtCoin;
6
15
  }
7
16
  interface BtcPsbtResult {
8
17
  /** The signed, NOT finalized PSBT. Finalize + broadcast with your own stack. */
@@ -46,7 +55,7 @@ declare class BtcChain {
46
55
  * check re-opens replay of a stale signed PSBT.
47
56
  */
48
57
  generatePsbtSignRequest(props: BtcPsbtSignRequestProps): SignRequest<BtcPsbtResult>;
49
- /** Parse a `crypto-psbt` reply: the signed (not finalized) PSBT bytes. */
58
+ /** Parse a `crypto-psbt` / `crypto-psbt-extend` reply: the signed (not finalized) PSBT bytes. */
50
59
  parsePsbt(input: Ur | string): BtcPsbtResult;
51
60
  /** Build a `btc-sign-request` (8101) for message signing. Reply: `btc-signature` (8102). */
52
61
  generateMessageSignRequest(props: BtcMessageSignRequestProps): SignRequest<BtcMessageSignatureResult>;
@@ -55,4 +64,4 @@ declare class BtcChain {
55
64
  }
56
65
  //#endregion
57
66
  export { BtcPsbtSignRequestProps as a, BtcPsbtResult as i, BtcMessageSignRequestProps as n, BtcMessageSignatureResult as r, BtcChain as t };
58
- //# sourceMappingURL=btc-Dn6_Y12c.d.cts.map
67
+ //# sourceMappingURL=btc-C4dVkxHb.d.cts.map
package/dist/btc.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_shared = require("./shared-nISrEktU.cjs");
3
- const require_btc = require("./btc-Fr6Jz4Fi.cjs");
2
+ const require_shared = require("./shared-Cu2yynP9.cjs");
3
+ const require_btc = require("./btc-ByzjzUKs.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-Dn6_Y12c.cjs";
2
+ import { a as BtcPsbtSignRequestProps, i as BtcPsbtResult, n as BtcMessageSignRequestProps, r as BtcMessageSignatureResult, t as BtcChain } from "./btc-C4dVkxHb.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-DfQ0q79G.js";
2
+ import { a as BtcPsbtSignRequestProps, i as BtcPsbtResult, n as BtcMessageSignRequestProps, r as BtcMessageSignatureResult, t as BtcChain } from "./btc-B8LfSEkv.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
- import { Z as EraSdkError, _ as Ur, d as UrScanner, f as AnimatedUr, u as TypedUrScanner } from "./shared-B8Wc7ViU.js";
2
- import { t as BtcChain } from "./btc-VyJKXq7d.js";
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";
3
3
  export { AnimatedUr, BtcChain, EraSdkError, TypedUrScanner, Ur, UrScanner };
@@ -0,0 +1,95 @@
1
+ import { B as cbMap, G as stripTags, H as cbText, K as EraSdkError, L as cbArray, N as asBytes, U as cbUint, V as cbTag, W as mapGet, _ as Ur, b as cborDecode, c as resolveRequestId, h as normalizeRequestId, i as requireRequestIdEcho, l as toUr, n as makeSignRequest, o as requireUrType, r as requireReplyMap, s as resolveContext, x as cborEncode, z as cbBytes } from "./shared-Bbgejrus.js";
2
+ import { i as parsePath, n as keypath304, r as normalizeXfp } from "./keypath-Bvv_cfHt.js";
3
+ //#region src/chains/cardano.ts
4
+ const REPLY_TYPES = ["cardano-signature"];
5
+ const UTXO_TAG = 2201;
6
+ const CERT_KEY_TAG = 2204;
7
+ var CardanoChain = class {
8
+ constructor(config) {
9
+ this.context = resolveContext(config);
10
+ }
11
+ /** Build a `cardano-sign-request` (2202). Reply: `cardano-signature` (2203). */
12
+ generateSignRequest(props) {
13
+ const requestId = resolveRequestId(this.context, props.requestId);
14
+ if (props.signData.length === 0) throw new EraSdkError("invalid-props", "signData must not be empty");
15
+ if (props.utxos.length === 0) throw new EraSdkError("invalid-props", "at least one utxo is required");
16
+ const utxos = props.utxos.map((utxo) => {
17
+ if (utxo.transactionHash.length !== 32) throw new EraSdkError("invalid-props", "utxo transactionHash must be 32 bytes");
18
+ if (!Number.isSafeInteger(utxo.index) || utxo.index < 0) throw new EraSdkError("invalid-props", "utxo index must be a non-negative integer");
19
+ const entries = [[1, cbBytes(utxo.transactionHash)], [2, cbUint(utxo.index)]];
20
+ if (utxo.amount !== void 0) entries.push([3, cbText(utxo.amount)]);
21
+ entries.push([4, keypath304(parsePath(utxo.path), normalizeXfp(utxo.xfp))]);
22
+ if (utxo.address !== void 0) entries.push([5, cbText(utxo.address)]);
23
+ return cbTag(UTXO_TAG, cbMap(entries));
24
+ });
25
+ const certKeys = (props.certKeys ?? []).map((certKey) => {
26
+ const entries = [];
27
+ if (certKey.keyHash !== void 0) entries.push([1, cbBytes(certKey.keyHash)]);
28
+ entries.push([2, keypath304(parsePath(certKey.path), normalizeXfp(certKey.xfp))]);
29
+ return cbTag(CERT_KEY_TAG, cbMap(entries));
30
+ });
31
+ const entries = [
32
+ [1, cbTag(37, cbBytes(requestId))],
33
+ [2, cbBytes(props.signData)],
34
+ [3, cbArray(utxos)]
35
+ ];
36
+ if (certKeys.length > 0) entries.push([4, cbArray(certKeys)]);
37
+ entries.push([5, cbText(props.origin ?? this.context.origin)]);
38
+ const ur = new Ur("cardano-sign-request", cborEncode(cbMap(entries)));
39
+ return makeSignRequest({
40
+ ur,
41
+ requestId,
42
+ replyTypes: REPLY_TYPES,
43
+ context: this.context,
44
+ parse: (reply) => parseCardanoSignature(reply, requestId)
45
+ });
46
+ }
47
+ /** Parse a `cardano-signature` standalone. Prefer `SignRequest.scanner().parse()`. */
48
+ parseSignature(input, expect) {
49
+ return parseCardanoSignature(toUr(input), expect?.requestId === void 0 ? void 0 : normalizeRequestId(expect.requestId));
50
+ }
51
+ };
52
+ function parseCardanoSignature(ur, expectedRequestId) {
53
+ requireUrType(ur, [...REPLY_TYPES], "cardano-signature");
54
+ const map = requireReplyMap(ur, "cardano-signature");
55
+ const requestId = requireRequestIdEcho(map, 1, expectedRequestId, "cardano-signature");
56
+ const witnessSet = asBytes(mapGet(map, 2));
57
+ if (!witnessSet || witnessSet.length === 0) throw new EraSdkError("malformed-reply", "cardano-signature is missing the witness set (key 2)");
58
+ return {
59
+ requestId,
60
+ witnessSet,
61
+ witnesses: parseWitnessSet(witnessSet)
62
+ };
63
+ }
64
+ /** `[vkey, signature]` pairs from a witness-set CBOR `{0: #6.258([...])}` (the set tag is optional). */
65
+ function parseWitnessSet(witnessSet) {
66
+ let decoded;
67
+ try {
68
+ decoded = cborDecode(witnessSet);
69
+ } catch (e) {
70
+ throw new EraSdkError("malformed-reply", `witness set is not readable CBOR: ${e.message}`);
71
+ }
72
+ const root = stripTags(decoded);
73
+ if (root.kind !== "map") throw new EraSdkError("malformed-reply", "witness set is not a CBOR map");
74
+ const vkeyWitnesses = mapGet(root, 0);
75
+ const list = vkeyWitnesses === void 0 ? void 0 : stripTags(vkeyWitnesses);
76
+ if (list?.kind !== "array") throw new EraSdkError("malformed-reply", "witness set carries no vkey witnesses (key 0)");
77
+ const witnesses = [];
78
+ for (const item of list.items) {
79
+ const pair = stripTags(item);
80
+ if (pair.kind !== "array" || pair.items.length < 2) throw new EraSdkError("malformed-reply", "malformed vkey witness");
81
+ const vkey = asBytes(pair.items[0]);
82
+ const signature = asBytes(pair.items[1]);
83
+ if (!vkey || vkey.length !== 32 || !signature || signature.length !== 64) throw new EraSdkError("malformed-reply", "vkey witness is not [32-byte key, 64-byte signature]");
84
+ witnesses.push({
85
+ vkey,
86
+ signature
87
+ });
88
+ }
89
+ if (witnesses.length === 0) throw new EraSdkError("malformed-reply", "witness set is empty");
90
+ return witnesses;
91
+ }
92
+ //#endregion
93
+ export { parseWitnessSet as n, CardanoChain as t };
94
+
95
+ //# sourceMappingURL=cardano-BCT55gwf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cardano-BCT55gwf.js","names":["UrValue"],"sources":["../src/chains/cardano.ts"],"sourcesContent":["import { cborDecode } from '../cbor/decode';\nimport { cborEncode } from '../cbor/encode';\nimport type { CborValue } from '../cbor/model';\nimport {\n asBytes,\n cbArray,\n cbBytes,\n cbMap,\n cbTag,\n cbText,\n cbUint,\n mapGet,\n stripTags,\n} from '../cbor/model';\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/** One transaction input the device must sign for. */\nexport interface CardanoUtxoRef {\n /** 32-byte hash of the transaction that created the UTXO. */\n readonly transactionHash: Uint8Array;\n readonly index: number;\n /** Full signing path for this input, e.g. `m/1852'/1815'/0'/0/0`. */\n readonly path: string;\n readonly xfp: string | number;\n /** Lovelace amount as a decimal string — device display. */\n readonly amount?: string;\n /** Bech32 address of the UTXO — device display. */\n readonly address?: string;\n}\n\n/** A certificate/withdrawal key the transaction additionally needs a witness from. */\nexport interface CardanoCertKeyRef {\n /** Full signing path, e.g. the stake key `m/1852'/1815'/0'/2/0`. */\n readonly path: string;\n readonly xfp: string | number;\n /** 28-byte key hash — device display/matching. */\n readonly keyHash?: Uint8Array;\n}\n\nexport interface CardanoSignRequestProps {\n readonly requestId?: Uint8Array | string;\n /**\n * The FULL transaction CBOR (`[body, witness_set, is_valid, aux_data]`) as\n * your Cardano tooling serializes it. The device extracts the body (first\n * array element) and signs its BLAKE2b-256 hash.\n */\n readonly signData: Uint8Array;\n /** At least one; the device signs once per UNIQUE path across utxos + certKeys. */\n readonly utxos: readonly CardanoUtxoRef[];\n readonly certKeys?: readonly CardanoCertKeyRef[];\n readonly origin?: string;\n}\n\nexport interface CardanoWitness {\n /** 32-byte verification key. */\n readonly vkey: Uint8Array;\n /** 64-byte Ed25519 signature over the tx-body BLAKE2b-256 hash. */\n readonly signature: Uint8Array;\n}\n\nexport interface CardanoSignatureResult {\n readonly requestId: Uint8Array;\n /** The witness-set CBOR verbatim (`{0: #6.258([[vkey, sig]…])}`) — merge it into your tx. */\n readonly witnessSet: Uint8Array;\n /** The `[vkey, signature]` pairs parsed out of the witness set. */\n readonly witnesses: readonly CardanoWitness[];\n}\n\nconst REPLY_TYPES = ['cardano-signature'] as const;\nconst UTXO_TAG = 2201;\nconst CERT_KEY_TAG = 2204;\n\nexport class CardanoChain {\n private readonly context: ChainContext;\n\n constructor(config?: EraConnectConfig) {\n this.context = resolveContext(config);\n }\n\n /** Build a `cardano-sign-request` (2202). Reply: `cardano-signature` (2203). */\n generateSignRequest(props: CardanoSignRequestProps): SignRequest<CardanoSignatureResult> {\n const requestId = resolveRequestId(this.context, props.requestId);\n if (props.signData.length === 0) {\n throw new EraSdkError('invalid-props', 'signData must not be empty');\n }\n if (props.utxos.length === 0) {\n throw new EraSdkError('invalid-props', 'at least one utxo is required');\n }\n\n const utxos = props.utxos.map((utxo) => {\n if (utxo.transactionHash.length !== 32) {\n throw new EraSdkError('invalid-props', 'utxo transactionHash must be 32 bytes');\n }\n if (!Number.isSafeInteger(utxo.index) || utxo.index < 0) {\n throw new EraSdkError('invalid-props', 'utxo index must be a non-negative integer');\n }\n const entries: [number, CborValue][] = [\n [1, cbBytes(utxo.transactionHash)],\n [2, cbUint(utxo.index)],\n ];\n if (utxo.amount !== undefined) entries.push([3, cbText(utxo.amount)]);\n entries.push([4, keypath304(parsePath(utxo.path), normalizeXfp(utxo.xfp))]);\n if (utxo.address !== undefined) entries.push([5, cbText(utxo.address)]);\n return cbTag(UTXO_TAG, cbMap(entries));\n });\n\n const certKeys = (props.certKeys ?? []).map((certKey) => {\n const entries: [number, CborValue][] = [];\n if (certKey.keyHash !== undefined) entries.push([1, cbBytes(certKey.keyHash)]);\n entries.push([2, keypath304(parsePath(certKey.path), normalizeXfp(certKey.xfp))]);\n return cbTag(CERT_KEY_TAG, cbMap(entries));\n });\n\n const entries: [number, CborValue][] = [\n [1, cbTag(37, cbBytes(requestId))],\n [2, cbBytes(props.signData)],\n [3, cbArray(utxos)],\n ];\n if (certKeys.length > 0) entries.push([4, cbArray(certKeys)]);\n entries.push([5, cbText(props.origin ?? this.context.origin)]);\n\n const ur = new UrValue('cardano-sign-request', cborEncode(cbMap(entries)));\n return makeSignRequest({\n ur,\n requestId,\n replyTypes: REPLY_TYPES,\n context: this.context,\n parse: (reply) => parseCardanoSignature(reply, requestId),\n });\n }\n\n /** Parse a `cardano-signature` standalone. Prefer `SignRequest.scanner().parse()`. */\n parseSignature(input: Ur | string, expect?: ExpectedReply): CardanoSignatureResult {\n return parseCardanoSignature(\n toUr(input),\n expect?.requestId === undefined ? undefined : normalizeRequestId(expect.requestId),\n );\n }\n}\n\nfunction parseCardanoSignature(\n ur: Ur,\n expectedRequestId: Uint8Array | undefined,\n): CardanoSignatureResult {\n requireUrType(ur, [...REPLY_TYPES], 'cardano-signature');\n const map = requireReplyMap(ur, 'cardano-signature');\n const requestId = requireRequestIdEcho(map, 1, expectedRequestId, 'cardano-signature');\n\n const witnessSet = asBytes(mapGet(map, 2));\n if (!witnessSet || witnessSet.length === 0) {\n throw new EraSdkError(\n 'malformed-reply',\n 'cardano-signature is missing the witness set (key 2)',\n );\n }\n return { requestId, witnessSet, witnesses: parseWitnessSet(witnessSet) };\n}\n\n/** `[vkey, signature]` pairs from a witness-set CBOR `{0: #6.258([...])}` (the set tag is optional). */\nexport function parseWitnessSet(witnessSet: Uint8Array): CardanoWitness[] {\n let decoded: CborValue;\n try {\n decoded = cborDecode(witnessSet);\n } catch (e) {\n throw new EraSdkError(\n 'malformed-reply',\n `witness set is not readable CBOR: ${(e as Error).message}`,\n );\n }\n const root = stripTags(decoded);\n if (root.kind !== 'map') {\n throw new EraSdkError('malformed-reply', 'witness set is not a CBOR map');\n }\n const vkeyWitnesses = mapGet(root, 0);\n const list = vkeyWitnesses === undefined ? undefined : stripTags(vkeyWitnesses);\n if (list?.kind !== 'array') {\n throw new EraSdkError('malformed-reply', 'witness set carries no vkey witnesses (key 0)');\n }\n const witnesses: CardanoWitness[] = [];\n for (const item of list.items) {\n const pair = stripTags(item);\n if (pair.kind !== 'array' || pair.items.length < 2) {\n throw new EraSdkError('malformed-reply', 'malformed vkey witness');\n }\n const vkey = asBytes(pair.items[0]);\n const signature = asBytes(pair.items[1]);\n if (!vkey || vkey.length !== 32 || !signature || signature.length !== 64) {\n throw new EraSdkError(\n 'malformed-reply',\n 'vkey witness is not [32-byte key, 64-byte signature]',\n );\n }\n witnesses.push({ vkey, signature });\n }\n if (witnesses.length === 0) {\n throw new EraSdkError('malformed-reply', 'witness set is empty');\n }\n return witnesses;\n}\n"],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ const require_shared = require("./shared-Cu2yynP9.cjs");
2
+ const require_keypath = require("./keypath-pzm_YIfN.cjs");
3
+ //#region src/chains/cardano.ts
4
+ const REPLY_TYPES = ["cardano-signature"];
5
+ const UTXO_TAG = 2201;
6
+ const CERT_KEY_TAG = 2204;
7
+ var CardanoChain = class {
8
+ constructor(config) {
9
+ this.context = require_shared.resolveContext(config);
10
+ }
11
+ /** Build a `cardano-sign-request` (2202). Reply: `cardano-signature` (2203). */
12
+ generateSignRequest(props) {
13
+ const requestId = require_shared.resolveRequestId(this.context, props.requestId);
14
+ if (props.signData.length === 0) throw new require_shared.EraSdkError("invalid-props", "signData must not be empty");
15
+ if (props.utxos.length === 0) throw new require_shared.EraSdkError("invalid-props", "at least one utxo is required");
16
+ const utxos = props.utxos.map((utxo) => {
17
+ if (utxo.transactionHash.length !== 32) throw new require_shared.EraSdkError("invalid-props", "utxo transactionHash must be 32 bytes");
18
+ if (!Number.isSafeInteger(utxo.index) || utxo.index < 0) throw new require_shared.EraSdkError("invalid-props", "utxo index must be a non-negative integer");
19
+ const entries = [[1, require_shared.cbBytes(utxo.transactionHash)], [2, require_shared.cbUint(utxo.index)]];
20
+ if (utxo.amount !== void 0) entries.push([3, require_shared.cbText(utxo.amount)]);
21
+ entries.push([4, require_keypath.keypath304(require_keypath.parsePath(utxo.path), require_keypath.normalizeXfp(utxo.xfp))]);
22
+ if (utxo.address !== void 0) entries.push([5, require_shared.cbText(utxo.address)]);
23
+ return require_shared.cbTag(UTXO_TAG, require_shared.cbMap(entries));
24
+ });
25
+ const certKeys = (props.certKeys ?? []).map((certKey) => {
26
+ const entries = [];
27
+ if (certKey.keyHash !== void 0) entries.push([1, require_shared.cbBytes(certKey.keyHash)]);
28
+ entries.push([2, require_keypath.keypath304(require_keypath.parsePath(certKey.path), require_keypath.normalizeXfp(certKey.xfp))]);
29
+ return require_shared.cbTag(CERT_KEY_TAG, require_shared.cbMap(entries));
30
+ });
31
+ const entries = [
32
+ [1, require_shared.cbTag(37, require_shared.cbBytes(requestId))],
33
+ [2, require_shared.cbBytes(props.signData)],
34
+ [3, require_shared.cbArray(utxos)]
35
+ ];
36
+ if (certKeys.length > 0) entries.push([4, require_shared.cbArray(certKeys)]);
37
+ entries.push([5, require_shared.cbText(props.origin ?? this.context.origin)]);
38
+ const ur = new require_shared.Ur("cardano-sign-request", require_shared.cborEncode(require_shared.cbMap(entries)));
39
+ return require_shared.makeSignRequest({
40
+ ur,
41
+ requestId,
42
+ replyTypes: REPLY_TYPES,
43
+ context: this.context,
44
+ parse: (reply) => parseCardanoSignature(reply, requestId)
45
+ });
46
+ }
47
+ /** Parse a `cardano-signature` standalone. Prefer `SignRequest.scanner().parse()`. */
48
+ parseSignature(input, expect) {
49
+ return parseCardanoSignature(require_shared.toUr(input), expect?.requestId === void 0 ? void 0 : require_shared.normalizeRequestId(expect.requestId));
50
+ }
51
+ };
52
+ function parseCardanoSignature(ur, expectedRequestId) {
53
+ require_shared.requireUrType(ur, [...REPLY_TYPES], "cardano-signature");
54
+ const map = require_shared.requireReplyMap(ur, "cardano-signature");
55
+ const requestId = require_shared.requireRequestIdEcho(map, 1, expectedRequestId, "cardano-signature");
56
+ const witnessSet = require_shared.asBytes(require_shared.mapGet(map, 2));
57
+ if (!witnessSet || witnessSet.length === 0) throw new require_shared.EraSdkError("malformed-reply", "cardano-signature is missing the witness set (key 2)");
58
+ return {
59
+ requestId,
60
+ witnessSet,
61
+ witnesses: parseWitnessSet(witnessSet)
62
+ };
63
+ }
64
+ /** `[vkey, signature]` pairs from a witness-set CBOR `{0: #6.258([...])}` (the set tag is optional). */
65
+ function parseWitnessSet(witnessSet) {
66
+ let decoded;
67
+ try {
68
+ decoded = require_shared.cborDecode(witnessSet);
69
+ } catch (e) {
70
+ throw new require_shared.EraSdkError("malformed-reply", `witness set is not readable CBOR: ${e.message}`);
71
+ }
72
+ const root = require_shared.stripTags(decoded);
73
+ if (root.kind !== "map") throw new require_shared.EraSdkError("malformed-reply", "witness set is not a CBOR map");
74
+ const vkeyWitnesses = require_shared.mapGet(root, 0);
75
+ const list = vkeyWitnesses === void 0 ? void 0 : require_shared.stripTags(vkeyWitnesses);
76
+ if (list?.kind !== "array") throw new require_shared.EraSdkError("malformed-reply", "witness set carries no vkey witnesses (key 0)");
77
+ const witnesses = [];
78
+ for (const item of list.items) {
79
+ const pair = require_shared.stripTags(item);
80
+ if (pair.kind !== "array" || pair.items.length < 2) throw new require_shared.EraSdkError("malformed-reply", "malformed vkey witness");
81
+ const vkey = require_shared.asBytes(pair.items[0]);
82
+ const signature = require_shared.asBytes(pair.items[1]);
83
+ if (!vkey || vkey.length !== 32 || !signature || signature.length !== 64) throw new require_shared.EraSdkError("malformed-reply", "vkey witness is not [32-byte key, 64-byte signature]");
84
+ witnesses.push({
85
+ vkey,
86
+ signature
87
+ });
88
+ }
89
+ if (witnesses.length === 0) throw new require_shared.EraSdkError("malformed-reply", "witness set is empty");
90
+ return witnesses;
91
+ }
92
+ //#endregion
93
+ Object.defineProperty(exports, "CardanoChain", {
94
+ enumerable: true,
95
+ get: function() {
96
+ return CardanoChain;
97
+ }
98
+ });
99
+ Object.defineProperty(exports, "parseWitnessSet", {
100
+ enumerable: true,
101
+ get: function() {
102
+ return parseWitnessSet;
103
+ }
104
+ });
105
+
106
+ //# sourceMappingURL=cardano-CzYswGzK.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cardano-CzYswGzK.cjs","names":["resolveContext","resolveRequestId","EraSdkError","cbBytes","cbUint","cbText","keypath304","parsePath","normalizeXfp","cbTag","cbMap","cbArray","UrValue","cborEncode","makeSignRequest","toUr","normalizeRequestId","requireReplyMap","requireRequestIdEcho","asBytes","mapGet","cborDecode","stripTags"],"sources":["../src/chains/cardano.ts"],"sourcesContent":["import { cborDecode } from '../cbor/decode';\nimport { cborEncode } from '../cbor/encode';\nimport type { CborValue } from '../cbor/model';\nimport {\n asBytes,\n cbArray,\n cbBytes,\n cbMap,\n cbTag,\n cbText,\n cbUint,\n mapGet,\n stripTags,\n} from '../cbor/model';\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/** One transaction input the device must sign for. */\nexport interface CardanoUtxoRef {\n /** 32-byte hash of the transaction that created the UTXO. */\n readonly transactionHash: Uint8Array;\n readonly index: number;\n /** Full signing path for this input, e.g. `m/1852'/1815'/0'/0/0`. */\n readonly path: string;\n readonly xfp: string | number;\n /** Lovelace amount as a decimal string — device display. */\n readonly amount?: string;\n /** Bech32 address of the UTXO — device display. */\n readonly address?: string;\n}\n\n/** A certificate/withdrawal key the transaction additionally needs a witness from. */\nexport interface CardanoCertKeyRef {\n /** Full signing path, e.g. the stake key `m/1852'/1815'/0'/2/0`. */\n readonly path: string;\n readonly xfp: string | number;\n /** 28-byte key hash — device display/matching. */\n readonly keyHash?: Uint8Array;\n}\n\nexport interface CardanoSignRequestProps {\n readonly requestId?: Uint8Array | string;\n /**\n * The FULL transaction CBOR (`[body, witness_set, is_valid, aux_data]`) as\n * your Cardano tooling serializes it. The device extracts the body (first\n * array element) and signs its BLAKE2b-256 hash.\n */\n readonly signData: Uint8Array;\n /** At least one; the device signs once per UNIQUE path across utxos + certKeys. */\n readonly utxos: readonly CardanoUtxoRef[];\n readonly certKeys?: readonly CardanoCertKeyRef[];\n readonly origin?: string;\n}\n\nexport interface CardanoWitness {\n /** 32-byte verification key. */\n readonly vkey: Uint8Array;\n /** 64-byte Ed25519 signature over the tx-body BLAKE2b-256 hash. */\n readonly signature: Uint8Array;\n}\n\nexport interface CardanoSignatureResult {\n readonly requestId: Uint8Array;\n /** The witness-set CBOR verbatim (`{0: #6.258([[vkey, sig]…])}`) — merge it into your tx. */\n readonly witnessSet: Uint8Array;\n /** The `[vkey, signature]` pairs parsed out of the witness set. */\n readonly witnesses: readonly CardanoWitness[];\n}\n\nconst REPLY_TYPES = ['cardano-signature'] as const;\nconst UTXO_TAG = 2201;\nconst CERT_KEY_TAG = 2204;\n\nexport class CardanoChain {\n private readonly context: ChainContext;\n\n constructor(config?: EraConnectConfig) {\n this.context = resolveContext(config);\n }\n\n /** Build a `cardano-sign-request` (2202). Reply: `cardano-signature` (2203). */\n generateSignRequest(props: CardanoSignRequestProps): SignRequest<CardanoSignatureResult> {\n const requestId = resolveRequestId(this.context, props.requestId);\n if (props.signData.length === 0) {\n throw new EraSdkError('invalid-props', 'signData must not be empty');\n }\n if (props.utxos.length === 0) {\n throw new EraSdkError('invalid-props', 'at least one utxo is required');\n }\n\n const utxos = props.utxos.map((utxo) => {\n if (utxo.transactionHash.length !== 32) {\n throw new EraSdkError('invalid-props', 'utxo transactionHash must be 32 bytes');\n }\n if (!Number.isSafeInteger(utxo.index) || utxo.index < 0) {\n throw new EraSdkError('invalid-props', 'utxo index must be a non-negative integer');\n }\n const entries: [number, CborValue][] = [\n [1, cbBytes(utxo.transactionHash)],\n [2, cbUint(utxo.index)],\n ];\n if (utxo.amount !== undefined) entries.push([3, cbText(utxo.amount)]);\n entries.push([4, keypath304(parsePath(utxo.path), normalizeXfp(utxo.xfp))]);\n if (utxo.address !== undefined) entries.push([5, cbText(utxo.address)]);\n return cbTag(UTXO_TAG, cbMap(entries));\n });\n\n const certKeys = (props.certKeys ?? []).map((certKey) => {\n const entries: [number, CborValue][] = [];\n if (certKey.keyHash !== undefined) entries.push([1, cbBytes(certKey.keyHash)]);\n entries.push([2, keypath304(parsePath(certKey.path), normalizeXfp(certKey.xfp))]);\n return cbTag(CERT_KEY_TAG, cbMap(entries));\n });\n\n const entries: [number, CborValue][] = [\n [1, cbTag(37, cbBytes(requestId))],\n [2, cbBytes(props.signData)],\n [3, cbArray(utxos)],\n ];\n if (certKeys.length > 0) entries.push([4, cbArray(certKeys)]);\n entries.push([5, cbText(props.origin ?? this.context.origin)]);\n\n const ur = new UrValue('cardano-sign-request', cborEncode(cbMap(entries)));\n return makeSignRequest({\n ur,\n requestId,\n replyTypes: REPLY_TYPES,\n context: this.context,\n parse: (reply) => parseCardanoSignature(reply, requestId),\n });\n }\n\n /** Parse a `cardano-signature` standalone. Prefer `SignRequest.scanner().parse()`. */\n parseSignature(input: Ur | string, expect?: ExpectedReply): CardanoSignatureResult {\n return parseCardanoSignature(\n toUr(input),\n expect?.requestId === undefined ? undefined : normalizeRequestId(expect.requestId),\n );\n }\n}\n\nfunction parseCardanoSignature(\n ur: Ur,\n expectedRequestId: Uint8Array | undefined,\n): CardanoSignatureResult {\n requireUrType(ur, [...REPLY_TYPES], 'cardano-signature');\n const map = requireReplyMap(ur, 'cardano-signature');\n const requestId = requireRequestIdEcho(map, 1, expectedRequestId, 'cardano-signature');\n\n const witnessSet = asBytes(mapGet(map, 2));\n if (!witnessSet || witnessSet.length === 0) {\n throw new EraSdkError(\n 'malformed-reply',\n 'cardano-signature is missing the witness set (key 2)',\n );\n }\n return { requestId, witnessSet, witnesses: parseWitnessSet(witnessSet) };\n}\n\n/** `[vkey, signature]` pairs from a witness-set CBOR `{0: #6.258([...])}` (the set tag is optional). */\nexport function parseWitnessSet(witnessSet: Uint8Array): CardanoWitness[] {\n let decoded: CborValue;\n try {\n decoded = cborDecode(witnessSet);\n } catch (e) {\n throw new EraSdkError(\n 'malformed-reply',\n `witness set is not readable CBOR: ${(e as Error).message}`,\n );\n }\n const root = stripTags(decoded);\n if (root.kind !== 'map') {\n throw new EraSdkError('malformed-reply', 'witness set is not a CBOR map');\n }\n const vkeyWitnesses = mapGet(root, 0);\n const list = vkeyWitnesses === undefined ? undefined : stripTags(vkeyWitnesses);\n if (list?.kind !== 'array') {\n throw new EraSdkError('malformed-reply', 'witness set carries no vkey witnesses (key 0)');\n }\n const witnesses: CardanoWitness[] = [];\n for (const item of list.items) {\n const pair = stripTags(item);\n if (pair.kind !== 'array' || pair.items.length < 2) {\n throw new EraSdkError('malformed-reply', 'malformed vkey witness');\n }\n const vkey = asBytes(pair.items[0]);\n const signature = asBytes(pair.items[1]);\n if (!vkey || vkey.length !== 32 || !signature || signature.length !== 64) {\n throw new EraSdkError(\n 'malformed-reply',\n 'vkey witness is not [32-byte key, 64-byte signature]',\n );\n }\n witnesses.push({ vkey, signature });\n }\n if (witnesses.length === 0) {\n throw new EraSdkError('malformed-reply', 'witness set is empty');\n }\n return witnesses;\n}\n"],"mappings":"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"}