当我们编写一个应用到多个权限共同治理的合约,如合约的升级,变量的设置,合约的暂停分别由不同的角色地址来共同治理时,我们通常可以使用AccessControlUpgradeable或AccessControl合约来实现统一管理 这两个合约唯一的区别是AccessControlUpgradeable多提供了两个函数,如果你的合约本身是一个可升级合约,AccessControlUpgradeable提供了初始化调用的方法,用作你的合约本身在部署时需要对管理权限做的一些设置,如图
我们可以看到AccessControlUpgradeable提供了一个__AccessControl_init()的空方法,你可以在方法内实现你需要做的初始化权限设置,
这使得你可以在可升级合约中同步对权限进行一个初始化的设置,如图
讲完AccessControlUpgradeable和AccessControl的区别,我们回到这两个工具合约的主要使用,这两个合约提供如:
grantRole(bytes32 role, address account)
:授予角色给账户。调用者必须具有该角色的管理员角色。revokeRole(bytes32 role, address account)
:从账户撤销角色。调用者必须具有该角色的管理员角色。renounceRole(bytes32 role, address account)
:撤销调用者自己持有的角色。hasRole(bytes32 role, address account)
:检查账户是否持有指定角色。getRoleAdmin(bytes32 role)
:获取控制指定角色的管理员角色。_grantRole(bytes32 role, address account)
:内部方法,授予角色给账户。_revokeRole(bytes32 role, address account)
:内部方法,从账户撤销角色。_setRoleAdmin(bytes32 role, bytes32 adminRole)
:内部方法,设置角色的管理员角色。
在我们的合约中具体的使用如图
首先我们需要先用bytes32类型来声明我们的权限角色,以上我们可以看到我们声明了两个角色分别为ADMIN_ROLE(用于多签小组执行特定函数)和UPGRADER_ROLE(用于合约升级执行升级函数),我们在初始化中分别为他们授予不同的地址(但在图中我给他们设置的为相同的地址,都为部署者的地址)
你会发现我们并没有声明DEFAULT_ADMIN_ROLE,但是没有编译报错,这是因为AccessControlUpgradeable中自带了DEFAULT_ADMIN_ROLE的声明,如图
至于为什么是0x00,这个是有特殊作用的,我们下面会讲到,先回到正题。
由于我这里给以上三个不同权限设置了相同的地址,所以我们接下来可以去除某地址的权限,现在我们的部署者分别有ADMIN_ROLE和UPGRADER_ROLE权限,但是我要给ADMIN_ROLE和UPGRADER_ROLE设置不同的地址,并删除原来的地址(部署者地址),我们可以用DEFAULT_ADMIN_ROLE权限地址去执行grantRole(bytes32,address)
函数,该函数允许拥有adminRole权限的地址去为其他权限添加地址,如传参grantRole(0x00....0,userAddress)则表示为userAddress地址添加
DEFAULT_ADMIN_ROLE的权限,此时拥有DEFAULT_ADMIN_ROLE权限的分别为userAddress和部署者(意味着一个权限可以有多个用户地址),
接着我们可以用revokeRole(bytes32 role, address account)来删除部署者地址的DEFAULT_ADMIN_ROLE权限,这样就完成了不同地址的不同权限管理
接着我们来聊一下为什么需要用DEFAULT_ADMIN_ROLE来执行grantRole和
revokeRole,并且为什么DEFAULT_ADMIN_ROLE被声明为0x00,这是因为用grantRole来授予其他角色不同权限时,调用者必须拥有删除或添加权限的最高权限,也就是操作权限的管理员,如图
在调用grantRole时,使用onlyRole来检查是否有权限来执行该函数,而在getRoleAdmin中的返回值是什么呢,因为我们从来没有设置过_roles[role].adminRole,所以他会返回默认返回值,这个值是多少?
正是0x00,所以现在知道DEFAULT_ADMIN_ROLE为什么被设置为0x00了,因为getRoleAdmin(role)返回值为0x00,所以 onlyRole(getRoleAdmin(role))就等同于onlyRole(DEFAULT_ADMIN_ROLE),所以我们的DEFAULT_ADMIN_ROLE用有修改其他权限的最高权限,那除了DEFAULT_ADMIN_ROLE外的其他角色就无法执行grantRole和
revokeRole吗,实际上也是可以的,我们需要调用_setRoleAdmin()函数来为每个权限角色分别设置一个独属于他们的管理员,每个角色的管理员可以为其他地址添加或取消单属于这个角色权限的地址,在我们需要设定权限的函数中,我们只需要添加如图的modifier即可规定哪个权限角色可以执行该函数。
AccessControlUpgradeable权限管理合约为我们的主合约提供了非常完善和高灵活性的权限管理,可以完美的对我们的合约进行一个权限的规划。