This library implements member-based access control as a component in Cairo for Starknet, which allows an address to be assigned multiple roles using a single storage mapping and in a single transaction, saving on storage and transaction costs.
The design of this library was originally inspired by OpenZeppelin's access control library, as well as Python's flags and Vyper's enums.
This library uses u128
values in the form of 2n, where n
is in the range 0 <= n < 128
, to represent user-defined roles as members. The primary benefit of this approach is that multiple roles can be granted or revoked using a single storage variable and in a single transaction, saving on storage and transaction costs. The only drawback is that users are limited to 128 roles per contract.
Note that this access control library also relies on an admin address with superuser privileges i.e. the admin can grant or revoke any roles for any address, including the admin itself. This may introduce certain trust assumptions for the admin depending on your usage of the library.
We recommend users to define the roles in a separate Cairo file. For example:
mod user_roles {
const MANAGER: u128 = 1;
const STAFF: u128 = 2;
const USER: u128 = 4;
}
Multiple roles can be represented as a single value by performing bitwise AND. Using the above example, an address can be assigned both the MANAGER
and STAFF
roles using a single value of 3 (equivalent to user_roles::MANAGER | user_roles::STAFF
or 2 ** 0 + 2 ** 1
).
Similarly, multiple roles can be granted, revoked or checked for in a single transaction using bitwise operations:
- granting role(s) is a bitwise AND operation of the currently assigned value and the value of the new role(s);
- revoking role(s) is a bitwise AND operation of the currently assigned value and the complement (bitwise NOT) of the value of the role(s) to be revoked; and
- checking for membership is a bitwise OR operation of the currently assigned value and the value of the role(s) being checked for.
To use this library, add the repository as a dependency in your Scarb.toml
:
[dependencies]
access_control = { git = "https://github.com/lindy-labs/access_control.git" }
Next, define the available roles in a separate Cairo file:
mod user_roles {
const MANAGER: u128 = 1;
const STAFF: u128 = 2;
const USER: u128 = 4;
}
then import both the component and the roles into your Cairo contract.
For example, assuming you have a project named my_project
in the top-level Scarb.toml
, and a src/
folder with the roles defined in a user_roles
module in roles.cairo
:
use starknet::ContractAddress;
#[starknet::interface]
trait IMockContract<TContractState> {
fn is_manager(self: @TContractState, user: ContractAddress) -> bool;
}
#[starknet::contract]
mod mock_contract {
use access_control::access_control_component;
use my_project::roles::user_roles;
use starknet::ContractAddress;
use super::IMockContract;
component!(path: access_control_component, storage: access_control, event: AccessControlEvent);
#[abi(embed_v0)]
impl AccessControlPublic = access_control_component::AccessControl<ContractState>;
impl AccessControlHelpers = access_control_component::AccessControlHelpers<ContractState>;
#[storage]
struct Storage {
#[substorage(v0)]
access_control: access_control_component::Storage
}
#[event]
#[derive(Copy, Drop, starknet::Event)]
enum Event {
AccessControlEvent: access_control_component::Event
}
#[constructor]
fn constructor(ref self: ContractState, admin: ContractAddress, roles: Option<u128>) {
self.access_control.initializer(admin, roles);
}
#[abi(embed_v0)]
impl IMockContractImpl of IMockContract<ContractState> {
fn is_manager(self: @ContractState, user: ContractAddress) -> bool {
self.access_control.has_role(user_roles::MANAGER, user)
}
}
}
To run the tests:
scarb test
The Access Control library is not currently formally verified, but it will soon be formally verified by Lindy Labs' formal verification unit.
We welcome contributions of any kind! Please feel free to submit an issue or open a PR if you have a solution to an existing bug.
This library is released under the MIT License.