Skip to content

Commit 46fd6ac

Browse files
committed
Setup documentation
1 parent afa3ac0 commit 46fd6ac

File tree

21 files changed

+1821
-22
lines changed

21 files changed

+1821
-22
lines changed

contracts/access/README.adoc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
= Utilities
2+
3+
[.readme-notice]
4+
NOTE: This document is better viewed at https://docs.openzeppelin.com/community-contracts/proxy
5+
6+
This directory contains utility contracts to restrict access control in smart contracts. These include:
7+
8+
* {AccessManagerLight}: A simpler version of an AccessManager that uses `bytes8` roles to allow function calls identified by their 4-bytes selector.
9+
10+
== AccessManager
11+
12+
{{AccessManagerLight}}
13+
14+
15+

contracts/access/manager/AccessManagerLight.sol

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,84 +5,104 @@ pragma solidity ^0.8.20;
55
import {IAuthority} from "@openzeppelin/contracts/access/manager/IAuthority.sol";
66
import {Masks} from "../../utils/Masks.sol";
77

8+
/**
9+
* @dev Light version of an AccessManager contract that defines `bytes8` roles
10+
* that are stored as requirements (see {getRequirements}) for each function.
11+
*
12+
* Each requirement is a bitmask of roles that are allowed to call a function
13+
* identified by its `bytes4` selector. Users have their permissioned stored
14+
* as a bitmask of roles they belong to.
15+
*
16+
* The admin role is a special role that has access to all functions and can
17+
* manage the roles of other users.
18+
*/
819
contract AccessManagerLight is IAuthority {
920
using Masks for *;
1021

11-
uint8 public constant ADMIN = 0x00;
12-
uint8 public constant PUBLIC = 0xFF;
13-
Masks.Mask public immutable ADMIN_MASK = ADMIN.toMask();
14-
Masks.Mask public immutable PUBLIC_MASK = PUBLIC.toMask();
22+
uint8 public constant ADMIN_ROLE = 0x00;
23+
uint8 public constant PUBLIC_ROLE = 0xFF;
24+
Masks.Mask public immutable ADMIN_MASK = ADMIN_ROLE.toMask();
25+
Masks.Mask public immutable PUBLIC_MASK = PUBLIC_ROLE.toMask();
1526

16-
mapping(address => Masks.Mask) private _permissions;
17-
mapping(address => mapping(bytes4 => Masks.Mask)) private _restrictions;
27+
mapping(address => Masks.Mask) private _groups;
28+
mapping(address => mapping(bytes4 => Masks.Mask)) private _requirements;
1829
mapping(uint8 => Masks.Mask) private _admin;
1930

2031
event GroupAdded(address indexed user, uint8 indexed group);
2132
event GroupRemoved(address indexed user, uint8 indexed group);
2233
event GroupAdmins(uint8 indexed group, Masks.Mask admins);
23-
event Requirements(address indexed target, bytes4 indexed selector, Masks.Mask groups);
34+
event RequirementsSet(address indexed target, bytes4 indexed selector, Masks.Mask groups);
2435

25-
error MissingPermissions(address user, Masks.Mask permissions, Masks.Mask restriction);
36+
error MissingPermissions(address user, Masks.Mask permissions, Masks.Mask requirement);
2637

27-
modifier onlyRole(Masks.Mask restriction) {
38+
/// @dev Throws if the specified requirement is not met by the caller's permissions (see {getGroups}).
39+
modifier onlyRole(Masks.Mask requirement) {
2840
Masks.Mask permissions = getGroups(msg.sender);
29-
if (permissions.intersection(restriction).isEmpty()) {
30-
revert MissingPermissions(msg.sender, permissions, restriction);
41+
if (permissions.intersection(requirement).isEmpty()) {
42+
revert MissingPermissions(msg.sender, permissions, requirement);
3143
}
3244
_;
3345
}
3446

47+
/// @dev Initializes the contract with the `admin` as the first member of the admin group.
3548
constructor(address admin) {
3649
_addGroup(admin, 0);
3750
}
3851

39-
// Getters
52+
/// @dev Returns whether the `caller` has the required permissions to call the `target` with the `selector`.
4053
function canCall(address caller, address target, bytes4 selector) public view returns (bool) {
4154
return !getGroups(caller).intersection(getRequirements(target, selector)).isEmpty();
4255
}
4356

57+
/// @dev Returns the groups that the `user` belongs to.
4458
function getGroups(address user) public view returns (Masks.Mask) {
45-
return _permissions[user].union(PUBLIC_MASK);
59+
return _groups[user].union(PUBLIC_MASK);
4660
}
4761

62+
/// @dev Returns the admins of the `group`.
4863
function getGroupAdmins(uint8 group) public view returns (Masks.Mask) {
4964
return _admin[group].union(ADMIN_MASK); // Admin have power over all groups
5065
}
5166

67+
/// @dev Returns the requirements for the `target` and `selector`.
5268
function getRequirements(address target, bytes4 selector) public view returns (Masks.Mask) {
53-
return _restrictions[target][selector].union(ADMIN_MASK); // Admins can call an function
69+
return _requirements[target][selector].union(ADMIN_MASK); // Admins can call an function
5470
}
5571

56-
// Group management
72+
/// @dev Adds the `user` to the `group`. Emits {GroupAdded} event.
5773
function addGroup(address user, uint8 group) public onlyRole(getGroupAdmins(group)) {
5874
_addGroup(user, group);
5975
}
6076

77+
/// @dev Removes the `user` from the `group`. Emits {GroupRemoved} event.
6178
function remGroup(address user, uint8 group) public onlyRole(getGroupAdmins(group)) {
6279
_remGroup(user, group);
6380
}
6481

82+
/// @dev Internal version of {addGroup} without access control.
6583
function _addGroup(address user, uint8 group) internal {
66-
_permissions[user] = _permissions[user].union(group.toMask());
84+
_groups[user] = _groups[user].union(group.toMask());
6785
emit GroupAdded(user, group);
6886
}
6987

88+
/// @dev Internal version of {remGroup} without access control.
7089
function _remGroup(address user, uint8 group) internal {
71-
_permissions[user] = _permissions[user].difference(group.toMask());
90+
_groups[user] = _groups[user].difference(group.toMask());
7291
emit GroupRemoved(user, group);
7392
}
7493

75-
// Group admin management
94+
/// @dev Sets the `admins` of the `group`. Emits {GroupAdmins} event.
7695
function setGroupAdmins(uint8 group, uint8[] calldata admins) public onlyRole(ADMIN_MASK) {
7796
_setGroupAdmins(group, admins.toMask());
7897
}
7998

99+
/// @dev Internal version of {_setGroupAdmins} without access control.
80100
function _setGroupAdmins(uint8 group, Masks.Mask admins) internal {
81101
_admin[group] = admins;
82102
emit GroupAdmins(group, admins);
83103
}
84104

85-
// Requirement management
105+
/// @dev Sets the `groups` requirements for the `selectors` of the `target`.
86106
function setRequirements(
87107
address target,
88108
bytes4[] calldata selectors,
@@ -94,8 +114,9 @@ contract AccessManagerLight is IAuthority {
94114
}
95115
}
96116

117+
/// @dev Internal version of {_setRequirements} without access control.
97118
function _setRequirements(address target, bytes4 selector, Masks.Mask groups) internal {
98-
_restrictions[target][selector] = groups;
99-
emit Requirements(target, selector, groups);
119+
_requirements[target][selector] = groups;
120+
emit RequirementsSet(target, selector, groups);
100121
}
101122
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// contracts/MyStablecoinAllowlist.sol
2+
// SPDX-License-Identifier: MIT
3+
pragma solidity ^0.8.22;
4+
5+
import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol";
6+
import {ERC20Allowlist, ERC20} from "../../token/ERC20/extensions/ERC20Allowlist.sol";
7+
8+
contract MyStablecoinAllowlist is ERC20Allowlist, AccessManaged {
9+
constructor(address initialAuthority) ERC20("MyStablecoin", "MST") AccessManaged(initialAuthority) {}
10+
11+
function allowUser(address user) public restricted {
12+
_allowUser(user);
13+
}
14+
15+
function disallowUser(address user) public restricted {
16+
_disallowUser(user);
17+
}
18+
}

contracts/proxy/HybridProxy.sol

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,35 @@ import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.s
77
import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol";
88
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
99

10+
/**
11+
* @dev A version of an ERC-1967 proxy that uses the address stored in the implementation slot as a beacon.
12+
*
13+
* The design allows to set an initial beacon that the contract may quit by upgrading to its own implementation
14+
* afterwards.
15+
*
16+
* WARNING: The fallback mechanism relies on the implementation not to define the {IBeacon-implementation} function.
17+
* Consider that if your implementation has this function, it'll be assumed as the beacon address, meaning that
18+
* the returned address will be used as this proxy's implementation.
19+
*/
1020
contract HybridProxy is Proxy {
21+
/**
22+
* @dev Initializes the proxy with an initial implementation. If data is present, it will be used to initialize the
23+
* implementation using a delegate call.
24+
*/
1125
constructor(address implementation, bytes memory data) {
1226
ERC1967Utils.upgradeToAndCall(implementation, "");
1327
if (data.length > 0) {
1428
Address.functionDelegateCall(_implementation(), data);
1529
}
1630
}
1731

32+
/**
33+
* @dev Returns the current implementation address according to ERC-1967's implementation slot.
34+
*
35+
* IMPORTANT: The way this function identifies whether the implementation is a beacon, is by checking
36+
* if it implements the {IBeacon-implementation} function. Consider that an actual implementation could
37+
* define this function, mistakenly identifying it as a beacon.
38+
*/
1839
function _implementation() internal view override returns (address) {
1940
address implementation = ERC1967Utils.getImplementation();
2041
try IBeacon(implementation).implementation() returns (address result) {

contracts/proxy/README.adoc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
= Utilities
2+
3+
[.readme-notice]
4+
NOTE: This document is better viewed at https://docs.openzeppelin.com/community-contracts/proxy
5+
6+
Variants of proxy patterns, which are contracts that allow to forward a call to an implementation contract by using `delegatecall`. This contracts include:
7+
8+
* {HybridProxy}: An ERC-1967 proxy that uses the implementation slot as a beacon in a way that a user can upgrade to an implementation of their choice.
9+
10+
== General
11+
12+
{{HybridProxy}}
13+
14+
15+

contracts/token/README.adoc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
= Utilities
2+
3+
[.readme-notice]
4+
NOTE: This document is better viewed at https://docs.openzeppelin.com/community-contracts/token
5+
6+
Set of extensions and utilities for tokens (e.g ERC-20, ERC-721, ERC-1155) and derivated ERCs (e.g. ERC-4626, ERC-1363).
7+
8+
* {OnTokenTransferAdapter}: Adapter of the ERC-1363 receiver interface to comply with Chainlink's 667 interface.
9+
* {ERC20Allowlist}: Extension of ERC20 with transfers and approvals that require users to be registered into an allowlist.
10+
* {ERC20Blocklist}: Extension of ERC20 with transfers and approvals that can be disabled by adding users into a blocklist.
11+
* {ERC20Collateral}: Oracle-agnostic extension of ERC20 that limits the total supply based on a collateral amount.
12+
* {ERC20Custodian}: Extension of ERC20 that implements an access-control agnostic approach to define a custodian that can freeze user's transfers and approvals.
13+
* {ERC4626Fees}: ERC4626 vault with fees on entry (deposit/mint) or exit (withdraw/redeem).
14+
15+
== General
16+
17+
{{OnTokenTransferAdapter}}
18+
19+
== ERC20
20+
21+
{{ERC20Allowlist}}
22+
23+
{{ERC20Blocklist}}
24+
25+
{{ERC20Collateral}}
26+
27+
{{ERC20Custodial}}
28+
29+
{{ERC4626Fees}}
30+
31+
32+

contracts/utils/README.adoc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
= Utilities
2+
3+
[.readme-notice]
4+
NOTE: This document is better viewed at https://docs.openzeppelin.com/community-contracts/utils
5+
6+
Miscellaneous contracts and libraries containing utility functions you can use to improve security, work with new data types, or safely use low-level primitives.
7+
8+
* {Masks}: Library to handle `bytes32` masks.
9+
* {ERC7739Utils}: Utilities library that implements a defensive rehashing mechanism to prevent replayability of smart contract signatures based on ERC-7739.
10+
* {ERC7739Signer}: An abstract contract to validate signatures following the rehashing scheme from `ERC7739Utils`.
11+
12+
== Cryptography
13+
14+
{{ERC7739Signer}}
15+
16+
{{ERC7739Utils}}
17+
18+
== Libraries
19+
20+
{{Masks}}
21+

docs/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Documentation is hosted at https://docs.openzeppelin.com/contracts.
2+
3+
All of the content for the site is in this repository. The guides are in the
4+
[docs](/docs) directory, and the API Reference is extracted from comments in
5+
the source code. If you want to help improve the content, this is the
6+
repository you should be contributing to.
7+
8+
[`solidity-docgen`](https://github.com/OpenZeppelin/solidity-docgen) is the
9+
program that extracts the API Reference from source code.
10+
11+
The [`docs.openzeppelin.com`](https://github.com/OpenZeppelin/docs.openzeppelin.com)
12+
repository hosts the configuration for the entire site, which includes
13+
documentation for all of the OpenZeppelin projects.
14+
15+
To run the docs locally you should run `npm run docs:watch` on this
16+
repository.

docs/antora.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name: community-contracts
2+
title: Community Contracts
3+
version: 0.0.0-alpha.0
4+
prerelease: true
5+
nav:
6+
- modules/ROOT/nav.adoc
7+
- modules/api/nav.adoc

docs/config.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const path = require('path');
2+
const fs = require('fs');
3+
4+
/** @type import('solidity-docgen/dist/config').UserConfig */
5+
module.exports = {
6+
outputDir: 'docs/modules/api/pages',
7+
templates: 'docs/templates',
8+
exclude: ['mocks'],
9+
pageExtension: '.adoc',
10+
pages: (_, file, config) => {
11+
// For each contract file, find the closest README.adoc and return its location as the output page path.
12+
const sourcesDir = path.resolve(config.root, config.sourcesDir);
13+
let dir = path.resolve(config.root, file.absolutePath);
14+
while (dir.startsWith(sourcesDir)) {
15+
dir = path.dirname(dir);
16+
if (fs.existsSync(path.join(dir, 'README.adoc'))) {
17+
return path.relative(sourcesDir, dir) + config.pageExtension;
18+
}
19+
}
20+
},
21+
};

0 commit comments

Comments
 (0)