-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathERC3643ComplianceModule.sol
More file actions
131 lines (114 loc) · 5.01 KB
/
Copy pathERC3643ComplianceModule.sol
File metadata and controls
131 lines (114 loc) · 5.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//SPDX-License-Identifier: MPL-2.0
pragma solidity ^0.8.20;
/* ==== OpenZeppelin === */
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
/* ==== Interface and other library === */
import {IERC3643Compliance} from "../interfaces/IERC3643Compliance.sol";
import {ERC3643ComplianceModuleInvariantStorage} from "./library/ERC3643ComplianceModuleInvariantStorage.sol";
/**
* @title ERC3643ComplianceModule
* @notice Core ERC-3643 compliance module: tracks the tokens bound to this engine.
*/
abstract contract ERC3643ComplianceModule is Context, IERC3643Compliance, ERC3643ComplianceModuleInvariantStorage {
/* ==== Type declaration === */
using EnumerableSet for EnumerableSet.AddressSet;
/* ==== State Variables === */
// Token binding tracking
/**
* @notice Set of tokens allowed to call the compliance callbacks.
*/
EnumerableSet.AddressSet internal _boundTokens;
/* ==== Modifier === */
modifier onlyBoundToken() {
_checkBoundToken();
_;
}
modifier onlyComplianceManager() {
_onlyComplianceManager();
_;
}
/*//////////////////////////////////////////////////////////////
PUBLIC/public FUNCTIONS
//////////////////////////////////////////////////////////////*/
/* ============ State functions ============ */
/**
* @inheritdoc IERC3643Compliance
* @dev Operator warning: "multi-tenant" means one RuleEngine is shared by
* multiple token contracts. In that setup, bind only tokens that are equally
* trusted and governed together.
* @custom:security-note Operation rules (stateful rules such as `RuleConditionalTransferLightMock`
* or `RuleMintAllowanceMock`) maintain per-address accounting that is shared across all bound tokens.
* Binding tokens from different issuers to the same engine will silently cross-contaminate
* their accounting. Only bind tokens that are equally trusted and governed together.
*/
function bindToken(address token) public virtual override {
_authorizeComplianceBindingChange(token);
_bindToken(token);
}
/**
* @inheritdoc IERC3643Compliance
* @dev Operator warning: unbinding is an administrative operation and does not
* erase any state already stored by external rule contracts in a previously
* shared ("multi-tenant") setup.
*/
function unbindToken(address token) public virtual override {
_authorizeComplianceBindingChange(token);
_unbindToken(token);
}
/// @inheritdoc IERC3643Compliance
function isTokenBound(address token) public view virtual override returns (bool) {
return _boundTokens.contains(token);
}
/// @inheritdoc IERC3643Compliance
function getTokenBound() public view virtual override returns (address) {
if (_boundTokens.length() > 0) {
// Note that there are no guarantees on the ordering of values inside the array,
// and it may change when more values are added or removed.
return _boundTokens.pos(0);
} else {
return address(0);
}
}
/*//////////////////////////////////////////////////////////////
INTERNAL/PRIVATE FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @dev Removes a token from the bound set.
* @param token The token to unbind; reverts when it is not currently bound.
*/
function _unbindToken(address token) internal virtual {
// remove() returns false when the token was not bound, so a separate
// contains() lookup is unnecessary.
require(_boundTokens.remove(token), RuleEngine_ERC3643Compliance_TokenNotBound());
emit TokenUnbound(token);
}
/**
* @dev Adds a token to the bound set.
* @param token The token to bind; reverts on the zero address or when already bound.
*/
function _bindToken(address token) internal virtual {
require(token != address(0), RuleEngine_ERC3643Compliance_InvalidTokenAddress());
// add() returns false when the token is already bound, so a separate
// contains() lookup is unnecessary.
require(_boundTokens.add(token), RuleEngine_ERC3643Compliance_TokenAlreadyBound());
emit TokenBound(token);
}
/**
* @dev Authorization hook for bind/unbind, implemented by the deployable contracts.
* @param token The token being bound or unbound.
*/
function _authorizeComplianceBindingChange(address token) internal virtual;
/**
* @dev Access control hook guarding compliance management operations.
*/
function _onlyComplianceManager() internal virtual;
/**
* @dev Reverts when the caller is not a bound token.
*/
function _checkBoundToken() internal view virtual {
if (!_boundTokens.contains(_msgSender())) {
revert RuleEngine_ERC3643Compliance_UnauthorizedCaller();
}
}
}