深入剖析Solmate库 #03:RolesAuthority.sol

RolesAuthority.sol 通过 256 位位图实现角色×能力的细粒度权限矩阵,支持公开函数、角色授权与自治管理三级访问控制。

一、概述

版本说明:[solmate]:main分支 [commit: 89365b8],[forge-std]:v1.15.0

源码https://github.com/RevelationOfTuring/solmate/blob/main/src/auth/authorities/RolesAuthority.sol

RolesAuthority 是 solmate 权限体系中的 基于角色的访问控制(RBAC) 具体实现。

解决的核心问题:如何用极低的 gas 成本,管理"谁可以调用哪个合约的哪个函数"。

  • 继承 Auth → 自身的管理函数也受权限保护(requiresAuth
  • 实现 Authority 接口 → 对外提供 canCall() 供其他 Auth 合约查询
  • bytes32 位图(bitmap) 存储角色信息,一次 & 运算判断权限,支持最多 256 个角色(role 0~255)

在 solmate 权限体系中的位置:

用户调用受保护函数
  → requiresAuth 修饰符
    → isAuthorized(msg.sender, msg.sig)
      → authority.canCall(user, target, sig)
        → RolesAuthority.canCall()  ← 就是这个合约

二、适用场景

适合 不适合
中小型 DeFi 协议的权限管理 需要超过 256 个角色的超大型系统
角色数量有限、权限关系清晰的场景 需要角色层级/继承关系(如 admin > manager > user)
需要极致 gas 优化的链上权限判断 需要角色有效期/自动过期机制
多个合约共享同一套角色体系 需要多签/时间锁等复杂治理
需要公开函数(任何人可调用)的灵活配置 需要细粒度参数级别的权限控制

三、合约结构总览

RolesAuthority is Auth, Authority
│
├── Events(3 个事件)
│   ├── UserRoleUpdated          ← 用户角色变更
│   ├── PublicCapabilityUpdated  ← 公开权限变更
│   └── RoleCapabilityUpdated    ← 角色权限变更
│
├── Constructor
│   └── constructor(_owner, _authority) → Auth(_owner, _authority)
│
├── Modifier(继承自 Auth)
│   └── requiresAuth             ← 权限校验,保护所有管理函数
│
├── Storage(3 个映射)
│   ├── getUserRoles             ← address → bytes32(用户角色位图)
│   ├── isCapabilityPublic       ← address → bytes4 → bool(函数是否公开)
│   └── getRolesWithCapability   ← address → bytes4 → bytes32(函数角色位图)
│
├── View Functions(3 个查询)
│   ├── doesUserHaveRole()       ← 查询用户是否拥有某角色
│   ├── doesRoleHaveCapability() ← 查询角色是否能调某函数
│   └── canCall()                ← Authority 接口:综合权限判断(核心)
│
└── Admin Functions(3 个管理,均需 requiresAuth)
    ├── setPublicCapability()    ← 设置函数公开/非公开
    ├── setRoleCapability()      ← 设置角色对函数的权限
    └── setUserRole()            ← 给用户分配/撤销角色

四、源码逐行解析

4.1 继承关系

contract RolesAuthority is Auth, Authority { ... }
父合约/接口 提供的能力
Auth owner 状态变量、authority 状态变量、requiresAuth 修饰符、isAuthorized() 函数、setAuthority()transferOwnership()
Authority canCall() 接口定义,本合约需要 override 实现

设计决策:同时继承两者,使得 RolesAuthority 既是一个"被管理的合约"(自身函数受 Auth 保护),又是一个"权限提供者"(为其他合约提供 canCall 查询)。

4.2 Events

UserRoleUpdated

event UserRoleUpdated(address indexed user, uint8 indexed role, bool enabled);

作用:记录用户角色变更,方便链下系统(前端/索引服务)追踪谁被授予或撤销了哪个角色。

参数 类型 indexed 含义
user address 被修改角色的用户地址
role uint8 角色编号(0~255)
enabled bool true = 授予角色,false = 撤销角色

触发时机setUserRole() 执行成功后。

PublicCapabilityUpdated

event PublicCapabilityUpdated(address indexed target, bytes4 indexed functionSig, bool enabled);

作用:记录函数公开状态变更,方便监控哪些函数被设为公开或取消公开。

参数 类型 indexed 含义
target address 目标合约地址
functionSig bytes4 函数选择器(如 vault.withdraw.selector
enabled bool true = 设为公开,false = 取消公开

触发时机setPublicCapability() 执行成功后。

RoleCapabilityUpdated

event RoleCapabilityUpdated(uint8 indexed role, address indexed target, bytes4 indexed functionSig, bool enabled);

作用:记录角色权限变更,追踪哪个角色被授予或撤销了对某个函数的调用权限。

参数 类型 indexed 含义
role uint8 角色编号(0~255)
target address 目标合约地址
functionSig bytes4 函数选择器
enabled bool true = 授予权限,false = 撤销权限

触发时机setRoleCapability() 执行成功后。

设计决策

  • 三个 indexed 参数已达 EVM 事件上限,方便链下按角色/合约/函数过滤日志
  • enabled 不加 indexed,因为 bool 过滤意义不大,放在 data 字段即可

4.3 Constructor

constructor(address _owner, Authority _authority) Auth(_owner, _authority) {}

作用:初始化合约,设置初始 owner 和 authority,所有逻辑透传给父合约 Auth。

参数 类型 含义
_owner address 合约所有者地址,拥有最高管理权限,可调用所有 requiresAuth 函数
_authority Authority 外部权限合约地址,可传 Authority(address(0)) 表示不使用外部授权,仅依赖 owner

设计决策

  • 构造函数体为空,所有初始化逻辑透传给父合约 Auth
  • Auth 的构造函数会设置 ownerauthority,并触发 OwnershipTransferredAuthorityUpdated 事件
  • 不做零地址校验,调用方需自行确保传入有效地址

4.4 Storage

getUserRoles

mapping(address => bytes32) public getUserRoles;

作用:存储每个用户拥有的角色集合,用 bytes32 位图表示。

Key Value 含义
address(用户地址) bytes32(256 位位图) 第 N 位为 1 表示用户拥有角色 N
示例:值为 0x...05(二进制 ...0101)
  bit:  ... bit3 bit2 bit1 bit0    ← 从右往左读
  值:   ...  0    1    0    1
→ 拥有角色 0(bit0=1)和角色 2(bit2=1)

设计决策:用 bytes32 位图而非 mapping(uint8 => bool) 或数组,因为:

  • 1 个 storage slot(32 字节)存 256 个角色
  • 可直接与函数角色位图做 & 运算,O(1) 判断权限

isCapabilityPublic

mapping(address => mapping(bytes4 => bool)) public isCapabilityPublic;

作用:标记某个合约的某个函数是否对所有人公开(无需角色即可调用)。

Key1 Key2 Value 含义
address(目标合约) bytes4(函数选择器) bool true = 该函数对所有人公开

设计决策:独立 bool 映射,在 canCall 中作为 || 短路条件优先判断,公开函数直接跳过位图运算,节省 gas。

getRolesWithCapability

mapping(address => mapping(bytes4 => bytes32)) public getRolesWithCapability;

作用:存储哪些角色可以调用某个合约的某个函数,用 bytes32 位图表示。

Key1 Key2 Value 含义
address(目标合约) bytes4(函数选择器) bytes32(256 位位图) 第 N 位为 1 表示角色 N 可调用该函数

设计决策:与 getUserRoles 格式完全一致的位图,确保可以直接 & 运算。

4.5 查询函数

doesUserHaveRole

function doesUserHaveRole(address user, uint8 role) public view virtual returns (bool) {
    return (uint256(getUserRoles[user]) >> role) & 1 != 0;
}

作用:查询某个用户是否拥有指定角色。通过位移取位操作,从用户角色位图中提取指定 bit 的值。

参数 类型 含义
user address 要查询的用户地址
role uint8 要查询的角色编号(0~255)
返回值 bool true = 用户拥有该角色

位运算拆解(bit 位从右往左数,bit0 在最右边):

原始位图:  bit3 bit2 bit1 bit0
             0    1    0    1     (用户有角色 0 和 2)

查询 role=2:
  原始:        0  1  0  1
  >> 2:        0  0  0  1     ← bit2 移到 bit0 位置
  & 1:         0  0  0  1     → != 0 → true ✅

查询 role=1:
  原始:        0  1  0  1
  >> 1:        0  0  1  0     ← bit1 移到 bit0 位置
  & 1:         0  0  0  0     → == 0 → false ❌

设计决策:先转 uint256 再位移,因为 Solidity 的 >> 运算符需要数值类型,bytes32 不支持直接位移。

doesRoleHaveCapability

function doesRoleHaveCapability(
    uint8 role,
    address target,
    bytes4 functionSig
) public view virtual returns (bool) {
    return (uint256(getRolesWithCapability[target][functionSig]) >> role) & 1 != 0;
}

作用:查询某个角色是否有权调用指定合约的指定函数。逻辑与 doesUserHaveRole 完全一致,只是数据源从用户角色位图变成了函数角色位图。

参数 类型 含义
role uint8 要查询的角色编号(0~255)
target address 目标合约地址
functionSig bytes4 目标函数选择器(如 Vault.withdraw.selector
返回值 bool true = 该角色有权调用该函数

canCall(核心函数)

function canCall(
    address user,
    address target,
    bytes4 functionSig
) public view virtual override returns (bool) {
    return
        // 条件 1:函数是否公开
        isCapabilityPublic[target][functionSig] ||
        // 条件 2:用户角色位图 AND 函数角色位图,非零则有权限
        // 注:用户角色位图 & 函数角色位图,只要 & 结果不全为 0,说明至少有一个角色既属于用户、又有权调用该函数 → 放行。
        bytes32(0) != getUserRoles[user] & getRolesWithCapability[target][functionSig];
}

作用Authority 接口的核心实现。判断某个用户是否有权调用某个合约的某个函数。这是外部 Auth 合约通过 isAuthorized() 间接调用的入口,是整个 RBAC 权限判断的最终裁决者。

参数 类型 含义
user address 调用者地址(通常是 msg.sender
target address 被调用的目标合约地址(通常是 address(this)
functionSig bytes4 被调用函数的选择器(通常是 msg.sig
返回值 bool true = 允许调用,false = 拒绝调用

两级判断逻辑

条件 1(短路优先):isCapabilityPublic[target][sig] == true
  → 该函数是公开的,任何人都能调用,直接返回 true,跳过位图运算

条件 2(位图 AND):getUserRoles[user] & getRolesWithCapability[target][sig] != bytes32(0)
  → 用户角色位图 AND 函数角色位图
  → 只要结果不全为 0,说明至少有一个角色既属于用户、又有权调用该函数 → 放行

完整示例

用户位图(Alice 有角色 0、2):
  bit:  bit3 bit2 bit1 bit0
         0    1    0    1

函数位图(角色 1、2 可调用 withdraw):
  bit:  bit3 bit2 bit1 bit0
         0    1    1    0

AND 运算:
         0    1    0    1   ← Alice
       & 0    1    1    0   ← withdraw 允许的角色
       ─────────────────
         0    1    0    0   ← 角色 2 重叠!!= 0 → 有权限 ✅

设计决策

  • || 短路:公开函数跳过位图运算,节省 ~1 SLOAD 的 gas
  • & 运算优先级高于 !=,所以 bytes32(0) != A & B 无需额外括号
  • 一次 & 运算同时检查 256 个角色,O(1),无论系统有多少角色

4.6 管理函数(均需 requiresAuth)

setPublicCapability

function setPublicCapability(
    address target,
    bytes4 functionSig,
    bool enabled
) public virtual requiresAuth {
    // 直接设置布尔值
    isCapabilityPublic[target][functionSig] = enabled;
    emit PublicCapabilityUpdated(target, functionSig, enabled);
}

作用:设置某个合约的某个函数是否为公开可调用。设为公开后,canCall 会短路返回 true,任何人无需角色即可调用该函数。

参数 类型 含义
target address 目标合约地址(要配置的合约)
functionSig bytes4 函数选择器(如 Vault.deposit.selector
enabled bool true = 任何人可调用(公开),false = 需要角色才能调用

设计决策:最简单的布尔赋值。设为 truecanCall 第一个条件短路返回,跳过所有角色检查。

setRoleCapability

function setRoleCapability(
    uint8 role,
    address target,
    bytes4 functionSig,
    bool enabled
) public virtual requiresAuth {
    if (enabled) {
        // 如果是授予权限:
        // 将第 role 位置 1,授予该角色调用权限
        getRolesWithCapability[target][functionSig] |= bytes32(1 << role);
    } else {
        // 如果是撤销权限:
        // 将第 role 位清 0,撤销该角色调用权限
        getRolesWithCapability[target][functionSig] &= ~bytes32(1 << role);
    }
    emit RoleCapabilityUpdated(role, target, functionSig, enabled);
}

作用:设置某个角色是否有权调用指定合约的指定函数。通过位运算在函数角色位图中置位或清位,控制"角色 → 函数"的映射关系。

参数 类型 含义
role uint8 角色编号(0~255)
target address 目标合约地址
functionSig bytes4 函数选择器
enabled bool true = 授予该角色调用权限,false = 撤销该角色调用权限

位运算拆解

授予角色 2 调用权限(enabled = true):
  1 << 2       = 0100          ← 生成掩码,只有 bit2 为 1
  原始位图       = 1001
  |= 0100      = 1101          ← OR:bit2 被置 1,其他不变

撤销角色 2 调用权限(enabled = false):
  1 << 2       = 0100
  ~0100        = 1011          ← 取反:只有 bit2 为 0
  原始位图       = 1101
  &= 1011      = 1001          ← AND:bit2 被清 0,其他不变

设计决策

  • |= 置位和 &= ~ 清位是经典的位运算惯用法,只影响目标位,不破坏其他角色的配置
  • 1 << role 中 role 为 uint8(0~255),与 bytes32 的 256 位完美匹配,不存在越界

setUserRole

function setUserRole(
    address user,
    uint8 role,
    bool enabled
) public virtual requiresAuth {
    if (enabled) {
        // 如果是授予权限:
        // 将用户角色位图的第 role 位置 1
        getUserRoles[user] |= bytes32(1 << role);
    } else {
        // 如果是撤销权限:
        // 将用户角色位图的第 role 位清 0
        getUserRoles[user] &= ~bytes32(1 << role);
    }
    emit UserRoleUpdated(user, role, enabled);
}

作用:给用户分配或撤销角色。通过位运算在用户角色位图中置位或清位,控制"用户 → 角色"的映射关系。与 setRoleCapability 位运算逻辑完全相同,操作目标从函数角色位图变为用户角色位图。

参数 类型 含义
user address 要配置的用户地址
role uint8 角色编号(0~255)
enabled bool true = 分配角色,false = 撤销角色

设计决策

  • setRoleCapability 使用相同的位运算模式,保持代码一致性
  • 不做零地址校验,address(0) 也可以被分配角色(需业务层自行防范)

五、完整调用流程图

5.1 权限判断流程(canCall 被调用时)

外部合约调用受保护函数
         │
         ▼
  requiresAuth 修饰符(Auth 合约)
  作用:校验 msg.sender 是否有权调用当前函数
         │
         ▼
  isAuthorized(msg.sender, msg.sig)
  作用:核心授权判断,先查 authority 再查 owner
         │
         ▼
  authority 是否为零地址?
         │
    ┌────┴────┐
   YES       NO
    │         │
    │         ▼
    │   authority.canCall(user, target, sig)
    │   作用:调用 RolesAuthority 判断角色权限
    │         │
    │         ▼
    │   ┌─────────────────────────────┐
    │   │ isCapabilityPublic[t][sig]? │
    │   └──────┬──────────┬───────────┘
    │         YES        NO
    │          │          │
    │          ▼          ▼
    │       return   getUserRoles[user]
    │        true    & getRolesWithCapability[t][sig]
    │                     │
    │                     ▼
    │               ┌──────────┐
    │               │ != 0x0 ? │
    │               └──┬────┬──┘
    │                 YES   NO
    │                  │     │
    │                  ▼     ▼
    │               return  return
    │                true   false
    │                        │
    └────────┬───────────────┘
             ▼
      canCall 返回 false(或 authority 为零地址)?
      检查 user == owner?
             │
          ┌──┴──┐
         YES   NO
          │     │
          ▼     ▼
       通过   revert("UNAUTHORIZED")

5.2 角色配置流程

管理员(owner 或被 authority 授权者)
         │
         ▼
    requiresAuth 校验通过
         │
    ┌────┴─────────────┬──────────────────┐
    ▼                  ▼                  ▼
setUserRole()    setRoleCapability()   setPublicCapability()
作用:分配/       作用:设置角色        作用:设置函数
撤销用户角色     对函数的调用权限       是否公开
    │                  │                  │
    ▼                  ▼                  ▼
位运算修改         位运算修改           直接设置
用户角色位图       函数角色位图         bool 值
(getUserRoles)    (getRolesWithCap)    (isCapabilityPublic)
    │                  │                  │
    ▼                  ▼                  ▼
emit              emit                emit
UserRoleUpdated   RoleCapabilityUpdated   PublicCapabilityUpdated

5.3 典型部署与配置流程

1. deploy RolesAuthority(deployer, Authority(address(0)))
   → 创建权限管理合约,deployer 为 owner
         │
2. deploy Vault(deployer, rolesAuthority)
   → 创建业务合约,挂载权限合约
         │
3. authority.setRoleCapability(ROLE_ID, vault, sig, true)
   → 配置哪些角色可以调用哪些函数
         │
4. authority.setUserRole(alice, ROLE_ID, true)
   → 给用户分配角色
         │
5. alice 调用 vault.protectedFunc()
         │
   requiresAuth → isAuthorized → authority.canCall()
         │
   getUserRoles[alice] & getRolesWithCapability[vault][sig]
         │
   结果 != 0 → 通过 ✅

六、设计思想

6.1 极简主义

  • 整个合约不到 100 行有效代码
  • 没有角色名称映射、没有枚举,角色就是 0~255 的数字
  • 没有批量操作函数,保持接口最小化
  • 构造函数为空,逻辑完全透传给父合约

6.2 Gas 极致优化

操作 Gas 成本 原因
canCall 判断 ~2 SLOAD + 1 AND 位图一次性检查 256 个角色,O(1)
公开函数判断 ~1 SLOAD \|\| 短路返回,跳过位图运算
角色分配/撤销 ~1 SLOAD + 1 SSTORE 单 slot 位运算,不涉及数组扩展

对比传统循环遍历方式:

// 传统做法 — O(n),最坏循环 256 次
for (uint8 i = 0; i < 256; i++) {
    if (userHasRole[user][i] && roleCanCall[i][target][sig]) return true;
}

// 位图做法 — O(1),一条指令
getUserRoles[user] & getRolesWithCapability[target][functionSig] != 0

6.3 可组合性

  • 实现 Authority 接口 → 可插入任何继承了 Auth 的合约
  • 继承 Auth → 自身管理也受权限保护,可以实现"角色管理角色"
  • 所有函数标记 virtual → 子合约可自由 override 扩展

6.4 防御性设计

  • 管理函数全部用 requiresAuth 保护,非授权者无法修改权限
  • 位运算天然安全:uint8 role 范围 0~255,与 bytes32 的 256 位完美匹配,不存在越界
  • 撤销权限用 &= ~mask,只影响目标位,不破坏其他角色的设置

七、安全注意事项

风险 说明 建议
owner 单点风险 owner 可随意分配/撤销所有角色和权限 部署完成后将 owner 转移给多签钱包或 Timelock
无零地址校验 setUserRole 可给 address(0) 分配角色 业务层自行校验,或继承后 override 加校验
无过期机制 角色一旦分配永久有效,直到手动撤销 如需过期,需自行扩展(加 expiry 映射)
Authority 死锁 若 RolesAuthority 自身的 authority 合约异常(revert),即使 owner 也可能无法调管理函数 参考 Auth.setAuthority 的特殊处理;或将 authority 设为 address(0) 仅依赖 owner
公开函数风险 setPublicCapability(true) 后所有人可调用该函数 谨慎使用,配合事件监控,上线前审计
indexed 参数上限 RoleCapabilityUpdated 已用满 3 个 indexed 若需更多过滤维度,需链下解析 data 字段
角色编号无语义 角色只是 0~255 的数字,链上无名称 在前端/文档/常量中维护角色编号与名称的映射表
无批量操作 一次只能设置一个角色/一个权限 如需批量操作,可封装 multicall 或写辅助合约

八、与同类方案对比

特性 solmate RolesAuthority OpenZeppelin AccessControl
角色存储 bytes32 位图(1 slot / 用户) mapping(role => mapping(user => bool))
角色数量 最多 256 无限制(bytes32 哈希作为 role ID)
角色标识 纯数字 0~255 可读哈希,如 keccak256("MINTER_ROLE")
权限判断 gas ~2 SLOAD + 1 AND(O(1),一次检查所有角色) ~1 SLOAD(O(1)),但每个角色需单独查询
角色管理 owner / authority 统一管理 每个角色有独立的 adminRole
角色层级 ❌ 无 ✅ 支持(roleAdmin 机制)
函数级权限 ✅ 原生支持 (target, functionSig) 粒度 ❌ 需自行在函数内用 hasRole 检查
公开函数 isCapabilityPublic 内置支持 ❌ 需自行实现
批量操作
代码量 ~80 行 ~150 行
适合场景 Gas 敏感、角色少、需函数级控制 角色多、需层级管理、生态兼容性优先

九、实战:继承该合约编写业务合约

9.1 业务合约

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {Auth, Authority} from "solmate/auth/Auth.sol";

/*
 * @title Vault — 使用 RolesAuthority 做权限控制的金库合约
 * @notice 存取款功能受角色保护,展示 RolesAuthority 的典型用法
 */
contract Vault is Auth {
    // 角色常量(编译时内联,不占 storage,可读性好)
    uint8 public constant ROLE_DEPOSITOR = 0;
    uint8 public constant ROLE_WITHDRAWER = 1;
    uint8 public constant ROLE_ADMIN = 2;

    // 用户余额
    mapping(address => uint256) public balances;

    constructor(address _owner, Authority _authority) Auth(_owner, _authority) {}

    /// @notice 存款,需要 DEPOSITOR 角色或 owner
    function deposit() external payable requiresAuth {
        balances[msg.sender] += msg.value;
    }

    /// @notice 取款,需要 WITHDRAWER 角色或 owner
    function withdraw(uint256 amount) external requiresAuth {
        require(balances[msg.sender] >= amount, "INSUFFICIENT");
        balances[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
    }

    /// @notice 紧急提取全部资金,需要 ADMIN 角色或 owner
    function emergencyWithdraw() external requiresAuth {
        payable(msg.sender).transfer(address(this).balance);
    }
}

9.2 部署与配置

import {RolesAuthority} from "solmate/auth/authorities/RolesAuthority.sol";

// 1. 部署权限合约(deployer 为初始 owner)
RolesAuthority authority = new RolesAuthority(deployer, Authority(address(0)));

// 2. 部署业务合约,挂载权限合约
Vault vault = new Vault(deployer, authority);

// 3. 配置:哪些角色可以调用哪些函数
authority.setRoleCapability(0, address(vault), Vault.deposit.selector, true);
authority.setRoleCapability(1, address(vault), Vault.withdraw.selector, true);
authority.setRoleCapability(2, address(vault), Vault.emergencyWithdraw.selector, true);

// 4. 分配角色给用户
authority.setUserRole(alice, 0, true);   // Alice 可存款
authority.setUserRole(alice, 1, true);   // Alice 也可取款
authority.setUserRole(bob, 0, true);     // Bob 只能存款

// 5.(可选)将 deposit 设为公开,任何人可存
authority.setPublicCapability(address(vault), Vault.deposit.selector, true);

// 6.(生产环境)将 owner 转给多签,消除单点风险
authority.transferOwnership(multisigAddress);
vault.transferOwnership(multisigAddress);

9.3 调用验证

Alice 调用 vault.withdraw(100):
  → requiresAuth
  → authority.canCall(alice, vault, withdraw.selector)
  → getUserRoles[alice] = ...011(角色 0、1)
  → getRolesWithCapability[vault][withdraw] = ...010(角色 1)
  → AND = ...010 != 0 → 通过 ✅

Bob 调用 vault.withdraw(100):
  → requiresAuth
  → authority.canCall(bob, vault, withdraw.selector)
  → getUserRoles[bob] = ...001(角色 0)
  → getRolesWithCapability[vault][withdraw] = ...010(角色 1)
  → AND = ...000 == 0 → 拒绝
  → bob != owner → revert("UNAUTHORIZED") ❌

十、测试实战

目标合约

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {Auth} from "solmate/auth/Auth.sol";

/// @dev 被保护的目标合约,用于端到端验证
contract Target is Auth {
    constructor(address _owner, Authority _authority) Auth(_owner, _authority) {}

    function protectedFunc() external view requiresAuth returns (bool) {
        return true;
    }

    function anotherFunc() external view requiresAuth returns (bool) {
        return true;
    }
}

全部foundry测试合约https://github.com/RevelationOfTuring/foundry-solmate/blob/main/test/auth/authorities/RolesAuthority.t.sol

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论