3 Types of Authorization in TON Smart Contracts
Source: Dev.to
Introduction
In smart contracts we often need to restrict certain actions to specific actors.
A common example is a wallet contract: we must authorize message senders to ensure only valid users can send funds.
Below are three common ways to authorize messages in TON smart contracts. These mechanisms are used for external messages unless noted otherwise.
Signature‑based Authorization (External Messages)
The contract verifies the message’s signature using a public key stored in its data. This method is deterministic and works because contract storage is public, so private keys cannot be kept inside the contract.
// wallet3-code.fc (excerpt)
() recv_external(slice in_msg) impure {
var signature = in_msg~load_bits(512);
;; some code
var (stored_seqno, stored_subwallet, public_key) =
(ds~load_uint(32), ds~load_uint(32), ds~load_uint(256));
;;
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
accept_message();
;; other code
}
Source: wallet‑v3 contract on GitHub
Why it works
- Determinism – Cryptographic verification is deterministic, unlike random number generation.
- Public storage – The public key can be safely stored in the contract’s state.
Signature‑based authorization is the most widely used method for external messages.
Address Comparison (Internal Messages)
For internal messages the contract simply compares the sender’s address with an address stored in its state.
// nft-collection-editable.fc (excerpt)
() recv_internal(cell in_msg_full, slice in_msg_body) impure {
if (in_msg_body.slice_empty?()) {
;; ignore empty messages
return ();
}
slice cs = in_msg_full.begin_parse();
int flags = cs~load_uint(4);
if (flags & 1) {
;; ignore all bounced messages
return ();
}
slice sender_address = cs~load_msg_addr();
int op = in_msg_body~load_uint(32);
int query_id = in_msg_body~load_uint(64);
var (owner_address, next_item_index, content, nft_item_code, royalty_params) = load_data();
;; some code
throw_unless(401, equal_slices(sender_address, owner_address));
;; some code
}
Source: NFT Collection contract on GitHub
The contract stores the owner address and throws an error if the sender does not match it. This approach can be extended to store multiple authorized addresses for different roles.
Authorization for Multiple Senders (Jetton Wallet Example)
When a contract must authorize an arbitrary number of senders—e.g., in Jetton contracts—we need a more flexible approach. The Jetton Wallet verifies that the sender wallet belongs to the same Jetton Master or is the deterministic wallet address derived from the sender’s address.
// jetton-wallet.fc (excerpt)
() receive_jettons(slice in_msg_body, slice sender_address, int my_ton_balance, int msg_value)
impure inline_ref {
(int status, int balance, slice owner_address, slice jetton_master_address) = load_data();
int query_id = in_msg_body~load_query_id();
int jetton_amount = in_msg_body~load_coins();
slice from_address = in_msg_body~load_msg_addr();
slice response_address = in_msg_body~load_msg_addr();
throw_unless(error::not_valid_wallet,
equal_slices_bits(jetton_master_address, sender_address)
|
equal_slices_bits(
calculate_user_jetton_wallet_address(
from_address,
jetton_master_address,
my_code()
),
sender_address
)
);
;; other code
}
Source: Jetton Wallet contract on GitHub
Key points:
calculate_user_jetton_wallet_addressderives the expected wallet address from the sender’s address, the Jetton Master address, and the contract’s code (my_code()).my_code()returns the current contract code via thec7register (see the TVM registers documentation).- The contract can also store another contract’s code in its storage to compute external contract addresses.
- When initialization parameters are required, they must be validated carefully; if both contracts share a known parameter (e.g., an admin address), it is safer to read it from your own storage rather than from the message.
Conclusion
Choosing the right authorization strategy is a crucial part of designing a TON smart contract’s architecture. Use signature verification for external messages, address comparison for simple internal checks, and deterministic address calculations (as in the Jetton Wallet) when you need to authorize many possible senders.
Happy coding! 🚀