/r/gnoswap/access
Access
Centralized role-based access control system for GnoSwap protocol contracts.
Overview
The Access package provides a unified permission management system for all GnoSwap protocol contracts. It manages role-to-address mappings and provides convenient assertion functions for authorization checks throughout the protocol.
This package acts as a centralized registry where each protocol component (pool, router, staker, etc.) registers its address under a specific role. Other contracts can then query this registry to verify permissions before executing privileged operations. Admin role ownership is managed by the RBAC realm and is updated on ownership transfer; this package only stores the latest role address.
Architecture
The access control system consists of:
- Role Registry: Maps role names (strings) to contract addresses
- Role Management: Functions to set/remove roles (RBAC-only)
- Authorization Checks: Functions to verify if an address has a specific role
- Assert Helpers: Convenience functions that panic on authorization failure
System Roles
The following roles are used across the GnoSwap protocol:
- admin: Protocol administrator with elevated privileges
- devops: DevOps operations for system maintenance
- governance: Governance contract for protocol decisions
- router: Swap router for token exchanges
- pool: Pool management contract
- position: Position NFT management
- staker: Liquidity staking contract
- emission: GNS token emission controller
- protocol_fee: Protocol fee collection and distribution
- launchpad: Token launchpad for new projects
- gov_staker: Governance staking contract
- xgns: xGNS token contract for governance
Key Functions
Role Management (RBAC Only)
SetRoleAddress
Sets or updates a role's address. Creates new role if it doesn't exist.
The admin role is updated by RBAC ownership transfers and should not be managed directly by other contracts.
1// Only callable by RBAC contract
2access.SetRoleAddress(cur, "router", routerAddress)
RemoveRole
Removes a role from the system.
1// Only callable by RBAC contract
2access.RemoveRole(cur, "old_role")
Role Query Functions
GetAddress
Returns the address for a role and whether it exists.
1addr, exists := access.GetAddress("router")
2if !exists {
3 // Handle missing role
4}
MustGetAddress
Returns the address for a role or panics if it doesn't exist.
1// Panics if role doesn't exist
2routerAddr := access.MustGetAddress("router")
GetRoleAddresses
Returns a copy of all role-to-address mappings.
1allRoles := access.GetRoleAddresses()
2for roleName, addr := range allRoles {
3 println(roleName, "->", addr)
4}
Authorization Functions
IsAuthorized
Checks if an address has a specific role (non-panicking).
1if access.IsAuthorized("admin", caller) {
2 // Caller is admin
3}
Assert Functions (Panic on Failure)
These functions panic with a descriptive error if authorization fails:
AssertIsAdmin
Requires admin role.
1access.AssertIsAdmin(caller)
AssertIsGovernance
Requires governance role.
1access.AssertIsGovernance(caller)
AssertIsAdminOrGovernance
Requires either admin or governance role.
1access.AssertIsAdminOrGovernance(caller)
Role-Specific Assertions
1access.AssertIsRouter(caller)
2access.AssertIsPool(caller)
3access.AssertIsPosition(caller)
4access.AssertIsStaker(caller)
5access.AssertIsEmission(caller)
6access.AssertIsProtocolFee(caller)
7access.AssertIsLaunchpad(caller)
8access.AssertIsGovStaker(caller)
9access.AssertIsGovXGNS(caller)
AssertIsAuthorized
Generic authorization check for any role.
1access.AssertIsAuthorized("custom_role", caller)
AssertHasAnyRole
Requires the caller to have at least one of the specified roles.
1access.AssertHasAnyRole(caller, "admin", "governance", "devops")
Validation Functions
AssertIsValidAddress
Panics if the address is invalid.
1access.AssertIsValidAddress(addr)
AssertIsUser
Panics if the caller is not a user realm (i.e., a contract is calling).
1access.AssertIsUser(r)
Usage Examples
Example 1: Protecting Admin Functions
1package pool
2
3import "gno.land/r/gnoswap/access"
4
5func SetPoolFeeRate(rate uint64) {
6 caller := std.PrevRealm().Addr()
7 access.AssertIsAdminOrGovernance(caller)
8
9 // Admin/governance authorized, proceed
10 setFeeRate(rate)
11}
Example 2: Role-Based Function Access
1package staker
2
3import "gno.land/r/gnoswap/access"
4
5func DistributeRewards(amount uint64) {
6 caller := std.PrevRealm().Addr()
7 access.AssertIsEmission(caller)
8
9 // Only emission contract can distribute
10 distributeToStakers(amount)
11}
Example 3: Multi-Role Authorization
1package common
2
3import "gno.land/r/gnoswap/access"
4
5func EmergencyPause() {
6 caller := std.PrevRealm().Addr()
7 access.AssertHasAnyRole(caller, "admin", "devops", "governance")
8
9 // Any of the authorized roles can pause
10 pauseProtocol()
11}
Example 4: Non-Panicking Authorization Check
1package router
2
3import "gno.land/r/gnoswap/access"
4
5func GetSwapFee(caller address) uint64 {
6 // Lower fee for admin
7 if access.IsAuthorized("admin", caller) {
8 return 0 // Admin gets free swaps
9 }
10
11 return standardFee
12}
Security Model
Centralized Management
- All role assignments are managed through this single contract
- Provides a unified view of permissions across the entire protocol
- Prevents inconsistencies in authorization logic
RBAC-Only Updates
- Only the RBAC contract can modify role assignments
- Uses package address verification to enforce this restriction
- Prevents unauthorized role manipulation
Explicit Authorization
- All authorization checks are explicit and auditable
- Panic-based assertions make authorization failures obvious
- No implicit or default permissions
Integration with RBAC
The Access contract works in conjunction with the RBAC (Role-Based Access Control) package:
- RBAC: Manages role definitions and ownership transfer
- Access: Provides centralized role-to-address registry and authorization checks
Role updates flow: RBAC.UpdateRoleAddress() → Access.SetRoleAddress()
Best Practices
- Use Assertions for Critical Functions: Always use assert functions for operations that should only proceed with proper authorization
- Check Existence Before Use: Use
GetAddresswhen you need to handle missing roles gracefully - Document Role Requirements: Clearly document which roles are required for each function
- Avoid Hardcoding Addresses: Always use role-based checks instead of hardcoding addresses
- Test Authorization: Thoroughly test all authorization paths in your contracts
Error Handling
Authorization failures result in panics with descriptive error messages:
"unauthorized: caller X is not Y"- Caller doesn't have required role"role X does not exist"- Role hasn't been registered"invalid address: X"- Address validation failed"caller is not user"- Contract called user-only function
Limitations
- Role names are case-sensitive strings
- Each role can only map to one address at a time
- Role changes take effect immediately (no timelock)