@anywalletkit/wallet-ton 0.2.4 → 0.3.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.
package/dist/index.js CHANGED
@@ -30,6 +30,13 @@ var UnsafeTransactionError = class extends SignerError {
30
30
  this.code = code;
31
31
  }
32
32
  };
33
+ var MessageIsTransactionError = class extends SignerError {
34
+ code = "message-is-transaction";
35
+ constructor() {
36
+ super("This message has the shape of a transaction (a 32-byte TON hash, or bytes naming this key as a Solana signer) and is not signed; use signTonProof / signTonConnectData for TON Connect, or signTransaction so it can be reviewed.");
37
+ this.name = "MessageIsTransactionError";
38
+ }
39
+ };
33
40
  var SimulationError = class extends Error {
34
41
  exitCode;
35
42
  aborted;
@@ -40,6 +47,17 @@ var SimulationError = class extends Error {
40
47
  this.aborted = details.aborted ?? false;
41
48
  }
42
49
  };
50
+ var BroadcastUnknownError = class extends Error {
51
+ /** Always true: the transaction may be on its way. */
52
+ maybeSent = true;
53
+ /** Hex hash of the signed body. */
54
+ hash;
55
+ constructor(hash, cause) {
56
+ super(`Broadcast outcome unknown for ${hash}: ${cause instanceof Error ? cause.message.slice(0, 200) : String(cause).slice(0, 200)}`, { cause });
57
+ this.name = "BroadcastUnknownError";
58
+ this.hash = hash;
59
+ }
60
+ };
43
61
 
44
62
  // src/toncenter.ts
45
63
  import { NetworkError } from "@anywalletkit/core";
@@ -77,12 +95,15 @@ function tonCenterEndpoint(endpoint, opts = {}) {
77
95
  }
78
96
  if (!res.ok || json.ok !== true) {
79
97
  const detail = typeof json.error === "string" ? json.error : JSON.stringify(json.error ?? json);
80
- throw new NetworkError(`${method}: ${detail} (HTTP ${res.status})`, { provider: url });
98
+ throw Object.assign(new NetworkError(`${method}: ${detail} (HTTP ${res.status})`, { provider: url }), { answered: true });
81
99
  }
82
100
  return json.result;
83
101
  }
84
102
  };
85
103
  }
104
+ function isAnsweredRefusal(e) {
105
+ return e?.answered === true;
106
+ }
86
107
  function createTonRpc(config) {
87
108
  const target = config.tonClient ?? config.provider;
88
109
  if (target === void 0) return void 0;
@@ -95,14 +116,17 @@ function createTonRpc(config) {
95
116
  return {
96
117
  async call(method, params) {
97
118
  let lastError;
119
+ let answered = true;
98
120
  for (let i = 0; i < attempts; i++) {
99
121
  try {
100
122
  return await list[i % list.length].call(method, params);
101
123
  } catch (e) {
102
124
  lastError = e;
125
+ answered &&= isAnsweredRefusal(e);
103
126
  }
104
127
  }
105
- throw new NetworkError(`All TON endpoints failed for ${method}`, { cause: lastError });
128
+ const err = new NetworkError(`All TON endpoints failed for ${method}`, { cause: lastError });
129
+ throw answered ? Object.assign(err, { answered: true }) : err;
106
130
  }
107
131
  };
108
132
  }
@@ -638,6 +662,19 @@ function describeOne(i, to, value, body, hasStateInit, bounceableOverride, warni
638
662
  return item;
639
663
  }
640
664
  var hex = (n) => `0x${n.toString(16).padStart(8, "0")}`;
665
+ var IGNORE_ERRORS = 2;
666
+ function sendModeOf(sendMode) {
667
+ return (sendMode ?? DEFAULT_SEND_MODE) | IGNORE_ERRORS;
668
+ }
669
+ function drainModes(mode) {
670
+ return [...(mode & 128) !== 0 ? ["carries-all-balance"] : [], ...(mode & 32) !== 0 ? ["destroys-account"] : []];
671
+ }
672
+ function withMode(i, item, mode, warnings) {
673
+ for (const code of drainModes(mode)) {
674
+ warnings.push({ code, message: code === "carries-all-balance" ? "send mode 128: the message carries the whole remaining balance" : "send mode 32: the wallet account is destroyed once it is empty", index: i });
675
+ }
676
+ return { ...item, mode };
677
+ }
641
678
  function describeMessageRelaxed(i, m, warnings, ctx = {}) {
642
679
  if (m.info.type !== "internal") {
643
680
  warnings.push({ code: "unknown-body", message: "not an internal message", index: i });
@@ -659,7 +696,7 @@ function describeTransaction(tx, ctx = {}) {
659
696
  return { kind: "contract-call", items, warnings };
660
697
  }
661
698
  external2 = { seqno: parsed.seqno, validUntil: parsed.validUntil, walletId: parsed.walletId, signed: parsed.signed };
662
- parsed.messages.forEach((m, i) => items.push(describeMessageRelaxed(i, m.message, warnings, ctx)));
699
+ parsed.messages.forEach((m, i) => items.push(withMode(i, describeMessageRelaxed(i, m.message, warnings, ctx), m.mode, warnings)));
663
700
  } else {
664
701
  const list = Array.isArray(tx) ? tx : [tx];
665
702
  list.forEach((t, i) => {
@@ -677,7 +714,7 @@ function describeTransaction(tx, ctx = {}) {
677
714
  } catch {
678
715
  warnings.push({ code: "unknown-body", message: "bodyBoc is not a valid BoC", index: i });
679
716
  }
680
- items.push(describeOne(i, to, BigInt(t.value), body, t.stateInit !== void 0, t.bounceable, warnings, ctx));
717
+ items.push(withMode(i, describeOne(i, to, BigInt(t.value), body, t.stateInit !== void 0, t.bounceable, warnings, ctx), sendModeOf(t.sendMode), warnings));
681
718
  });
682
719
  }
683
720
  const kinds = new Set(items.map((x) => x.kind));
@@ -783,25 +820,34 @@ var WalletAccountReadOnlyTon = class {
783
820
  if (src instanceof Cell4) return loadStateInit(src.beginParse());
784
821
  return src;
785
822
  }
786
- transactionMessage(tx) {
787
- const value = big(tx.value);
788
- if (value <= 0n) throw new UnsafeTransactionError("zero-value", "TON message value must be positive.");
823
+ /**
824
+ * A destination address with the checks every payment gets: workchain 0 or -1, and a friendly
825
+ * address's test-only flag matching the wallet's network. Used for a native message's `to` and a
826
+ * jetton transfer's recipient and response destination (round 3, L8).
827
+ */
828
+ checkedAddress(to) {
789
829
  let address;
790
830
  let bounceable = true;
791
831
  let testOnly;
792
- if (Address4.isFriendly(tx.to)) {
793
- const f = Address4.parseFriendly(tx.to);
832
+ if (Address4.isFriendly(to)) {
833
+ const f = Address4.parseFriendly(to);
794
834
  address = f.address;
795
835
  bounceable = f.isBounceable;
796
836
  testOnly = f.isTestOnly;
797
837
  } else {
798
- address = Address4.parse(tx.to);
838
+ address = Address4.parse(to);
799
839
  }
800
840
  if (address.workChain !== 0 && address.workChain !== -1) throw new UnsafeTransactionError("exotic-workchain", `Refusing to send to workchain ${address.workChain}.`);
801
841
  const testnet = this._config.network === "testnet";
802
842
  if (testOnly !== void 0 && testOnly !== testnet) {
803
843
  throw new UnsafeTransactionError("network-mismatch", testOnly ? "Test-only address on a mainnet wallet." : "Mainnet address on a testnet wallet.");
804
844
  }
845
+ return { address, bounceable };
846
+ }
847
+ transactionMessage(tx) {
848
+ const value = big(tx.value);
849
+ if (value <= 0n) throw new UnsafeTransactionError("zero-value", "TON message value must be positive.");
850
+ const { address, bounceable } = this.checkedAddress(tx.to);
805
851
  const body = tx.body instanceof Cell4 ? tx.body : typeof tx.body === "string" ? tx.body === "" ? void 0 : commentCell(tx.body) : tx.bodyBoc ? Cell4.fromBase64(tx.bodyBoc) : void 0;
806
852
  const init = this.stateInitOf(tx.stateInit);
807
853
  const message = internal({ to: address, value, bounce: tx.bounceable ?? bounceable, ...body ? { body } : {}, ...init ? { init } : {} });
@@ -810,14 +856,15 @@ var WalletAccountReadOnlyTon = class {
810
856
  async jettonTransferMessage(o) {
811
857
  const amount = big(o.amount);
812
858
  if (amount <= 0n) throw new UnsafeTransactionError("zero-value", "Jetton amount must be positive.");
813
- const recipient = Address4.parse(o.recipient);
859
+ const recipient = this.checkedAddress(o.recipient).address;
860
+ const response = o.responseDestination ? this.checkedAddress(o.responseDestination).address : void 0;
814
861
  const jw = await this.getJettonWalletAddress(o.token);
815
862
  await this.jettonWalletData(o.token, jw);
816
863
  const forwardTonAmount = o.forwardTonAmount !== void 0 ? big(o.forwardTonAmount) : 1n;
817
864
  if (o.memo !== void 0 && o.forwardPayload !== void 0) throw new UnsafeTransactionError("memo-and-payload", "Pass either memo or forwardPayload, not both: a jetton transfer carries one forward payload.");
818
865
  const payload = o.forwardPayload ?? o.memo;
819
866
  const forwardPayload = payload === void 0 ? null : typeof payload === "string" ? commentCell(payload) : payload;
820
- const body = beginCell3().storeUint(JETTON_TRANSFER_OP2, 32).storeUint(o.queryId !== void 0 ? BigInt(o.queryId) : randomQueryId(), 64).storeCoins(amount).storeAddress(recipient).storeAddress(o.responseDestination ? Address4.parse(o.responseDestination) : this._address).storeMaybeRef(o.customPayload ?? null).storeCoins(forwardTonAmount).storeMaybeRef(forwardPayload).endCell();
867
+ const body = beginCell3().storeUint(JETTON_TRANSFER_OP2, 32).storeUint(o.queryId !== void 0 ? BigInt(o.queryId) : randomQueryId(), 64).storeCoins(amount).storeAddress(recipient).storeAddress(response ?? this._address).storeMaybeRef(o.customPayload ?? null).storeCoins(forwardTonAmount).storeMaybeRef(forwardPayload).endCell();
821
868
  const attached = o.attachedValue !== void 0 ? big(o.attachedValue) : (this._config.jettonAttachedValue !== void 0 ? big(this._config.jettonAttachedValue) : DEFAULT_JETTON_ATTACHED) + forwardTonAmount;
822
869
  return { message: internal({ to: jw, value: attached, bounce: true, body }) };
823
870
  }
@@ -935,8 +982,75 @@ function randomQueryId() {
935
982
 
936
983
  // src/account.ts
937
984
  import { LocalSigner, feeLimit, requireCurve } from "@anywalletkit/core";
985
+ import { Buffer as Buffer5 } from "buffer";
938
986
  import { Cell as Cell5 } from "@ton/core";
987
+
988
+ // src/tonconnect.ts
989
+ import { sha256 } from "@noble/hashes/sha2.js";
990
+ import { Buffer as Buffer4 } from "buffer";
991
+ var utf82 = (s) => new TextEncoder().encode(s);
992
+ var int32BE = (n) => {
993
+ const b = new Uint8Array(4);
994
+ new DataView(b.buffer).setInt32(0, n, false);
995
+ return b;
996
+ };
997
+ var uint32 = (n, littleEndian) => {
998
+ const b = new Uint8Array(4);
999
+ new DataView(b.buffer).setUint32(0, n, littleEndian);
1000
+ return b;
1001
+ };
1002
+ var uint64 = (n, littleEndian) => {
1003
+ const b = new Uint8Array(8);
1004
+ new DataView(b.buffer).setBigUint64(0, BigInt(n), littleEndian);
1005
+ return b;
1006
+ };
1007
+ var concat = (...parts) => {
1008
+ const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
1009
+ let o = 0;
1010
+ for (const p of parts) {
1011
+ out.set(p, o);
1012
+ o += p.length;
1013
+ }
1014
+ return out;
1015
+ };
1016
+ function checkCommon(address, domain, timestamp) {
1017
+ if (address.hash.length !== 32) throw new RangeError("TON address hash must be 32 bytes.");
1018
+ if (!Number.isInteger(address.workChain)) throw new TypeError("TON address workChain must be an integer.");
1019
+ if (typeof domain !== "string" || domain.length === 0) throw new TypeError("TON Connect domain must be a non-empty string.");
1020
+ if (typeof timestamp !== "number" || !Number.isSafeInteger(timestamp) || timestamp < 0) throw new TypeError("TON Connect timestamp must be a non-negative integer (unix seconds).");
1021
+ }
1022
+ function base64Bytes(raw) {
1023
+ const s = raw.replace(/[\t\n\f\r ]/g, "");
1024
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(s) || s.replace(/=+$/, "").length % 4 === 1) throw new TypeError("signData binary payload must be base64.");
1025
+ return new Uint8Array(Buffer4.from(s, "base64"));
1026
+ }
1027
+ function tonProofDigest(address, request) {
1028
+ checkCommon(address, request.domain, request.timestamp);
1029
+ if (typeof request.payload !== "string") throw new TypeError("ton_proof payload must be a string.");
1030
+ const domain = utf82(request.domain);
1031
+ const inner = concat(utf82("ton-proof-item-v2/"), int32BE(address.workChain), address.hash, uint32(domain.length, true), domain, uint64(request.timestamp, true), utf82(request.payload));
1032
+ return sha256(concat(new Uint8Array([255, 255]), utf82("ton-connect"), sha256(inner)));
1033
+ }
1034
+ function tonSignDataDigest(address, request) {
1035
+ checkCommon(address, request.domain, request.timestamp);
1036
+ const p = request.payload;
1037
+ let payload;
1038
+ if (p?.type === "text" && typeof p.text === "string") payload = utf82(p.text);
1039
+ else if (p?.type === "binary" && typeof p.bytes === "string") payload = base64Bytes(p.bytes);
1040
+ else throw new TypeError('signData payload must be { type: "text", text } or { type: "binary", bytes }; cell payloads are not supported.');
1041
+ const domain = utf82(request.domain);
1042
+ return sha256(concat(new Uint8Array([255, 255]), utf82("ton-connect/sign-data/"), int32BE(address.workChain), address.hash, uint32(domain.length, false), domain, uint64(request.timestamp, false), utf82(p.type === "text" ? "txt" : "bin"), uint32(payload.length, false), payload));
1043
+ }
1044
+
1045
+ // src/account.ts
939
1046
  var toHex = (u8) => Array.from(u8, (b) => b.toString(16).padStart(2, "0")).join("");
1047
+ var carriesKey = (bytes, key) => {
1048
+ outer: for (let i = 0; i + key.length <= bytes.length; i++) {
1049
+ for (let j = 0; j < key.length; j++) if (bytes[i + j] !== key[j]) continue outer;
1050
+ return true;
1051
+ }
1052
+ return false;
1053
+ };
940
1054
  var WalletAccountTon = class _WalletAccountTon extends WalletAccountReadOnlyTon {
941
1055
  /** seqno and fees come from the network before signing. */
942
1056
  needsNetwork = true;
@@ -960,11 +1074,46 @@ var WalletAccountTon = class _WalletAccountTon extends WalletAccountReadOnlyTon
960
1074
  const m = /^m\/44'\/607'\/(\d+)'$/.exec(this.path);
961
1075
  return m ? Number(m[1]) : void 0;
962
1076
  }
963
- /** Hex ed25519 signature over the raw message bytes (ton_proof and the like). */
1077
+ /**
1078
+ * Hex ed25519 signature over the raw message bytes. A 32-byte message — string or bytes — is
1079
+ * refused with {@link MessageIsTransactionError}: the wallet contract checks a transfer's
1080
+ * signature over the 32-byte hash of its signing cell, so those bytes could be one. Bytes that
1081
+ * carry this account's public key are refused as well: one ed25519 key (an external signer) may
1082
+ * also be a Solana wallet, and a Solana transaction names its signer's key. TON Connect goes
1083
+ * through {@link signTonProof} and {@link signTonConnectData}.
1084
+ */
964
1085
  async sign(message) {
965
1086
  const bytes = typeof message === "string" ? new TextEncoder().encode(message) : message;
1087
+ if (bytes.length === 32 || carriesKey(bytes, this.publicKey)) throw new MessageIsTransactionError();
966
1088
  return toHex(await this.signer.sign(bytes, { scheme: "ed25519", context: "ton-message" }));
967
1089
  }
1090
+ /**
1091
+ * TON Connect `ton_proof` for this wallet's address: the `proof` object of the connect reply. The
1092
+ * digest is built here (0xffff ‖ "ton-connect" ‖ …), so no request can turn it into a transfer.
1093
+ * `domain` is the host the wallet saw the request come from.
1094
+ */
1095
+ async signTonProof(request) {
1096
+ const digest = tonProofDigest(this._address, request);
1097
+ const signature = await this.signer.sign(digest, { scheme: "ed25519", context: "ton-proof" });
1098
+ return {
1099
+ timestamp: request.timestamp,
1100
+ domain: { lengthBytes: new TextEncoder().encode(request.domain).length, value: request.domain },
1101
+ payload: request.payload,
1102
+ signature: Buffer5.from(signature).toString("base64")
1103
+ };
1104
+ }
1105
+ /** TON Connect `signData` (text / binary) for this wallet's address: the reply the dApp gets. */
1106
+ async signTonConnectData(request) {
1107
+ const digest = tonSignDataDigest(this._address, request);
1108
+ const signature = await this.signer.sign(digest, { scheme: "ed25519", context: "ton-sign-data" });
1109
+ return {
1110
+ signature: Buffer5.from(signature).toString("base64"),
1111
+ address: this._address.toRawString(),
1112
+ timestamp: request.timestamp,
1113
+ domain: request.domain,
1114
+ payload: request.payload.type === "text" ? { type: "text", text: request.payload.text } : { type: "binary", bytes: request.payload.bytes }
1115
+ };
1116
+ }
968
1117
  checkMaxFee(fee, limit, op) {
969
1118
  if (limit !== void 0 && fee > BigInt(limit)) throw new Error(`Exceeded maximum fee cost for ${op} operation.`);
970
1119
  }
@@ -972,7 +1121,22 @@ var WalletAccountTon = class _WalletAccountTon extends WalletAccountReadOnlyTon
972
1121
  * Checks → seqno → preflight → signed body. The preflight runs on the
973
1122
  * zero-signed body (ignore_chksig), so nothing is signed before it passes.
974
1123
  */
975
- async prepare(messages, limit, op) {
1124
+ /**
1125
+ * Refuse send modes that drain the wallet — 128 (the whole remaining balance) and 32 (the account
1126
+ * destroyed once empty) — unless `allowDrainingTransactions`. Checked locally, before any seqno or
1127
+ * emulation, so it holds with `simulate: false` too (round 3, M11); the emulation check stays.
1128
+ */
1129
+ assertNoDrain(modes) {
1130
+ if (this._config.allowDrainingTransactions === true) return;
1131
+ const found = modes.flatMap(drainModes);
1132
+ if (found.length) throw new UnsafeTransactionError("drain", `Refusing a message with send mode ${found.includes("carries-all-balance") ? "128 (it carries the whole remaining balance)" : "32 (it destroys the wallet account once empty)"}; set allowDrainingTransactions to override.`);
1133
+ }
1134
+ /** The send modes of a prebuilt V5R1 body; an unreadable body is left to the emulation and the node. */
1135
+ bodyModes(body) {
1136
+ return parseV5R1Body(body)?.messages.map((m) => m.mode) ?? [];
1137
+ }
1138
+ async signMessages(messages, limit, op) {
1139
+ this.assertNoDrain(messages.map((m) => sendModeOf(m.mode)));
976
1140
  const want = messages.reduce((sum, m) => sum + (m.message.info.type === "internal" ? m.message.info.value.coins : 0n), 0n);
977
1141
  if (want > 0n) {
978
1142
  const balance = await this.getBalance();
@@ -1004,29 +1168,37 @@ var WalletAccountTon = class _WalletAccountTon extends WalletAccountReadOnlyTon
1004
1168
  /** Sign without broadcasting. Returns the external-message BODY as base64 BoC (pass it back to sendTransaction as a Cell). */
1005
1169
  async signTransaction(tx, options = {}) {
1006
1170
  if (tx instanceof Cell5) {
1171
+ this.assertNoDrain(this.bodyModes(tx));
1007
1172
  await this.preflight(tx, feeLimit(this._config.transactionMaxFee, options), "transaction");
1008
1173
  return tx.toBoc().toString("base64");
1009
1174
  }
1010
- const { body } = await this.prepare(this.toMessages(tx), feeLimit(this._config.transactionMaxFee, options), "transaction");
1175
+ const { body } = await this.signMessages(this.toMessages(tx), feeLimit(this._config.transactionMaxFee, options), "transaction");
1011
1176
  return body.toBoc().toString("base64");
1012
1177
  }
1013
1178
  async broadcast(body) {
1014
1179
  const rpc = this.requireRpc("send transactions");
1015
1180
  const init = await this.initIfNeeded();
1016
- await rpc.sendBoc(v5r1ExternalMessage(this._address, body, init));
1017
- return body.hash().toString("hex");
1181
+ const hash = body.hash().toString("hex");
1182
+ try {
1183
+ await rpc.sendBoc(v5r1ExternalMessage(this._address, body, init));
1184
+ } catch (e) {
1185
+ if (isAnsweredRefusal(e)) throw e;
1186
+ throw new BroadcastUnknownError(hash, e);
1187
+ }
1188
+ return hash;
1018
1189
  }
1019
1190
  /** Sign and broadcast; `hash` is the body hash (hex) — resolve it with getTransactionReceipt. */
1020
1191
  async sendTransaction(tx, options = {}) {
1021
1192
  this.requireRpc("send transactions");
1022
- const { body, fee } = tx instanceof Cell5 ? { body: tx, fee: await this.preflight(tx, feeLimit(this._config.transactionMaxFee, options), "transaction") } : await this.prepare(this.toMessages(tx), feeLimit(this._config.transactionMaxFee, options), "transaction");
1193
+ if (tx instanceof Cell5) this.assertNoDrain(this.bodyModes(tx));
1194
+ const { body, fee } = tx instanceof Cell5 ? { body: tx, fee: await this.preflight(tx, feeLimit(this._config.transactionMaxFee, options), "transaction") } : await this.signMessages(this.toMessages(tx), feeLimit(this._config.transactionMaxFee, options), "transaction");
1023
1195
  return { hash: await this.broadcast(body), fee };
1024
1196
  }
1025
1197
  /** TON (no `token`) or jetton transfer. */
1026
1198
  async transfer(o, writeOptions = {}) {
1027
1199
  this.requireRpc("transfer tokens");
1028
1200
  const message = o.token ? await this.jettonTransferMessage(o) : this.transactionMessage({ to: o.recipient, value: o.amount, ...o.memo !== void 0 ? { body: o.memo } : {} });
1029
- const { body, fee } = await this.prepare([message], feeLimit(this._config.transferMaxFee, writeOptions), "transfer");
1201
+ const { body, fee } = await this.signMessages([message], feeLimit(this._config.transferMaxFee, writeOptions), "transfer");
1030
1202
  return { hash: await this.broadcast(body), fee };
1031
1203
  }
1032
1204
  /** The same wallet as a {@link WalletAccountReadOnlyTon}. */
@@ -1042,7 +1214,7 @@ var WalletAccountTon = class _WalletAccountTon extends WalletAccountReadOnlyTon
1042
1214
 
1043
1215
  // src/manager.ts
1044
1216
  import { LocalSigner as LocalSigner2, SignerError as SignerError2, WalletManager, isSeedSigner, zeroize } from "@anywalletkit/core";
1045
- import { mnemonicToPrivateKey } from "@ton/crypto";
1217
+ import { mnemonicToPrivateKey, mnemonicValidate } from "@ton/crypto";
1046
1218
  var TON_BASE_PATH = "m/44'/607'";
1047
1219
  var TON_MNEMONIC_PATH = "ton-mnemonic";
1048
1220
  var WalletManagerTon = class _WalletManagerTon extends WalletManager {
@@ -1057,6 +1229,7 @@ var WalletManagerTon = class _WalletManagerTon extends WalletManager {
1057
1229
  static async fromTonMnemonic(words, config = {}) {
1058
1230
  const list = (Array.isArray(words) ? words : words.trim().split(/\s+/)).map((w) => w.toLowerCase());
1059
1231
  if (list.length !== 24) throw new Error("A TON mnemonic has 24 words.");
1232
+ if (!await mnemonicValidate(list)) throw new Error("Not a valid TON mnemonic: check the words (a typo would open a different, empty wallet).");
1060
1233
  const pair = await mnemonicToPrivateKey(list);
1061
1234
  const privateKey = new Uint8Array(pair.secretKey.subarray(0, 32));
1062
1235
  const publicKey = new Uint8Array(pair.publicKey);
@@ -1123,6 +1296,7 @@ var WalletManagerTon = class _WalletManagerTon extends WalletManager {
1123
1296
  // src/index.ts
1124
1297
  installBufferGlobal();
1125
1298
  export {
1299
+ BroadcastUnknownError,
1126
1300
  CAIP2,
1127
1301
  DEFAULT_SEND_MODE,
1128
1302
  DEFAULT_TONAPI,
@@ -1130,6 +1304,7 @@ export {
1130
1304
  JETTON_TRANSFER_OP,
1131
1305
  MAINNET_GLOBAL_ID,
1132
1306
  MAX_ACTIONS,
1307
+ MessageIsTransactionError,
1133
1308
  SEND_MODE,
1134
1309
  SimulationError,
1135
1310
  TESTNET_GLOBAL_ID,
@@ -1155,6 +1330,7 @@ export {
1155
1330
  emulateViaTonapi,
1156
1331
  encodeCursor as encodeHistoryCursor,
1157
1332
  installBufferGlobal,
1333
+ isAnsweredRefusal,
1158
1334
  mapActions as mapTonapiActions,
1159
1335
  mapTonapiEvent,
1160
1336
  mapToncenterTransaction,
@@ -1168,6 +1344,8 @@ export {
1168
1344
  runEmulation,
1169
1345
  tonCenterEndpoint,
1170
1346
  tonExplorerTxUrl,
1347
+ tonProofDigest,
1348
+ tonSignDataDigest,
1171
1349
  v5r1Address,
1172
1350
  v5r1Code,
1173
1351
  v5r1ExternalMessage,