@anywalletkit/wallet-ton 0.2.4 → 0.2.5

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.d.cts CHANGED
@@ -218,6 +218,16 @@ declare class UnsafeTransactionError extends SignerError {
218
218
  readonly code: string;
219
219
  constructor(code: string, message: string);
220
220
  }
221
+ /**
222
+ * `sign()` refused a 32-byte message: the wallet contract checks a transfer's signature over the
223
+ * 32-byte hash of its signing cell, so a signature over 32 raw bytes may authorise one. TON Connect
224
+ * proofs and signData go through `signTonProof` / `signTonConnectData`; transactions through
225
+ * `signTransaction` / `sendTransaction`, where they are reviewed.
226
+ */
227
+ declare class MessageIsTransactionError extends SignerError {
228
+ readonly code = "message-is-transaction";
229
+ constructor();
230
+ }
221
231
  /** Emulation of the external message failed (non-zero compute exit, aborted, action phase failed); nothing was signed or sent. */
222
232
  declare class SimulationError extends Error {
223
233
  readonly exitCode: number | undefined;
@@ -617,6 +627,62 @@ declare class WalletAccountReadOnlyTon implements IWalletAccountReadOnly {
617
627
  verify(message: string | Uint8Array, signature: string): Promise<boolean>;
618
628
  }
619
629
 
630
+ /** What a dApp asks `ton_proof` for: its domain (host), the time of the proof, its nonce. */
631
+ interface TonProofRequest {
632
+ /** The dApp's host as the wallet saw it (`app.example`), never the dApp's own claim. */
633
+ domain: string;
634
+ /** Unix seconds. */
635
+ timestamp: number;
636
+ /** The dApp's `ton_proof` payload. */
637
+ payload: string;
638
+ }
639
+ /** The `proof` object of a TON Connect `ton_proof` reply. */
640
+ interface TonProof {
641
+ timestamp: number;
642
+ domain: {
643
+ lengthBytes: number;
644
+ value: string;
645
+ };
646
+ payload: string;
647
+ /** Base64 ed25519 signature. */
648
+ signature: string;
649
+ }
650
+ /** A `signData` payload. `cell` payloads need schema-aware rendering and are not signed here. */
651
+ type TonSignDataPayload = {
652
+ type: 'text';
653
+ text: string;
654
+ } | {
655
+ type: 'binary';
656
+ bytes: string;
657
+ };
658
+ /** What a dApp asks `signData` for. */
659
+ interface TonSignDataRequest {
660
+ /** The dApp's host as the wallet saw it. */
661
+ domain: string;
662
+ /** Unix seconds. */
663
+ timestamp: number;
664
+ payload: TonSignDataPayload;
665
+ }
666
+ /** The TON Connect `signData` reply. */
667
+ interface TonSignedData {
668
+ /** Base64 ed25519 signature. */
669
+ signature: string;
670
+ /** Raw form (`0:…`) of the address that signed. */
671
+ address: string;
672
+ timestamp: number;
673
+ domain: string;
674
+ payload: TonSignDataPayload;
675
+ }
676
+ /** The address parts the digests bind to. */
677
+ interface TonAddressParts {
678
+ workChain: number;
679
+ hash: Uint8Array;
680
+ }
681
+ /** The 32 bytes a wallet signs for `ton_proof`. */
682
+ declare function tonProofDigest(address: TonAddressParts, request: TonProofRequest): Uint8Array;
683
+ /** The 32 bytes a wallet signs for `signData` (text / binary). */
684
+ declare function tonSignDataDigest(address: TonAddressParts, request: TonSignDataRequest): Uint8Array;
685
+
620
686
  /**
621
687
  * Signing TON account on Wallet V5R1. It never holds a key: the V5R1 body is
622
688
  * signed by `signer.sign(signingCell.hash(), {scheme: 'ed25519', context:
@@ -642,14 +708,29 @@ declare class WalletAccountTon extends WalletAccountReadOnlyTon implements IWall
642
708
  static fromPrivateKey(privateKey: Uint8Array, publicKey: Uint8Array, path: string, config?: TonConfig): WalletAccountTon;
643
709
  /** Account index for the standard m/44'/607'/i' path. */
644
710
  get index(): number | undefined;
645
- /** Hex ed25519 signature over the raw message bytes (ton_proof and the like). */
711
+ /**
712
+ * Hex ed25519 signature over the raw message bytes. A 32-byte message — string or bytes — is
713
+ * refused with {@link MessageIsTransactionError}: the wallet contract checks a transfer's
714
+ * signature over the 32-byte hash of its signing cell, so those bytes could be one. Bytes that
715
+ * carry this account's public key are refused as well: one ed25519 key (an external signer) may
716
+ * also be a Solana wallet, and a Solana transaction names its signer's key. TON Connect goes
717
+ * through {@link signTonProof} and {@link signTonConnectData}.
718
+ */
646
719
  sign(message: string | Uint8Array): Promise<string>;
720
+ /**
721
+ * TON Connect `ton_proof` for this wallet's address: the `proof` object of the connect reply. The
722
+ * digest is built here (0xffff ‖ "ton-connect" ‖ …), so no request can turn it into a transfer.
723
+ * `domain` is the host the wallet saw the request come from.
724
+ */
725
+ signTonProof(request: TonProofRequest): Promise<TonProof>;
726
+ /** TON Connect `signData` (text / binary) for this wallet's address: the reply the dApp gets. */
727
+ signTonConnectData(request: TonSignDataRequest): Promise<TonSignedData>;
647
728
  private checkMaxFee;
648
729
  /**
649
730
  * Checks → seqno → preflight → signed body. The preflight runs on the
650
731
  * zero-signed body (ignore_chksig), so nothing is signed before it passes.
651
732
  */
652
- private prepare;
733
+ private signMessages;
653
734
  private preflight;
654
735
  /** Sign without broadcasting. Returns the external-message BODY as base64 BoC (pass it back to sendTransaction as a Cell). */
655
736
  signTransaction(tx: TonSendable, options?: WriteOptions): Promise<string>;
@@ -701,4 +782,4 @@ declare class WalletManagerTon extends WalletManager<WalletAccountTon, TonConfig
701
782
  getFeeRates(): Promise<FeeRates>;
702
783
  }
703
784
 
704
- export { CAIP2, DEFAULT_SEND_MODE, DEFAULT_TONAPI, type DecodedBody, type EmulateOptions, type EmulationOutcome, type EmulationSource, type EmulationVerdict, type GetMethodResult, JETTON_TRANSFER_OP, MAINNET_GLOBAL_ID, MAX_ACTIONS, type ParsedV5R1Body, SEND_MODE, SimulationError, TESTNET_GLOBAL_ID, TON_BASE_PATH, TON_MNEMONIC_PATH, type TonAccountState, type TonAccountStatus, TonCenter, type TonConfig, type TonDescribedKind, type TonDescribedMessage, type TonDescription, type TonEmulateConfig, type TonEndpoint, type TonFees, type TonJettonDetails, type TonProvider, type TonReceipt, type TonRisk, type TonRpc, type TonSendable, type TonSimulation, type TonSimulationSource, type TonTransaction, type TonTransferOptions, type TonWarning, type TonWarningCode, UnsafeTransactionError, type V5R1Message, type V5R1SigningParams, type V5R1WalletIdParts, V5R1_CODE_HEX, V5R1_OP, WalletAccountReadOnlyTon, WalletAccountTon, WalletManagerTon, commentCell, createTonRpc as createTonClient, createTonRpc, decodeBody, WalletManagerTon as default, describeMessageRelaxed, describeTransaction, emulateExternal, emulateViaTonapi, installBufferGlobal, parseEmulation, parseTonapiConsequences, parseTonapiRisk, parseTonapiTrace, parseV5R1Body, resolveEmulate, runEmulation, tonCenterEndpoint, v5r1Address, v5r1Code, v5r1ExternalMessage, v5r1PackSigned, v5r1PackUnsigned, v5r1SigningMessage, v5r1StateInit, v5r1WalletId };
785
+ export { CAIP2, DEFAULT_SEND_MODE, DEFAULT_TONAPI, type DecodedBody, type EmulateOptions, type EmulationOutcome, type EmulationSource, type EmulationVerdict, type GetMethodResult, JETTON_TRANSFER_OP, MAINNET_GLOBAL_ID, MAX_ACTIONS, MessageIsTransactionError, type ParsedV5R1Body, SEND_MODE, SimulationError, TESTNET_GLOBAL_ID, TON_BASE_PATH, TON_MNEMONIC_PATH, type TonAccountState, type TonAccountStatus, type TonAddressParts, TonCenter, type TonConfig, type TonDescribedKind, type TonDescribedMessage, type TonDescription, type TonEmulateConfig, type TonEndpoint, type TonFees, type TonJettonDetails, type TonProof, type TonProofRequest, type TonProvider, type TonReceipt, type TonRisk, type TonRpc, type TonSendable, type TonSignDataPayload, type TonSignDataRequest, type TonSignedData, type TonSimulation, type TonSimulationSource, type TonTransaction, type TonTransferOptions, type TonWarning, type TonWarningCode, UnsafeTransactionError, type V5R1Message, type V5R1SigningParams, type V5R1WalletIdParts, V5R1_CODE_HEX, V5R1_OP, WalletAccountReadOnlyTon, WalletAccountTon, WalletManagerTon, commentCell, createTonRpc as createTonClient, createTonRpc, decodeBody, WalletManagerTon as default, describeMessageRelaxed, describeTransaction, emulateExternal, emulateViaTonapi, installBufferGlobal, parseEmulation, parseTonapiConsequences, parseTonapiRisk, parseTonapiTrace, parseV5R1Body, resolveEmulate, runEmulation, tonCenterEndpoint, tonProofDigest, tonSignDataDigest, v5r1Address, v5r1Code, v5r1ExternalMessage, v5r1PackSigned, v5r1PackUnsigned, v5r1SigningMessage, v5r1StateInit, v5r1WalletId };
package/dist/index.d.ts CHANGED
@@ -218,6 +218,16 @@ declare class UnsafeTransactionError extends SignerError {
218
218
  readonly code: string;
219
219
  constructor(code: string, message: string);
220
220
  }
221
+ /**
222
+ * `sign()` refused a 32-byte message: the wallet contract checks a transfer's signature over the
223
+ * 32-byte hash of its signing cell, so a signature over 32 raw bytes may authorise one. TON Connect
224
+ * proofs and signData go through `signTonProof` / `signTonConnectData`; transactions through
225
+ * `signTransaction` / `sendTransaction`, where they are reviewed.
226
+ */
227
+ declare class MessageIsTransactionError extends SignerError {
228
+ readonly code = "message-is-transaction";
229
+ constructor();
230
+ }
221
231
  /** Emulation of the external message failed (non-zero compute exit, aborted, action phase failed); nothing was signed or sent. */
222
232
  declare class SimulationError extends Error {
223
233
  readonly exitCode: number | undefined;
@@ -617,6 +627,62 @@ declare class WalletAccountReadOnlyTon implements IWalletAccountReadOnly {
617
627
  verify(message: string | Uint8Array, signature: string): Promise<boolean>;
618
628
  }
619
629
 
630
+ /** What a dApp asks `ton_proof` for: its domain (host), the time of the proof, its nonce. */
631
+ interface TonProofRequest {
632
+ /** The dApp's host as the wallet saw it (`app.example`), never the dApp's own claim. */
633
+ domain: string;
634
+ /** Unix seconds. */
635
+ timestamp: number;
636
+ /** The dApp's `ton_proof` payload. */
637
+ payload: string;
638
+ }
639
+ /** The `proof` object of a TON Connect `ton_proof` reply. */
640
+ interface TonProof {
641
+ timestamp: number;
642
+ domain: {
643
+ lengthBytes: number;
644
+ value: string;
645
+ };
646
+ payload: string;
647
+ /** Base64 ed25519 signature. */
648
+ signature: string;
649
+ }
650
+ /** A `signData` payload. `cell` payloads need schema-aware rendering and are not signed here. */
651
+ type TonSignDataPayload = {
652
+ type: 'text';
653
+ text: string;
654
+ } | {
655
+ type: 'binary';
656
+ bytes: string;
657
+ };
658
+ /** What a dApp asks `signData` for. */
659
+ interface TonSignDataRequest {
660
+ /** The dApp's host as the wallet saw it. */
661
+ domain: string;
662
+ /** Unix seconds. */
663
+ timestamp: number;
664
+ payload: TonSignDataPayload;
665
+ }
666
+ /** The TON Connect `signData` reply. */
667
+ interface TonSignedData {
668
+ /** Base64 ed25519 signature. */
669
+ signature: string;
670
+ /** Raw form (`0:…`) of the address that signed. */
671
+ address: string;
672
+ timestamp: number;
673
+ domain: string;
674
+ payload: TonSignDataPayload;
675
+ }
676
+ /** The address parts the digests bind to. */
677
+ interface TonAddressParts {
678
+ workChain: number;
679
+ hash: Uint8Array;
680
+ }
681
+ /** The 32 bytes a wallet signs for `ton_proof`. */
682
+ declare function tonProofDigest(address: TonAddressParts, request: TonProofRequest): Uint8Array;
683
+ /** The 32 bytes a wallet signs for `signData` (text / binary). */
684
+ declare function tonSignDataDigest(address: TonAddressParts, request: TonSignDataRequest): Uint8Array;
685
+
620
686
  /**
621
687
  * Signing TON account on Wallet V5R1. It never holds a key: the V5R1 body is
622
688
  * signed by `signer.sign(signingCell.hash(), {scheme: 'ed25519', context:
@@ -642,14 +708,29 @@ declare class WalletAccountTon extends WalletAccountReadOnlyTon implements IWall
642
708
  static fromPrivateKey(privateKey: Uint8Array, publicKey: Uint8Array, path: string, config?: TonConfig): WalletAccountTon;
643
709
  /** Account index for the standard m/44'/607'/i' path. */
644
710
  get index(): number | undefined;
645
- /** Hex ed25519 signature over the raw message bytes (ton_proof and the like). */
711
+ /**
712
+ * Hex ed25519 signature over the raw message bytes. A 32-byte message — string or bytes — is
713
+ * refused with {@link MessageIsTransactionError}: the wallet contract checks a transfer's
714
+ * signature over the 32-byte hash of its signing cell, so those bytes could be one. Bytes that
715
+ * carry this account's public key are refused as well: one ed25519 key (an external signer) may
716
+ * also be a Solana wallet, and a Solana transaction names its signer's key. TON Connect goes
717
+ * through {@link signTonProof} and {@link signTonConnectData}.
718
+ */
646
719
  sign(message: string | Uint8Array): Promise<string>;
720
+ /**
721
+ * TON Connect `ton_proof` for this wallet's address: the `proof` object of the connect reply. The
722
+ * digest is built here (0xffff ‖ "ton-connect" ‖ …), so no request can turn it into a transfer.
723
+ * `domain` is the host the wallet saw the request come from.
724
+ */
725
+ signTonProof(request: TonProofRequest): Promise<TonProof>;
726
+ /** TON Connect `signData` (text / binary) for this wallet's address: the reply the dApp gets. */
727
+ signTonConnectData(request: TonSignDataRequest): Promise<TonSignedData>;
647
728
  private checkMaxFee;
648
729
  /**
649
730
  * Checks → seqno → preflight → signed body. The preflight runs on the
650
731
  * zero-signed body (ignore_chksig), so nothing is signed before it passes.
651
732
  */
652
- private prepare;
733
+ private signMessages;
653
734
  private preflight;
654
735
  /** Sign without broadcasting. Returns the external-message BODY as base64 BoC (pass it back to sendTransaction as a Cell). */
655
736
  signTransaction(tx: TonSendable, options?: WriteOptions): Promise<string>;
@@ -701,4 +782,4 @@ declare class WalletManagerTon extends WalletManager<WalletAccountTon, TonConfig
701
782
  getFeeRates(): Promise<FeeRates>;
702
783
  }
703
784
 
704
- export { CAIP2, DEFAULT_SEND_MODE, DEFAULT_TONAPI, type DecodedBody, type EmulateOptions, type EmulationOutcome, type EmulationSource, type EmulationVerdict, type GetMethodResult, JETTON_TRANSFER_OP, MAINNET_GLOBAL_ID, MAX_ACTIONS, type ParsedV5R1Body, SEND_MODE, SimulationError, TESTNET_GLOBAL_ID, TON_BASE_PATH, TON_MNEMONIC_PATH, type TonAccountState, type TonAccountStatus, TonCenter, type TonConfig, type TonDescribedKind, type TonDescribedMessage, type TonDescription, type TonEmulateConfig, type TonEndpoint, type TonFees, type TonJettonDetails, type TonProvider, type TonReceipt, type TonRisk, type TonRpc, type TonSendable, type TonSimulation, type TonSimulationSource, type TonTransaction, type TonTransferOptions, type TonWarning, type TonWarningCode, UnsafeTransactionError, type V5R1Message, type V5R1SigningParams, type V5R1WalletIdParts, V5R1_CODE_HEX, V5R1_OP, WalletAccountReadOnlyTon, WalletAccountTon, WalletManagerTon, commentCell, createTonRpc as createTonClient, createTonRpc, decodeBody, WalletManagerTon as default, describeMessageRelaxed, describeTransaction, emulateExternal, emulateViaTonapi, installBufferGlobal, parseEmulation, parseTonapiConsequences, parseTonapiRisk, parseTonapiTrace, parseV5R1Body, resolveEmulate, runEmulation, tonCenterEndpoint, v5r1Address, v5r1Code, v5r1ExternalMessage, v5r1PackSigned, v5r1PackUnsigned, v5r1SigningMessage, v5r1StateInit, v5r1WalletId };
785
+ export { CAIP2, DEFAULT_SEND_MODE, DEFAULT_TONAPI, type DecodedBody, type EmulateOptions, type EmulationOutcome, type EmulationSource, type EmulationVerdict, type GetMethodResult, JETTON_TRANSFER_OP, MAINNET_GLOBAL_ID, MAX_ACTIONS, MessageIsTransactionError, type ParsedV5R1Body, SEND_MODE, SimulationError, TESTNET_GLOBAL_ID, TON_BASE_PATH, TON_MNEMONIC_PATH, type TonAccountState, type TonAccountStatus, type TonAddressParts, TonCenter, type TonConfig, type TonDescribedKind, type TonDescribedMessage, type TonDescription, type TonEmulateConfig, type TonEndpoint, type TonFees, type TonJettonDetails, type TonProof, type TonProofRequest, type TonProvider, type TonReceipt, type TonRisk, type TonRpc, type TonSendable, type TonSignDataPayload, type TonSignDataRequest, type TonSignedData, type TonSimulation, type TonSimulationSource, type TonTransaction, type TonTransferOptions, type TonWarning, type TonWarningCode, UnsafeTransactionError, type V5R1Message, type V5R1SigningParams, type V5R1WalletIdParts, V5R1_CODE_HEX, V5R1_OP, WalletAccountReadOnlyTon, WalletAccountTon, WalletManagerTon, commentCell, createTonRpc as createTonClient, createTonRpc, decodeBody, WalletManagerTon as default, describeMessageRelaxed, describeTransaction, emulateExternal, emulateViaTonapi, installBufferGlobal, parseEmulation, parseTonapiConsequences, parseTonapiRisk, parseTonapiTrace, parseV5R1Body, resolveEmulate, runEmulation, tonCenterEndpoint, tonProofDigest, tonSignDataDigest, v5r1Address, v5r1Code, v5r1ExternalMessage, v5r1PackSigned, v5r1PackUnsigned, v5r1SigningMessage, v5r1StateInit, v5r1WalletId };
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;
@@ -935,8 +942,75 @@ function randomQueryId() {
935
942
 
936
943
  // src/account.ts
937
944
  import { LocalSigner, feeLimit, requireCurve } from "@anywalletkit/core";
945
+ import { Buffer as Buffer5 } from "buffer";
938
946
  import { Cell as Cell5 } from "@ton/core";
947
+
948
+ // src/tonconnect.ts
949
+ import { sha256 } from "@noble/hashes/sha2.js";
950
+ import { Buffer as Buffer4 } from "buffer";
951
+ var utf82 = (s) => new TextEncoder().encode(s);
952
+ var int32BE = (n) => {
953
+ const b = new Uint8Array(4);
954
+ new DataView(b.buffer).setInt32(0, n, false);
955
+ return b;
956
+ };
957
+ var uint32 = (n, littleEndian) => {
958
+ const b = new Uint8Array(4);
959
+ new DataView(b.buffer).setUint32(0, n, littleEndian);
960
+ return b;
961
+ };
962
+ var uint64 = (n, littleEndian) => {
963
+ const b = new Uint8Array(8);
964
+ new DataView(b.buffer).setBigUint64(0, BigInt(n), littleEndian);
965
+ return b;
966
+ };
967
+ var concat = (...parts) => {
968
+ const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
969
+ let o = 0;
970
+ for (const p of parts) {
971
+ out.set(p, o);
972
+ o += p.length;
973
+ }
974
+ return out;
975
+ };
976
+ function checkCommon(address, domain, timestamp) {
977
+ if (address.hash.length !== 32) throw new RangeError("TON address hash must be 32 bytes.");
978
+ if (!Number.isInteger(address.workChain)) throw new TypeError("TON address workChain must be an integer.");
979
+ if (typeof domain !== "string" || domain.length === 0) throw new TypeError("TON Connect domain must be a non-empty string.");
980
+ if (typeof timestamp !== "number" || !Number.isSafeInteger(timestamp) || timestamp < 0) throw new TypeError("TON Connect timestamp must be a non-negative integer (unix seconds).");
981
+ }
982
+ function base64Bytes(raw) {
983
+ const s = raw.replace(/[\t\n\f\r ]/g, "");
984
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(s) || s.replace(/=+$/, "").length % 4 === 1) throw new TypeError("signData binary payload must be base64.");
985
+ return new Uint8Array(Buffer4.from(s, "base64"));
986
+ }
987
+ function tonProofDigest(address, request) {
988
+ checkCommon(address, request.domain, request.timestamp);
989
+ if (typeof request.payload !== "string") throw new TypeError("ton_proof payload must be a string.");
990
+ const domain = utf82(request.domain);
991
+ 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));
992
+ return sha256(concat(new Uint8Array([255, 255]), utf82("ton-connect"), sha256(inner)));
993
+ }
994
+ function tonSignDataDigest(address, request) {
995
+ checkCommon(address, request.domain, request.timestamp);
996
+ const p = request.payload;
997
+ let payload;
998
+ if (p?.type === "text" && typeof p.text === "string") payload = utf82(p.text);
999
+ else if (p?.type === "binary" && typeof p.bytes === "string") payload = base64Bytes(p.bytes);
1000
+ else throw new TypeError('signData payload must be { type: "text", text } or { type: "binary", bytes }; cell payloads are not supported.');
1001
+ const domain = utf82(request.domain);
1002
+ 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));
1003
+ }
1004
+
1005
+ // src/account.ts
939
1006
  var toHex = (u8) => Array.from(u8, (b) => b.toString(16).padStart(2, "0")).join("");
1007
+ var carriesKey = (bytes, key) => {
1008
+ outer: for (let i = 0; i + key.length <= bytes.length; i++) {
1009
+ for (let j = 0; j < key.length; j++) if (bytes[i + j] !== key[j]) continue outer;
1010
+ return true;
1011
+ }
1012
+ return false;
1013
+ };
940
1014
  var WalletAccountTon = class _WalletAccountTon extends WalletAccountReadOnlyTon {
941
1015
  /** seqno and fees come from the network before signing. */
942
1016
  needsNetwork = true;
@@ -960,11 +1034,46 @@ var WalletAccountTon = class _WalletAccountTon extends WalletAccountReadOnlyTon
960
1034
  const m = /^m\/44'\/607'\/(\d+)'$/.exec(this.path);
961
1035
  return m ? Number(m[1]) : void 0;
962
1036
  }
963
- /** Hex ed25519 signature over the raw message bytes (ton_proof and the like). */
1037
+ /**
1038
+ * Hex ed25519 signature over the raw message bytes. A 32-byte message — string or bytes — is
1039
+ * refused with {@link MessageIsTransactionError}: the wallet contract checks a transfer's
1040
+ * signature over the 32-byte hash of its signing cell, so those bytes could be one. Bytes that
1041
+ * carry this account's public key are refused as well: one ed25519 key (an external signer) may
1042
+ * also be a Solana wallet, and a Solana transaction names its signer's key. TON Connect goes
1043
+ * through {@link signTonProof} and {@link signTonConnectData}.
1044
+ */
964
1045
  async sign(message) {
965
1046
  const bytes = typeof message === "string" ? new TextEncoder().encode(message) : message;
1047
+ if (bytes.length === 32 || carriesKey(bytes, this.publicKey)) throw new MessageIsTransactionError();
966
1048
  return toHex(await this.signer.sign(bytes, { scheme: "ed25519", context: "ton-message" }));
967
1049
  }
1050
+ /**
1051
+ * TON Connect `ton_proof` for this wallet's address: the `proof` object of the connect reply. The
1052
+ * digest is built here (0xffff ‖ "ton-connect" ‖ …), so no request can turn it into a transfer.
1053
+ * `domain` is the host the wallet saw the request come from.
1054
+ */
1055
+ async signTonProof(request) {
1056
+ const digest = tonProofDigest(this._address, request);
1057
+ const signature = await this.signer.sign(digest, { scheme: "ed25519", context: "ton-proof" });
1058
+ return {
1059
+ timestamp: request.timestamp,
1060
+ domain: { lengthBytes: new TextEncoder().encode(request.domain).length, value: request.domain },
1061
+ payload: request.payload,
1062
+ signature: Buffer5.from(signature).toString("base64")
1063
+ };
1064
+ }
1065
+ /** TON Connect `signData` (text / binary) for this wallet's address: the reply the dApp gets. */
1066
+ async signTonConnectData(request) {
1067
+ const digest = tonSignDataDigest(this._address, request);
1068
+ const signature = await this.signer.sign(digest, { scheme: "ed25519", context: "ton-sign-data" });
1069
+ return {
1070
+ signature: Buffer5.from(signature).toString("base64"),
1071
+ address: this._address.toRawString(),
1072
+ timestamp: request.timestamp,
1073
+ domain: request.domain,
1074
+ payload: request.payload.type === "text" ? { type: "text", text: request.payload.text } : { type: "binary", bytes: request.payload.bytes }
1075
+ };
1076
+ }
968
1077
  checkMaxFee(fee, limit, op) {
969
1078
  if (limit !== void 0 && fee > BigInt(limit)) throw new Error(`Exceeded maximum fee cost for ${op} operation.`);
970
1079
  }
@@ -972,7 +1081,7 @@ var WalletAccountTon = class _WalletAccountTon extends WalletAccountReadOnlyTon
972
1081
  * Checks → seqno → preflight → signed body. The preflight runs on the
973
1082
  * zero-signed body (ignore_chksig), so nothing is signed before it passes.
974
1083
  */
975
- async prepare(messages, limit, op) {
1084
+ async signMessages(messages, limit, op) {
976
1085
  const want = messages.reduce((sum, m) => sum + (m.message.info.type === "internal" ? m.message.info.value.coins : 0n), 0n);
977
1086
  if (want > 0n) {
978
1087
  const balance = await this.getBalance();
@@ -1007,7 +1116,7 @@ var WalletAccountTon = class _WalletAccountTon extends WalletAccountReadOnlyTon
1007
1116
  await this.preflight(tx, feeLimit(this._config.transactionMaxFee, options), "transaction");
1008
1117
  return tx.toBoc().toString("base64");
1009
1118
  }
1010
- const { body } = await this.prepare(this.toMessages(tx), feeLimit(this._config.transactionMaxFee, options), "transaction");
1119
+ const { body } = await this.signMessages(this.toMessages(tx), feeLimit(this._config.transactionMaxFee, options), "transaction");
1011
1120
  return body.toBoc().toString("base64");
1012
1121
  }
1013
1122
  async broadcast(body) {
@@ -1019,14 +1128,14 @@ var WalletAccountTon = class _WalletAccountTon extends WalletAccountReadOnlyTon
1019
1128
  /** Sign and broadcast; `hash` is the body hash (hex) — resolve it with getTransactionReceipt. */
1020
1129
  async sendTransaction(tx, options = {}) {
1021
1130
  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");
1131
+ 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
1132
  return { hash: await this.broadcast(body), fee };
1024
1133
  }
1025
1134
  /** TON (no `token`) or jetton transfer. */
1026
1135
  async transfer(o, writeOptions = {}) {
1027
1136
  this.requireRpc("transfer tokens");
1028
1137
  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");
1138
+ const { body, fee } = await this.signMessages([message], feeLimit(this._config.transferMaxFee, writeOptions), "transfer");
1030
1139
  return { hash: await this.broadcast(body), fee };
1031
1140
  }
1032
1141
  /** The same wallet as a {@link WalletAccountReadOnlyTon}. */
@@ -1130,6 +1239,7 @@ export {
1130
1239
  JETTON_TRANSFER_OP,
1131
1240
  MAINNET_GLOBAL_ID,
1132
1241
  MAX_ACTIONS,
1242
+ MessageIsTransactionError,
1133
1243
  SEND_MODE,
1134
1244
  SimulationError,
1135
1245
  TESTNET_GLOBAL_ID,
@@ -1168,6 +1278,8 @@ export {
1168
1278
  runEmulation,
1169
1279
  tonCenterEndpoint,
1170
1280
  tonExplorerTxUrl,
1281
+ tonProofDigest,
1282
+ tonSignDataDigest,
1171
1283
  v5r1Address,
1172
1284
  v5r1Code,
1173
1285
  v5r1ExternalMessage,