Introduction
In most smart contract platforms, access control is based on the caller's address — a pattern that leads to fragile permission systems and common exploits like reentrancy. Radix takes a fundamentally different approach: access is gated by badges, which are standard resources (fungible or non-fungible) that serve as unforgeable credentials. A caller is authorised not because of who they are but because of what they hold.
This pattern is central to Scrypto development and appears in virtually every non-trivial dApp on Radix.
How It Works
Access Rules
When a component is instantiated, its methods can be protected with access rules that specify which badge(s) must be present for a call to succeed. The Radix Engine checks these rules automatically before executing any method — there is no manual require(msg.sender == owner) logic.
enable_method_auth! {
roles {
admin => updatable_by: [];
minter => updatable_by: [admin];
},
methods {
mint_tokens => restrict_to: [minter, admin];
update_price => restrict_to: [admin];
buy => PUBLIC;
}
}Proofs and the Auth Zone
When a method requires a badge, the caller provides a Proof — a cryptographic attestation that a resource exists in the caller's possession without transferring it. Proofs can be placed on the Auth Zone (a transaction-scoped container) so that multiple method calls within the same transaction can share the same authorisation context.
# Moving Badge-Gated and Restricted Resources
Because authorisation on Radix depends on presenting a Proof, a resource whose withdrawal is gated by a badge cannot be moved with the Radix Wallet's built-in transfer screen — that flow builds a plain withdraw-and-deposit transaction manifest and never creates the proof the access rule demands. Moving it requires a manifest — typically supplied by a dApp — that creates the required proof first (the CREATE_PROOF_FROM_ACCOUNT_OF_AMOUNT pattern shown above) and then performs the withdrawal within the same transaction.
For dApp builders this means: if your users hold badge-gated assets, give them an in-app action that sends the wallet a correctly-authorised manifest, rather than expecting a manual wallet transfer to succeed. It is also why soulbound badges stay put — their Withdraw action is locked outright, so no proof can unlock it.
Common Badge Patterns
Admin Badge
The most basic pattern: mint a single non-fungible badge at instantiation and return it to the deployer. Methods like withdraw_fees, update_config, or pause are gated behind this badge.
User Badge
The User Badge Pattern issues a non-fungible badge to each user when they register. The badge's non-fungible data stores user-specific state (balances, permissions, membership tier). Methods read the caller's badge data to personalise behaviour without maintaining a separate user registry.
Multi-Signature
Access rules support boolean logic: require_n_of(2, [badge_a, badge_b, badge_c]) creates a 2-of-3 multi-sig gate. This is useful for treasury management, protocol upgrades, or any high-stakes operation.
Soulbound Badges
By creating a non-transferable resource (restrict Deposit and Withdraw actions), a badge becomes soulbound to the original recipient's account. This is ideal for identity credentials, certificates, or membership tokens that should not change hands.
