@chainlink/ace 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.foundry-version +1 -0
- package/.github/CODEOWNERS +1 -0
- package/.github/workflows/auto-release-version.yml +107 -0
- package/.github/workflows/create-version-pr.yml +95 -0
- package/.github/workflows/forge-docs.yml +90 -0
- package/.github/workflows/forge-test.yml +59 -0
- package/.solhint-test.json +18 -0
- package/.solhint.json +16 -0
- package/.solhintignore +3 -0
- package/.solhintignore-test +2 -0
- package/Glossary.md +141 -0
- package/LICENSE +59 -0
- package/README.md +218 -0
- package/assets/chainlink-logo.svg +21 -0
- package/chainlink-ace-License-grants +2 -0
- package/foundry.toml +33 -0
- package/getting_started/GETTING_STARTED.md +477 -0
- package/getting_started/MyVault.sol +48 -0
- package/getting_started/advanced/.env.example +36 -0
- package/getting_started/advanced/GETTING_STARTED_ADVANCED.md +431 -0
- package/getting_started/advanced/SanctionsList.sol +25 -0
- package/getting_started/advanced/SanctionsPolicy.sol +58 -0
- package/package.json +41 -0
- package/packages/cross-chain-identity/README.md +148 -0
- package/packages/cross-chain-identity/docs/API_GUIDE.md +120 -0
- package/packages/cross-chain-identity/docs/API_REFERENCE.md +271 -0
- package/packages/cross-chain-identity/docs/CONCEPTS.md +253 -0
- package/packages/cross-chain-identity/docs/CREDENTIAL_FLOW.md +195 -0
- package/packages/cross-chain-identity/docs/SECURITY.md +70 -0
- package/packages/cross-chain-identity/src/CredentialRegistry.sol +245 -0
- package/packages/cross-chain-identity/src/CredentialRegistryIdentityValidator.sol +339 -0
- package/packages/cross-chain-identity/src/CredentialRegistryIdentityValidatorPolicy.sol +71 -0
- package/packages/cross-chain-identity/src/IdentityRegistry.sol +123 -0
- package/packages/cross-chain-identity/src/TrustedIssuerRegistry.sol +140 -0
- package/packages/cross-chain-identity/src/interfaces/ICredentialDataValidator.sol +30 -0
- package/packages/cross-chain-identity/src/interfaces/ICredentialRegistry.sol +170 -0
- package/packages/cross-chain-identity/src/interfaces/ICredentialRequirements.sol +192 -0
- package/packages/cross-chain-identity/src/interfaces/ICredentialValidator.sol +37 -0
- package/packages/cross-chain-identity/src/interfaces/IIdentityRegistry.sol +85 -0
- package/packages/cross-chain-identity/src/interfaces/IIdentityValidator.sol +18 -0
- package/packages/cross-chain-identity/src/interfaces/ITrustedIssuerRegistry.sol +61 -0
- package/packages/cross-chain-identity/test/CredentialRegistry.t.sol +220 -0
- package/packages/cross-chain-identity/test/CredentialRegistryIdentityValidator.t.sol +554 -0
- package/packages/cross-chain-identity/test/CredentialRegistryIdentityValidatorPolicy.t.sol +114 -0
- package/packages/cross-chain-identity/test/IdentityRegistry.t.sol +106 -0
- package/packages/cross-chain-identity/test/IdentityValidator.t.sol +969 -0
- package/packages/cross-chain-identity/test/TrustedIssuerRegistry.t.sol +123 -0
- package/packages/cross-chain-identity/test/helpers/BaseProxyTest.sol +112 -0
- package/packages/cross-chain-identity/test/helpers/MockCredentialDataValidator.sol +26 -0
- package/packages/cross-chain-identity/test/helpers/MockCredentialRegistryReverting.sol +131 -0
- package/packages/policy-management/README.md +197 -0
- package/packages/policy-management/docs/API_GUIDE.md +290 -0
- package/packages/policy-management/docs/API_REFERENCE.md +173 -0
- package/packages/policy-management/docs/CONCEPTS.md +156 -0
- package/packages/policy-management/docs/CUSTOM_POLICIES_TUTORIAL.md +195 -0
- package/packages/policy-management/docs/POLICY_ORDERING_GUIDE.md +91 -0
- package/packages/policy-management/docs/SECURITY.md +57 -0
- package/packages/policy-management/src/core/Policy.sol +124 -0
- package/packages/policy-management/src/core/PolicyEngine.sol +382 -0
- package/packages/policy-management/src/core/PolicyFactory.sol +92 -0
- package/packages/policy-management/src/core/PolicyProtected.sol +126 -0
- package/packages/policy-management/src/extractors/ComplianceTokenForceTransferExtractor.sol +57 -0
- package/packages/policy-management/src/extractors/ComplianceTokenFreezeUnfreezeExtractor.sol +54 -0
- package/packages/policy-management/src/extractors/ComplianceTokenMintBurnExtractor.sol +61 -0
- package/packages/policy-management/src/extractors/ERC20ApproveExtractor.sol +57 -0
- package/packages/policy-management/src/extractors/ERC20TransferExtractor.sol +62 -0
- package/packages/policy-management/src/extractors/ERC3643ForcedTransferExtractor.sol +56 -0
- package/packages/policy-management/src/extractors/ERC3643FreezeUnfreezeExtractor.sol +55 -0
- package/packages/policy-management/src/extractors/ERC3643MintBurnExtractor.sol +51 -0
- package/packages/policy-management/src/extractors/ERC3643SetAddressFrozenExtractor.sol +51 -0
- package/packages/policy-management/src/interfaces/IExtractor.sol +17 -0
- package/packages/policy-management/src/interfaces/IMapper.sol +17 -0
- package/packages/policy-management/src/interfaces/IPolicy.sol +61 -0
- package/packages/policy-management/src/interfaces/IPolicyEngine.sol +264 -0
- package/packages/policy-management/src/interfaces/IPolicyProtected.sol +48 -0
- package/packages/policy-management/src/policies/AllowPolicy.sol +104 -0
- package/packages/policy-management/src/policies/BypassPolicy.sol +90 -0
- package/packages/policy-management/src/policies/IntervalPolicy.sol +223 -0
- package/packages/policy-management/src/policies/MaxPolicy.sol +73 -0
- package/packages/policy-management/src/policies/OnlyAuthorizedSenderPolicy.sol +84 -0
- package/packages/policy-management/src/policies/OnlyOwnerPolicy.sol +35 -0
- package/packages/policy-management/src/policies/PausePolicy.sol +82 -0
- package/packages/policy-management/src/policies/README.md +632 -0
- package/packages/policy-management/src/policies/RejectPolicy.sol +89 -0
- package/packages/policy-management/src/policies/RoleBasedAccessControlPolicy.sol +162 -0
- package/packages/policy-management/src/policies/SecureMintPolicy.sol +271 -0
- package/packages/policy-management/src/policies/VolumePolicy.sol +133 -0
- package/packages/policy-management/src/policies/VolumeRatePolicy.sol +192 -0
- package/packages/policy-management/test/PolicyEngine.t.sol +368 -0
- package/packages/policy-management/test/PolicyFactory.t.sol +114 -0
- package/packages/policy-management/test/PolicyProtectedToken.t.sol +75 -0
- package/packages/policy-management/test/extractors/ComplianceTokenForceTransferExtractor.t.sol +59 -0
- package/packages/policy-management/test/extractors/ComplianceTokenFreezeUnfreezeExtractor.t.sol +74 -0
- package/packages/policy-management/test/extractors/ComplianceTokenMintBurnExtractor.t.sol +92 -0
- package/packages/policy-management/test/extractors/ERC20ApproveExtractor.t.sol +58 -0
- package/packages/policy-management/test/extractors/ERC3643ForcedTransferExtractor.t.sol +59 -0
- package/packages/policy-management/test/extractors/ERC3643FreezeUnfreezeExtractor.t.sol +74 -0
- package/packages/policy-management/test/extractors/ERC3643MintBurnExtractor.t.sol +73 -0
- package/packages/policy-management/test/extractors/ERC3643SetAddressFrozenExtractor.t.sol +56 -0
- package/packages/policy-management/test/helpers/BaseProxyTest.sol +75 -0
- package/packages/policy-management/test/helpers/CustomMapper.sol +26 -0
- package/packages/policy-management/test/helpers/DummyExtractor.sol +11 -0
- package/packages/policy-management/test/helpers/ExpectedParameterPolicy.sol +39 -0
- package/packages/policy-management/test/helpers/MockAggregatorV3.sol +51 -0
- package/packages/policy-management/test/helpers/MockToken.sol +66 -0
- package/packages/policy-management/test/helpers/MockTokenExtractor.sol +34 -0
- package/packages/policy-management/test/helpers/PolicyAlwaysAllowed.sol +45 -0
- package/packages/policy-management/test/helpers/PolicyAlwaysContinue.sol +23 -0
- package/packages/policy-management/test/helpers/PolicyAlwaysRejected.sol +23 -0
- package/packages/policy-management/test/helpers/PolicyFailingRun.sol +22 -0
- package/packages/policy-management/test/policies/AllowPolicy.t.sol +174 -0
- package/packages/policy-management/test/policies/BypassPolicy.t.sol +159 -0
- package/packages/policy-management/test/policies/IntervalPolicy.t.sol +307 -0
- package/packages/policy-management/test/policies/MaxPolicy.t.sol +54 -0
- package/packages/policy-management/test/policies/OnlyAuthorizedSenderPolicy.t.sol +95 -0
- package/packages/policy-management/test/policies/OnlyOwnerPolicy.t.sol +47 -0
- package/packages/policy-management/test/policies/PausePolicy.t.sol +75 -0
- package/packages/policy-management/test/policies/RejectPolicy.t.sol +182 -0
- package/packages/policy-management/test/policies/RoleBasedAccessControlPolicy.t.sol +223 -0
- package/packages/policy-management/test/policies/SecureMintPolicy.t.sol +442 -0
- package/packages/policy-management/test/policies/VolumePolicy.t.sol +158 -0
- package/packages/policy-management/test/policies/VolumeRatePolicy.t.sol +165 -0
- package/packages/tokens/erc-20/src/ComplianceTokenERC20.sol +345 -0
- package/packages/tokens/erc-20/src/ComplianceTokenStoreERC20.sol +29 -0
- package/packages/tokens/erc-20/test/ComplianceTokenERC20.t.sol +556 -0
- package/packages/tokens/erc-20/test/helpers/BaseProxyTest.sol +75 -0
- package/packages/tokens/erc-3643/README.md +24 -0
- package/packages/tokens/erc-3643/src/ComplianceTokenERC3643.sol +564 -0
- package/packages/tokens/erc-3643/src/ComplianceTokenStoreERC3643.sol +30 -0
- package/packages/tokens/erc-3643/test/ComplianceTokenERC3643.t.sol +815 -0
- package/packages/tokens/erc-3643/test/helpers/BaseProxyTest.sol +76 -0
- package/packages/tokens/erc-3643/test/helpers/ExpectedContextPolicy.sol +32 -0
- package/packages/vendor/erc-3643/compliance/modular/IModularCompliance.sol +220 -0
- package/packages/vendor/erc-3643/registry/interface/IClaimTopicsRegistry.sol +101 -0
- package/packages/vendor/erc-3643/registry/interface/IIdentityRegistry.sol +251 -0
- package/packages/vendor/erc-3643/registry/interface/IIdentityRegistryStorage.sol +191 -0
- package/packages/vendor/erc-3643/registry/interface/ITrustedIssuersRegistry.sol +161 -0
- package/packages/vendor/erc-3643/token/IToken.sol +457 -0
- package/packages/vendor/onchain-id/interface/IClaimIssuer.sol +53 -0
- package/packages/vendor/onchain-id/interface/IERC734.sol +110 -0
- package/packages/vendor/onchain-id/interface/IERC735.sol +105 -0
- package/packages/vendor/onchain-id/interface/IIdentity.sol +26 -0
- package/packages/vendor/onchain-id/interface/IImplementationAuthority.sol +21 -0
- package/remappings.txt +6 -0
- package/script/DeployComplianceTokenERC20.s.sol +191 -0
- package/script/DeployComplianceTokenERC3643.s.sol +208 -0
- package/script/DeploySimpleComplianceToken.s.sol +38 -0
- package/script/getting_started/DeployGettingStarted.s.sol +74 -0
- package/script/getting_started/advanced/DeployAdvancedGettingStarted.s.sol +332 -0
- package/script/getting_started/advanced/DeploySanctionsList.s.sol +26 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
import {IExtractor} from "../interfaces/IExtractor.sol";
|
|
5
|
+
import {IMapper} from "../interfaces/IMapper.sol";
|
|
6
|
+
import {IPolicy} from "../interfaces/IPolicy.sol";
|
|
7
|
+
import {IPolicyEngine} from "../interfaces/IPolicyEngine.sol";
|
|
8
|
+
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
|
9
|
+
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
10
|
+
|
|
11
|
+
contract PolicyEngine is Initializable, OwnableUpgradeable, IPolicyEngine {
|
|
12
|
+
uint256 private constant MAX_POLICIES = 8;
|
|
13
|
+
|
|
14
|
+
/// @custom:storage-location erc7201:policy-management.PolicyEngine
|
|
15
|
+
struct PolicyEngineStorage {
|
|
16
|
+
bool defaultPolicyAllow;
|
|
17
|
+
mapping(bytes4 selector => address extractor) extractorBySelector;
|
|
18
|
+
mapping(address policy => address mapper) policyMappers;
|
|
19
|
+
mapping(address target => bool attached) targetAttached;
|
|
20
|
+
mapping(address target => mapping(bytes4 selector => address[] policies)) targetPolicies;
|
|
21
|
+
mapping(address target => mapping(bytes4 selector => mapping(address policy => bytes32[] policyParameterNames)))
|
|
22
|
+
targetPolicyParameters;
|
|
23
|
+
mapping(address target => bool hasTargetDefault) targetHasDefault;
|
|
24
|
+
mapping(address target => bool targetDefaultPolicyAllow) targetDefaultPolicyAllow;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// keccak256(abi.encode(uint256(keccak256("policy-management.PolicyEngine")) - 1)) &
|
|
28
|
+
// ~bytes32(uint256(0xff))
|
|
29
|
+
// solhint-disable-next-line const-name-snakecase
|
|
30
|
+
bytes32 private constant policyEngineStorageLocation =
|
|
31
|
+
0xa1f0e32dde2a220dbeed9998863e2afeb333bc7b502562572bef1aa4cf5bf300;
|
|
32
|
+
|
|
33
|
+
function _policyEngineStorage() private pure returns (PolicyEngineStorage storage $) {
|
|
34
|
+
// solhint-disable-next-line no-inline-assembly
|
|
35
|
+
assembly {
|
|
36
|
+
$.slot := policyEngineStorageLocation
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
constructor() {
|
|
41
|
+
_disableInitializers();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @dev Initializes the policy engine.
|
|
46
|
+
* @param defaultAllow The default policy result. True to allow, false to reject.
|
|
47
|
+
*/
|
|
48
|
+
function initialize(bool defaultAllow, address initialOwner) public virtual initializer {
|
|
49
|
+
__PolicyEngine_init(defaultAllow, initialOwner);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function __PolicyEngine_init(bool defaultAllow, address initialOwner) internal onlyInitializing {
|
|
53
|
+
__PolicyEngine_init_unchained(defaultAllow);
|
|
54
|
+
__Ownable_init(initialOwner);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function __PolicyEngine_init_unchained(bool defaultAllow) internal onlyInitializing {
|
|
58
|
+
_policyEngineStorage().defaultPolicyAllow = defaultAllow;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// @inheritdoc IPolicyEngine
|
|
62
|
+
// TODO: need to review the permissioing of this function
|
|
63
|
+
function attach() public {
|
|
64
|
+
_attachTarget(msg.sender);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function _attachTarget(address target) internal {
|
|
68
|
+
if (_policyEngineStorage().targetAttached[target]) {
|
|
69
|
+
revert IPolicyEngine.TargetAlreadyAttached(target);
|
|
70
|
+
}
|
|
71
|
+
_policyEngineStorage().targetAttached[target] = true;
|
|
72
|
+
emit TargetAttached(target);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// @inheritdoc IPolicyEngine
|
|
76
|
+
// TODO: need to review the permissioing of this function
|
|
77
|
+
function detach() public {
|
|
78
|
+
if (!_policyEngineStorage().targetAttached[msg.sender]) {
|
|
79
|
+
revert IPolicyEngine.TargetNotAttached(msg.sender);
|
|
80
|
+
}
|
|
81
|
+
_policyEngineStorage().targetAttached[msg.sender] = false;
|
|
82
|
+
emit TargetDetached(msg.sender);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// @inheritdoc IPolicyEngine
|
|
86
|
+
function setDefaultPolicyAllow(bool defaultAllow) public onlyOwner {
|
|
87
|
+
_policyEngineStorage().defaultPolicyAllow = defaultAllow;
|
|
88
|
+
emit DefaultPolicyAllowSet(defaultAllow);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// @inheritdoc IPolicyEngine
|
|
92
|
+
function setTargetDefaultPolicyAllow(address target, bool defaultAllow) public onlyOwner {
|
|
93
|
+
PolicyEngineStorage storage $ = _policyEngineStorage();
|
|
94
|
+
$.targetHasDefault[target] = true;
|
|
95
|
+
$.targetDefaultPolicyAllow[target] = defaultAllow;
|
|
96
|
+
emit TargetDefaultPolicyAllowSet(target, defaultAllow);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// @inheritdoc IPolicyEngine
|
|
100
|
+
function setPolicyMapper(address policy, address mapper) public onlyOwner {
|
|
101
|
+
_policyEngineStorage().policyMappers[policy] = mapper;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// @inheritdoc IPolicyEngine
|
|
105
|
+
function getPolicyMapper(address policy) external view returns (address) {
|
|
106
|
+
return _policyEngineStorage().policyMappers[policy];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// @inheritdoc IPolicyEngine
|
|
110
|
+
function check(IPolicyEngine.Payload calldata payload) public view virtual override {
|
|
111
|
+
address[] memory policies = _policyEngineStorage().targetPolicies[msg.sender][payload.selector];
|
|
112
|
+
|
|
113
|
+
if (policies.length == 0) {
|
|
114
|
+
_checkDefaultPolicyAllowRevert(msg.sender, payload.selector);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
IPolicyEngine.Parameter[] memory extractedParameters = _extractParameters(payload);
|
|
119
|
+
for (uint256 i = 0; i < policies.length; i++) {
|
|
120
|
+
address policy = policies[i];
|
|
121
|
+
|
|
122
|
+
bytes[] memory policyParameterValues = _policyParameterValues(
|
|
123
|
+
policy, _policyEngineStorage().targetPolicyParameters[msg.sender][payload.selector][policy], extractedParameters
|
|
124
|
+
);
|
|
125
|
+
try IPolicy(policy).run(payload.sender, msg.sender, payload.selector, policyParameterValues, payload.context)
|
|
126
|
+
returns (IPolicyEngine.PolicyResult policyResult) {
|
|
127
|
+
if (policyResult == IPolicyEngine.PolicyResult.Allowed) {
|
|
128
|
+
return;
|
|
129
|
+
} // else continue to next policy
|
|
130
|
+
} catch (bytes memory err) {
|
|
131
|
+
_handlePolicyError(payload, policy, err);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
_checkDefaultPolicyAllowRevert(msg.sender, payload.selector);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/// @inheritdoc IPolicyEngine
|
|
139
|
+
function run(IPolicyEngine.Payload calldata payload) public virtual override {
|
|
140
|
+
address[] memory policies = _policyEngineStorage().targetPolicies[msg.sender][payload.selector];
|
|
141
|
+
if (policies.length == 0) {
|
|
142
|
+
_checkDefaultPolicyAllowRevert(msg.sender, payload.selector);
|
|
143
|
+
emit PolicyRunComplete(payload.sender, msg.sender, payload.selector);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
IPolicyEngine.Parameter[] memory extractedParameters = _extractParameters(payload);
|
|
148
|
+
for (uint256 i = 0; i < policies.length; i++) {
|
|
149
|
+
address policy = policies[i];
|
|
150
|
+
|
|
151
|
+
bytes[] memory policyParameterValues = _policyParameterValues(
|
|
152
|
+
policy, _policyEngineStorage().targetPolicyParameters[msg.sender][payload.selector][policy], extractedParameters
|
|
153
|
+
);
|
|
154
|
+
try IPolicy(policy).run(payload.sender, msg.sender, payload.selector, policyParameterValues, payload.context)
|
|
155
|
+
returns (IPolicyEngine.PolicyResult policyResult) {
|
|
156
|
+
// solhint-disable-next-line no-empty-blocks
|
|
157
|
+
try IPolicy(policy).postRun(
|
|
158
|
+
payload.sender, msg.sender, payload.selector, policyParameterValues, payload.context
|
|
159
|
+
) {} catch (bytes memory err) {
|
|
160
|
+
revert IPolicyEngine.PolicyPostRunError(payload.selector, policy, err);
|
|
161
|
+
}
|
|
162
|
+
if (policyResult == IPolicyEngine.PolicyResult.Allowed) {
|
|
163
|
+
emit PolicyRunComplete(payload.sender, msg.sender, payload.selector);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
} catch (bytes memory err) {
|
|
167
|
+
_handlePolicyError(payload, policy, err);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
_checkDefaultPolicyAllowRevert(msg.sender, payload.selector);
|
|
172
|
+
emit PolicyRunComplete(payload.sender, msg.sender, payload.selector);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/// @inheritdoc IPolicyEngine
|
|
176
|
+
function setExtractor(bytes4 selector, address extractor) public virtual override onlyOwner {
|
|
177
|
+
_policyEngineStorage().extractorBySelector[selector] = extractor;
|
|
178
|
+
emit ExtractorSet(selector, extractor);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/// @inheritdoc IPolicyEngine
|
|
182
|
+
function setExtractors(bytes4[] calldata selectors, address extractor) public virtual override onlyOwner {
|
|
183
|
+
for (uint256 i = 0; i < selectors.length; i++) {
|
|
184
|
+
setExtractor(selectors[i], extractor);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// @inheritdoc IPolicyEngine
|
|
189
|
+
function getExtractor(bytes4 selector) public view virtual override returns (address) {
|
|
190
|
+
return _policyEngineStorage().extractorBySelector[selector];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// @inheritdoc IPolicyEngine
|
|
194
|
+
function addPolicy(
|
|
195
|
+
address target,
|
|
196
|
+
bytes4 selector,
|
|
197
|
+
address policy,
|
|
198
|
+
bytes32[] calldata policyParameterNames
|
|
199
|
+
)
|
|
200
|
+
public
|
|
201
|
+
virtual
|
|
202
|
+
override
|
|
203
|
+
onlyOwner
|
|
204
|
+
{
|
|
205
|
+
_checkPolicyConfiguration(target, selector, policy);
|
|
206
|
+
IPolicy(policy).onInstall(selector);
|
|
207
|
+
_policyEngineStorage().targetPolicies[target][selector].push(policy);
|
|
208
|
+
_policyEngineStorage().targetPolicyParameters[target][selector][policy] = policyParameterNames;
|
|
209
|
+
emit PolicyAdded(target, selector, policy);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/// @inheritdoc IPolicyEngine
|
|
213
|
+
function addPolicyAt(
|
|
214
|
+
address target,
|
|
215
|
+
bytes4 selector,
|
|
216
|
+
address policy,
|
|
217
|
+
bytes32[] calldata policyParameterNames,
|
|
218
|
+
uint256 position
|
|
219
|
+
)
|
|
220
|
+
public
|
|
221
|
+
virtual
|
|
222
|
+
override
|
|
223
|
+
onlyOwner
|
|
224
|
+
{
|
|
225
|
+
address[] storage policies = _policyEngineStorage().targetPolicies[target][selector];
|
|
226
|
+
if (position > policies.length) {
|
|
227
|
+
revert IPolicyEngine.InvalidConfiguration("Position is greater than the number of policies");
|
|
228
|
+
}
|
|
229
|
+
_checkPolicyConfiguration(target, selector, policy);
|
|
230
|
+
IPolicy(policy).onInstall(selector);
|
|
231
|
+
policies.push();
|
|
232
|
+
for (uint256 i = policies.length - 1; i > position; i--) {
|
|
233
|
+
policies[i] = policies[i - 1];
|
|
234
|
+
}
|
|
235
|
+
policies[position] = policy;
|
|
236
|
+
_policyEngineStorage().targetPolicyParameters[target][selector][policy] = policyParameterNames;
|
|
237
|
+
emit PolicyAdded(target, selector, policy);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/// @inheritdoc IPolicyEngine
|
|
241
|
+
function removePolicy(address target, bytes4 selector, address policy) public virtual override onlyOwner {
|
|
242
|
+
address[] storage policies = _policyEngineStorage().targetPolicies[target][selector];
|
|
243
|
+
for (uint256 i = 0; i < policies.length; i++) {
|
|
244
|
+
if (policies[i] == policy) {
|
|
245
|
+
IPolicy(policy).onUninstall(selector);
|
|
246
|
+
|
|
247
|
+
for (uint256 j = i; j < policies.length - 1; j++) {
|
|
248
|
+
policies[j] = policies[j + 1];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
policies.pop();
|
|
252
|
+
emit PolicyRemoved(target, selector, policy);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/// @inheritdoc IPolicyEngine
|
|
259
|
+
function getPolicies(
|
|
260
|
+
address target,
|
|
261
|
+
bytes4 selector
|
|
262
|
+
)
|
|
263
|
+
public
|
|
264
|
+
view
|
|
265
|
+
virtual
|
|
266
|
+
override
|
|
267
|
+
returns (address[] memory policies)
|
|
268
|
+
{
|
|
269
|
+
return _policyEngineStorage().targetPolicies[target][selector];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function _handlePolicyError(Payload memory payload, address policy, bytes memory err) internal pure {
|
|
273
|
+
(bytes4 errorSelector, bytes memory errorData) = _decodeError(err);
|
|
274
|
+
if (errorSelector == IPolicyEngine.PolicyRejected.selector) {
|
|
275
|
+
revert IPolicyEngine.PolicyRunRejected(payload.selector, policy, abi.decode(errorData, (string)));
|
|
276
|
+
} else {
|
|
277
|
+
revert IPolicyEngine.PolicyRunError(payload.selector, policy, err);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function _checkDefaultPolicyAllowRevert(address target, bytes4 selector) private view {
|
|
282
|
+
PolicyEngineStorage storage $ = _policyEngineStorage();
|
|
283
|
+
bool defaultAllow = $.defaultPolicyAllow;
|
|
284
|
+
if ($.targetHasDefault[target]) {
|
|
285
|
+
defaultAllow = $.targetDefaultPolicyAllow[target];
|
|
286
|
+
}
|
|
287
|
+
if (!defaultAllow) {
|
|
288
|
+
revert IPolicyEngine.PolicyRunRejected(0, address(0), "no policy allowed the action and default is reject");
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function _checkPolicyConfiguration(address target, bytes4 selector, address policy) private view {
|
|
293
|
+
if (policy == address(0)) {
|
|
294
|
+
revert IPolicyEngine.InvalidConfiguration("Policy address cannot be zero");
|
|
295
|
+
}
|
|
296
|
+
if (_policyEngineStorage().targetPolicies[target][selector].length >= MAX_POLICIES) {
|
|
297
|
+
revert IPolicyEngine.InvalidConfiguration("Maximum policies reached");
|
|
298
|
+
}
|
|
299
|
+
address[] memory policies = _policyEngineStorage().targetPolicies[target][selector];
|
|
300
|
+
for (uint256 i = 0; i < policies.length; i++) {
|
|
301
|
+
if (policies[i] == policy) {
|
|
302
|
+
revert IPolicyEngine.InvalidConfiguration("Policy already added");
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function _extractParameters(IPolicyEngine.Payload memory payload)
|
|
308
|
+
private
|
|
309
|
+
view
|
|
310
|
+
returns (IPolicyEngine.Parameter[] memory)
|
|
311
|
+
{
|
|
312
|
+
IExtractor extractor = IExtractor(_policyEngineStorage().extractorBySelector[payload.selector]);
|
|
313
|
+
IPolicyEngine.Parameter[] memory extractedParameters;
|
|
314
|
+
|
|
315
|
+
if (address(extractor) == address(0)) {
|
|
316
|
+
return extractedParameters;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
try extractor.extract(payload) returns (IPolicyEngine.Parameter[] memory _extractedParameters) {
|
|
320
|
+
extractedParameters = _extractedParameters;
|
|
321
|
+
} catch (bytes memory err) {
|
|
322
|
+
revert IPolicyEngine.ExtractorError(payload.selector, address(extractor), err);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return extractedParameters;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function _policyParameterValues(
|
|
329
|
+
address policy,
|
|
330
|
+
bytes32[] memory policyParameterNames,
|
|
331
|
+
IPolicyEngine.Parameter[] memory extractedParameters
|
|
332
|
+
)
|
|
333
|
+
private
|
|
334
|
+
view
|
|
335
|
+
returns (bytes[] memory)
|
|
336
|
+
{
|
|
337
|
+
address mapper = _policyEngineStorage().policyMappers[policy];
|
|
338
|
+
// use custom mapper if set
|
|
339
|
+
if (mapper != address(0)) {
|
|
340
|
+
try IMapper(mapper).map(extractedParameters) returns (bytes[] memory mappedParameters) {
|
|
341
|
+
return mappedParameters;
|
|
342
|
+
} catch (bytes memory err) {
|
|
343
|
+
revert IPolicyEngine.PolicyMapperError(policy, err);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
bytes[] memory policyParameterValues = new bytes[](policyParameterNames.length);
|
|
348
|
+
|
|
349
|
+
uint256 parameterCount = policyParameterNames.length;
|
|
350
|
+
if (parameterCount == 0) {
|
|
351
|
+
return policyParameterValues;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
uint256 mappedParameterCount = 0;
|
|
355
|
+
for (uint256 i = 0; i < extractedParameters.length; i++) {
|
|
356
|
+
for (uint256 j = 0; j < parameterCount; j++) {
|
|
357
|
+
if (extractedParameters[i].name == policyParameterNames[j]) {
|
|
358
|
+
policyParameterValues[j] = extractedParameters[i].value;
|
|
359
|
+
mappedParameterCount++;
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (mappedParameterCount == parameterCount) {
|
|
364
|
+
return policyParameterValues;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
revert IPolicyEngine.InvalidConfiguration("Missing policy parameters");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function _decodeError(bytes memory err) internal pure returns (bytes4, bytes memory) {
|
|
371
|
+
// If the error length is less than 4, it is not a valid error
|
|
372
|
+
if (err.length < 4) {
|
|
373
|
+
return (0, err);
|
|
374
|
+
}
|
|
375
|
+
bytes4 selector = bytes4(err);
|
|
376
|
+
bytes memory errorData = new bytes(err.length - 4);
|
|
377
|
+
for (uint256 i = 0; i < err.length - 4; i++) {
|
|
378
|
+
errorData[i] = err[i + 4];
|
|
379
|
+
}
|
|
380
|
+
return (selector, errorData);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
import {Policy} from "./Policy.sol";
|
|
5
|
+
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @title PolicyFactory
|
|
9
|
+
* @notice Factory contract for creating deterministic minimal proxy clones of policy implementations.
|
|
10
|
+
* @dev Uses OpenZeppelin's Clones library to create deterministic minimal proxies (EIP-1167) of policy contracts.
|
|
11
|
+
* Each policy is deployed with a unique salt derived from the creator's address and a unique policy ID,
|
|
12
|
+
* ensuring deterministic addresses and preventing duplicate deployments.
|
|
13
|
+
*/
|
|
14
|
+
contract PolicyFactory {
|
|
15
|
+
/// @notice Emitted when a new policy is created
|
|
16
|
+
event PolicyCreated(address policy);
|
|
17
|
+
|
|
18
|
+
/// @notice Emitted when policy initialization fails
|
|
19
|
+
error PolicyInitializationFailed(bytes reason);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @notice Creates a new policy contract using deterministic minimal proxy cloning.
|
|
23
|
+
* @dev If a policy with the same salt already exists, returns the existing address instead of reverting.
|
|
24
|
+
* Uses CREATE2 for deterministic deployment addresses. The policy is automatically initialized
|
|
25
|
+
* with the provided parameters after deployment.
|
|
26
|
+
* @param implementation The address of the policy implementation contract to clone
|
|
27
|
+
* @param uniquePolicyId A unique identifier for this policy (combined with msg.sender to create salt)
|
|
28
|
+
* @param policyEngine The address of the policy engine that will manage this policy
|
|
29
|
+
* @param initialOwner The address that will own the newly created policy contract
|
|
30
|
+
* @param configData ABI-encoded configuration data specific to the policy implementation
|
|
31
|
+
* @return policyAddress The address of the created (or existing) policy contract
|
|
32
|
+
*/
|
|
33
|
+
function createPolicy(
|
|
34
|
+
address implementation,
|
|
35
|
+
bytes32 uniquePolicyId,
|
|
36
|
+
address policyEngine,
|
|
37
|
+
address initialOwner,
|
|
38
|
+
bytes calldata configData
|
|
39
|
+
)
|
|
40
|
+
public
|
|
41
|
+
returns (address policyAddress)
|
|
42
|
+
{
|
|
43
|
+
bytes32 salt = getSalt(msg.sender, uniquePolicyId);
|
|
44
|
+
policyAddress = Clones.predictDeterministicAddress(implementation, salt);
|
|
45
|
+
if (policyAddress.code.length > 0) {
|
|
46
|
+
return policyAddress;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
policyAddress = Clones.cloneDeterministic(implementation, salt);
|
|
50
|
+
|
|
51
|
+
try Policy(policyAddress).initialize(policyEngine, initialOwner, configData) {
|
|
52
|
+
emit PolicyCreated(policyAddress);
|
|
53
|
+
} catch (bytes memory reason) {
|
|
54
|
+
revert PolicyInitializationFailed(reason);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @notice Predicts the deterministic address where a policy would be deployed.
|
|
60
|
+
* @dev Useful for calculating policy addresses before deployment or checking if a policy already exists.
|
|
61
|
+
* Uses the same salt generation as createPolicy to ensure address consistency.
|
|
62
|
+
* @param creator The address of the account that would create the policy
|
|
63
|
+
* @param implementation The address of the policy implementation contract
|
|
64
|
+
* @param uniquePolicyId The unique identifier for the policy
|
|
65
|
+
* @return The predicted address where the policy would be deployed
|
|
66
|
+
*/
|
|
67
|
+
function predictPolicyAddress(
|
|
68
|
+
address creator,
|
|
69
|
+
address implementation,
|
|
70
|
+
bytes32 uniquePolicyId
|
|
71
|
+
)
|
|
72
|
+
public
|
|
73
|
+
view
|
|
74
|
+
returns (address)
|
|
75
|
+
{
|
|
76
|
+
bytes32 salt = getSalt(creator, uniquePolicyId);
|
|
77
|
+
return Clones.predictDeterministicAddress(implementation, salt);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @notice Generates a deterministic salt for policy deployment.
|
|
82
|
+
* @dev Combines the sender address and unique policy ID to create a unique salt.
|
|
83
|
+
* This ensures that the same creator cannot deploy multiple policies with the same ID,
|
|
84
|
+
* while allowing different creators to use the same policy ID.
|
|
85
|
+
* @param sender The address of the policy creator
|
|
86
|
+
* @param uniquePolicyId The unique identifier for the policy
|
|
87
|
+
* @return The generated salt for deterministic deployment
|
|
88
|
+
*/
|
|
89
|
+
function getSalt(address sender, bytes32 uniquePolicyId) public pure returns (bytes32) {
|
|
90
|
+
return keccak256(abi.encodePacked(sender, uniquePolicyId));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {IPolicyEngine} from "../interfaces/IPolicyEngine.sol";
|
|
5
|
+
import {IPolicyProtected} from "../interfaces/IPolicyProtected.sol";
|
|
6
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
7
|
+
import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
|
|
8
|
+
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
|
9
|
+
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @title PolicyProtected.sol
|
|
13
|
+
* @dev Base implementation for attaching a policy engine to a smart contract. Uses ERC-7201 storage
|
|
14
|
+
* to not conflict with other storage slots of extending contracts. Provides modifiers to be attached to methods
|
|
15
|
+
* of the extending contract to run the policy engine before executing the method.
|
|
16
|
+
*/
|
|
17
|
+
abstract contract PolicyProtected is Initializable, OwnableUpgradeable, ERC165Upgradeable, IPolicyProtected {
|
|
18
|
+
/// @custom:storage-location erc7201:policy-management.PolicyProtected
|
|
19
|
+
struct PolicyProtectedStorage {
|
|
20
|
+
IPolicyEngine policyEngine;
|
|
21
|
+
mapping(address sender => bytes context) senderContext; // use transient storage eventually
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// keccak256(abi.encode(uint256(keccak256("policy-management.PolicyProtected")) - 1)) &
|
|
25
|
+
// ~bytes32(uint256(0xff))
|
|
26
|
+
// solhint-disable-next-line const-name-snakecase
|
|
27
|
+
bytes32 private constant policyProtectedStorageLocation =
|
|
28
|
+
0x381e6510830aa5d1f847c166134370760011d6c9becccc73371e64e18c3c4f00;
|
|
29
|
+
|
|
30
|
+
function _policyProtectedStorage() private pure returns (PolicyProtectedStorage storage $) {
|
|
31
|
+
// solhint-disable-next-line no-inline-assembly
|
|
32
|
+
assembly {
|
|
33
|
+
$.slot := policyProtectedStorageLocation
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
_disableInitializers();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function __PolicyProtected_init(address initialOwner, address policyEngine) internal onlyInitializing {
|
|
42
|
+
__Ownable_init(initialOwner);
|
|
43
|
+
__ERC165_init();
|
|
44
|
+
__PolicyProtected_init_unchained(policyEngine);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function __PolicyProtected_init_unchained(address policyEngine) internal onlyInitializing {
|
|
48
|
+
_attachPolicyEngine(policyEngine);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* @dev Modifier to run the policy engine on the current method.
|
|
52
|
+
* @notice After the function execution completes, any context that was set will be automatically cleared.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
modifier runPolicy() {
|
|
56
|
+
if (address(_policyProtectedStorage().policyEngine) == address(0)) {
|
|
57
|
+
revert IPolicyEngine.PolicyEngineUndefined();
|
|
58
|
+
}
|
|
59
|
+
bytes memory context = getContext();
|
|
60
|
+
_policyProtectedStorage().policyEngine.run(
|
|
61
|
+
IPolicyEngine.Payload({selector: msg.sig, sender: msg.sender, data: msg.data[4:], context: context})
|
|
62
|
+
);
|
|
63
|
+
_;
|
|
64
|
+
if (context.length > 0) {
|
|
65
|
+
clearContext();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @dev Modifier to run the policy engine on the current method with the provided context.
|
|
71
|
+
* @param context Additional information or authorization to perform the operation.
|
|
72
|
+
*/
|
|
73
|
+
modifier runPolicyWithContext(bytes calldata context) {
|
|
74
|
+
if (address(_policyProtectedStorage().policyEngine) == address(0)) {
|
|
75
|
+
revert IPolicyEngine.PolicyEngineUndefined();
|
|
76
|
+
}
|
|
77
|
+
_policyProtectedStorage().policyEngine.run(
|
|
78
|
+
IPolicyEngine.Payload({selector: msg.sig, sender: msg.sender, data: msg.data[4:], context: context})
|
|
79
|
+
);
|
|
80
|
+
_;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// @inheritdoc IPolicyProtected
|
|
84
|
+
function attachPolicyEngine(address policyEngine) external virtual override onlyOwner {
|
|
85
|
+
_attachPolicyEngine(policyEngine);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function _attachPolicyEngine(address policyEngine) internal {
|
|
89
|
+
_policyProtectedStorage().policyEngine = IPolicyEngine(policyEngine);
|
|
90
|
+
IPolicyEngine(policyEngine).attach();
|
|
91
|
+
emit PolicyEngineAttached(policyEngine);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// @inheritdoc IPolicyProtected
|
|
95
|
+
function getPolicyEngine() public view virtual override returns (address) {
|
|
96
|
+
return address(_policyProtectedStorage().policyEngine);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// @inheritdoc IPolicyProtected
|
|
100
|
+
function setContext(bytes calldata context) public override {
|
|
101
|
+
_policyProtectedStorage().senderContext[msg.sender] = context;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// @inheritdoc IPolicyProtected
|
|
105
|
+
function getContext() public view override returns (bytes memory) {
|
|
106
|
+
return _policyProtectedStorage().senderContext[msg.sender];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// @inheritdoc IPolicyProtected
|
|
110
|
+
function clearContext() public override {
|
|
111
|
+
delete _policyProtectedStorage().senderContext[msg.sender];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @dev See {IERC165-supportsInterface}.
|
|
116
|
+
*/
|
|
117
|
+
function supportsInterface(bytes4 interfaceId)
|
|
118
|
+
public
|
|
119
|
+
view
|
|
120
|
+
virtual
|
|
121
|
+
override(ERC165Upgradeable, IERC165)
|
|
122
|
+
returns (bool)
|
|
123
|
+
{
|
|
124
|
+
return interfaceId == type(IPolicyProtected).interfaceId || super.supportsInterface(interfaceId);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
import {IExtractor} from "@chainlink/policy-management/interfaces/IExtractor.sol";
|
|
5
|
+
import {IPolicyEngine} from "@chainlink/policy-management/interfaces/IPolicyEngine.sol";
|
|
6
|
+
import {ComplianceTokenERC20} from "../../../tokens/erc-20/src/ComplianceTokenERC20.sol";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @title ComplianceTokenForceTransferExtractor
|
|
10
|
+
* @notice Extracts parameters from Compliance Token ERC20 forced transfer function calls.
|
|
11
|
+
* @dev This extractor supports the forceTransfer() function selector from the ComplianceTokenERC20 contract
|
|
12
|
+
* and extracts the from address, to address, and amount parameters. Force transfers allow
|
|
13
|
+
* authorized agents to move tokens between addresses without approval for compliance purposes.
|
|
14
|
+
*/
|
|
15
|
+
contract ComplianceTokenForceTransferExtractor is IExtractor {
|
|
16
|
+
/// @notice Parameter key for the sender/from address in forced transfer operations
|
|
17
|
+
bytes32 public constant PARAM_FROM = keccak256("from");
|
|
18
|
+
|
|
19
|
+
/// @notice Parameter key for the recipient/to address in forced transfer operations
|
|
20
|
+
bytes32 public constant PARAM_TO = keccak256("to");
|
|
21
|
+
|
|
22
|
+
/// @notice Parameter key for the amount being forcefully transferred
|
|
23
|
+
bytes32 public constant PARAM_AMOUNT = keccak256("amount");
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @inheritdoc IExtractor
|
|
27
|
+
* @dev Extracts parameters from ComplianceTokenERC20 forceTransfer(address from, address to, uint256 amount)
|
|
28
|
+
* function calls.
|
|
29
|
+
* @param payload The policy engine payload containing the function selector and calldata
|
|
30
|
+
* @return An array of three parameters: PARAM_FROM, PARAM_TO, and PARAM_AMOUNT
|
|
31
|
+
*/
|
|
32
|
+
function extract(IPolicyEngine.Payload calldata payload)
|
|
33
|
+
public
|
|
34
|
+
pure
|
|
35
|
+
virtual
|
|
36
|
+
returns (IPolicyEngine.Parameter[] memory)
|
|
37
|
+
{
|
|
38
|
+
address from = address(0);
|
|
39
|
+
address to = address(0);
|
|
40
|
+
uint256 amount = 0;
|
|
41
|
+
|
|
42
|
+
// Handle forceTransfer(address from, address to, uint256 amount)
|
|
43
|
+
if (payload.selector == ComplianceTokenERC20.forceTransfer.selector) {
|
|
44
|
+
(from, to, amount) = abi.decode(payload.data, (address, address, uint256));
|
|
45
|
+
} else {
|
|
46
|
+
revert IPolicyEngine.UnsupportedSelector(payload.selector);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Build the parameter array with extracted values
|
|
50
|
+
IPolicyEngine.Parameter[] memory result = new IPolicyEngine.Parameter[](3);
|
|
51
|
+
result[0] = IPolicyEngine.Parameter(PARAM_FROM, abi.encode(from));
|
|
52
|
+
result[1] = IPolicyEngine.Parameter(PARAM_TO, abi.encode(to));
|
|
53
|
+
result[2] = IPolicyEngine.Parameter(PARAM_AMOUNT, abi.encode(amount));
|
|
54
|
+
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
}
|