Introduction
Transferring cryptocurrency directly within a chat environment is an attractive concept. Decentralized finance remains in its early stages; integrating it into familiar everyday tools is a promising way to widen adoption.
One of the greatest benefits of such an approach is that transfers are bound to chats rather than blockchain addresses—as long as the sender and receiver share a chat, the sender does not need to know the receiver's address. The receiver may not even have one until they claim the transfer. In the meantime, the funds are securely held in an escrow smart contract, ensuring availability and protection without requiring the recipient to be blockchain-ready in advance.
Existing implementations on Telegram demonstrate that such integrations can be intuitive and fast, but so far they were built on custodial models—where the service provider holds and controls the assets until the recipient claims them. This may be convenient, but it deviates from the original ideas that gave rise to decentralized finance and blockchain technology in the first place.
The aim of @push is to reach the same seamless user experience while maintaining a non-custodial approach. No intermediary ever holds the private keys or has the ability to move funds. All assets are held in transparent smart contracts on the TON blockchain, and all transfers are verified using cryptographic signatures.
This article covers the architecture and cryptographic foundations of @push, along with technical solutions implemented to achieve both security and usability.
What is @push?
@push is a Telegram bot enabling non-custodial, chat-native crypto transfers on Telegram. It supports both fungible tokens (TON, jettons) and NFTs (including Telegram Gifts), with bidirectional flows for both sending checks and requesting invoices. The sender signs and funds a check on-chain; the recipient proves identity with a Telegram Ed25519 signature and claims the transfer, without paying gas. All sensitive signing happens on the client; the backend only orchestrates messages and state.

Key Innovations
The @push system introduces several architectural and protocol-level mechanisms:
Platform signature verification on-chain. A novel approach to identity verification that leverages Telegram's Ed25519 signatures for authentication in smart contracts, eliminating the need for blockchain addresses prior to receiving funds.
Sender-paid gasless claiming. A mechanism enabling recipients to claim checks without possessing native cryptocurrency for transaction fees, achieved through prepaid gas storage within the smart contract.
Privacy-preserving chat binding. The use of anonymous chat instance identifiers for authorization without exposing actual chat IDs or membership information.
Together, they achieve a low-friction transfer experience consistent with the decentralized security model.
System Architecture
The architecture of @push is designed to connect Telegram's user interface with secure on-chain asset transfers. It consists of four primary components:
Telegram Bot. Handles inline queries, generates check messages, and updates message states.
Telegram Mini App. Provides the interactive UI for signing and claiming checks, integrated with TON Connect.
TON Smart Contract. Acts as the escrow vault, securely holding funds until released to the rightful recipient.
Backend API. Coordinates the flow between other components, managing state changes and message updates.
End-to-End Flow
The complete transfer flow consists of six stages:
- Inline Query. Sender types
@push 10 USDTin chat; bot suggests transfer options. - Create Check. Bot posts message with action button; stores record in backend API.
- Sign Check. Sender uses Mini App + TON Connect to sign and send
op::create_checkon-chain. - Store Check. Smart contract locks funds; backend API polls confirmation and updates message.
- Sign Claim. Recipient signs with Telegram signature and sends external
op::cash_checkmessage. - Cash Check. Smart contract verifies signature, releases funds, and deletes check record.

The implementation of each stage will be discussed in more detail in the following sections.
The current implementation includes several convenience features such as fiat currency conversion, arithmetic operations for bill splitting, and message attachments—details are provided in the Appendix.
Digital Signatures
The core idea of @push is the ability to verify Telegram users' identity on-chain. Since 2024, Telegram supports signing user data in Mini Apps via public-key cryptography. Telegram uses Ed25519—a state-of-the-art signature scheme, superior to other techniques (such as RSA with SHA-256) in both security and performance. Most importantly, it's also an integral part of the TON blockchain, being built into TVM and directly accessible to smart contracts for signature verification.
When a user opens a Mini App, Telegram passes user data (such as ID, username, etc.) to the Mini App along with a digital signature. The signature is created with Telegram's root private key, while their public key is publicly available on their official website. This enables the Mini App (or associated smart contracts) to securely authenticate the user without server-side verification.
Notably, Telegram signs not only the username, but also the chat instance where the Mini App was opened. This enables both user authentication and authorization with permissions specific to members of a given chat—whether a personal chat, group, or channel. This property enables the implementation of in-chat crypto transfers.
The reliance on Telegram signatures introduces a single external dependency. However, Telegram's operational scale and public key infrastructure make it a significantly more robust trust anchor than smaller custodial operators or exchange-based solutions, while still ensuring that all monetary transactions are resolved on-chain.
Gasless Claiming
A critical design feature is the ability for recipients to claim checks without possessing any native coin balance to cover transaction fees. This is essential for onboarding new users who may not yet have a wallet.
The sender prepays all gas costs when creating the check: the cost of check creation, signature verification, and funds withdrawal. The prepaid gas is stored with the check in the smart contract. When the recipient claims, they send a gasless external message that uses the prepaid gas to execute. This enables users to create a wallet dynamically and receive their first cryptocurrency transfer without any prior blockchain interaction or deposit.
Check Binding Mechanisms
@push supports two distinct binding mechanisms for checks, each serving different use cases:
Chat Instance Binding. The check is bound to an anonymous chat instance identifier. Any member of the specific chat where the check was created can claim it. The chat instance is an opaque identifier that doesn't reveal the actual chat ID, preserving privacy. This enables transfers within private chats, groups, and channels.
Username Binding. The check is bound to a specific Telegram username (e.g.,
@alice). Only the user holding that username at claim time can receive the transfer. This is useful to ensure the specific user receives it regardless of the chat context.
Both mechanisms rely on Telegram's signature to authenticate the claim, but differ in what data is being verified. The sender selects the binding type when creating the check based on their security and privacy requirements.
Transfer Direction: Checks and Invoices
While the core architecture remains constant, @push supports bidirectional flows that differ in who initiates the transfer:
Checks (Sender-Initiated). The standard flow described throughout this paper. The sender creates and signs the check on-chain, prepaying gas. Any authorized recipient can claim it. This represents the traditional "push" model.
Invoices (Recipient-Initiated). A reverse flow for payment requests. The invoice creator prepares an unsigned check record via the bot, but the actual on-chain
op::create_checkmessage must be signed by the payer rather than the requester. The smart contract operations remain identical—only the UI flow and signing party differ. This enables payment requests and bill splitting scenarios.
Both directions use the same smart contract, cryptographic verification, and claiming mechanisms.
Check Creation: Telegram Bot
Inline Query Mode
The concept of a "crypto check message" on Telegram—a transferable message redeemable for cryptocurrency—has been explored by several existing products. Some implementations leverage the inline query mode, a Telegram feature that enables users to interact with bots and Mini Apps while composing a new message.
Users begin typing a message prefixed with the bot username (e.g., @push 10 USD...), and the bot responds with multiple suggestions (see Figure 1). Users can then select from these suggestions, sending one of the pre-formatted messages to the chat. This mechanism provides an efficient interface for initiating cryptocurrency transfers.
Inline Buttons
Once the selected option is sent, the bot receives a chosen_inline_result callback. It tells which of the suggestions was selected and also provides the bot with access to the sent message—allowing to modify its text, media, and inline buttons. At this point, a Check record in the database should be created to save parameters chosen by the user.
Then we can inject an inline button—a button below the message allowing for various actions. In our case, it opens a Mini App with a special start parameter—the ID of the created check.
Since we can also modify the message text, we update its status to something like "Waiting for a signature from %username%". The bot can also send a private message to the user—to remind the check is waiting to be signed.
Additional implementation details for dynamic cover images are provided in the Appendix 2.
Check Creation: Signing via Mini App
Some existing services include a private key for each check message in the Mini App button start parameter. While this appears convenient, since any message sent by a bot originates from the backend server, the service provider inevitably has knowledge of the key at creation time. This effectively makes the service custodial, as the provider could in principle retain and use the key.
The design of @push requires that no sensitive keys be shared with the backend. The check is created unsigned, and the actual transfer is sent only via the client-side Mini App. The op::create_check message is prepared to include the current chat instance or the recipient's username and is then signed with TON Connect using any TON-compatible wallet.
The current @push Mini App implementation also leverages Telegram's native UI APIs to achieve a seamless user experience. Details about UI implementation and WebGL graphics are provided in the Appendix 3.
Check Creation: Backend API
Along with the op::create_check on-chain message, a POST /checks/{check_id}/mark-sending request is sent to the backend API. The request handler updates the message's text and button to the "Signing" status.
The handler also initiates short polling of the smart contract get_check_info method, waiting for the new check to appear. When it appears, the on-chain data fields are compared to those stored in the backend database. This comparison is critical to prevent forgery by a dishonest sender. Without it, they could substitute essential parameters (such as amount or token) in their on-chain message, while the Telegram message would still display the original values.
If any discrepancies are detected, the message is updated to reflect the actual on-chain parameters. Finally, the status is updated to "Ready to Receive".
Check Creation: Smart Contract
To create a check, the smart contract expects an internal message—either from a sender's wallet for native TON, from one of its own jetton wallets for jetton transfers, or NFT transfer notification messages for NFT checks. Several essential checks and constraints are required during this process.
Gas prepayment validation. Whether it is a native TON, jetton, or NFT transfer, the contract verifies that sufficient TON is attached to cover all gas costs (as described in the Gasless Claiming section). The required TON amount must be included in the initial internal message and will be stored with the check.
Cell depth constraints. The
in_msg_full.cell_depth()is limited to the expected depth to constrain pathological inputs.Several wallets build message cells sub-optimally, resulting in unnecessary nesting that exceeds minimal depth. The validation must account for this practical reality.
Storage structure optimization. The contract storage structure is critical in TON. As the number of stored records grows, dict (
HashmapE) operations become expensive. To ensure gas costs never exceed the prepaid amount (or thegas_creditlimit for external messages), we limit the key size ().The maximum gas is for reading and for writing. Using 20-bit keys instead of conventional 32-bit keys results in fewer possible records (~1 million vs ~4.3 billion) but lower maximum gas (10,000 vs 16,000 for writing).
Due to the Patricia Tree structure underlying TON dicts, sequential (serial) IDs are significantly more cost-effective than random ones.
An alternative approach would be to use separate contracts for each sender or each check. While in some respects more rigorous and scalable, this approach imposes additional overhead and incurs costs associated with contract deployment and orchestration.
Jetton whitelist verification. For jetton transfers, the sender address must belong to a whitelist of owned jetton wallets to ensure genuine transfers and prevent fake jetton spam.
The whitelist is populated via a special
op::set_acloperation, accessible only to a privileged sudoer address configured in the contract's init data. The whitelist cannot be part of the init data itself due to a circular dependency: jetton wallet addresses are derived from a hash of the contract's code and init data, so the addresses aren't known until after deployment completes.After deployment, the sudoer address calls
op::set_aclto populate the whitelist with the now-known jetton wallet addresses.The whitelist can also distinguish optimized jettons (like USDT) that require significantly less gas for transfers, allowing the contract to require lower withdrawal fees for these tokens.
After all validation steps complete successfully, the check data is serialized and stored in the contract storage. The stored fields include the asset type (native TON, jetton wallet address, or NFT contract address), the binding the sender chose (chat instance or username), an optional comment, the created_at timestamp, and the original sender's address (for authorization of future operations).
Check Claiming: Mini App
To claim a pending check, the receiver must obtain a signature of either their username or the current chat instance. This is accomplished via signed init_data provided by Telegram when the user opens the Mini App.
Telegram provides a prepareSignedPayload method that allows filtering of unneeded user properties (such as real user ID) from the signature. This enables the check to be handled using only chat_instance—an anonymous context identifier that does not reveal any information about the real chat ID.
Arbitrary data can be included in the signature. This is essential for signing the recipient wallet address. Without this, the external message sent by the receiver could be intercepted by an attacker using public overlays or mempools, enabling withdrawal address tampering.
The address can be passed either to the Mini App ?startapp= string parameter, or using the payload field of the prepareSignedPayload method. The method also accepts two useful fields:
isPayloadBinary: true, enabling binary payload transmission (in Base64 encoding), avoiding TON address serialization during on-chain verification.shouldSignHash: true, enabling SHA-256 hash signing of the payload message instead of the message itself, mitigating on-chain message size limitations.
After obtaining the signature, the external op::cash_check message is sent directly to the smart contract. It contains the check ID, an authentication timestamp, a username (or chat instance), the receiver address, and the signature itself.
In contrast to the check creation process, this message is not signed by the receiver's wallet (it uses the Telegram signature for authentication) and carries no value (leveraging the prepaid gas from the sender).
Check Claiming: Backend API
Along with the external message, a POST /checks/{check_id}/mark-receiving request is sent to the backend API. The request handler updates the message's status to "Receiving" and initiates short polling of the smart contract to detect when the withdrawal completes.
Check Claiming: Smart Contract
The on-chain verification of the op::cash_check message is the cornerstone of @push technology. Several validation steps are required before the escrow smart contract releases the check to the receiver's address.
Signature verification. First, we calculate a
hashof the payload message using the format described by Telegram usingHASHEXT_SHA256TVM instruction. Then, we evaluatecheck_data_signaturefunction from the FunC standard library providing the hash, the signature and the Telegram public key, which is hardcoded in the contract.The
check_data_signaturefunction only accepts messages of limited size, making the hashing step essential. Both operations cost approximately 2500 gas units together—critical since all validation must complete within thegas_creditlimit before the external message is accepted.Workchain validation. The receiver address must have
workchain_id == 0, as the workchain ID is not included in the Telegram signature.Timestamp validation. The
auth_datetimestamp must be greater thancreated_atto prevent replay attacks. Since Telegram usernames are not soul-bound and chat memberships can change, this prevents users from claiming checks with old signatures obtained before username transfers or chat membership revocations.Binding verification. The stored username or chat instance (from check creation) must match the one included in the Telegram signature.
At this point the message is considered verified. The contract then accepts the message, releases the check to the receiver's address, and deletes the check record.
Critical security constraint. With gasless external messages, accepting the message before validation would allow failed checks to consume gas from the contract balance without deleting the check record. This would enable replay attacks that drain the contract. Therefore, all validation must complete before message acceptance.
Threat Model
The described security model addresses several attack vectors:
1. Mempool/Overlay MITM Attacks
- Threat: Attacker intercepts external claim messages from public TON overlays/mempools and replaces recipient address.
- Mitigation: The recipient's wallet address is included in the Telegram-signed payload. Any tampering breaks the signature verification on-chain.
2. Replay Attacks
- Threat: Attacker reuses old signatures to claim checks after username transfer or chat membership changes.
- Mitigation: The
auth_datetimestamp must be greater than the check'screated_at. This prevents old signatures from claiming new checks. Additionally, successful claims delete the check record, preventing re-execution.
3. Backend Tampering
- Threat: Malicious or compromised bot server attempts to steal funds or forge transfers.
- Mitigation: Client-side signing via TON Connect means the server never sees private keys. The API can only update UI state—it cannot move funds or create valid signatures.
4. Gas Drain DoS Attacks
- Threat: Attacker sends invalid external messages repeatedly to drain contract balance through verification costs.
- Mitigation: All validation occurs within the
gas_creditlimit before accepting the message. Failed claims cost nothing to the contract and cannot be replayed after rejection.
5. Fake Jetton Spam
- Threat: Attacker sends fake jetton transfers to create fraudulent checks.
- Mitigation: The smart contract maintains a whitelist of trusted jetton wallet addresses, rejecting transfers from unknown sources.
Conclusion & Future Directions
@push demonstrates that a chat-native cryptocurrency transfer experience can be both smooth and non-custodial. By combining Telegram user interface capabilities with the TON blockchain settlement guarantees, it delivers a user experience equal to custodial services while avoiding their trust liabilities.
The key insights:
- Client-side signing via web interfaces can provide non-custodial security without sacrificing UX.
- Modern messaging platforms provide sufficient cryptographic primitives for efficient on-chain identity verification.
- External message gas credits enable gasless claiming when combined with sender-paid gas prepayment.
Future work may explore:
- Multi-chain support via bridge contracts or cross-chain message protocols.
- Reduced Telegram dependency through additional identity verification layers (social recovery, biometrics).
- Batch operations enabling multiple checks to be created or claimed in a single transaction for improved efficiency.
These insights from @push may inform a broader class of applications seeking to merge the usability of messaging and social platforms with the trustlessness of blockchain systems.
Appendix: Implementation Details
This appendix provides additional technical details about user interface and user experience implementation aspects that, while not central to the cryptographic and architectural innovations, contribute to the overall system quality.
A1. Additional User-Facing Features
Beyond the core cryptographic and architectural innovations, @push includes several convenience features that enhance usability:
Fiat Currency Support. Users can specify amounts in fiat currencies (USD, EUR, CNY, etc.), which are automatically converted to cryptocurrency equivalents at current market rates. The inline preview displays both the crypto amount and fiat equivalent, eliminating the need for manual calculations or external rate lookups.
Arithmetic Operations. The inline query parser supports basic arithmetic expressions (
@push 300/4 EUR), enabling users to calculate split payments or shared expenses directly within the chat interface without switching to calculator applications.Message Attachments. Checks support optional text comments and emoji, allowing users to add context or personal messages to their transfers. These messages are stored with the check record and displayed throughout the transfer lifecycle.
Notification System. The bot provides status notifications for check lifecycle events (signed, claimed, received). Users can control notification preferences via the
/mutecommand, balancing awareness with notification fatigue.Multi-Wallet Compatibility. The system integrates with any TON-compatible wallet via TON Connect protocol, avoiding vendor lock-in and enabling users to maintain their existing wallet preferences and security practices.
A2. Dynamic Cover Image Generation
To render dynamic status text on check message cover images, the system combines pre-rendered SVG templates with programmatic text-to-path conversion using the given font.
The implementation uses opentype.js for font parsing and makerjs for SVG path calculation. The following code demonstrates the conversion:
import opentype from 'opentype.js';
import makerjs from 'makerjs';
import { JSDOM } from 'jsdom';
const FONT = opentype.loadSync('./fonts/Nunito800.ttf');
export async function textToSvgPath(text: string, options?: Options) {
const model = new makerjs.models.Text(FONT, text);
const svg = makerjs.exporter.toSVG(model, options);
return new JSDOM(svg).window.document.querySelector('path')?.getAttribute('d') ?? '';
}This approach enables dynamic status updates on check images to enhance usability.
A3. Mini App UI and WebGL Graphics
The Mini App user interface leverages several Telegram APIs to achieve a native look and feel:
- System buttons API for native-style action buttons.
- History navigation API for seamless back-button integration.
- Theme system respecting user's dark/light mode preferences.
- Localization adapting to user's language settings.
For enhanced visual experience, the token logo is augmented with a WebGL particle animation implementing natural physics. The animation features "flying particles" with burst effects on interaction, running entirely at GPU level. This enables rendering thousands of animated particles simultaneously while maintaining excellent performance even on resource-constrained devices.