Кошельки TON: версии контрактов v3/v4/v5

Разберёшь отличия Wallet v3R2, v4R2 и v5R1: адрес, seqno, plugins и когда какую версию выбирать в коде.

Кошелёк — это смарт-контракт

В TON «кошелёк» — не запись в БД биржи, а смарт-контракт с публичным ключом в storage. Внешнее сообщение с корректной подписью увеличивает seqno и отправляет внутренние сообщения. Версия контракта (v3R2, v4R2, v5R1) задаёт формат подписи, storage и возможности — и меняет адрес при том же ключе.

На момент публикации ориентир в @ton/ton: WalletContractV3R2, WalletContractV4 (v4R2), WalletContractV5R1.


Адрес зависит от версии

typescript
// @ton/ton ^15.1.0, @ton/crypto ^3.3.0
import { mnemonicToPrivateKey } from "@ton/crypto";
import {
  WalletContractV3R2,
  WalletContractV4,
  WalletContractV5R1,
} from "@ton/ton";

// Только sandbox/testnet-мнемоника. Не подставляй mainnet-seed.
const words =
  "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".split(
    " ",
  );
const { publicKey } = await mnemonicToPrivateKey(words);

const v3 = WalletContractV3R2.create({ workchain: 0, publicKey });
const v4 = WalletContractV4.create({ workchain: 0, publicKey });
// networkGlobalId: -239 mainnet, -3 testnet
const v5 = WalletContractV5R1.create({
  publicKey,
  walletId: { networkGlobalId: -3 },
});

console.log("v3R2", v3.address.toString({ bounceable: false, testOnly: true }));
console.log("v4R2", v4.address.toString({ bounceable: false, testOnly: true }));
console.log("v5R1", v5.address.toString({ bounceable: false, testOnly: true }));

Один ключ → три разных адреса. Импорт «того же seed» в приложение с другой версией по умолчанию покажет пустой кошелёк.

ВерсияКласс в @ton/tonЧто важно
v3R2WalletContractV3R2Классика: seqno + subwallet_id, без plugins
v4R2WalletContractV4+ plugins (подписки контрактов на кошелёк)
v5R1WalletContractV5R1W5: другой wallet_id, сеть в id, расширения (extensions)

Как это работает

  • Адрес = хеш StateInit (code + data). Другой code/data → другой адрес.
  • Replay protection у v3/v4 — поле seqno; повтор того же external с тем же seqno отклоняется.
  • v5 разделяет mainnet/testnet через walletId.networkGlobalId — нельзя «случайно» подписать mainnet-сообщение форматом testnet.
  • UI-кошельки (Tonkeeper и др.) выбирают версию при создании/импорте; в коде версию задаёшь явно.

Частые ошибки

Считал адрес через WalletContractV4, а в Tonkeeper кошелёк на v5 → балансы «пропали»; это другой контракт. Сверь версию в настройках кошелька и в коде.

Создал v5 без networkGlobalId для testnet → адрес и подпись не совпадут с тем, что ждёт сеть. Для testnet передавай -3.

Пишешь «последняя версия кошелька» в доках/тикетах → указывай v3R2 / v4R2 / v5R1 явно.


Что дальше

Материалы gramdocs.tech носят образовательный характер и не являются финансовой, юридической или инвестиционной рекомендацией. Работа с блокчейном TON и токеном Gram связана с рисками потери средств. Правовая информация