From 59d2757e0cf780219f011661a5b8f1dc4f284950 Mon Sep 17 00:00:00 2001 From: b4cksl4sh Date: Fri, 28 Aug 2026 15:08:57 +0300 Subject: [PATCH] feat: add tg wallet to the list of known wallets in demo backend --- .../src/server/wrappers/wallet-contract-tg.ts | 28 +++++++++++++++++++ .../src/server/wrappers/wallets-data.ts | 14 +++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 apps/demo-dapp-with-react-ui/src/server/wrappers/wallet-contract-tg.ts diff --git a/apps/demo-dapp-with-react-ui/src/server/wrappers/wallet-contract-tg.ts b/apps/demo-dapp-with-react-ui/src/server/wrappers/wallet-contract-tg.ts new file mode 100644 index 000000000..52f08d699 --- /dev/null +++ b/apps/demo-dapp-with-react-ui/src/server/wrappers/wallet-contract-tg.ts @@ -0,0 +1,28 @@ +import { beginCell, Cell, contractAddress } from '@ton/ton'; +import { Buffer } from 'buffer'; + +// Per-account "trampoline" of the Telegram wallet (WalletTg): the only code deployed +// for each account. The actual WalletTg bytecode lives in the network config +// (param -123), and the trampoline jumps into it, so the state init code below +// never changes across WalletTg revisions. +const walletTgTrampolineCode = 'te6cckEBAQEAGgAAMP8AIJgh10mDCLnyQN+Ahfgz0O0eIO1T2WlCfjk='; + +// 0xXX prefix of the current WalletTg storage layout (bumped on each contract revision). +const walletTgStorageRevision = 0x00; + +export const WALLET_TG_SUBWALLET_ID_MAINNET = 0x7fff7f11; +export const WALLET_TG_SUBWALLET_ID_TESTNET = 0x7fff7ffd; + +export class WalletContractTg { + static create(args: { workchain: number; publicKey: Buffer; walletId?: number | null }) { + const code = Cell.fromBase64(walletTgTrampolineCode); + const data = beginCell() + .storeUint(walletTgStorageRevision, 8) + .storeUint(0, 32) // seqno + .storeUint(args.walletId ?? WALLET_TG_SUBWALLET_ID_MAINNET, 32) + .storeBuffer(args.publicKey, 32) + .endCell(); + const init = { code, data }; + return { init, address: contractAddress(args.workchain, init) }; + } +} diff --git a/apps/demo-dapp-with-react-ui/src/server/wrappers/wallets-data.ts b/apps/demo-dapp-with-react-ui/src/server/wrappers/wallets-data.ts index cacff571f..65fc5e840 100644 --- a/apps/demo-dapp-with-react-ui/src/server/wrappers/wallets-data.ts +++ b/apps/demo-dapp-with-react-ui/src/server/wrappers/wallets-data.ts @@ -14,6 +14,7 @@ import { } from '@ton/ton'; import { Buffer } from 'buffer'; import { WalletContractV4R1 } from './wallet-contract-v4-r1'; +import { WalletContractTg } from './wallet-contract-tg'; const knownWallets = [ { contract: WalletContractV1R1, loadData: loadWalletV1Data }, @@ -26,7 +27,8 @@ const knownWallets = [ { contract: WalletContractV4R1, loadData: loadWalletV4Data }, { contract: WalletContractV4R2, loadData: loadWalletV4Data }, { contract: WalletContractV5Beta, loadData: loadWalletV5BetaData }, - { contract: WalletContractV5R1, loadData: loadWalletV5Data } + { contract: WalletContractV5R1, loadData: loadWalletV5Data }, + { contract: WalletContractTg, loadData: loadWalletTgData } ].map(({ contract, loadData }) => ({ contract: contract, loadData: loadData, @@ -78,6 +80,16 @@ function loadWalletV5Data(cs: Slice) { return { isSignatureAuthAllowed, seqno, publicKey, walletId, plugins }; } +function loadWalletTgData(cs: Slice) { + // A future WalletTg revision may append fields after publicKey, so the + // remainder of the cell is intentionally left unread. + const storageRevision = cs.loadUint(8); + const seqno = cs.loadUint(32); + const walletId = cs.loadUint(32); + const publicKey = cs.loadBuffer(32); + return { storageRevision, seqno, walletId, publicKey }; +} + export function tryParsePublicKey(stateInit: StateInit): Buffer | null { if (!stateInit.code || !stateInit.data) { return null;