or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

access-control.mdcrosschain.mdfinance.mdgovernance.mdindex.mdmetatx.mdproxy.mdsecurity.mdtokens.mdutilities.md

access-control.mddocs/

0

# Access Control

1

2

OpenZeppelin Contracts provides flexible access control mechanisms through role-based and ownership-based patterns, enabling secure function access management in smart contracts.

3

4

## Role-Based Access Control

5

6

### AccessControl

7

8

Flexible role-based access control mechanism where roles are identified by bytes32 identifiers.

9

10

```solidity { .api }

11

abstract contract AccessControl is Context, IAccessControl, ERC165 {

12

bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

13

14

function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool);

15

function hasRole(bytes32 role, address account) public view virtual returns (bool);

16

function getRoleAdmin(bytes32 role) public view virtual returns (bytes32);

17

function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role));

18

function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role));

19

function renounceRole(bytes32 role, address account) public virtual;

20

}

21

22

interface IAccessControl {

23

function hasRole(bytes32 role, address account) external view returns (bool);

24

function getRoleAdmin(bytes32 role) external view returns (bytes32);

25

function grantRole(bytes32 role, address account) external;

26

function revokeRole(bytes32 role, address account) external;

27

function renounceRole(bytes32 role, address account) external;

28

}

29

```

30

31

**Events:**

32

- `RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole)`

33

- `RoleGranted(bytes32 indexed role, address indexed account, address indexed sender)`

34

- `RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender)`

35

36

**Modifiers:**

37

- `onlyRole(bytes32 role)` - Restricts access to accounts with specific role

38

39

**Usage Example:**

40

41

```solidity

42

contract MyContract is AccessControl {

43

bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");

44

bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

45

46

constructor() {

47

_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);

48

_grantRole(MANAGER_ROLE, msg.sender);

49

}

50

51

function managerOnlyFunction() public onlyRole(MANAGER_ROLE) {

52

// Only accounts with MANAGER_ROLE can call this

53

}

54

55

function operatorFunction() public onlyRole(OPERATOR_ROLE) {

56

// Only accounts with OPERATOR_ROLE can call this

57

}

58

}

59

```

60

61

### AccessControlEnumerable

62

63

Extension of AccessControl that allows enumeration of role members.

64

65

```solidity { .api }

66

abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {

67

function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool);

68

function getRoleMember(bytes32 role, uint256 index) public view virtual returns (address);

69

function getRoleMemberCount(bytes32 role) public view virtual returns (uint256);

70

}

71

72

interface IAccessControlEnumerable is IAccessControl {

73

function getRoleMember(bytes32 role, uint256 index) external view returns (address);

74

function getRoleMemberCount(bytes32 role) external view returns (uint256);

75

}

76

```

77

78

**Usage Example:**

79

80

```solidity

81

contract MyEnumerableContract is AccessControlEnumerable {

82

bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

83

84

function getAllMinters() public view returns (address[] memory) {

85

uint256 count = getRoleMemberCount(MINTER_ROLE);

86

address[] memory minters = new address[](count);

87

for (uint256 i = 0; i < count; i++) {

88

minters[i] = getRoleMember(MINTER_ROLE, i);

89

}

90

return minters;

91

}

92

}

93

```

94

95

### AccessControlCrossChain

96

97

Extension for cross-chain access control scenarios.

98

99

```solidity { .api }

100

abstract contract AccessControlCrossChain is AccessControl, CrossChainEnabled {

101

function hasRole(bytes32 role, address account) public view virtual override returns (bool);

102

}

103

```

104

105

## Ownership-Based Access Control

106

107

### Ownable

108

109

Simple ownership access control with a single owner.

110

111

```solidity { .api }

112

abstract contract Ownable is Context {

113

address private _owner;

114

115

event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

116

117

constructor();

118

119

function owner() public view virtual returns (address);

120

function renounceOwnership() public virtual onlyOwner;

121

function transferOwnership(address newOwner) public virtual onlyOwner;

122

}

123

```

124

125

**Events:**

126

- `OwnershipTransferred(address indexed previousOwner, address indexed newOwner)`

127

128

**Modifiers:**

129

- `onlyOwner()` - Restricts access to the contract owner

130

131

**Usage Example:**

132

133

```solidity

134

contract MyOwnableContract is Ownable {

135

uint256 public value;

136

137

function setValue(uint256 _value) public onlyOwner {

138

value = _value;

139

}

140

141

function emergencyStop() public onlyOwner {

142

// Only owner can call emergency functions

143

}

144

}

145

```

146

147

### Ownable2Step

148

149

Two-step ownership transfer for enhanced security.

150

151

```solidity { .api }

152

abstract contract Ownable2Step is Ownable {

153

address private _pendingOwner;

154

155

event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

156

157

function pendingOwner() public view virtual returns (address);

158

function transferOwnership(address newOwner) public virtual override onlyOwner;

159

function acceptOwnership() external;

160

}

161

```

162

163

**Events:**

164

- `OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner)`

165

166

**Usage Example:**

167

168

```solidity

169

contract SecureContract is Ownable2Step {

170

function initiateOwnershipTransfer(address newOwner) public onlyOwner {

171

transferOwnership(newOwner);

172

// newOwner must call acceptOwnership() to complete the transfer

173

}

174

}

175

```

176

177

## Access Control Patterns

178

179

### Combining Access Control Mechanisms

180

181

You can combine different access control patterns for more sophisticated permission systems:

182

183

```solidity

184

contract HybridAccessControl is AccessControl, Ownable {

185

bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

186

bytes32 public constant USER_ROLE = keccak256("USER_ROLE");

187

188

constructor() {

189

_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);

190

_grantRole(ADMIN_ROLE, msg.sender);

191

}

192

193

// Only owner or admin role can call

194

function sensitiveFunction() public {

195

require(

196

owner() == msg.sender || hasRole(ADMIN_ROLE, msg.sender),

197

"Not authorized"

198

);

199

// Function logic

200

}

201

202

// Only owner can grant admin role

203

function grantAdminRole(address account) public onlyOwner {

204

grantRole(ADMIN_ROLE, account);

205

}

206

}

207

```

208

209

### Role Hierarchy

210

211

Create role hierarchies by setting role admins:

212

213

```solidity

214

contract HierarchicalRoles is AccessControl {

215

bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

216

bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");

217

bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

218

219

constructor() {

220

_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);

221

_grantRole(ADMIN_ROLE, msg.sender);

222

223

// Set role hierarchy

224

_setRoleAdmin(MANAGER_ROLE, ADMIN_ROLE);

225

_setRoleAdmin(OPERATOR_ROLE, MANAGER_ROLE);

226

}

227

228

function adminFunction() public onlyRole(ADMIN_ROLE) {

229

// Only admins can call

230

}

231

232

function managerFunction() public onlyRole(MANAGER_ROLE) {

233

// Only managers can call

234

}

235

236

function operatorFunction() public onlyRole(OPERATOR_ROLE) {

237

// Only operators can call

238

}

239

}

240

```

241

242

## Security Best Practices

243

244

### Role Management

245

246

1. **Least Privilege**: Grant minimum required permissions

247

2. **Role Separation**: Separate concerns into different roles

248

3. **Admin Role Protection**: Carefully manage DEFAULT_ADMIN_ROLE holders

249

4. **Role Renunciation**: Allow role holders to renounce their roles

250

251

### Ownership Transfer

252

253

1. **Two-Step Transfer**: Use Ownable2Step for critical contracts

254

2. **Verification**: Always verify new owner addresses

255

3. **Emergency Procedures**: Have procedures for ownership recovery

256

4. **Multi-Signature**: Consider multi-signature wallets for critical owners

257

258

### Common Pitfalls

259

260

1. **Role Admin Loops**: Avoid circular role admin dependencies

261

2. **Ownership Renunciation**: Be cautious about renouncing ownership permanently

262

3. **Role Discovery**: Remember that role membership is public on-chain

263

4. **Gas Costs**: Consider gas costs when enumerating role members

264

265

## Testing Access Control

266

267

```solidity

268

// Example test patterns

269

contract AccessControlTest {

270

function testOnlyOwnerFunctions() public {

271

// Test that only owner can call owner-only functions

272

vm.prank(owner);

273

contract.ownerOnlyFunction();

274

275

vm.expectRevert("Ownable: caller is not the owner");

276

vm.prank(user);

277

contract.ownerOnlyFunction();

278

}

279

280

function testRoleBasedAccess() public {

281

// Test role-based access

282

vm.prank(admin);

283

contract.grantRole(MANAGER_ROLE, manager);

284

285

vm.prank(manager);

286

contract.managerFunction();

287

288

vm.expectRevert();

289

vm.prank(user);

290

contract.managerFunction();

291

}

292

}

293

```