@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
@@ -0,0 +1,82 @@
1
+ const require_shared = require("./shared-Cu2yynP9.cjs");
2
+ const require_keypath = require("./keypath-pzm_YIfN.cjs");
3
+ let _noble_hashes_blake2b = require("@noble/hashes/blake2b");
4
+ //#region src/chains/sui.ts
5
+ const REPLY_TYPES = ["sui-signature"];
6
+ var SuiChain = class {
7
+ constructor(config) {
8
+ this.context = require_shared.resolveContext(config);
9
+ }
10
+ /** Build a `sui-sign-request` (7101). Reply: `sui-signature` (7102). */
11
+ generateSignRequest(props) {
12
+ if (props.intentMessage.length === 0) throw new require_shared.EraSdkError("invalid-props", "intentMessage must not be empty");
13
+ return this.build("sui-sign-request", require_shared.cbBytes(props.intentMessage), props);
14
+ }
15
+ /** Build a `sui-sign-hash-request` (7103) — signs the given 32-byte digest directly. */
16
+ generateSignHashRequest(props) {
17
+ if (props.messageHash.length !== 32) throw new require_shared.EraSdkError("invalid-props", "messageHash must be 32 bytes");
18
+ return this.build("sui-sign-hash-request", require_shared.cbText(require_shared.bytesToHex(props.messageHash)), props);
19
+ }
20
+ build(urType, signData, props) {
21
+ const requestId = require_shared.resolveRequestId(this.context, props.requestId);
22
+ const path = require_keypath.parsePath(props.path);
23
+ if (!path.every((level) => level.hardened)) throw new require_shared.EraSdkError("invalid-props", `Sui signing paths are fully hardened (SLIP-10 Ed25519), got ${props.path}`);
24
+ const xfp = require_keypath.normalizeXfp(props.xfp);
25
+ const entries = [
26
+ [1, require_shared.cbTag(37, require_shared.cbBytes(requestId))],
27
+ [2, signData],
28
+ [3, require_shared.cbArray([require_keypath.keypath304(path, xfp)])]
29
+ ];
30
+ if (props.address !== void 0) {
31
+ const address = props.address instanceof Uint8Array ? props.address : require_shared.hexToBytes(props.address);
32
+ if (address.length !== 32) throw new require_shared.EraSdkError("invalid-props", "Sui address must be 32 bytes");
33
+ entries.push([4, require_shared.cbArray([require_shared.cbBytes(address)])]);
34
+ }
35
+ entries.push([5, require_shared.cbText(props.origin ?? this.context.origin)]);
36
+ const ur = new require_shared.Ur(urType, require_shared.cborEncode(require_shared.cbMap(entries)));
37
+ return require_shared.makeSignRequest({
38
+ ur,
39
+ requestId,
40
+ replyTypes: REPLY_TYPES,
41
+ context: this.context,
42
+ parse: (reply) => parseSuiSignature(reply, requestId)
43
+ });
44
+ }
45
+ /** Parse a `sui-signature` standalone. Prefer `SignRequest.scanner().parse()`. */
46
+ parseSignature(input, expect) {
47
+ return parseSuiSignature(require_shared.toUr(input), expect?.requestId === void 0 ? void 0 : require_shared.normalizeRequestId(expect.requestId));
48
+ }
49
+ };
50
+ /** BLAKE2b-256 of the intent message — the digest the device signs for `sui-sign-request`. */
51
+ function suiIntentDigest(intentMessage) {
52
+ return (0, _noble_hashes_blake2b.blake2b)(intentMessage, { dkLen: 32 });
53
+ }
54
+ function parseSuiSignature(ur, expectedRequestId) {
55
+ require_shared.requireUrType(ur, [...REPLY_TYPES], "sui-signature");
56
+ const map = require_shared.requireReplyMap(ur, "sui-signature");
57
+ const requestId = require_shared.requireRequestIdEcho(map, 1, expectedRequestId, "sui-signature");
58
+ const signature = require_shared.asBytes(require_shared.mapGet(map, 2));
59
+ if (!signature || signature.length !== 64) throw new require_shared.EraSdkError("malformed-reply", `sui-signature signature is ${signature?.length ?? 0} bytes, expected 64`);
60
+ const publicKey = require_shared.asBytes(require_shared.mapGet(map, 3));
61
+ if (!publicKey || publicKey.length !== 32) throw new require_shared.EraSdkError("malformed-reply", "sui-signature is missing the 32-byte public key (key 3)");
62
+ return {
63
+ requestId,
64
+ signature,
65
+ publicKey
66
+ };
67
+ }
68
+ //#endregion
69
+ Object.defineProperty(exports, "SuiChain", {
70
+ enumerable: true,
71
+ get: function() {
72
+ return SuiChain;
73
+ }
74
+ });
75
+ Object.defineProperty(exports, "suiIntentDigest", {
76
+ enumerable: true,
77
+ get: function() {
78
+ return suiIntentDigest;
79
+ }
80
+ });
81
+
82
+ //# sourceMappingURL=sui-CYRn331O.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sui-CYRn331O.cjs","names":["resolveContext","EraSdkError","cbBytes","cbText","bytesToHex","resolveRequestId","parsePath","normalizeXfp","cbTag","cbArray","keypath304","hexToBytes","UrValue","cborEncode","cbMap","makeSignRequest","toUr","normalizeRequestId","blake2b","requireReplyMap","requireRequestIdEcho","asBytes","mapGet"],"sources":["../src/chains/sui.ts"],"sourcesContent":["import { blake2b } from '@noble/hashes/blake2b';\nimport { cborEncode } from '../cbor/encode';\nimport type { CborValue } from '../cbor/model';\nimport { asBytes, cbArray, cbBytes, cbMap, cbTag, cbText, mapGet } from '../cbor/model';\nimport { bytesToHex, hexToBytes } 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\nexport interface SuiSignRequestProps {\n readonly requestId?: Uint8Array | string;\n /**\n * The COMPLETE BCS intent message (intent prefix + transaction bytes) as\n * your Sui tooling produces it — the device signs BLAKE2b-256 of exactly\n * these bytes.\n */\n readonly intentMessage: Uint8Array;\n /** Fully hardened SLIP-10 path, e.g. `m/44'/784'/0'/0'/0'`. */\n readonly path: string;\n readonly xfp: string | number;\n /** 32-byte Sui address (raw or `0x` hex) — device display. */\n readonly address?: Uint8Array | string;\n readonly origin?: string;\n}\n\nexport interface SuiSignHashRequestProps {\n readonly requestId?: Uint8Array | string;\n /** The 32-byte digest to sign directly (the hash-request variant). */\n readonly messageHash: Uint8Array;\n readonly path: string;\n readonly xfp: string | number;\n readonly address?: Uint8Array | string;\n readonly origin?: string;\n}\n\nexport interface SuiSignatureResult {\n readonly requestId: Uint8Array;\n /** 64-byte Ed25519 signature. */\n readonly signature: Uint8Array;\n /** The 32-byte signer public key the device answered with. */\n readonly publicKey: Uint8Array;\n}\n\nconst REPLY_TYPES = ['sui-signature'] as const;\n\nexport class SuiChain {\n private readonly context: ChainContext;\n\n constructor(config?: EraConnectConfig) {\n this.context = resolveContext(config);\n }\n\n /** Build a `sui-sign-request` (7101). Reply: `sui-signature` (7102). */\n generateSignRequest(props: SuiSignRequestProps): SignRequest<SuiSignatureResult> {\n if (props.intentMessage.length === 0) {\n throw new EraSdkError('invalid-props', 'intentMessage must not be empty');\n }\n return this.build('sui-sign-request', cbBytes(props.intentMessage), props);\n }\n\n /** Build a `sui-sign-hash-request` (7103) — signs the given 32-byte digest directly. */\n generateSignHashRequest(props: SuiSignHashRequestProps): SignRequest<SuiSignatureResult> {\n if (props.messageHash.length !== 32) {\n throw new EraSdkError('invalid-props', 'messageHash must be 32 bytes');\n }\n // The hash travels as a HEX STRING on this variant (device contract).\n return this.build('sui-sign-hash-request', cbText(bytesToHex(props.messageHash)), props);\n }\n\n private build(\n urType: string,\n signData: CborValue,\n props: Omit<SuiSignRequestProps, 'intentMessage'> & { address?: Uint8Array | string },\n ): SignRequest<SuiSignatureResult> {\n const requestId = resolveRequestId(this.context, props.requestId);\n const path = parsePath(props.path);\n if (!path.every((level) => level.hardened)) {\n throw new EraSdkError(\n 'invalid-props',\n `Sui signing paths are fully hardened (SLIP-10 Ed25519), got ${props.path}`,\n );\n }\n const xfp = normalizeXfp(props.xfp);\n\n const entries: [number, CborValue][] = [\n [1, cbTag(37, cbBytes(requestId))],\n [2, signData],\n [3, cbArray([keypath304(path, xfp)])],\n ];\n if (props.address !== undefined) {\n const address =\n props.address instanceof Uint8Array ? props.address : hexToBytes(props.address);\n if (address.length !== 32) {\n throw new EraSdkError('invalid-props', 'Sui address must be 32 bytes');\n }\n entries.push([4, cbArray([cbBytes(address)])]);\n }\n entries.push([5, cbText(props.origin ?? this.context.origin)]);\n\n const ur = new UrValue(urType, cborEncode(cbMap(entries)));\n return makeSignRequest({\n ur,\n requestId,\n replyTypes: REPLY_TYPES,\n context: this.context,\n parse: (reply) => parseSuiSignature(reply, requestId),\n });\n }\n\n /** Parse a `sui-signature` standalone. Prefer `SignRequest.scanner().parse()`. */\n parseSignature(input: Ur | string, expect?: ExpectedReply): SuiSignatureResult {\n return parseSuiSignature(\n toUr(input),\n expect?.requestId === undefined ? undefined : normalizeRequestId(expect.requestId),\n );\n }\n}\n\n/** BLAKE2b-256 of the intent message — the digest the device signs for `sui-sign-request`. */\nexport function suiIntentDigest(intentMessage: Uint8Array): Uint8Array {\n return blake2b(intentMessage, { dkLen: 32 });\n}\n\nfunction parseSuiSignature(ur: Ur, expectedRequestId: Uint8Array | undefined): SuiSignatureResult {\n requireUrType(ur, [...REPLY_TYPES], 'sui-signature');\n const map = requireReplyMap(ur, 'sui-signature');\n const requestId = requireRequestIdEcho(map, 1, expectedRequestId, 'sui-signature');\n const signature = asBytes(mapGet(map, 2));\n if (!signature || signature.length !== 64) {\n throw new EraSdkError(\n 'malformed-reply',\n `sui-signature signature is ${signature?.length ?? 0} bytes, expected 64`,\n );\n }\n const publicKey = asBytes(mapGet(map, 3));\n if (!publicKey || publicKey.length !== 32) {\n throw new EraSdkError(\n 'malformed-reply',\n 'sui-signature is missing the 32-byte public key (key 3)',\n );\n }\n return { requestId, signature, publicKey };\n}\n"],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import { _ as Ur, a as SignRequest, i as ExpectedReply, r as EraConnectConfig } from "./shared-CI8zhQiE.js";
2
+ //#region src/chains/sui.d.ts
3
+ interface SuiSignRequestProps {
4
+ readonly requestId?: Uint8Array | string;
5
+ /**
6
+ * The COMPLETE BCS intent message (intent prefix + transaction bytes) as
7
+ * your Sui tooling produces it — the device signs BLAKE2b-256 of exactly
8
+ * these bytes.
9
+ */
10
+ readonly intentMessage: Uint8Array;
11
+ /** Fully hardened SLIP-10 path, e.g. `m/44'/784'/0'/0'/0'`. */
12
+ readonly path: string;
13
+ readonly xfp: string | number;
14
+ /** 32-byte Sui address (raw or `0x` hex) — device display. */
15
+ readonly address?: Uint8Array | string;
16
+ readonly origin?: string;
17
+ }
18
+ interface SuiSignHashRequestProps {
19
+ readonly requestId?: Uint8Array | string;
20
+ /** The 32-byte digest to sign directly (the hash-request variant). */
21
+ readonly messageHash: Uint8Array;
22
+ readonly path: string;
23
+ readonly xfp: string | number;
24
+ readonly address?: Uint8Array | string;
25
+ readonly origin?: string;
26
+ }
27
+ interface SuiSignatureResult {
28
+ readonly requestId: Uint8Array;
29
+ /** 64-byte Ed25519 signature. */
30
+ readonly signature: Uint8Array;
31
+ /** The 32-byte signer public key the device answered with. */
32
+ readonly publicKey: Uint8Array;
33
+ }
34
+ declare class SuiChain {
35
+ private readonly context;
36
+ constructor(config?: EraConnectConfig);
37
+ /** Build a `sui-sign-request` (7101). Reply: `sui-signature` (7102). */
38
+ generateSignRequest(props: SuiSignRequestProps): SignRequest<SuiSignatureResult>;
39
+ /** Build a `sui-sign-hash-request` (7103) — signs the given 32-byte digest directly. */
40
+ generateSignHashRequest(props: SuiSignHashRequestProps): SignRequest<SuiSignatureResult>;
41
+ private build;
42
+ /** Parse a `sui-signature` standalone. Prefer `SignRequest.scanner().parse()`. */
43
+ parseSignature(input: Ur | string, expect?: ExpectedReply): SuiSignatureResult;
44
+ }
45
+ /** BLAKE2b-256 of the intent message — the digest the device signs for `sui-sign-request`. */
46
+ declare function suiIntentDigest(intentMessage: Uint8Array): Uint8Array;
47
+ //#endregion
48
+ export { suiIntentDigest as a, SuiSignatureResult as i, SuiSignHashRequestProps as n, SuiSignRequestProps as r, SuiChain as t };
49
+ //# sourceMappingURL=sui-DI0ruW7E.d.ts.map
package/dist/sui.cjs ADDED
@@ -0,0 +1,10 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_shared = require("./shared-Cu2yynP9.cjs");
3
+ const require_sui = require("./sui-CYRn331O.cjs");
4
+ exports.AnimatedUr = require_shared.AnimatedUr;
5
+ exports.EraSdkError = require_shared.EraSdkError;
6
+ exports.SuiChain = require_sui.SuiChain;
7
+ exports.TypedUrScanner = require_shared.TypedUrScanner;
8
+ exports.Ur = require_shared.Ur;
9
+ exports.UrScanner = require_shared.UrScanner;
10
+ exports.suiIntentDigest = require_sui.suiIntentDigest;
package/dist/sui.d.cts ADDED
@@ -0,0 +1,3 @@
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 suiIntentDigest, i as SuiSignatureResult, n as SuiSignHashRequestProps, r as SuiSignRequestProps, t as SuiChain } from "./sui-6WX6VPlW.cjs";
3
+ export { AnimatedUr, type EraConnectConfig, type EraErrorCode, EraSdkError, type ExpectedReply, type SignRequest, SuiChain, type SuiSignHashRequestProps, type SuiSignRequestProps, type SuiSignatureResult, TypedUrScanner, Ur, UrScanner, suiIntentDigest };
package/dist/sui.d.ts ADDED
@@ -0,0 +1,3 @@
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 suiIntentDigest, i as SuiSignatureResult, n as SuiSignHashRequestProps, r as SuiSignRequestProps, t as SuiChain } from "./sui-DI0ruW7E.js";
3
+ export { AnimatedUr, type EraConnectConfig, type EraErrorCode, EraSdkError, type ExpectedReply, type SignRequest, SuiChain, type SuiSignHashRequestProps, type SuiSignRequestProps, type SuiSignatureResult, TypedUrScanner, Ur, UrScanner, suiIntentDigest };
package/dist/sui.js ADDED
@@ -0,0 +1,3 @@
1
+ import { K as EraSdkError, _ as Ur, d as UrScanner, f as AnimatedUr, u as TypedUrScanner } from "./shared-Bbgejrus.js";
2
+ import { n as suiIntentDigest, t as SuiChain } from "./sui-BtbPG6Xt.js";
3
+ export { AnimatedUr, EraSdkError, SuiChain, TypedUrScanner, Ur, UrScanner, suiIntentDigest };
@@ -1,4 +1,5 @@
1
- import { A as utf8Encode, E as equalBytes, G as cbMap, J as cbUint, K as cbTag, M as keypath304, N as normalizeXfp, P as parsePath, W as cbBytes, X as stripTags, Y as mapGet, Z as EraSdkError, _ as Ur, c as resolveRequestId, g as uuidStringify, h as normalizeRequestId, l as toUr, n as makeSignRequest, o as requireUrType, q as cbText, r as requireReplyMap, s as resolveContext, x as cborEncode } from "./shared-B8Wc7ViU.js";
1
+ import { A as utf8Encode, B as cbMap, E as equalBytes, G as stripTags, H as cbText, K as EraSdkError, U as cbUint, V as cbTag, W as mapGet, _ as Ur, c as resolveRequestId, g as uuidStringify, h as normalizeRequestId, 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
  //#region src/chains/ton.ts
3
4
  /** `ton-sign-request` dataType (CBOR key 3). */
4
5
  const TonDataType = {
@@ -83,4 +84,4 @@ function normalizeEchoedId(echoed) {
83
84
  //#endregion
84
85
  export { TonDataType as n, TonChain as t };
85
86
 
86
- //# sourceMappingURL=ton-5Uvi5ifg.js.map
87
+ //# sourceMappingURL=ton-BSPKU5B9.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ton-5Uvi5ifg.js","names":["UrValue"],"sources":["../src/chains/ton.ts"],"sourcesContent":["import { cborEncode } from '../cbor/encode';\nimport type { CborValue } from '../cbor/model';\nimport { cbBytes, cbMap, cbTag, cbText, cbUint, mapGet, stripTags } from '../cbor/model';\nimport { equalBytes, utf8Encode } from '../core/bytes';\nimport { EraSdkError } from '../core/errors';\nimport { normalizeRequestId, uuidStringify } 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 requireUrType,\n resolveContext,\n resolveRequestId,\n toUr,\n} from './shared';\n\n/** `ton-sign-request` dataType (CBOR key 3). */\nexport const TonDataType = {\n /** `signData` is a Bag-of-Cells; the device signs the ROOT CELL's representation hash. */\n transaction: 1,\n /**\n * TON Connect proof: the device signs\n * `sha256(0xFFFF || \"ton-connect\" || sha256(signData))`.\n */\n tonProof: 2,\n} as const;\nexport type TonDataType = (typeof TonDataType)[keyof typeof TonDataType];\n\nexport interface TonSignRequestProps {\n readonly requestId?: Uint8Array | string;\n /** BoC bytes (transaction) or the raw proof payload (tonProof). */\n readonly signData: Uint8Array;\n /** Defaults to `transaction`. */\n readonly dataType?: TonDataType;\n /** The account path, e.g. `m/44'/607'/0'` (V4R2 and V5R1 share it — the wallet-contract version affects only the address). */\n readonly path: string;\n readonly xfp: string | number;\n /** User-friendly bounceable address TEXT (`UQ…`/`EQ…`) — shown on the device. */\n readonly address?: string;\n readonly origin?: string;\n}\n\nexport interface TonSignatureResult {\n readonly requestId: Uint8Array;\n /** 64-byte Ed25519 signature over the digest for the request's dataType. */\n readonly signature: Uint8Array;\n}\n\nconst REPLY_TYPES = ['ton-signature'] as const;\n\nexport class TonChain {\n static readonly DataType = TonDataType;\n\n private readonly context: ChainContext;\n\n constructor(config?: EraConnectConfig) {\n this.context = resolveContext(config);\n }\n\n /** Build a `ton-sign-request` (7201). Reply: `ton-signature` (7202). */\n generateSignRequest(props: TonSignRequestProps): SignRequest<TonSignatureResult> {\n const requestId = resolveRequestId(this.context, props.requestId);\n const path = parsePath(props.path);\n const xfp = normalizeXfp(props.xfp);\n if (props.signData.length === 0) {\n throw new EraSdkError('invalid-props', 'signData must not be empty');\n }\n\n const entries: [number, CborValue][] = [\n // TON ecosystem quirk: the request id travels as the ASCII BYTES of the\n // hyphenated UUID string, wrapped in tag 37 (that is what Tonkeeper-\n // style integrations emit and what the device echoes back verbatim).\n [1, cbTag(37, cbBytes(utf8Encode(uuidStringify(requestId))))],\n [2, cbBytes(props.signData)],\n [3, cbUint(props.dataType ?? TonDataType.transaction)],\n [4, keypath304(path, xfp)],\n ];\n if (props.address !== undefined) entries.push([5, cbText(props.address)]);\n entries.push([6, cbText(props.origin ?? this.context.origin)]);\n\n const ur = new UrValue('ton-sign-request', cborEncode(cbMap(entries)));\n return makeSignRequest({\n ur,\n requestId,\n replyTypes: REPLY_TYPES,\n context: this.context,\n parse: (reply) => parseTonSignature(reply, requestId),\n });\n }\n\n /** Parse a `ton-signature` standalone. Prefer `SignRequest.scanner().parse()`. */\n parseSignature(input: Ur | string, expect?: ExpectedReply): TonSignatureResult {\n return parseTonSignature(\n toUr(input),\n expect?.requestId === undefined ? undefined : normalizeRequestId(expect.requestId),\n );\n }\n}\n\nfunction parseTonSignature(ur: Ur, expectedRequestId: Uint8Array | undefined): TonSignatureResult {\n requireUrType(ur, [...REPLY_TYPES], 'ton-signature');\n const map = requireReplyMap(ur, 'ton-signature');\n\n // The device echoes the request id BYTES verbatim (tag-37 wrapped). On this\n // chain those bytes are normally the ASCII of the UUID string; a bare\n // 16-byte binary echo is accepted too for forward compatibility.\n const echoedValue = mapGet(map, 1);\n const echoed = echoedValue === undefined ? undefined : stripTags(echoedValue);\n if (echoed?.kind !== 'bytes') {\n throw new EraSdkError('malformed-reply', 'ton-signature does not echo the request id (key 1)');\n }\n const requestId = normalizeEchoedId(echoed.value);\n if (expectedRequestId !== undefined) {\n if (requestId === null || !equalBytes(requestId, expectedRequestId)) {\n throw new EraSdkError(\n 'request-id-mismatch',\n 'ton-signature echoes a different request id — it answers another sign request, not this one',\n );\n }\n }\n\n const sigValue = mapGet(map, 2);\n const sig = sigValue === undefined ? undefined : stripTags(sigValue);\n if (sig?.kind !== 'bytes') {\n throw new EraSdkError('malformed-reply', 'ton-signature is missing the signature (key 2)');\n }\n if (sig.value.length !== 64) {\n throw new EraSdkError(\n 'malformed-reply',\n `ton-signature signature is ${sig.value.length} bytes, expected 64`,\n );\n }\n return { requestId: requestId ?? new Uint8Array(16), signature: sig.value };\n}\n\n/** ASCII-UUID-string bytes (36) or raw binary (16) → 16-byte id; null if neither. */\nfunction normalizeEchoedId(echoed: Uint8Array): Uint8Array | null {\n if (echoed.length === 16) return echoed;\n if (echoed.length === 36) {\n let text = '';\n for (const b of echoed) {\n if (b > 0x7f) return null;\n text += String.fromCharCode(b);\n }\n try {\n return normalizeRequestId(text);\n } catch {\n return null;\n }\n }\n return null;\n}\n"],"mappings":";;;AAoBA,MAAa,cAAc;;CAEzB,aAAa;;;;;CAKb,UAAU;AACZ;AAuBA,MAAM,cAAc,CAAC,eAAe;AAEpC,IAAa,WAAb,MAAsB;CAKpB,YAAY,QAA2B;EACrC,KAAK,UAAU,eAAe,MAAM;CACtC;;CAGA,oBAAoB,OAA6D;EAC/E,MAAM,YAAY,iBAAiB,KAAK,SAAS,MAAM,SAAS;EAChE,MAAM,OAAO,UAAU,MAAM,IAAI;EACjC,MAAM,MAAM,aAAa,MAAM,GAAG;EAClC,IAAI,MAAM,SAAS,WAAW,GAC5B,MAAM,IAAI,YAAY,iBAAiB,4BAA4B;EAGrE,MAAM,UAAiC;GAIrC,CAAC,GAAG,MAAM,IAAI,QAAQ,WAAW,cAAc,SAAS,CAAC,CAAC,CAAC,CAAC;GAC5D,CAAC,GAAG,QAAQ,MAAM,QAAQ,CAAC;GAC3B,CAAC,GAAG,OAAO,MAAM,YAAY,YAAY,WAAW,CAAC;GACrD,CAAC,GAAG,WAAW,MAAM,GAAG,CAAC;EAC3B;EACA,IAAI,MAAM,YAAY,KAAA,GAAW,QAAQ,KAAK,CAAC,GAAG,OAAO,MAAM,OAAO,CAAC,CAAC;EACxE,QAAQ,KAAK,CAAC,GAAG,OAAO,MAAM,UAAU,KAAK,QAAQ,MAAM,CAAC,CAAC;EAE7D,MAAM,KAAK,IAAIA,GAAQ,oBAAoB,WAAW,MAAM,OAAO,CAAC,CAAC;EACrE,OAAO,gBAAgB;GACrB;GACA;GACA,YAAY;GACZ,SAAS,KAAK;GACd,QAAQ,UAAU,kBAAkB,OAAO,SAAS;EACtD,CAAC;CACH;;CAGA,eAAe,OAAoB,QAA4C;EAC7E,OAAO,kBACL,KAAK,KAAK,GACV,QAAQ,cAAc,KAAA,IAAY,KAAA,IAAY,mBAAmB,OAAO,SAAS,CACnF;CACF;AACF;AA9CE,SAAgB,WAAW;AAgD7B,SAAS,kBAAkB,IAAQ,mBAA+D;CAChG,cAAc,IAAI,CAAC,GAAG,WAAW,GAAG,eAAe;CACnD,MAAM,MAAM,gBAAgB,IAAI,eAAe;CAK/C,MAAM,cAAc,OAAO,KAAK,CAAC;CACjC,MAAM,SAAS,gBAAgB,KAAA,IAAY,KAAA,IAAY,UAAU,WAAW;CAC5E,IAAI,QAAQ,SAAS,SACnB,MAAM,IAAI,YAAY,mBAAmB,oDAAoD;CAE/F,MAAM,YAAY,kBAAkB,OAAO,KAAK;CAChD,IAAI,sBAAsB,KAAA,GACpB;MAAA,cAAc,QAAQ,CAAC,WAAW,WAAW,iBAAiB,GAChE,MAAM,IAAI,YACR,uBACA,6FACF;CAAA;CAIJ,MAAM,WAAW,OAAO,KAAK,CAAC;CAC9B,MAAM,MAAM,aAAa,KAAA,IAAY,KAAA,IAAY,UAAU,QAAQ;CACnE,IAAI,KAAK,SAAS,SAChB,MAAM,IAAI,YAAY,mBAAmB,gDAAgD;CAE3F,IAAI,IAAI,MAAM,WAAW,IACvB,MAAM,IAAI,YACR,mBACA,8BAA8B,IAAI,MAAM,OAAO,oBACjD;CAEF,OAAO;EAAE,WAAW,6BAAa,IAAI,WAAW,EAAE;EAAG,WAAW,IAAI;CAAM;AAC5E;;AAGA,SAAS,kBAAkB,QAAuC;CAChE,IAAI,OAAO,WAAW,IAAI,OAAO;CACjC,IAAI,OAAO,WAAW,IAAI;EACxB,IAAI,OAAO;EACX,KAAK,MAAM,KAAK,QAAQ;GACtB,IAAI,IAAI,KAAM,OAAO;GACrB,QAAQ,OAAO,aAAa,CAAC;EAC/B;EACA,IAAI;GACF,OAAO,mBAAmB,IAAI;EAChC,QAAQ;GACN,OAAO;EACT;CACF;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"ton-BSPKU5B9.js","names":["UrValue"],"sources":["../src/chains/ton.ts"],"sourcesContent":["import { cborEncode } from '../cbor/encode';\nimport type { CborValue } from '../cbor/model';\nimport { cbBytes, cbMap, cbTag, cbText, cbUint, mapGet, stripTags } from '../cbor/model';\nimport { equalBytes, utf8Encode } from '../core/bytes';\nimport { EraSdkError } from '../core/errors';\nimport { normalizeRequestId, uuidStringify } 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 requireUrType,\n resolveContext,\n resolveRequestId,\n toUr,\n} from './shared';\n\n/** `ton-sign-request` dataType (CBOR key 3). */\nexport const TonDataType = {\n /** `signData` is a Bag-of-Cells; the device signs the ROOT CELL's representation hash. */\n transaction: 1,\n /**\n * TON Connect proof: the device signs\n * `sha256(0xFFFF || \"ton-connect\" || sha256(signData))`.\n */\n tonProof: 2,\n} as const;\nexport type TonDataType = (typeof TonDataType)[keyof typeof TonDataType];\n\nexport interface TonSignRequestProps {\n readonly requestId?: Uint8Array | string;\n /** BoC bytes (transaction) or the raw proof payload (tonProof). */\n readonly signData: Uint8Array;\n /** Defaults to `transaction`. */\n readonly dataType?: TonDataType;\n /** The account path, e.g. `m/44'/607'/0'` (V4R2 and V5R1 share it — the wallet-contract version affects only the address). */\n readonly path: string;\n readonly xfp: string | number;\n /** User-friendly bounceable address TEXT (`UQ…`/`EQ…`) — shown on the device. */\n readonly address?: string;\n readonly origin?: string;\n}\n\nexport interface TonSignatureResult {\n readonly requestId: Uint8Array;\n /** 64-byte Ed25519 signature over the digest for the request's dataType. */\n readonly signature: Uint8Array;\n}\n\nconst REPLY_TYPES = ['ton-signature'] as const;\n\nexport class TonChain {\n static readonly DataType = TonDataType;\n\n private readonly context: ChainContext;\n\n constructor(config?: EraConnectConfig) {\n this.context = resolveContext(config);\n }\n\n /** Build a `ton-sign-request` (7201). Reply: `ton-signature` (7202). */\n generateSignRequest(props: TonSignRequestProps): SignRequest<TonSignatureResult> {\n const requestId = resolveRequestId(this.context, props.requestId);\n const path = parsePath(props.path);\n const xfp = normalizeXfp(props.xfp);\n if (props.signData.length === 0) {\n throw new EraSdkError('invalid-props', 'signData must not be empty');\n }\n\n const entries: [number, CborValue][] = [\n // TON ecosystem quirk: the request id travels as the ASCII BYTES of the\n // hyphenated UUID string, wrapped in tag 37 (that is what Tonkeeper-\n // style integrations emit and what the device echoes back verbatim).\n [1, cbTag(37, cbBytes(utf8Encode(uuidStringify(requestId))))],\n [2, cbBytes(props.signData)],\n [3, cbUint(props.dataType ?? TonDataType.transaction)],\n [4, keypath304(path, xfp)],\n ];\n if (props.address !== undefined) entries.push([5, cbText(props.address)]);\n entries.push([6, cbText(props.origin ?? this.context.origin)]);\n\n const ur = new UrValue('ton-sign-request', cborEncode(cbMap(entries)));\n return makeSignRequest({\n ur,\n requestId,\n replyTypes: REPLY_TYPES,\n context: this.context,\n parse: (reply) => parseTonSignature(reply, requestId),\n });\n }\n\n /** Parse a `ton-signature` standalone. Prefer `SignRequest.scanner().parse()`. */\n parseSignature(input: Ur | string, expect?: ExpectedReply): TonSignatureResult {\n return parseTonSignature(\n toUr(input),\n expect?.requestId === undefined ? undefined : normalizeRequestId(expect.requestId),\n );\n }\n}\n\nfunction parseTonSignature(ur: Ur, expectedRequestId: Uint8Array | undefined): TonSignatureResult {\n requireUrType(ur, [...REPLY_TYPES], 'ton-signature');\n const map = requireReplyMap(ur, 'ton-signature');\n\n // The device echoes the request id BYTES verbatim (tag-37 wrapped). On this\n // chain those bytes are normally the ASCII of the UUID string; a bare\n // 16-byte binary echo is accepted too for forward compatibility.\n const echoedValue = mapGet(map, 1);\n const echoed = echoedValue === undefined ? undefined : stripTags(echoedValue);\n if (echoed?.kind !== 'bytes') {\n throw new EraSdkError('malformed-reply', 'ton-signature does not echo the request id (key 1)');\n }\n const requestId = normalizeEchoedId(echoed.value);\n if (expectedRequestId !== undefined) {\n if (requestId === null || !equalBytes(requestId, expectedRequestId)) {\n throw new EraSdkError(\n 'request-id-mismatch',\n 'ton-signature echoes a different request id — it answers another sign request, not this one',\n );\n }\n }\n\n const sigValue = mapGet(map, 2);\n const sig = sigValue === undefined ? undefined : stripTags(sigValue);\n if (sig?.kind !== 'bytes') {\n throw new EraSdkError('malformed-reply', 'ton-signature is missing the signature (key 2)');\n }\n if (sig.value.length !== 64) {\n throw new EraSdkError(\n 'malformed-reply',\n `ton-signature signature is ${sig.value.length} bytes, expected 64`,\n );\n }\n return { requestId: requestId ?? new Uint8Array(16), signature: sig.value };\n}\n\n/** ASCII-UUID-string bytes (36) or raw binary (16) → 16-byte id; null if neither. */\nfunction normalizeEchoedId(echoed: Uint8Array): Uint8Array | null {\n if (echoed.length === 16) return echoed;\n if (echoed.length === 36) {\n let text = '';\n for (const b of echoed) {\n if (b > 0x7f) return null;\n text += String.fromCharCode(b);\n }\n try {\n return normalizeRequestId(text);\n } catch {\n return null;\n }\n }\n return null;\n}\n"],"mappings":";;;;AAoBA,MAAa,cAAc;;CAEzB,aAAa;;;;;CAKb,UAAU;AACZ;AAuBA,MAAM,cAAc,CAAC,eAAe;AAEpC,IAAa,WAAb,MAAsB;CAKpB,YAAY,QAA2B;EACrC,KAAK,UAAU,eAAe,MAAM;CACtC;;CAGA,oBAAoB,OAA6D;EAC/E,MAAM,YAAY,iBAAiB,KAAK,SAAS,MAAM,SAAS;EAChE,MAAM,OAAO,UAAU,MAAM,IAAI;EACjC,MAAM,MAAM,aAAa,MAAM,GAAG;EAClC,IAAI,MAAM,SAAS,WAAW,GAC5B,MAAM,IAAI,YAAY,iBAAiB,4BAA4B;EAGrE,MAAM,UAAiC;GAIrC,CAAC,GAAG,MAAM,IAAI,QAAQ,WAAW,cAAc,SAAS,CAAC,CAAC,CAAC,CAAC;GAC5D,CAAC,GAAG,QAAQ,MAAM,QAAQ,CAAC;GAC3B,CAAC,GAAG,OAAO,MAAM,YAAY,YAAY,WAAW,CAAC;GACrD,CAAC,GAAG,WAAW,MAAM,GAAG,CAAC;EAC3B;EACA,IAAI,MAAM,YAAY,KAAA,GAAW,QAAQ,KAAK,CAAC,GAAG,OAAO,MAAM,OAAO,CAAC,CAAC;EACxE,QAAQ,KAAK,CAAC,GAAG,OAAO,MAAM,UAAU,KAAK,QAAQ,MAAM,CAAC,CAAC;EAE7D,MAAM,KAAK,IAAIA,GAAQ,oBAAoB,WAAW,MAAM,OAAO,CAAC,CAAC;EACrE,OAAO,gBAAgB;GACrB;GACA;GACA,YAAY;GACZ,SAAS,KAAK;GACd,QAAQ,UAAU,kBAAkB,OAAO,SAAS;EACtD,CAAC;CACH;;CAGA,eAAe,OAAoB,QAA4C;EAC7E,OAAO,kBACL,KAAK,KAAK,GACV,QAAQ,cAAc,KAAA,IAAY,KAAA,IAAY,mBAAmB,OAAO,SAAS,CACnF;CACF;AACF;AA9CE,SAAgB,WAAW;AAgD7B,SAAS,kBAAkB,IAAQ,mBAA+D;CAChG,cAAc,IAAI,CAAC,GAAG,WAAW,GAAG,eAAe;CACnD,MAAM,MAAM,gBAAgB,IAAI,eAAe;CAK/C,MAAM,cAAc,OAAO,KAAK,CAAC;CACjC,MAAM,SAAS,gBAAgB,KAAA,IAAY,KAAA,IAAY,UAAU,WAAW;CAC5E,IAAI,QAAQ,SAAS,SACnB,MAAM,IAAI,YAAY,mBAAmB,oDAAoD;CAE/F,MAAM,YAAY,kBAAkB,OAAO,KAAK;CAChD,IAAI,sBAAsB,KAAA,GACpB;MAAA,cAAc,QAAQ,CAAC,WAAW,WAAW,iBAAiB,GAChE,MAAM,IAAI,YACR,uBACA,6FACF;CAAA;CAIJ,MAAM,WAAW,OAAO,KAAK,CAAC;CAC9B,MAAM,MAAM,aAAa,KAAA,IAAY,KAAA,IAAY,UAAU,QAAQ;CACnE,IAAI,KAAK,SAAS,SAChB,MAAM,IAAI,YAAY,mBAAmB,gDAAgD;CAE3F,IAAI,IAAI,MAAM,WAAW,IACvB,MAAM,IAAI,YACR,mBACA,8BAA8B,IAAI,MAAM,OAAO,oBACjD;CAEF,OAAO;EAAE,WAAW,6BAAa,IAAI,WAAW,EAAE;EAAG,WAAW,IAAI;CAAM;AAC5E;;AAGA,SAAS,kBAAkB,QAAuC;CAChE,IAAI,OAAO,WAAW,IAAI,OAAO;CACjC,IAAI,OAAO,WAAW,IAAI;EACxB,IAAI,OAAO;EACX,KAAK,MAAM,KAAK,QAAQ;GACtB,IAAI,IAAI,KAAM,OAAO;GACrB,QAAQ,OAAO,aAAa,CAAC;EAC/B;EACA,IAAI;GACF,OAAO,mBAAmB,IAAI;EAChC,QAAQ;GACN,OAAO;EACT;CACF;CACA,OAAO;AACT"}
@@ -1,4 +1,5 @@
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
  //#region src/chains/ton.ts
3
4
  /** `ton-sign-request` dataType (CBOR key 3). */
4
5
  const TonDataType = {
@@ -18,14 +19,14 @@ var TonChain = class {
18
19
  /** Build a `ton-sign-request` (7201). Reply: `ton-signature` (7202). */
19
20
  generateSignRequest(props) {
20
21
  const requestId = require_shared.resolveRequestId(this.context, props.requestId);
21
- const path = require_shared.parsePath(props.path);
22
- const xfp = require_shared.normalizeXfp(props.xfp);
22
+ const path = require_keypath.parsePath(props.path);
23
+ const xfp = require_keypath.normalizeXfp(props.xfp);
23
24
  if (props.signData.length === 0) throw new require_shared.EraSdkError("invalid-props", "signData must not be empty");
24
25
  const entries = [
25
26
  [1, require_shared.cbTag(37, require_shared.cbBytes(require_shared.utf8Encode(require_shared.uuidStringify(requestId))))],
26
27
  [2, require_shared.cbBytes(props.signData)],
27
28
  [3, require_shared.cbUint(props.dataType ?? TonDataType.transaction)],
28
- [4, require_shared.keypath304(path, xfp)]
29
+ [4, require_keypath.keypath304(path, xfp)]
29
30
  ];
30
31
  if (props.address !== void 0) entries.push([5, require_shared.cbText(props.address)]);
31
32
  entries.push([6, require_shared.cbText(props.origin ?? this.context.origin)]);
@@ -94,4 +95,4 @@ Object.defineProperty(exports, "TonDataType", {
94
95
  }
95
96
  });
96
97
 
97
- //# sourceMappingURL=ton-DUJGXdGa.cjs.map
98
+ //# sourceMappingURL=ton-CI2s9puH.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ton-DUJGXdGa.cjs","names":["resolveContext","resolveRequestId","parsePath","normalizeXfp","EraSdkError","cbTag","cbBytes","utf8Encode","uuidStringify","cbUint","keypath304","cbText","UrValue","cborEncode","cbMap","makeSignRequest","toUr","normalizeRequestId","requireReplyMap","mapGet","stripTags","equalBytes"],"sources":["../src/chains/ton.ts"],"sourcesContent":["import { cborEncode } from '../cbor/encode';\nimport type { CborValue } from '../cbor/model';\nimport { cbBytes, cbMap, cbTag, cbText, cbUint, mapGet, stripTags } from '../cbor/model';\nimport { equalBytes, utf8Encode } from '../core/bytes';\nimport { EraSdkError } from '../core/errors';\nimport { normalizeRequestId, uuidStringify } 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 requireUrType,\n resolveContext,\n resolveRequestId,\n toUr,\n} from './shared';\n\n/** `ton-sign-request` dataType (CBOR key 3). */\nexport const TonDataType = {\n /** `signData` is a Bag-of-Cells; the device signs the ROOT CELL's representation hash. */\n transaction: 1,\n /**\n * TON Connect proof: the device signs\n * `sha256(0xFFFF || \"ton-connect\" || sha256(signData))`.\n */\n tonProof: 2,\n} as const;\nexport type TonDataType = (typeof TonDataType)[keyof typeof TonDataType];\n\nexport interface TonSignRequestProps {\n readonly requestId?: Uint8Array | string;\n /** BoC bytes (transaction) or the raw proof payload (tonProof). */\n readonly signData: Uint8Array;\n /** Defaults to `transaction`. */\n readonly dataType?: TonDataType;\n /** The account path, e.g. `m/44'/607'/0'` (V4R2 and V5R1 share it — the wallet-contract version affects only the address). */\n readonly path: string;\n readonly xfp: string | number;\n /** User-friendly bounceable address TEXT (`UQ…`/`EQ…`) — shown on the device. */\n readonly address?: string;\n readonly origin?: string;\n}\n\nexport interface TonSignatureResult {\n readonly requestId: Uint8Array;\n /** 64-byte Ed25519 signature over the digest for the request's dataType. */\n readonly signature: Uint8Array;\n}\n\nconst REPLY_TYPES = ['ton-signature'] as const;\n\nexport class TonChain {\n static readonly DataType = TonDataType;\n\n private readonly context: ChainContext;\n\n constructor(config?: EraConnectConfig) {\n this.context = resolveContext(config);\n }\n\n /** Build a `ton-sign-request` (7201). Reply: `ton-signature` (7202). */\n generateSignRequest(props: TonSignRequestProps): SignRequest<TonSignatureResult> {\n const requestId = resolveRequestId(this.context, props.requestId);\n const path = parsePath(props.path);\n const xfp = normalizeXfp(props.xfp);\n if (props.signData.length === 0) {\n throw new EraSdkError('invalid-props', 'signData must not be empty');\n }\n\n const entries: [number, CborValue][] = [\n // TON ecosystem quirk: the request id travels as the ASCII BYTES of the\n // hyphenated UUID string, wrapped in tag 37 (that is what Tonkeeper-\n // style integrations emit and what the device echoes back verbatim).\n [1, cbTag(37, cbBytes(utf8Encode(uuidStringify(requestId))))],\n [2, cbBytes(props.signData)],\n [3, cbUint(props.dataType ?? TonDataType.transaction)],\n [4, keypath304(path, xfp)],\n ];\n if (props.address !== undefined) entries.push([5, cbText(props.address)]);\n entries.push([6, cbText(props.origin ?? this.context.origin)]);\n\n const ur = new UrValue('ton-sign-request', cborEncode(cbMap(entries)));\n return makeSignRequest({\n ur,\n requestId,\n replyTypes: REPLY_TYPES,\n context: this.context,\n parse: (reply) => parseTonSignature(reply, requestId),\n });\n }\n\n /** Parse a `ton-signature` standalone. Prefer `SignRequest.scanner().parse()`. */\n parseSignature(input: Ur | string, expect?: ExpectedReply): TonSignatureResult {\n return parseTonSignature(\n toUr(input),\n expect?.requestId === undefined ? undefined : normalizeRequestId(expect.requestId),\n );\n }\n}\n\nfunction parseTonSignature(ur: Ur, expectedRequestId: Uint8Array | undefined): TonSignatureResult {\n requireUrType(ur, [...REPLY_TYPES], 'ton-signature');\n const map = requireReplyMap(ur, 'ton-signature');\n\n // The device echoes the request id BYTES verbatim (tag-37 wrapped). On this\n // chain those bytes are normally the ASCII of the UUID string; a bare\n // 16-byte binary echo is accepted too for forward compatibility.\n const echoedValue = mapGet(map, 1);\n const echoed = echoedValue === undefined ? undefined : stripTags(echoedValue);\n if (echoed?.kind !== 'bytes') {\n throw new EraSdkError('malformed-reply', 'ton-signature does not echo the request id (key 1)');\n }\n const requestId = normalizeEchoedId(echoed.value);\n if (expectedRequestId !== undefined) {\n if (requestId === null || !equalBytes(requestId, expectedRequestId)) {\n throw new EraSdkError(\n 'request-id-mismatch',\n 'ton-signature echoes a different request id — it answers another sign request, not this one',\n );\n }\n }\n\n const sigValue = mapGet(map, 2);\n const sig = sigValue === undefined ? undefined : stripTags(sigValue);\n if (sig?.kind !== 'bytes') {\n throw new EraSdkError('malformed-reply', 'ton-signature is missing the signature (key 2)');\n }\n if (sig.value.length !== 64) {\n throw new EraSdkError(\n 'malformed-reply',\n `ton-signature signature is ${sig.value.length} bytes, expected 64`,\n );\n }\n return { requestId: requestId ?? new Uint8Array(16), signature: sig.value };\n}\n\n/** ASCII-UUID-string bytes (36) or raw binary (16) → 16-byte id; null if neither. */\nfunction normalizeEchoedId(echoed: Uint8Array): Uint8Array | null {\n if (echoed.length === 16) return echoed;\n if (echoed.length === 36) {\n let text = '';\n for (const b of echoed) {\n if (b > 0x7f) return null;\n text += String.fromCharCode(b);\n }\n try {\n return normalizeRequestId(text);\n } catch {\n return null;\n }\n }\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ton-CI2s9puH.cjs","names":["resolveContext","resolveRequestId","parsePath","normalizeXfp","EraSdkError","cbTag","cbBytes","utf8Encode","uuidStringify","cbUint","keypath304","cbText","UrValue","cborEncode","cbMap","makeSignRequest","toUr","normalizeRequestId","requireReplyMap","mapGet","stripTags","equalBytes"],"sources":["../src/chains/ton.ts"],"sourcesContent":["import { cborEncode } from '../cbor/encode';\nimport type { CborValue } from '../cbor/model';\nimport { cbBytes, cbMap, cbTag, cbText, cbUint, mapGet, stripTags } from '../cbor/model';\nimport { equalBytes, utf8Encode } from '../core/bytes';\nimport { EraSdkError } from '../core/errors';\nimport { normalizeRequestId, uuidStringify } 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 requireUrType,\n resolveContext,\n resolveRequestId,\n toUr,\n} from './shared';\n\n/** `ton-sign-request` dataType (CBOR key 3). */\nexport const TonDataType = {\n /** `signData` is a Bag-of-Cells; the device signs the ROOT CELL's representation hash. */\n transaction: 1,\n /**\n * TON Connect proof: the device signs\n * `sha256(0xFFFF || \"ton-connect\" || sha256(signData))`.\n */\n tonProof: 2,\n} as const;\nexport type TonDataType = (typeof TonDataType)[keyof typeof TonDataType];\n\nexport interface TonSignRequestProps {\n readonly requestId?: Uint8Array | string;\n /** BoC bytes (transaction) or the raw proof payload (tonProof). */\n readonly signData: Uint8Array;\n /** Defaults to `transaction`. */\n readonly dataType?: TonDataType;\n /** The account path, e.g. `m/44'/607'/0'` (V4R2 and V5R1 share it — the wallet-contract version affects only the address). */\n readonly path: string;\n readonly xfp: string | number;\n /** User-friendly bounceable address TEXT (`UQ…`/`EQ…`) — shown on the device. */\n readonly address?: string;\n readonly origin?: string;\n}\n\nexport interface TonSignatureResult {\n readonly requestId: Uint8Array;\n /** 64-byte Ed25519 signature over the digest for the request's dataType. */\n readonly signature: Uint8Array;\n}\n\nconst REPLY_TYPES = ['ton-signature'] as const;\n\nexport class TonChain {\n static readonly DataType = TonDataType;\n\n private readonly context: ChainContext;\n\n constructor(config?: EraConnectConfig) {\n this.context = resolveContext(config);\n }\n\n /** Build a `ton-sign-request` (7201). Reply: `ton-signature` (7202). */\n generateSignRequest(props: TonSignRequestProps): SignRequest<TonSignatureResult> {\n const requestId = resolveRequestId(this.context, props.requestId);\n const path = parsePath(props.path);\n const xfp = normalizeXfp(props.xfp);\n if (props.signData.length === 0) {\n throw new EraSdkError('invalid-props', 'signData must not be empty');\n }\n\n const entries: [number, CborValue][] = [\n // TON ecosystem quirk: the request id travels as the ASCII BYTES of the\n // hyphenated UUID string, wrapped in tag 37 (that is what Tonkeeper-\n // style integrations emit and what the device echoes back verbatim).\n [1, cbTag(37, cbBytes(utf8Encode(uuidStringify(requestId))))],\n [2, cbBytes(props.signData)],\n [3, cbUint(props.dataType ?? TonDataType.transaction)],\n [4, keypath304(path, xfp)],\n ];\n if (props.address !== undefined) entries.push([5, cbText(props.address)]);\n entries.push([6, cbText(props.origin ?? this.context.origin)]);\n\n const ur = new UrValue('ton-sign-request', cborEncode(cbMap(entries)));\n return makeSignRequest({\n ur,\n requestId,\n replyTypes: REPLY_TYPES,\n context: this.context,\n parse: (reply) => parseTonSignature(reply, requestId),\n });\n }\n\n /** Parse a `ton-signature` standalone. Prefer `SignRequest.scanner().parse()`. */\n parseSignature(input: Ur | string, expect?: ExpectedReply): TonSignatureResult {\n return parseTonSignature(\n toUr(input),\n expect?.requestId === undefined ? undefined : normalizeRequestId(expect.requestId),\n );\n }\n}\n\nfunction parseTonSignature(ur: Ur, expectedRequestId: Uint8Array | undefined): TonSignatureResult {\n requireUrType(ur, [...REPLY_TYPES], 'ton-signature');\n const map = requireReplyMap(ur, 'ton-signature');\n\n // The device echoes the request id BYTES verbatim (tag-37 wrapped). On this\n // chain those bytes are normally the ASCII of the UUID string; a bare\n // 16-byte binary echo is accepted too for forward compatibility.\n const echoedValue = mapGet(map, 1);\n const echoed = echoedValue === undefined ? undefined : stripTags(echoedValue);\n if (echoed?.kind !== 'bytes') {\n throw new EraSdkError('malformed-reply', 'ton-signature does not echo the request id (key 1)');\n }\n const requestId = normalizeEchoedId(echoed.value);\n if (expectedRequestId !== undefined) {\n if (requestId === null || !equalBytes(requestId, expectedRequestId)) {\n throw new EraSdkError(\n 'request-id-mismatch',\n 'ton-signature echoes a different request id — it answers another sign request, not this one',\n );\n }\n }\n\n const sigValue = mapGet(map, 2);\n const sig = sigValue === undefined ? undefined : stripTags(sigValue);\n if (sig?.kind !== 'bytes') {\n throw new EraSdkError('malformed-reply', 'ton-signature is missing the signature (key 2)');\n }\n if (sig.value.length !== 64) {\n throw new EraSdkError(\n 'malformed-reply',\n `ton-signature signature is ${sig.value.length} bytes, expected 64`,\n );\n }\n return { requestId: requestId ?? new Uint8Array(16), signature: sig.value };\n}\n\n/** ASCII-UUID-string bytes (36) or raw binary (16) → 16-byte id; null if neither. */\nfunction normalizeEchoedId(echoed: Uint8Array): Uint8Array | null {\n if (echoed.length === 16) return echoed;\n if (echoed.length === 36) {\n let text = '';\n for (const b of echoed) {\n if (b > 0x7f) return null;\n text += String.fromCharCode(b);\n }\n try {\n return normalizeRequestId(text);\n } catch {\n return null;\n }\n }\n return null;\n}\n"],"mappings":"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"}
package/dist/ton.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_ton = require("./ton-DUJGXdGa.cjs");
2
+ const require_shared = require("./shared-Cu2yynP9.cjs");
3
+ const require_ton = require("./ton-CI2s9puH.cjs");
4
4
  exports.AnimatedUr = require_shared.AnimatedUr;
5
5
  exports.EraSdkError = require_shared.EraSdkError;
6
6
  exports.TonChain = require_ton.TonChain;
package/dist/ton.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 { n as TonDataType, t as TonChain } from "./ton-5Uvi5ifg.js";
1
+ import { K as EraSdkError, _ as Ur, d as UrScanner, f as AnimatedUr, u as TypedUrScanner } from "./shared-Bbgejrus.js";
2
+ import { n as TonDataType, t as TonChain } from "./ton-BSPKU5B9.js";
3
3
  export { AnimatedUr, EraSdkError, TonChain, TonDataType, TypedUrScanner, Ur, UrScanner };
@@ -1,5 +1,6 @@
1
- const require_shared = require("./shared-nISrEktU.cjs");
2
- const require_messages = require("./messages-BzcdWlg1.cjs");
1
+ const require_shared = require("./shared-Cu2yynP9.cjs");
2
+ const require_keypath = require("./keypath-pzm_YIfN.cjs");
3
+ const require_messages = require("./messages-BZXDY29v.cjs");
3
4
  let fflate = require("fflate");
4
5
  //#region src/tron-proto/gzip.ts
5
6
  /** Smallest possible gzip stream: 10-byte header + 8-byte trailer. */
@@ -87,12 +88,12 @@ var TronChain = class {
87
88
  /** Build a `keystone-sign-request` (6101). Reply: `keystone-sign-result` (6102). */
88
89
  generateSignRequest(props) {
89
90
  const requestId = require_shared.resolveRequestId(this.context, props.requestId);
90
- require_shared.parsePath(props.path);
91
- const xfp = require_shared.normalizeXfp(props.xfp);
91
+ require_keypath.parsePath(props.path);
92
+ const xfp = require_keypath.normalizeXfp(props.xfp);
92
93
  if (props.rawData.length === 0) throw new require_shared.EraSdkError("invalid-props", "rawData must not be empty");
93
94
  if (!/^[0-9a-fA-F]{64}$/.test(props.latestBlock.hash)) throw new require_shared.EraSdkError("invalid-props", "latestBlock.hash must be the FULL 64-hex block id (the device slices ref_block_hash from it)");
94
95
  const proto = require_messages.encodeSignRequestProto({
95
- xfpHex: require_shared.xfpToHex(xfp),
96
+ xfpHex: require_keypath.xfpToHex(xfp),
96
97
  signId: require_shared.uuidStringify(requestId),
97
98
  hdPath: props.path,
98
99
  timestamp: props.timestamp ?? 0,
@@ -160,4 +161,4 @@ Object.defineProperty(exports, "TronChain", {
160
161
  }
161
162
  });
162
163
 
163
- //# sourceMappingURL=tron-CQIEqjCA.cjs.map
164
+ //# sourceMappingURL=tron-B9wXdzlA.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"tron-CQIEqjCA.cjs","names":["gzipSync","EraSdkError","Gunzip","concatBytes","crc32","resolveContext","resolveRequestId","normalizeXfp","EraSdkError","encodeSignRequestProto","xfpToHex","uuidStringify","UrValue","cborEncode","cbMap","cbBytes","cbText","makeSignRequest","toUr","normalizeRequestId","requireReplyMap","asBytes","mapGet","decodeSignResultProto","splitSignedTronTx"],"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-B9wXdzlA.cjs","names":["gzipSync","EraSdkError","Gunzip","concatBytes","crc32","resolveContext","resolveRequestId","normalizeXfp","EraSdkError","encodeSignRequestProto","xfpToHex","uuidStringify","UrValue","cborEncode","cbMap","cbBytes","cbText","makeSignRequest","toUr","normalizeRequestId","requireReplyMap","asBytes","mapGet","decodeSignResultProto","splitSignedTronTx"],"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,5 +1,6 @@
1
- import { G as cbMap, L as xfpToHex, N as normalizeXfp, P as parsePath, T as concatBytes, W as cbBytes, Y as mapGet, Z as EraSdkError, _ as Ur, c as resolveRequestId, g as uuidStringify, h as normalizeRequestId, l as toUr, n as makeSignRequest, o as requireUrType, q as cbText, r as requireReplyMap, s as resolveContext, x as cborEncode, y as crc32, z as asBytes } from "./shared-B8Wc7ViU.js";
2
- import { n as encodeSignRequestProto, r as splitSignedTronTx, t as decodeSignResultProto } from "./messages-17Oup0-Z.js";
1
+ import { B as cbMap, H as cbText, K as EraSdkError, N as asBytes, T as concatBytes, W as mapGet, _ as Ur, c as resolveRequestId, g as uuidStringify, h as normalizeRequestId, l as toUr, n as makeSignRequest, o as requireUrType, r as requireReplyMap, s as resolveContext, x as cborEncode, y as crc32, z as cbBytes } from "./shared-Bbgejrus.js";
2
+ import { i as parsePath, r as normalizeXfp, s as xfpToHex } from "./keypath-Bvv_cfHt.js";
3
+ import { n as encodeSignRequestProto, r as splitSignedTronTx, t as decodeSignResultProto } from "./messages-DbnXy3dC.js";
3
4
  import { Gunzip, gzipSync } from "fflate";
4
5
  //#region src/tron-proto/gzip.ts
5
6
  /** Smallest possible gzip stream: 10-byte header + 8-byte trailer. */
@@ -155,4 +156,4 @@ function signIdToBytes(signId) {
155
156
  //#endregion
156
157
  export { TronChain as t };
157
158
 
158
- //# sourceMappingURL=tron-Bo9HnvVE.js.map
159
+ //# sourceMappingURL=tron-fufBDNuQ.js.map
@@ -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-fufBDNuQ.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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"}
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_tron = require("./tron-B9wXdzlA.cjs");
4
+ const require_messages = require("./messages-BZXDY29v.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 { t as TronChain } from "./tron-fufBDNuQ.js";
3
+ import { r as splitSignedTronTx } from "./messages-DbnXy3dC.js";
4
4
  export { AnimatedUr, EraSdkError, TronChain, TypedUrScanner, Ur, UrScanner, splitSignedTronTx };