@hwlt/era-connect 0.3.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 (131) hide show
  1. package/README.md +8 -14
  2. package/bch/package.json +6 -0
  3. package/cosmos/package.json +6 -0
  4. package/dist/bch-CK6dKGQA.d.ts +74 -0
  5. package/dist/bch-CpLACx0j.cjs +139 -0
  6. package/dist/bch-CpLACx0j.cjs.map +1 -0
  7. package/dist/bch-DHsIVJqK.d.cts +74 -0
  8. package/dist/bch-dMESlQfv.js +134 -0
  9. package/dist/bch-dMESlQfv.js.map +1 -0
  10. package/dist/bch.cjs +13 -0
  11. package/dist/bch.d.cts +26 -0
  12. package/dist/bch.d.ts +26 -0
  13. package/dist/bch.js +4 -0
  14. package/dist/{btc-VyJKXq7d.js → btc-BmVgvvBd.js} +17 -8
  15. package/dist/btc-BmVgvvBd.js.map +1 -0
  16. package/dist/{btc-Fr6Jz4Fi.cjs → btc-BxNww6kW.cjs} +20 -11
  17. package/dist/btc-BxNww6kW.cjs.map +1 -0
  18. package/dist/{btc-DfQ0q79G.d.ts → btc-uzt0uC-Q.d.ts} +15 -4
  19. package/dist/{btc-Dn6_Y12c.d.cts → btc-wogJ4INA.d.cts} +15 -4
  20. package/dist/btc.cjs +2 -2
  21. package/dist/btc.d.cts +1 -1
  22. package/dist/btc.d.ts +1 -1
  23. package/dist/btc.js +2 -2
  24. package/dist/{cardano-yuOygqwp.js → cardano-BCT55gwf.js} +3 -2
  25. package/dist/{cardano-yuOygqwp.js.map → cardano-BCT55gwf.js.map} +1 -1
  26. package/dist/{cardano-DP6U_K1J.cjs → cardano-CzYswGzK.cjs} +5 -4
  27. package/dist/{cardano-DP6U_K1J.cjs.map → cardano-CzYswGzK.cjs.map} +1 -1
  28. package/dist/cardano.cjs +2 -2
  29. package/dist/cardano.js +2 -2
  30. package/dist/cashaddr-BIzawizX.js +134 -0
  31. package/dist/cashaddr-BIzawizX.js.map +1 -0
  32. package/dist/cashaddr-Ci0_8ITN.cjs +151 -0
  33. package/dist/cashaddr-Ci0_8ITN.cjs.map +1 -0
  34. package/dist/cosmos-BYrhIqRk.d.cts +77 -0
  35. package/dist/cosmos-DDsgtUki.cjs +113 -0
  36. package/dist/cosmos-DDsgtUki.cjs.map +1 -0
  37. package/dist/cosmos-DqyjA6lg.d.ts +77 -0
  38. package/dist/cosmos-jUHNxgWC.js +102 -0
  39. package/dist/cosmos-jUHNxgWC.js.map +1 -0
  40. package/dist/cosmos.cjs +10 -0
  41. package/dist/cosmos.d.cts +3 -0
  42. package/dist/cosmos.d.ts +3 -0
  43. package/dist/cosmos.js +3 -0
  44. package/dist/{derive-C4JdycUh.js → derive-00JUOkmn.js} +14 -3
  45. package/dist/derive-00JUOkmn.js.map +1 -0
  46. package/dist/{derive-CuILJRPT.cjs → derive-BuNsfWlg.cjs} +25 -2
  47. package/dist/derive-BuNsfWlg.cjs.map +1 -0
  48. package/dist/{evm-kJb3-oiR.js → evm-CI1cuZsp.js} +3 -2
  49. package/dist/{evm-kJb3-oiR.js.map → evm-CI1cuZsp.js.map} +1 -1
  50. package/dist/{evm-BHj_2OTG.cjs → evm-yfKOc2dy.cjs} +6 -5
  51. package/dist/{evm-BHj_2OTG.cjs.map → evm-yfKOc2dy.cjs.map} +1 -1
  52. package/dist/evm.cjs +2 -2
  53. package/dist/evm.js +2 -2
  54. package/dist/gzip-CLzDX7AZ.cjs +79 -0
  55. package/dist/gzip-CLzDX7AZ.cjs.map +1 -0
  56. package/dist/gzip-DyZLtgJJ.js +68 -0
  57. package/dist/gzip-DyZLtgJJ.js.map +1 -0
  58. package/dist/index.cjs +120 -31
  59. package/dist/index.cjs.map +1 -1
  60. package/dist/index.d.cts +50 -3
  61. package/dist/index.d.ts +50 -3
  62. package/dist/index.js +90 -9
  63. package/dist/index.js.map +1 -1
  64. package/dist/keypath-Bvv_cfHt.js +75 -0
  65. package/dist/keypath-Bvv_cfHt.js.map +1 -0
  66. package/dist/keypath-pzm_YIfN.cjs +116 -0
  67. package/dist/keypath-pzm_YIfN.cjs.map +1 -0
  68. package/dist/{messages-17Oup0-Z.js → messages--XbVef_k.js} +39 -3
  69. package/dist/messages--XbVef_k.js.map +1 -0
  70. package/dist/{messages-BzcdWlg1.cjs → messages-orAQ52CI.cjs} +44 -2
  71. package/dist/messages-orAQ52CI.cjs.map +1 -0
  72. package/dist/{shared-B8Wc7ViU.js → shared-Bbgejrus.js} +2 -73
  73. package/dist/shared-Bbgejrus.js.map +1 -0
  74. package/dist/{shared-nISrEktU.cjs → shared-Cu2yynP9.cjs} +13 -114
  75. package/dist/shared-Cu2yynP9.cjs.map +1 -0
  76. package/dist/{solana-DdEn24X4.cjs → solana-BoQBy0hu.cjs} +6 -5
  77. package/dist/{solana-DdEn24X4.cjs.map → solana-BoQBy0hu.cjs.map} +1 -1
  78. package/dist/{solana-C7JQEAec.js → solana-BukH3AI3.js} +3 -2
  79. package/dist/{solana-C7JQEAec.js.map → solana-BukH3AI3.js.map} +1 -1
  80. package/dist/solana.cjs +2 -2
  81. package/dist/solana.js +2 -2
  82. package/dist/sui-6WX6VPlW.d.cts +49 -0
  83. package/dist/sui-BtbPG6Xt.js +71 -0
  84. package/dist/sui-BtbPG6Xt.js.map +1 -0
  85. package/dist/sui-CYRn331O.cjs +82 -0
  86. package/dist/sui-CYRn331O.cjs.map +1 -0
  87. package/dist/sui-DI0ruW7E.d.ts +49 -0
  88. package/dist/sui.cjs +10 -0
  89. package/dist/sui.d.cts +3 -0
  90. package/dist/sui.d.ts +3 -0
  91. package/dist/sui.js +3 -0
  92. package/dist/{ton-5Uvi5ifg.js → ton-BSPKU5B9.js} +3 -2
  93. package/dist/{ton-5Uvi5ifg.js.map → ton-BSPKU5B9.js.map} +1 -1
  94. package/dist/{ton-DUJGXdGa.cjs → ton-CI2s9puH.cjs} +6 -5
  95. package/dist/{ton-DUJGXdGa.cjs.map → ton-CI2s9puH.cjs.map} +1 -1
  96. package/dist/ton.cjs +2 -2
  97. package/dist/ton.js +2 -2
  98. package/dist/{tron-Bo9HnvVE.js → tron-BpUEYTPT.js} +5 -67
  99. package/dist/tron-BpUEYTPT.js.map +1 -0
  100. package/dist/{tron-CQIEqjCA.cjs → tron-DYPOg5cm.cjs} +10 -72
  101. package/dist/{tron-Bo9HnvVE.js.map → tron-DYPOg5cm.cjs.map} +1 -1
  102. package/dist/tron.cjs +3 -3
  103. package/dist/tron.js +3 -3
  104. package/dist/verify.cjs +414 -24
  105. package/dist/verify.cjs.map +1 -1
  106. package/dist/verify.d.cts +120 -1
  107. package/dist/verify.d.ts +120 -1
  108. package/dist/verify.js +408 -24
  109. package/dist/verify.js.map +1 -1
  110. package/dist/xrp-BXAeK4iH.d.cts +32 -0
  111. package/dist/xrp-BrHXUnX5.cjs +52 -0
  112. package/dist/xrp-BrHXUnX5.cjs.map +1 -0
  113. package/dist/xrp-C8LJO5b-.js +47 -0
  114. package/dist/xrp-C8LJO5b-.js.map +1 -0
  115. package/dist/xrp-DqCXGYEk.d.ts +32 -0
  116. package/dist/xrp.cjs +9 -0
  117. package/dist/xrp.d.cts +3 -0
  118. package/dist/xrp.d.ts +3 -0
  119. package/dist/xrp.js +3 -0
  120. package/package.json +49 -1
  121. package/sui/package.json +6 -0
  122. package/xrp/package.json +6 -0
  123. package/dist/btc-Fr6Jz4Fi.cjs.map +0 -1
  124. package/dist/btc-VyJKXq7d.js.map +0 -1
  125. package/dist/derive-C4JdycUh.js.map +0 -1
  126. package/dist/derive-CuILJRPT.cjs.map +0 -1
  127. package/dist/messages-17Oup0-Z.js.map +0 -1
  128. package/dist/messages-BzcdWlg1.cjs.map +0 -1
  129. package/dist/shared-B8Wc7ViU.js.map +0 -1
  130. package/dist/shared-nISrEktU.cjs.map +0 -1
  131. package/dist/tron-CQIEqjCA.cjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"tron-Bo9HnvVE.js","names":["UrValue"],"sources":["../src/tron-proto/gzip.ts","../src/chains/tron.ts"],"sourcesContent":["import { Gunzip, gzipSync } from 'fflate';\nimport { concatBytes } from '../core/bytes';\nimport { EraSdkError } from '../core/errors';\nimport { crc32 } from '../ur/crc32';\n\n/** Smallest possible gzip stream: 10-byte header + 8-byte trailer. */\nconst MIN_GZIP_BYTES = 18;\n\n/** Bytes fed to the inflater per step; bounds the overshoot past the cap. */\nconst SLICE_BYTES = 1024;\n\n/** Deterministic gzip (fixed level, zeroed mtime) for reproducible request bytes. */\nexport function gzipCompress(data: Uint8Array): Uint8Array {\n return gzipSync(data, { level: 9, mtime: 0 });\n}\n\n/**\n * Inflate with a hard output ceiling.\n *\n * A one-shot gunzip allocates the entire output before anyone can refuse it —\n * gzip reaches ~1000:1 in practice, so a few hundred scanned bytes could ask\n * for an arbitrary allocation. The decoder is therefore driven in slices and\n * the output counted as it arrives; the moment the total would pass the cap,\n * feeding stops and the reply is refused.\n *\n * The trailer's ISIZE (declared inflated length) is used twice: as a cheap\n * refusal of honest bombs before any work, and as a truncation check at the\n * end — an inflater hands back a partial buffer for a truncated stream\n * without erroring, and a genuine device reply always declares honestly.\n */\nexport function gunzipCapped(data: Uint8Array, maxOutputBytes: number): Uint8Array {\n if (data.length < MIN_GZIP_BYTES) {\n throw new EraSdkError('gzip-error', 'compressed payload is too short to be a gzip stream');\n }\n if (data[0] !== 0x1f || data[1] !== 0x8b) {\n throw new EraSdkError('gzip-error', 'compressed payload is not a gzip stream');\n }\n const n = data.length;\n const isize =\n (data[n - 4]! | (data[n - 3]! << 8) | (data[n - 2]! << 16) | (data[n - 1]! << 24)) >>> 0;\n if (isize > maxOutputBytes) {\n throw new EraSdkError(\n 'gzip-error',\n `compressed payload declares ${isize} bytes, over the ${maxOutputBytes} byte ceiling`,\n );\n }\n\n const chunks: Uint8Array[] = [];\n let total = 0;\n let overflowed = false;\n let malformed: string | null = null;\n\n const gunzip = new Gunzip((chunk) => {\n if (overflowed) return;\n if (total + chunk.length > maxOutputBytes) {\n overflowed = true;\n return;\n }\n chunks.push(chunk);\n total += chunk.length;\n });\n\n try {\n for (let offset = 0; offset < n && !overflowed; offset += SLICE_BYTES) {\n const end = Math.min(offset + SLICE_BYTES, n);\n const isLast = end === n;\n gunzip.push(data.slice(offset, end), isLast);\n }\n } catch (e) {\n malformed = (e as Error).message ?? 'inflate error';\n }\n\n if (overflowed) {\n throw new EraSdkError(\n 'gzip-error',\n `compressed payload inflates past the ${maxOutputBytes} byte ceiling`,\n );\n }\n if (malformed !== null) {\n throw new EraSdkError('gzip-error', `compressed payload is malformed: ${malformed}`);\n }\n const out = concatBytes(...chunks);\n if (out.length !== isize) {\n throw new EraSdkError(\n 'gzip-error',\n `compressed payload inflated to ${out.length} bytes but declares ${isize} — truncated or malformed`,\n );\n }\n // The trailer CRC32 (little-endian, bytes n-8..n-5) must cover the inflated\n // output. The streaming inflater does not verify it, and the reference\n // implementation's native decoder does — without this check a corrupted\n // stream, or a CONCATENATED multi-member stream (whose final member's CRC\n // cannot cover the whole output), would be accepted here and refused there.\n const declaredCrc =\n (data[n - 8]! | (data[n - 7]! << 8) | (data[n - 6]! << 16) | (data[n - 5]! << 24)) >>> 0;\n if (crc32(out) !== declaredCrc) {\n throw new EraSdkError('gzip-error', 'compressed payload is malformed: CRC mismatch');\n }\n return out;\n}\n","import { cborEncode } from '../cbor/encode';\nimport { asBytes, cbBytes, cbMap, cbText, mapGet } from '../cbor/model';\nimport { EraSdkError } from '../core/errors';\nimport { normalizeRequestId, uuidStringify } from '../core/rand';\nimport { normalizeXfp, parsePath, xfpToHex } from '../registry/keypath';\nimport { gunzipCapped, gzipCompress } from '../tron-proto/gzip';\nimport type { SignedTronTx, TronLatestBlock } from '../tron-proto/messages';\nimport {\n decodeSignResultProto,\n encodeSignRequestProto,\n splitSignedTronTx,\n} from '../tron-proto/messages';\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 requireUrType,\n resolveContext,\n resolveRequestId,\n toUr,\n} from './shared';\n\nexport type { TronLatestBlock } from '../tron-proto/messages';\n\nexport interface TronSignRequestProps {\n readonly requestId?: Uint8Array | string;\n /**\n * Serialized `Transaction.raw_data` — THE signing source of truth. The\n * device signs `sha256(rawData) = txID` and returns the transaction with\n * `raw_data` unmodified.\n */\n readonly rawData: Uint8Array;\n /** Full signing path, e.g. `m/44'/195'/0'/0/0`. */\n readonly path: string;\n readonly xfp: string | number;\n /**\n * Reference block context. Source it from a LIVE now-block query and pass\n * the FULL 64-hex block id.\n */\n readonly latestBlock: TronLatestBlock;\n /** On-device display only; safe to omit for opaque dApp transactions. */\n readonly display?: {\n readonly token?: string;\n readonly contractAddress?: string;\n readonly from?: string;\n readonly to?: string;\n readonly value?: string;\n readonly memo?: string;\n readonly fee?: number;\n readonly decimals?: number;\n };\n readonly timestamp?: number;\n readonly origin?: string;\n}\n\nexport interface TronSignatureResult {\n readonly requestId: Uint8Array;\n /** `sha256(raw_data)` hex, as computed by the device. */\n readonly txId: string;\n /** Hex of the fully assembled signed transaction — broadcast as-is. */\n readonly rawTx: string;\n /** The signed frame split into `raw_data` + signatures (65-byte r||s||recovery each). */\n readonly signedTx: SignedTronTx;\n}\n\n/**\n * Ceilings on the gzip blob a `keystone-sign-result` may carry. Tron is the\n * only chain whose reply is compressed, so it is the only one where a few\n * hundred scanned bytes can ask for an arbitrary allocation. Generous\n * multiples of the largest real device reply.\n */\nconst MAX_COMPRESSED_BYTES = 8 * 1024;\nconst MAX_INFLATED_BYTES = 64 * 1024;\n\nconst REPLY_TYPES = ['keystone-sign-result'] as const;\n\n/**\n * Tron signing rides the structured `keystone-sign-request` (6101) envelope —\n * a gzip-compressed protobuf inside CBOR `{1: gzip(protobuf), 2: origin}`.\n * The registry's generic `tron-sign-request` (5101) is NOT accepted by the\n * device and gets no response; do not emit it.\n */\nexport class TronChain {\n private readonly context: ChainContext;\n\n constructor(config?: EraConnectConfig) {\n this.context = resolveContext(config);\n }\n\n /** Build a `keystone-sign-request` (6101). Reply: `keystone-sign-result` (6102). */\n generateSignRequest(props: TronSignRequestProps): SignRequest<TronSignatureResult> {\n const requestId = resolveRequestId(this.context, props.requestId);\n parsePath(props.path); // validate shape; the wire carries the string form\n const xfp = normalizeXfp(props.xfp);\n if (props.rawData.length === 0) {\n throw new EraSdkError('invalid-props', 'rawData must not be empty');\n }\n if (!/^[0-9a-fA-F]{64}$/.test(props.latestBlock.hash)) {\n throw new EraSdkError(\n 'invalid-props',\n 'latestBlock.hash must be the FULL 64-hex block id (the device slices ref_block_hash from it)',\n );\n }\n\n const proto = encodeSignRequestProto({\n // Zero-padded to eight characters: the firmware parses this string with\n // a hex reader that yields 0 for anything shorter than 4 bytes, and a\n // zero fingerprint fails validation — a wallet whose fingerprint starts\n // with a zero byte (1 in 256) could not sign at all without the pad.\n xfpHex: xfpToHex(xfp),\n signId: uuidStringify(requestId),\n hdPath: props.path,\n timestamp: props.timestamp ?? 0,\n decimals: props.display?.decimals ?? 6,\n token: props.display?.token ?? '',\n contractAddress: props.display?.contractAddress,\n from: props.display?.from,\n to: props.display?.to,\n memo: props.display?.memo,\n value: props.display?.value,\n fee: props.display?.fee,\n latestBlock: props.latestBlock,\n rawData: props.rawData,\n });\n\n const ur = new UrValue(\n 'keystone-sign-request',\n cborEncode(\n cbMap([\n [1, cbBytes(gzipCompress(proto))],\n [2, cbText(props.origin ?? this.context.origin)],\n ]),\n ),\n );\n return makeSignRequest({\n ur,\n requestId,\n replyTypes: REPLY_TYPES,\n context: this.context,\n parse: (reply) => parseTronSignature(reply, requestId),\n });\n }\n\n /**\n * Parse a `keystone-sign-result` standalone. Tron carries the request id\n * INSIDE the protobuf (`signId`); passing `expect.requestId` is what makes\n * the echo check possible here — prefer `SignRequest.scanner().parse()`.\n */\n parseSignature(input: Ur | string, expect?: ExpectedReply): TronSignatureResult {\n return parseTronSignature(\n toUr(input),\n expect?.requestId === undefined ? undefined : normalizeRequestId(expect.requestId),\n );\n }\n}\n\nfunction parseTronSignature(\n ur: Ur,\n expectedRequestId: Uint8Array | undefined,\n): TronSignatureResult {\n requireUrType(ur, [...REPLY_TYPES], 'keystone-sign-result');\n const map = requireReplyMap(ur, 'keystone-sign-result');\n const compressed = asBytes(mapGet(map, 1));\n if (!compressed) {\n throw new EraSdkError('malformed-reply', 'keystone-sign-result is missing the payload (key 1)');\n }\n if (compressed.length > MAX_COMPRESSED_BYTES) {\n throw new EraSdkError(\n 'limit-exceeded',\n `keystone-sign-result payload is ${compressed.length} bytes, over the ${MAX_COMPRESSED_BYTES} byte ceiling`,\n );\n }\n const result = decodeSignResultProto(gunzipCapped(compressed, MAX_INFLATED_BYTES));\n\n // The signId echo is the ONLY anti-replay binding on this chain — the\n // device's own bytes are broadcast verbatim, so a stale reply that skipped\n // this check would finalize a payment the user did not approve now.\n if (expectedRequestId !== undefined) {\n const expected = uuidStringify(expectedRequestId).toLowerCase();\n if (result.signId.toLowerCase() !== expected) {\n throw new EraSdkError(\n 'request-id-mismatch',\n result.signId === ''\n ? 'keystone-sign-result does not echo the request id (signId)'\n : 'keystone-sign-result echoes a different request id — it answers another sign request, not this one',\n );\n }\n }\n if (result.rawTx === '') {\n throw new EraSdkError('malformed-reply', 'keystone-sign-result has no signed transaction');\n }\n const signedTx = splitSignedTronTx(result.rawTx);\n const requestId = expectedRequestId ?? signIdToBytes(result.signId);\n return { requestId, txId: result.txId, rawTx: result.rawTx, signedTx };\n}\n\nfunction signIdToBytes(signId: string): Uint8Array {\n try {\n return normalizeRequestId(signId);\n } catch {\n return new Uint8Array(16);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tron-DYPOg5cm.cjs","names":["resolveContext","resolveRequestId","normalizeXfp","EraSdkError","encodeSignRequestProto","xfpToHex","uuidStringify","UrValue","cborEncode","cbMap","cbBytes","gzipCompress","cbText","makeSignRequest","toUr","normalizeRequestId","requireReplyMap","asBytes","mapGet","decodeSignResultProto","gunzipCapped","splitSignedTronTx"],"sources":["../src/chains/tron.ts"],"sourcesContent":["import { cborEncode } from '../cbor/encode';\nimport { asBytes, cbBytes, cbMap, cbText, mapGet } from '../cbor/model';\nimport { EraSdkError } from '../core/errors';\nimport { normalizeRequestId, uuidStringify } from '../core/rand';\nimport { normalizeXfp, parsePath, xfpToHex } from '../registry/keypath';\nimport { gunzipCapped, gzipCompress } from '../tron-proto/gzip';\nimport type { SignedTronTx, TronLatestBlock } from '../tron-proto/messages';\nimport {\n decodeSignResultProto,\n encodeSignRequestProto,\n splitSignedTronTx,\n} from '../tron-proto/messages';\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 requireUrType,\n resolveContext,\n resolveRequestId,\n toUr,\n} from './shared';\n\nexport type { TronLatestBlock } from '../tron-proto/messages';\n\nexport interface TronSignRequestProps {\n readonly requestId?: Uint8Array | string;\n /**\n * Serialized `Transaction.raw_data` — THE signing source of truth. The\n * device signs `sha256(rawData) = txID` and returns the transaction with\n * `raw_data` unmodified.\n */\n readonly rawData: Uint8Array;\n /** Full signing path, e.g. `m/44'/195'/0'/0/0`. */\n readonly path: string;\n readonly xfp: string | number;\n /**\n * Reference block context. Source it from a LIVE now-block query and pass\n * the FULL 64-hex block id.\n */\n readonly latestBlock: TronLatestBlock;\n /** On-device display only; safe to omit for opaque dApp transactions. */\n readonly display?: {\n readonly token?: string;\n readonly contractAddress?: string;\n readonly from?: string;\n readonly to?: string;\n readonly value?: string;\n readonly memo?: string;\n readonly fee?: number;\n readonly decimals?: number;\n };\n readonly timestamp?: number;\n readonly origin?: string;\n}\n\nexport interface TronSignatureResult {\n readonly requestId: Uint8Array;\n /** `sha256(raw_data)` hex, as computed by the device. */\n readonly txId: string;\n /** Hex of the fully assembled signed transaction — broadcast as-is. */\n readonly rawTx: string;\n /** The signed frame split into `raw_data` + signatures (65-byte r||s||recovery each). */\n readonly signedTx: SignedTronTx;\n}\n\n/**\n * Ceilings on the gzip blob a `keystone-sign-result` may carry. Tron is the\n * only chain whose reply is compressed, so it is the only one where a few\n * hundred scanned bytes can ask for an arbitrary allocation. Generous\n * multiples of the largest real device reply.\n */\nconst MAX_COMPRESSED_BYTES = 8 * 1024;\nconst MAX_INFLATED_BYTES = 64 * 1024;\n\nconst REPLY_TYPES = ['keystone-sign-result'] as const;\n\n/**\n * Tron signing rides the structured `keystone-sign-request` (6101) envelope —\n * a gzip-compressed protobuf inside CBOR `{1: gzip(protobuf), 2: origin}`.\n * The registry's generic `tron-sign-request` (5101) is NOT accepted by the\n * device and gets no response; do not emit it.\n */\nexport class TronChain {\n private readonly context: ChainContext;\n\n constructor(config?: EraConnectConfig) {\n this.context = resolveContext(config);\n }\n\n /** Build a `keystone-sign-request` (6101). Reply: `keystone-sign-result` (6102). */\n generateSignRequest(props: TronSignRequestProps): SignRequest<TronSignatureResult> {\n const requestId = resolveRequestId(this.context, props.requestId);\n parsePath(props.path); // validate shape; the wire carries the string form\n const xfp = normalizeXfp(props.xfp);\n if (props.rawData.length === 0) {\n throw new EraSdkError('invalid-props', 'rawData must not be empty');\n }\n if (!/^[0-9a-fA-F]{64}$/.test(props.latestBlock.hash)) {\n throw new EraSdkError(\n 'invalid-props',\n 'latestBlock.hash must be the FULL 64-hex block id (the device slices ref_block_hash from it)',\n );\n }\n\n const proto = encodeSignRequestProto({\n // Zero-padded to eight characters: the firmware parses this string with\n // a hex reader that yields 0 for anything shorter than 4 bytes, and a\n // zero fingerprint fails validation — a wallet whose fingerprint starts\n // with a zero byte (1 in 256) could not sign at all without the pad.\n xfpHex: xfpToHex(xfp),\n signId: uuidStringify(requestId),\n hdPath: props.path,\n timestamp: props.timestamp ?? 0,\n decimals: props.display?.decimals ?? 6,\n token: props.display?.token ?? '',\n contractAddress: props.display?.contractAddress,\n from: props.display?.from,\n to: props.display?.to,\n memo: props.display?.memo,\n value: props.display?.value,\n fee: props.display?.fee,\n latestBlock: props.latestBlock,\n rawData: props.rawData,\n });\n\n const ur = new UrValue(\n 'keystone-sign-request',\n cborEncode(\n cbMap([\n [1, cbBytes(gzipCompress(proto))],\n [2, cbText(props.origin ?? this.context.origin)],\n ]),\n ),\n );\n return makeSignRequest({\n ur,\n requestId,\n replyTypes: REPLY_TYPES,\n context: this.context,\n parse: (reply) => parseTronSignature(reply, requestId),\n });\n }\n\n /**\n * Parse a `keystone-sign-result` standalone. Tron carries the request id\n * INSIDE the protobuf (`signId`); passing `expect.requestId` is what makes\n * the echo check possible here — prefer `SignRequest.scanner().parse()`.\n */\n parseSignature(input: Ur | string, expect?: ExpectedReply): TronSignatureResult {\n return parseTronSignature(\n toUr(input),\n expect?.requestId === undefined ? undefined : normalizeRequestId(expect.requestId),\n );\n }\n}\n\nfunction parseTronSignature(\n ur: Ur,\n expectedRequestId: Uint8Array | undefined,\n): TronSignatureResult {\n requireUrType(ur, [...REPLY_TYPES], 'keystone-sign-result');\n const map = requireReplyMap(ur, 'keystone-sign-result');\n const compressed = asBytes(mapGet(map, 1));\n if (!compressed) {\n throw new EraSdkError('malformed-reply', 'keystone-sign-result is missing the payload (key 1)');\n }\n if (compressed.length > MAX_COMPRESSED_BYTES) {\n throw new EraSdkError(\n 'limit-exceeded',\n `keystone-sign-result payload is ${compressed.length} bytes, over the ${MAX_COMPRESSED_BYTES} byte ceiling`,\n );\n }\n const result = decodeSignResultProto(gunzipCapped(compressed, MAX_INFLATED_BYTES));\n\n // The signId echo is the ONLY anti-replay binding on this chain — the\n // device's own bytes are broadcast verbatim, so a stale reply that skipped\n // this check would finalize a payment the user did not approve now.\n if (expectedRequestId !== undefined) {\n const expected = uuidStringify(expectedRequestId).toLowerCase();\n if (result.signId.toLowerCase() !== expected) {\n throw new EraSdkError(\n 'request-id-mismatch',\n result.signId === ''\n ? 'keystone-sign-result does not echo the request id (signId)'\n : 'keystone-sign-result echoes a different request id — it answers another sign request, not this one',\n );\n }\n }\n if (result.rawTx === '') {\n throw new EraSdkError('malformed-reply', 'keystone-sign-result has no signed transaction');\n }\n const signedTx = splitSignedTronTx(result.rawTx);\n const requestId = expectedRequestId ?? signIdToBytes(result.signId);\n return { requestId, txId: result.txId, rawTx: result.rawTx, signedTx };\n}\n\nfunction signIdToBytes(signId: string): Uint8Array {\n try {\n return normalizeRequestId(signId);\n } catch {\n return new Uint8Array(16);\n }\n}\n"],"mappings":"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"}
package/dist/tron.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_shared = require("./shared-nISrEktU.cjs");
3
- const require_tron = require("./tron-CQIEqjCA.cjs");
4
- const require_messages = require("./messages-BzcdWlg1.cjs");
2
+ const require_shared = require("./shared-Cu2yynP9.cjs");
3
+ const require_messages = require("./messages-orAQ52CI.cjs");
4
+ const require_tron = require("./tron-DYPOg5cm.cjs");
5
5
  exports.AnimatedUr = require_shared.AnimatedUr;
6
6
  exports.EraSdkError = require_shared.EraSdkError;
7
7
  exports.TronChain = require_tron.TronChain;
package/dist/tron.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Z as EraSdkError, _ as Ur, d as UrScanner, f as AnimatedUr, u as TypedUrScanner } from "./shared-B8Wc7ViU.js";
2
- import { t as TronChain } from "./tron-Bo9HnvVE.js";
3
- import { r as splitSignedTronTx } from "./messages-17Oup0-Z.js";
1
+ import { K as EraSdkError, _ as Ur, d as UrScanner, f as AnimatedUr, u as TypedUrScanner } from "./shared-Bbgejrus.js";
2
+ import { i as splitSignedTronTx } from "./messages--XbVef_k.js";
3
+ import { t as TronChain } from "./tron-BpUEYTPT.js";
4
4
  export { AnimatedUr, EraSdkError, TronChain, TypedUrScanner, Ur, UrScanner, splitSignedTronTx };
package/dist/verify.cjs CHANGED
@@ -1,16 +1,237 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_shared = require("./shared-nISrEktU.cjs");
3
- const require_derive = require("./derive-CuILJRPT.cjs");
4
- const require_cardano = require("./cardano-DP6U_K1J.cjs");
5
- const require_evm = require("./evm-BHj_2OTG.cjs");
6
- const require_ton = require("./ton-DUJGXdGa.cjs");
7
- const require_messages = require("./messages-BzcdWlg1.cjs");
2
+ const require_shared = require("./shared-Cu2yynP9.cjs");
3
+ const require_keypath = require("./keypath-pzm_YIfN.cjs");
4
+ const require_cashaddr = require("./cashaddr-Ci0_8ITN.cjs");
5
+ const require_derive = require("./derive-BuNsfWlg.cjs");
6
+ const require_cardano = require("./cardano-CzYswGzK.cjs");
7
+ const require_messages = require("./messages-orAQ52CI.cjs");
8
+ const require_evm = require("./evm-yfKOc2dy.cjs");
9
+ const require_sui = require("./sui-CYRn331O.cjs");
10
+ const require_ton = require("./ton-CI2s9puH.cjs");
8
11
  let _noble_curves_secp256k1 = require("@noble/curves/secp256k1");
12
+ let _noble_hashes_blake2b = require("@noble/hashes/blake2b");
13
+ let _noble_hashes_ripemd160 = require("@noble/hashes/ripemd160");
9
14
  let _noble_hashes_sha2 = require("@noble/hashes/sha2");
10
15
  let _noble_hashes_sha3 = require("@noble/hashes/sha3");
11
16
  let _scure_base = require("@scure/base");
12
17
  let _noble_curves_ed25519 = require("@noble/curves/ed25519");
13
- let _noble_hashes_blake2b = require("@noble/hashes/blake2b");
18
+ //#region src/verify/result.ts
19
+ const verified = {
20
+ ok: true,
21
+ checked: true
22
+ };
23
+ const failed = (reason) => ({
24
+ ok: false,
25
+ reason
26
+ });
27
+ const unverifiable = (reason) => ({
28
+ ok: true,
29
+ checked: false,
30
+ reason
31
+ });
32
+ //#endregion
33
+ //#region src/verify/bch.ts
34
+ const SIGHASH_FORKID_ALL = 65;
35
+ const BCH_TX_VERSION = 1;
36
+ const BCH_TX_LOCKTIME = 0;
37
+ const BCH_TX_SEQUENCE = 4294967293;
38
+ /** Hardened reader for the legacy (non-witness) transaction serialization. */
39
+ function decodeBchRawTx(rawTxHex) {
40
+ let bytes;
41
+ try {
42
+ bytes = require_shared.hexToBytes(rawTxHex);
43
+ } catch {
44
+ throw new require_shared.EraSdkError("malformed-reply", "signed transaction is not hex");
45
+ }
46
+ let offset = 0;
47
+ const need = (n) => {
48
+ if (offset + n > bytes.length) throw new require_shared.EraSdkError("malformed-reply", "signed transaction is truncated");
49
+ };
50
+ const readU32 = () => {
51
+ need(4);
52
+ const v = bytes[offset] | bytes[offset + 1] << 8 | bytes[offset + 2] << 16 | bytes[offset + 3] << 24;
53
+ offset += 4;
54
+ return v >>> 0;
55
+ };
56
+ const readU64 = () => {
57
+ need(8);
58
+ let v = 0n;
59
+ for (let i = 7; i >= 0; i--) v = v << 8n | BigInt(bytes[offset + i]);
60
+ offset += 8;
61
+ return v;
62
+ };
63
+ const readVarint = () => {
64
+ need(1);
65
+ const first = bytes[offset++];
66
+ if (first < 253) return first;
67
+ if (first === 253) {
68
+ need(2);
69
+ const v = bytes[offset] | bytes[offset + 1] << 8;
70
+ offset += 2;
71
+ return v;
72
+ }
73
+ throw new require_shared.EraSdkError("malformed-reply", "unreasonable varint in signed transaction");
74
+ };
75
+ const readSlice = (n) => {
76
+ need(n);
77
+ const s = bytes.subarray(offset, offset + n);
78
+ offset += n;
79
+ return s;
80
+ };
81
+ const version = readU32();
82
+ const inputCount = readVarint();
83
+ if (inputCount === 0 || inputCount > 1e3) throw new require_shared.EraSdkError("malformed-reply", "unreasonable input count in signed transaction");
84
+ const inputs = [];
85
+ for (let i = 0; i < inputCount; i++) {
86
+ const txidLE = readSlice(32);
87
+ const index = readU32();
88
+ const scriptSig = readSlice(readVarint());
89
+ const sequence = readU32();
90
+ inputs.push({
91
+ txidLE,
92
+ index,
93
+ scriptSig,
94
+ sequence
95
+ });
96
+ }
97
+ const outputCount = readVarint();
98
+ if (outputCount === 0 || outputCount > 1e3) throw new require_shared.EraSdkError("malformed-reply", "unreasonable output count in signed transaction");
99
+ const outputs = [];
100
+ for (let i = 0; i < outputCount; i++) {
101
+ const value = readU64();
102
+ const script = readSlice(readVarint());
103
+ outputs.push({
104
+ value,
105
+ script
106
+ });
107
+ }
108
+ const locktime = readU32();
109
+ if (offset !== bytes.length) throw new require_shared.EraSdkError("malformed-reply", "trailing bytes after signed transaction");
110
+ return {
111
+ version,
112
+ inputs,
113
+ outputs,
114
+ locktime
115
+ };
116
+ }
117
+ function sha256d(data) {
118
+ return (0, _noble_hashes_sha2.sha256)((0, _noble_hashes_sha2.sha256)(data));
119
+ }
120
+ function hash160(data) {
121
+ return (0, _noble_hashes_ripemd160.ripemd160)((0, _noble_hashes_sha2.sha256)(data));
122
+ }
123
+ function p2pkhScript(pubkeyHash) {
124
+ return require_shared.concatBytes(new Uint8Array([
125
+ 118,
126
+ 169,
127
+ 20
128
+ ]), pubkeyHash, new Uint8Array([136, 172]));
129
+ }
130
+ function p2shScript(scriptHash) {
131
+ return require_shared.concatBytes(new Uint8Array([169, 20]), scriptHash, new Uint8Array([135]));
132
+ }
133
+ function scriptForAddress(address) {
134
+ const decoded = require_cashaddr.decodeCashAddr(address);
135
+ return decoded.type === "p2pkh" ? p2pkhScript(decoded.hash) : p2shScript(decoded.hash);
136
+ }
137
+ function le32(value) {
138
+ return new Uint8Array([
139
+ value & 255,
140
+ value >>> 8 & 255,
141
+ value >>> 16 & 255,
142
+ value >>> 24 & 255
143
+ ]);
144
+ }
145
+ function le64(value) {
146
+ const out = /* @__PURE__ */ new Uint8Array(8);
147
+ let v = value;
148
+ for (let i = 0; i < 8; i++) {
149
+ out[i] = Number(v & 255n);
150
+ v >>= 8n;
151
+ }
152
+ return out;
153
+ }
154
+ function varintBytes(value) {
155
+ if (value < 253) return new Uint8Array([value]);
156
+ return new Uint8Array([
157
+ 253,
158
+ value & 255,
159
+ value >>> 8 & 255
160
+ ]);
161
+ }
162
+ /**
163
+ * BIP-143 sighash preimage with FORKID, exactly as consensus defines it:
164
+ * `version ‖ hashPrevouts ‖ hashSequence ‖ outpoint ‖ scriptCode ‖ value ‖
165
+ * sequence ‖ hashOutputs ‖ locktime ‖ hashType(LE)`, double-SHA256d.
166
+ */
167
+ function computeBchSighash(args) {
168
+ const { tx, inputIndex, scriptCode, value } = args;
169
+ const hashType = args.hashType ?? SIGHASH_FORKID_ALL;
170
+ const input = tx.inputs[inputIndex];
171
+ if (!input) throw new require_shared.EraSdkError("invalid-props", "sighash input index out of range");
172
+ const hashPrevouts = sha256d(require_shared.concatBytes(...tx.inputs.map((i) => require_shared.concatBytes(i.txidLE, le32(i.index)))));
173
+ const hashSequence = sha256d(require_shared.concatBytes(...tx.inputs.map((i) => le32(i.sequence))));
174
+ const hashOutputs = sha256d(require_shared.concatBytes(...tx.outputs.map((o) => require_shared.concatBytes(le64(o.value), varintBytes(o.script.length), o.script))));
175
+ return sha256d(require_shared.concatBytes(le32(tx.version), hashPrevouts, hashSequence, input.txidLE, le32(input.index), varintBytes(scriptCode.length), scriptCode, le64(value), le32(input.sequence), hashOutputs, le32(tx.locktime), le32(hashType)));
176
+ }
177
+ function toBigintValue(value, label) {
178
+ const v = typeof value === "number" ? BigInt(value) : value;
179
+ if (v <= 0n) throw new require_shared.EraSdkError("invalid-props", `${label} must be positive`);
180
+ return v;
181
+ }
182
+ function toPublicKeyBytes(publicKey) {
183
+ return typeof publicKey === "string" ? require_shared.hexToBytes(publicKey) : publicKey;
184
+ }
185
+ function verifyBchSignedTx(args) {
186
+ try {
187
+ const tx = decodeBchRawTx(args.rawTx);
188
+ if (tx.version !== BCH_TX_VERSION || tx.locktime !== BCH_TX_LOCKTIME) return failed(`transaction parameters (version ${tx.version}, locktime ${tx.locktime}) are not the device signer's (1, 0)`);
189
+ for (let i = 0; i < tx.inputs.length; i++) if (tx.inputs[i].sequence !== BCH_TX_SEQUENCE) return failed(`input ${i} sequence is not the device signer's 0xfffffffd`);
190
+ if (tx.inputs.length !== args.inputs.length) return failed(`signed transaction has ${tx.inputs.length} inputs, the request had ${args.inputs.length}`);
191
+ if (tx.outputs.length !== args.outputs.length) return failed(`signed transaction has ${tx.outputs.length} outputs, the request had ${args.outputs.length}`);
192
+ for (let i = 0; i < args.outputs.length; i++) {
193
+ const requested = args.outputs[i];
194
+ const actual = tx.outputs[i];
195
+ if (actual.value !== toBigintValue(requested.value, `output ${i} value`)) return failed(`output ${i} value differs from the request`);
196
+ if (!require_shared.equalBytes(actual.script, scriptForAddress(requested.address))) return failed(`output ${i} does not pay the requested address`);
197
+ }
198
+ for (let i = 0; i < args.inputs.length; i++) {
199
+ const requested = args.inputs[i];
200
+ const actual = tx.inputs[i];
201
+ const txidLE = require_shared.hexToBytes(requested.txid).reverse();
202
+ if (!require_shared.equalBytes(actual.txidLE, txidLE) || actual.index !== requested.index) return failed(`input ${i} spends a different outpoint than the request named`);
203
+ const script = actual.scriptSig;
204
+ if (script.length < 2) return failed(`input ${i} has no signature`);
205
+ const sigLen = script[0];
206
+ if (sigLen < 9 || 1 + sigLen + 1 > script.length) return failed(`input ${i} scriptSig is not a signature push`);
207
+ const sigWithType = script.subarray(1, 1 + sigLen);
208
+ const pubLen = script[1 + sigLen];
209
+ if (pubLen !== 33 || 1 + sigLen + 1 + pubLen !== script.length) return failed(`input ${i} scriptSig does not end with a compressed public key push`);
210
+ const pubkey = script.subarray(1 + sigLen + 1);
211
+ if (!require_shared.equalBytes(pubkey, toPublicKeyBytes(requested.publicKey))) return failed(`input ${i} was signed with a different public key than the request named — the transaction cannot spend the requested UTXO`);
212
+ const hashType = sigWithType[sigWithType.length - 1];
213
+ if (hashType !== SIGHASH_FORKID_ALL) return failed(`input ${i} uses sighash 0x${hashType.toString(16)}, expected SIGHASH_ALL|FORKID (0x41)`);
214
+ const sighash = computeBchSighash({
215
+ tx,
216
+ inputIndex: i,
217
+ scriptCode: p2pkhScript(hash160(pubkey)),
218
+ value: toBigintValue(requested.value, `input ${i} value`)
219
+ });
220
+ const signature = _noble_curves_secp256k1.secp256k1.Signature.fromDER(sigWithType.subarray(0, sigWithType.length - 1)).toCompactRawBytes();
221
+ if (!_noble_curves_secp256k1.secp256k1.verify(signature, sighash, pubkey)) return failed(`input ${i} signature does not verify against the BIP-143 FORKID sighash`);
222
+ }
223
+ if (args.txId !== void 0) {
224
+ if (require_shared.bytesToHex(sha256d(require_shared.hexToBytes(args.rawTx)).reverse()) !== args.txId.toLowerCase()) return failed("reply txId does not match the hash of the signed transaction");
225
+ }
226
+ return verified;
227
+ } catch (e) {
228
+ return failed(message$1(e));
229
+ }
230
+ }
231
+ function message$1(e) {
232
+ return e instanceof require_shared.EraSdkError || e instanceof Error ? e.message : String(e);
233
+ }
234
+ //#endregion
14
235
  //#region src/verify/psbt-reader.ts
15
236
  const MAGIC = [
16
237
  112,
@@ -149,21 +370,6 @@ function inputHas(psbt, index, keyType) {
149
370
  return inputEntries(psbt, index, keyType).length > 0;
150
371
  }
151
372
  //#endregion
152
- //#region src/verify/result.ts
153
- const verified = {
154
- ok: true,
155
- checked: true
156
- };
157
- const failed = (reason) => ({
158
- ok: false,
159
- reason
160
- });
161
- const unverifiable = (reason) => ({
162
- ok: true,
163
- checked: false,
164
- reason
165
- });
166
- //#endregion
167
373
  //#region src/verify/btc.ts
168
374
  /**
169
375
  * The `crypto-psbt` reply carries NO request id — this comparison IS the
@@ -282,10 +488,10 @@ function verifyCardanoSignature(args) {
282
488
  if (!ok) return failed("a witness signature does not verify against its own vkey");
283
489
  }
284
490
  if (args.account && args.signerPaths && args.signerPaths.length > 0) {
285
- const accountLevels = require_shared.parsePath(args.account.accountPath);
491
+ const accountLevels = require_keypath.parsePath(args.account.accountPath);
286
492
  const expected = /* @__PURE__ */ new Map();
287
493
  for (const path of new Set(args.signerPaths)) {
288
- const levels = require_shared.parsePath(path);
494
+ const levels = require_keypath.parsePath(path);
289
495
  if (levels.length !== accountLevels.length + 2 || !accountLevels.every((l, i) => levels[i].index === l.index && levels[i].hardened === l.hardened) || levels.slice(accountLevels.length).some((l) => l.hardened)) return failed(`signer path ${path} does not extend the account path with two soft components`);
290
496
  const tail = levels.slice(accountLevels.length).map((l) => l.index);
291
497
  const vkey = require_derive.cardanoSoftDerivePath(args.account.publicKey, args.account.chainCode, tail);
@@ -394,6 +600,21 @@ function skipItem(bytes, offset, depth) {
394
600
  }
395
601
  }
396
602
  //#endregion
603
+ //#region src/verify/cosmos.ts
604
+ function verifyCosmosSignature(args) {
605
+ if (args.signature.length !== 64) return failed("signature must be 64 bytes (compact r||s)");
606
+ if (args.publicKey.length !== 33) return failed("publicKey must be 33 bytes (compressed)");
607
+ if (args.expectedPublicKey !== void 0 && !require_shared.equalBytes(args.publicKey, args.expectedPublicKey)) return failed("the reply public key is not the linked account key");
608
+ const digest = args.digest === "keccak256" ? (0, _noble_hashes_sha3.keccak_256)(args.signData) : (0, _noble_hashes_sha2.sha256)(args.signData);
609
+ let ok;
610
+ try {
611
+ ok = _noble_curves_secp256k1.secp256k1.verify(args.signature, digest, args.publicKey);
612
+ } catch (e) {
613
+ return failed(`Cosmos signature could not be checked: ${e.message}`);
614
+ }
615
+ return ok ? verified : failed("the signature does not belong to this account");
616
+ }
617
+ //#endregion
397
618
  //#region src/verify/evm.ts
398
619
  /** EIP-191: 0x19 || "Ethereum Signed Message:\n" || len(message). */
399
620
  function personalSignDigest(message) {
@@ -449,6 +670,25 @@ function verifySolanaSignature(args) {
449
670
  return ok ? verified : failed("the signature does not belong to this account");
450
671
  }
451
672
  //#endregion
673
+ //#region src/verify/sui.ts
674
+ /** BLAKE2b-256 of the intent message (or the given hash) + Ed25519 verification. */
675
+ function verifySuiSignature(args) {
676
+ let digest;
677
+ if (args.messageHash !== void 0) {
678
+ if (args.messageHash.length !== 32) return failed("messageHash must be 32 bytes");
679
+ digest = args.messageHash;
680
+ } else if (args.intentMessage !== void 0) digest = require_sui.suiIntentDigest(args.intentMessage);
681
+ else return failed("provide intentMessage or messageHash");
682
+ if (args.expectedPublicKey !== void 0 && !require_shared.equalBytes(args.publicKey, args.expectedPublicKey)) return failed("the reply public key is not the linked account key");
683
+ let ok;
684
+ try {
685
+ ok = _noble_curves_ed25519.ed25519.verify(args.signature, digest, args.publicKey);
686
+ } catch (e) {
687
+ return failed(`Sui signature could not be checked: ${e.message}`);
688
+ }
689
+ return ok ? verified : failed("the signature does not belong to this account");
690
+ }
691
+ //#endregion
452
692
  //#region src/verify/ton-boc.ts
453
693
  /**
454
694
  * Minimal TON Bag-of-Cells reader + cell representation hash — exactly the
@@ -720,15 +960,165 @@ function compareWindow(repliedBytes, latestBlock) {
720
960
  return verified;
721
961
  }
722
962
  //#endregion
963
+ //#region src/verify/xrp.ts
964
+ /**
965
+ * XRP has no request id — this check IS the binding. The signed binary is
966
+ * split into its canonical fields; `TxnSignature` is removed; the remainder
967
+ * (prefixed with the XRPL signing tag `STX\0`) is hashed with SHA-512-half
968
+ * and the DER signature is verified against `SigningPubKey`, which must also
969
+ * equal the key your request carried.
970
+ *
971
+ * The field walker covers the types Payment-class transactions use; a
972
+ * transaction carrying an exotic field type comes back `checked: false`
973
+ * rather than a false verdict.
974
+ */
975
+ function verifyXrpSignature(args) {
976
+ let fields;
977
+ try {
978
+ fields = splitFields(args.signedTx);
979
+ } catch (e) {
980
+ const message = e.message;
981
+ if (message === "unsupported-field") return unverifiable("the transaction carries a field type this checker does not walk");
982
+ return failed(`the signed transaction is not readable: ${message}`);
983
+ }
984
+ const signingPubKey = fields.find((f) => f.header === 115);
985
+ const txnSignature = fields.find((f) => f.header === 116);
986
+ if (!signingPubKey || !txnSignature) return failed("the signed transaction is missing SigningPubKey or TxnSignature");
987
+ const expected = require_shared.hexToBytes(args.expectedSigningPubKey);
988
+ if (!require_shared.equalBytes(signingPubKey.value, expected)) return failed(`the transaction was signed with a different key (${require_shared.bytesToHex(signingPubKey.value)})`);
989
+ const payload = require_shared.concatBytes(new Uint8Array([
990
+ 83,
991
+ 84,
992
+ 88,
993
+ 0
994
+ ]), ...fields.filter((f) => f.header !== 116).map((f) => f.raw));
995
+ const digest = (0, _noble_hashes_sha2.sha512)(payload).slice(0, 32);
996
+ let ok;
997
+ try {
998
+ ok = _noble_curves_secp256k1.secp256k1.verify(_noble_curves_secp256k1.secp256k1.Signature.fromDER(txnSignature.value).toCompactRawBytes(), digest, expected);
999
+ } catch (e) {
1000
+ return failed(`XRP signature could not be checked: ${e.message}`);
1001
+ }
1002
+ return ok ? verified : failed("the signature does not verify against SigningPubKey");
1003
+ }
1004
+ /** Split a canonical STObject into its top-level fields. */
1005
+ function splitFields(bytes) {
1006
+ const fields = [];
1007
+ let pos = 0;
1008
+ while (pos < bytes.length) {
1009
+ const field = parseField(bytes, pos);
1010
+ fields.push(field.field);
1011
+ pos = field.end;
1012
+ }
1013
+ return fields;
1014
+ }
1015
+ /** Parse ONE field at `pos`; returns the field and the offset past it. */
1016
+ function parseField(bytes, pos) {
1017
+ const start = pos;
1018
+ const first = bytes[pos++];
1019
+ if (first === void 0) throw new Error("truncated");
1020
+ let type = first >> 4;
1021
+ let fieldCode = first & 15;
1022
+ if (type === 0) {
1023
+ const t = bytes[pos++];
1024
+ if (t === void 0) throw new Error("truncated type");
1025
+ type = t;
1026
+ }
1027
+ if (fieldCode === 0) {
1028
+ const f = bytes[pos++];
1029
+ if (f === void 0) throw new Error("truncated field code");
1030
+ fieldCode = f;
1031
+ }
1032
+ let valueStart = pos;
1033
+ switch (type) {
1034
+ case 1:
1035
+ pos += 2;
1036
+ break;
1037
+ case 2:
1038
+ pos += 4;
1039
+ break;
1040
+ case 5:
1041
+ pos += 32;
1042
+ break;
1043
+ case 6: {
1044
+ const head = bytes[pos];
1045
+ if (head === void 0) throw new Error("truncated amount");
1046
+ pos += (head & 128) !== 0 ? 48 : 8;
1047
+ break;
1048
+ }
1049
+ case 7:
1050
+ case 8: {
1051
+ const vl = readVl(bytes, pos);
1052
+ valueStart = vl.next;
1053
+ pos = vl.next + vl.length;
1054
+ break;
1055
+ }
1056
+ case 14:
1057
+ while (bytes[pos] !== 225) {
1058
+ if (pos >= bytes.length) throw new Error("unterminated inner object");
1059
+ pos = parseField(bytes, pos).end;
1060
+ }
1061
+ pos += 1;
1062
+ break;
1063
+ case 15:
1064
+ while (bytes[pos] !== 241) {
1065
+ if (pos >= bytes.length) throw new Error("unterminated array");
1066
+ pos = parseField(bytes, pos).end;
1067
+ }
1068
+ pos += 1;
1069
+ break;
1070
+ default: throw new Error("unsupported-field");
1071
+ }
1072
+ if (pos > bytes.length) throw new Error("truncated field");
1073
+ return {
1074
+ field: {
1075
+ header: type << 4 | (fieldCode <= 15 ? fieldCode : 0),
1076
+ value: bytes.slice(valueStart, pos),
1077
+ raw: bytes.slice(start, pos)
1078
+ },
1079
+ end: pos
1080
+ };
1081
+ }
1082
+ /** XRPL variable-length prefix. */
1083
+ function readVl(bytes, pos) {
1084
+ const b1 = bytes[pos];
1085
+ if (b1 === void 0) throw new Error("truncated length");
1086
+ if (b1 <= 192) return {
1087
+ length: b1,
1088
+ next: pos + 1
1089
+ };
1090
+ if (b1 <= 240) {
1091
+ const b2 = bytes[pos + 1];
1092
+ if (b2 === void 0) throw new Error("truncated length");
1093
+ return {
1094
+ length: 193 + (b1 - 193) * 256 + b2,
1095
+ next: pos + 2
1096
+ };
1097
+ }
1098
+ const b2 = bytes[pos + 1];
1099
+ const b3 = bytes[pos + 2];
1100
+ if (b2 === void 0 || b3 === void 0) throw new Error("truncated length");
1101
+ return {
1102
+ length: 12481 + (b1 - 241) * 65536 + b2 * 256 + b3,
1103
+ next: pos + 3
1104
+ };
1105
+ }
1106
+ //#endregion
723
1107
  exports.PsbtInputType = PsbtInputType;
724
1108
  exports.bocRootHash = bocRootHash;
1109
+ exports.computeBchSighash = computeBchSighash;
1110
+ exports.decodeBchRawTx = decodeBchRawTx;
725
1111
  exports.parsePsbt = parsePsbt;
1112
+ exports.verifyBchSignedTx = verifyBchSignedTx;
726
1113
  exports.verifyBtcMessageHeader = verifyBtcMessageHeader;
727
1114
  exports.verifyCardanoSignature = verifyCardanoSignature;
1115
+ exports.verifyCosmosSignature = verifyCosmosSignature;
728
1116
  exports.verifyEvmSignature = verifyEvmSignature;
729
1117
  exports.verifySignedPsbt = verifySignedPsbt;
730
1118
  exports.verifySolanaSignature = verifySolanaSignature;
1119
+ exports.verifySuiSignature = verifySuiSignature;
731
1120
  exports.verifyTonSignature = verifyTonSignature;
732
1121
  exports.verifyTronSignature = verifyTronSignature;
1122
+ exports.verifyXrpSignature = verifyXrpSignature;
733
1123
 
734
1124
  //# sourceMappingURL=verify.cjs.map