Technical reference for Radix's access control system. Covers the type hierarchy, role assignment, SBOR encoding, macros, and runtime authorization. Source: radix-engine-interface/, radix-engine/, scrypto/, radix-common/.
AccessRule
├── AllowAll
├── DenyAll
└── Protected(CompositeRequirement)
├── BasicRequirement(BasicRequirement)
│ ├── Require(ResourceOrNonFungible)
│ ├── AmountOf(Decimal, ResourceAddress)
│ ├── CountOf(u8, Vec<ResourceOrNonFungible>)
│ ├── AllOf(Vec<ResourceOrNonFungible>)
│ └── AnyOf(Vec<ResourceOrNonFungible>)
├── AnyOf(Vec<CompositeRequirement>) ← recursive OR
└── AllOf(Vec<CompositeRequirement>) ← recursive AND
ResourceOrNonFungible
├── NonFungible(NonFungibleGlobalId)
└── Resource(ResourceAddress)
radix-engine-interface/src/blueprints/resource/proof_rule.rs:280
pub enum AccessRule {
AllowAll, // disc 0 — no auth needed
DenyAll, // disc 1 — always rejected
Protected(CompositeRequirement), // disc 2 — evaluate requirement tree
}CompositeRequirement converts into AccessRule::Protected via From.
proof_rule.rs:157
pub enum CompositeRequirement {
BasicRequirement(BasicRequirement), // disc 0 (schema name: "ProofRule")
AnyOf(Vec<CompositeRequirement>), // disc 1 — any child passes → pass
AllOf(Vec<CompositeRequirement>), // disc 2 — all children must pass
}Builder methods: .or(other) and .and(other) flatten into existing AnyOf/AllOf when the left side already matches.
Implicit From conversions: ResourceAddress, NonFungibleGlobalId, ResourceOrNonFungible all convert into CompositeRequirement::BasicRequirement(Require(...)).
proof_rule.rs:101
pub enum BasicRequirement {
Require(ResourceOrNonFungible), // disc 0 — single proof present
AmountOf(Decimal, ResourceAddress), // disc 1 — proof amount ≥ threshold
CountOf(u8, Vec<ResourceOrNonFungible>), // disc 2 — M-of-N proofs present
AllOf(Vec<ResourceOrNonFungible>), // disc 3 — all listed proofs present
AnyOf(Vec<ResourceOrNonFungible>), // disc 4 — any listed proof present
}proof_rule.rs:23
pub enum ResourceOrNonFungible {
NonFungible(NonFungibleGlobalId), // disc 0 — specific NFT
Resource(ResourceAddress), // disc 1 — any proof of this resource
}proof_rule.rs:199–261
| Function | Returns | Purpose |
|---|---|---|
require(T) |
CompositeRequirement |
Wraps anything Into<CompositeRequirement> |
require_any_of(Vec<T>) |
CompositeRequirement |
BasicRequirement::AnyOf |
require_all_of(Vec<T>) |
CompositeRequirement |
BasicRequirement::AllOf |
require_n_of(count, Vec<T>) |
CompositeRequirement |
BasicRequirement::CountOf (M-of-N) |
require_amount(Decimal, resource) |
CompositeRequirement |
BasicRequirement::AmountOf |
signature(public_key) |
ResourceOrNonFungible |
NFT ID from public key hash (virtual badge) |
global_caller(addr) |
ResourceOrNonFungible |
NFT ID for global caller badge |
package_of_direct_caller(pkg) |
ResourceOrNonFungible |
NFT ID for package caller badge |
Signatures and caller identities are represented as virtual NonFungibleGlobalId proofs:
signature(pk)→NonFungibleGlobalId::from_public_key(pk)— the engine synthesizes this proof when a transaction is signed bypk. The resource is the well-known Secp256k1/Ed25519 signature virtual resource.global_caller(addr)→NonFungibleGlobalId::global_caller_badge(addr)— synthesized when a component ataddrcalls the protected method.package_of_direct_caller(pkg)→NonFungibleGlobalId::package_of_direct_caller_badge(pkg)— synthesized for calls from blueprints inpkg.
These virtual proofs are checked via implicit_non_fungible_proofs and simulate_all_proofs_under_resources in the AuthZone, not actual vault-backed proofs.
role_assignment.rs:243–270
pub enum OwnerRole {
None, // disc 0 — no owner, maps to DenyAll + updater None
Fixed(AccessRule), // disc 1 — immutable owner rule, updater None
Updatable(AccessRule), // disc 2 — mutable owner rule, updater Owner
}Converts to OwnerRoleEntry:
| OwnerRole variant | rule |
updater |
|---|---|---|
None |
DenyAll |
OwnerRoleUpdater::None |
Fixed(rule) |
rule |
OwnerRoleUpdater::None |
Updatable(rule) |
rule |
OwnerRoleUpdater::Owner |
pub enum OwnerRoleUpdater {
None, // immutable
Owner, // only current owner can update
Object, // the component itself can update (used for presecurified objects)
}| Constant | Value | Meaning |
|---|---|---|
OWNER_ROLE |
"_owner_" |
The owner role; always present |
SELF_ROLE |
"_self_" |
The component itself; resolved to require(global_caller(self_address)) at runtime |
role_assignment.rs:98 — transparent newtype over String.
role_assignment.rs:187 — Vec<RoleKey>. Methods are guarded by a RoleList that says "any of these roles may call this method." RoleList::none() = nobody.
role_assignment.rs:34
pub enum MethodAccessibility {
Public, // anyone
OuterObjectOnly, // only the containing object
RoleProtected(RoleList), // any role in the list
OwnPackageOnly, // only blueprints in same package
}MethodAccessibility::nobody() → RoleProtected(RoleList::none()).
role_assignment.rs:282 — IndexMap<RoleKey, Option<AccessRule>>. Maps role names to their access rules. None value means "fall through to owner role."
let mut roles = RoleAssignmentInit::new();
roles.define_role("admin", rule!(require(admin_badge)));
roles.define_role("minter", FallToOwner::OWNER); // None → falls to ownerinvocations.rs:124–153
pub trait ToRoleEntry {
fn to_role_entry(self) -> Option<AccessRule>;
}
// AccessRule → Some(rule)
// FallToOwner::OWNER → None (delegates to owner role)
// Option<AccessRule> → pass-throughWhen RoleAssignmentInit stores None for a role, the runtime falls back to the owner's access rule for authorization.
invocations.rs
| Ident | Input | Description |
|---|---|---|
"create" |
OwnerRoleEntry + IndexMap<ModuleId, RoleAssignmentInit> |
Initialize role assignment module |
"set" |
ModuleId + RoleKey + AccessRule |
Update a role's rule |
"set_owner" |
AccessRule |
Update the owner rule (requires current owner auth) |
"lock_owner" |
(none) | Make owner immutable (sets updater to None) |
"get" |
ModuleId + RoleKey |
Read a role's rule → Option<AccessRule> |
"get_owner_role" |
(none) | Read owner role entry → OwnerRoleEntry |
macros.rs:84–95
rule!(allow_all) // → AccessRule::AllowAll
rule!(deny_all) // → AccessRule::DenyAll
rule!(require(signature(pk))) // → AccessRule::Protected(...)
rule!(require(badge) && require(badge2)) // → Protected(AllOf([..]))
rule!(require(badge) || require(badge2)) // → Protected(AnyOf([..]))&& and || build CompositeRequirement::AllOf / AnyOf trees. Grouping with parentheses is supported.
scrypto/src/macros.rs:416–483 — used inside #[blueprint] modules.
enable_method_auth! {
roles {
admin => updatable_by: [OWNER];
minter => updatable_by: [OWNER, SELF, admin];
},
methods {
mint => restrict_to: [minter];
burn => restrict_to: [admin, minter];
get_balance => PUBLIC;
internal_only => NOBODY;
}
}Generates MethodRoles<T> struct, ROLE_STRINGS constants, and method_auth_template() function. Role names become struct fields; method names map to MethodAccessibility values.
scrypto/src/macros.rs:486–499 — guards static blueprint functions.
enable_function_auth! {
instantiate => rule!(require(admin_badge));
instantiate_global => rule!(deny_all);
}scrypto/src/macros.rs:540–544 — builds RoleAssignmentInit at instantiation time.
let roles = roles! {
admin => rule!(require(admin_badge));
minter => FallToOwner::OWNER;
};macros.rs:105–116 — simpler version, builds RoleAssignmentInit directly.
All types use SBOR Enum encoding (0x22). ROLE_ASSIGNMENT_TYPES_START = 0xe0 (224).
| Type | Well-Known ID | Variant | Disc | Fields |
|---|---|---|---|---|
| AccessRule | 0xe0 (224) |
AllowAll |
0 | (none) |
DenyAll |
1 | (none) | ||
Protected |
2 | CompositeRequirement |
||
| CompositeRequirement | 0xe1 (225) |
BasicRequirement |
0 | BasicRequirement |
AnyOf |
1 | Vec<CompositeRequirement> |
||
AllOf |
2 | Vec<CompositeRequirement> |
||
| CompositeRequirementList | 0xe2 (226) |
— | — | Array<CompositeRequirement> |
| BasicRequirement | 0xe3 (227) |
Require |
0 | ResourceOrNonFungible |
AmountOf |
1 | Decimal, ResourceAddress |
||
CountOf |
2 | u8, Vec<ResourceOrNonFungible> |
||
AllOf |
3 | Vec<ResourceOrNonFungible> |
||
AnyOf |
4 | Vec<ResourceOrNonFungible> |
||
| ResourceOrNonFungible | 0xe4 (228) |
NonFungible |
0 | NonFungibleGlobalId |
Resource |
1 | ResourceAddress |
||
| ResourceOrNonFungibleList | 0xe5 (229) |
— | — | Array<ResourceOrNonFungible> |
| OwnerRole | 0xe6 (230) |
None |
0 | (none) |
Fixed |
1 | AccessRule |
||
Updatable |
2 | AccessRule |
||
| RoleKey | 0xe7 (231) |
— | — | transparent String |
| ModuleId | (separate range) | Main |
0 | |
Metadata |
1 | |||
Royalty |
2 | |||
RoleAssignment |
3 |
Schema names preserved for backward compat: CompositeRequirement → "AccessRuleNode", BasicRequirement → "ProofRule".
# AccessRule::AllowAll
Enum<0u8>()
# AccessRule::DenyAll
Enum<1u8>()
# AccessRule::Protected(BasicRequirement::Require(Resource(xrd_resource)))
Enum<2u8>(
Enum<0u8>(
Enum<0u8>(
Enum<1u8>(
Address("resource_rdx1...")
)
)
)
)
# SET_OWNER_ROLE instruction
SET_OWNER_ROLE
Address("${component_address}")
Enum<0u8>(); # AccessRule::AllowAll
# SET_ROLE instruction
SET_ROLE
Address("${component_address}")
Enum<0u8>() # ModuleId::Main
"admin" # role name
Enum<0u8>(); # AccessRule::AllowAll
# LOCK_OWNER_ROLE instruction
LOCK_OWNER_ROLE
Address("${component_address}");
radix-engine/src/system/system_modules/auth/authorization.rs
-
Engine resolves
MethodAccessibilityfor the called method from the blueprint's staticMethodAuthTemplate. -
Based on accessibility:
Public→ authorized immediatelyOwnPackageOnly→ checks calling package matchesOuterObjectOnly→ checks caller is the outer objectRoleProtected(role_list)→ tries each role in the list (any-of semantics)
-
For each role key in the list:
- If key is
"_self_"→ synthesizesrule!(require(global_caller(self_address))) - Otherwise → looks up
AccessRulefrom the component's role assignment substate - If no rule stored for that role → falls back to owner role's
AccessRule
- If key is
-
check_authorization_against_access_rule:AllowAll→AuthorizedDenyAll→FailedProtected(requirement)→ evaluate recursively
verify_auth_rule recurses through CompositeRequirement:
BasicRequirement(rule)→verify_proof_ruleAnyOf(rules)→ short-circuit on first passAllOf(rules)→ short-circuit on first fail
verify_proof_rule checks the AuthZone stack:
Require(resource)→ any matching proof present?AmountOf(amount, resource)→ proof amount ≥ threshold?CountOf(count, resources)→ ≥ count matching proofs?AllOf(resources)→ all resources have matching proofs?AnyOf(resources)→ any resource has a matching proof?
For each auth_zone_stack_matches call, the engine checks three sources in order:
- Local implicit non-fungible proofs (signature virtual badges)
- Global caller's auth zone (proofs from the calling component's context)
- Current caller's parent auth zone (proofs from the transaction-level context)
For NonFungible rules, the engine also checks virtual_non_fungibles and simulate_all_proofs_under_resources before iterating actual proofs.
OwnerRole::Fixed(rule!(require(signature(&public_key))))OwnerRole::Updatable(rule!(require_n_of(
2,
vec![signature(&pk1), signature(&pk2), signature(&pk3)]
)))enable_method_auth! {
roles {
admin => updatable_by: [OWNER];
},
methods {
admin_method => restrict_to: [admin];
public_method => PUBLIC;
}
}
// At instantiation:
let roles = roles! {
admin => rule!(require(admin_badge));
};
// or to fall through to owner:
let roles = roles! {
admin => FallToOwner::OWNER;
};// _self_ role: only the component itself can call
enable_method_auth! {
methods {
internal_callback => restrict_to: [SELF];
}
}