@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.
Files changed (150) hide show
  1. package/.foundry-version +1 -0
  2. package/.github/CODEOWNERS +1 -0
  3. package/.github/workflows/auto-release-version.yml +107 -0
  4. package/.github/workflows/create-version-pr.yml +95 -0
  5. package/.github/workflows/forge-docs.yml +90 -0
  6. package/.github/workflows/forge-test.yml +59 -0
  7. package/.solhint-test.json +18 -0
  8. package/.solhint.json +16 -0
  9. package/.solhintignore +3 -0
  10. package/.solhintignore-test +2 -0
  11. package/Glossary.md +141 -0
  12. package/LICENSE +59 -0
  13. package/README.md +218 -0
  14. package/assets/chainlink-logo.svg +21 -0
  15. package/chainlink-ace-License-grants +2 -0
  16. package/foundry.toml +33 -0
  17. package/getting_started/GETTING_STARTED.md +477 -0
  18. package/getting_started/MyVault.sol +48 -0
  19. package/getting_started/advanced/.env.example +36 -0
  20. package/getting_started/advanced/GETTING_STARTED_ADVANCED.md +431 -0
  21. package/getting_started/advanced/SanctionsList.sol +25 -0
  22. package/getting_started/advanced/SanctionsPolicy.sol +58 -0
  23. package/package.json +41 -0
  24. package/packages/cross-chain-identity/README.md +148 -0
  25. package/packages/cross-chain-identity/docs/API_GUIDE.md +120 -0
  26. package/packages/cross-chain-identity/docs/API_REFERENCE.md +271 -0
  27. package/packages/cross-chain-identity/docs/CONCEPTS.md +253 -0
  28. package/packages/cross-chain-identity/docs/CREDENTIAL_FLOW.md +195 -0
  29. package/packages/cross-chain-identity/docs/SECURITY.md +70 -0
  30. package/packages/cross-chain-identity/src/CredentialRegistry.sol +245 -0
  31. package/packages/cross-chain-identity/src/CredentialRegistryIdentityValidator.sol +339 -0
  32. package/packages/cross-chain-identity/src/CredentialRegistryIdentityValidatorPolicy.sol +71 -0
  33. package/packages/cross-chain-identity/src/IdentityRegistry.sol +123 -0
  34. package/packages/cross-chain-identity/src/TrustedIssuerRegistry.sol +140 -0
  35. package/packages/cross-chain-identity/src/interfaces/ICredentialDataValidator.sol +30 -0
  36. package/packages/cross-chain-identity/src/interfaces/ICredentialRegistry.sol +170 -0
  37. package/packages/cross-chain-identity/src/interfaces/ICredentialRequirements.sol +192 -0
  38. package/packages/cross-chain-identity/src/interfaces/ICredentialValidator.sol +37 -0
  39. package/packages/cross-chain-identity/src/interfaces/IIdentityRegistry.sol +85 -0
  40. package/packages/cross-chain-identity/src/interfaces/IIdentityValidator.sol +18 -0
  41. package/packages/cross-chain-identity/src/interfaces/ITrustedIssuerRegistry.sol +61 -0
  42. package/packages/cross-chain-identity/test/CredentialRegistry.t.sol +220 -0
  43. package/packages/cross-chain-identity/test/CredentialRegistryIdentityValidator.t.sol +554 -0
  44. package/packages/cross-chain-identity/test/CredentialRegistryIdentityValidatorPolicy.t.sol +114 -0
  45. package/packages/cross-chain-identity/test/IdentityRegistry.t.sol +106 -0
  46. package/packages/cross-chain-identity/test/IdentityValidator.t.sol +969 -0
  47. package/packages/cross-chain-identity/test/TrustedIssuerRegistry.t.sol +123 -0
  48. package/packages/cross-chain-identity/test/helpers/BaseProxyTest.sol +112 -0
  49. package/packages/cross-chain-identity/test/helpers/MockCredentialDataValidator.sol +26 -0
  50. package/packages/cross-chain-identity/test/helpers/MockCredentialRegistryReverting.sol +131 -0
  51. package/packages/policy-management/README.md +197 -0
  52. package/packages/policy-management/docs/API_GUIDE.md +290 -0
  53. package/packages/policy-management/docs/API_REFERENCE.md +173 -0
  54. package/packages/policy-management/docs/CONCEPTS.md +156 -0
  55. package/packages/policy-management/docs/CUSTOM_POLICIES_TUTORIAL.md +195 -0
  56. package/packages/policy-management/docs/POLICY_ORDERING_GUIDE.md +91 -0
  57. package/packages/policy-management/docs/SECURITY.md +57 -0
  58. package/packages/policy-management/src/core/Policy.sol +124 -0
  59. package/packages/policy-management/src/core/PolicyEngine.sol +382 -0
  60. package/packages/policy-management/src/core/PolicyFactory.sol +92 -0
  61. package/packages/policy-management/src/core/PolicyProtected.sol +126 -0
  62. package/packages/policy-management/src/extractors/ComplianceTokenForceTransferExtractor.sol +57 -0
  63. package/packages/policy-management/src/extractors/ComplianceTokenFreezeUnfreezeExtractor.sol +54 -0
  64. package/packages/policy-management/src/extractors/ComplianceTokenMintBurnExtractor.sol +61 -0
  65. package/packages/policy-management/src/extractors/ERC20ApproveExtractor.sol +57 -0
  66. package/packages/policy-management/src/extractors/ERC20TransferExtractor.sol +62 -0
  67. package/packages/policy-management/src/extractors/ERC3643ForcedTransferExtractor.sol +56 -0
  68. package/packages/policy-management/src/extractors/ERC3643FreezeUnfreezeExtractor.sol +55 -0
  69. package/packages/policy-management/src/extractors/ERC3643MintBurnExtractor.sol +51 -0
  70. package/packages/policy-management/src/extractors/ERC3643SetAddressFrozenExtractor.sol +51 -0
  71. package/packages/policy-management/src/interfaces/IExtractor.sol +17 -0
  72. package/packages/policy-management/src/interfaces/IMapper.sol +17 -0
  73. package/packages/policy-management/src/interfaces/IPolicy.sol +61 -0
  74. package/packages/policy-management/src/interfaces/IPolicyEngine.sol +264 -0
  75. package/packages/policy-management/src/interfaces/IPolicyProtected.sol +48 -0
  76. package/packages/policy-management/src/policies/AllowPolicy.sol +104 -0
  77. package/packages/policy-management/src/policies/BypassPolicy.sol +90 -0
  78. package/packages/policy-management/src/policies/IntervalPolicy.sol +223 -0
  79. package/packages/policy-management/src/policies/MaxPolicy.sol +73 -0
  80. package/packages/policy-management/src/policies/OnlyAuthorizedSenderPolicy.sol +84 -0
  81. package/packages/policy-management/src/policies/OnlyOwnerPolicy.sol +35 -0
  82. package/packages/policy-management/src/policies/PausePolicy.sol +82 -0
  83. package/packages/policy-management/src/policies/README.md +632 -0
  84. package/packages/policy-management/src/policies/RejectPolicy.sol +89 -0
  85. package/packages/policy-management/src/policies/RoleBasedAccessControlPolicy.sol +162 -0
  86. package/packages/policy-management/src/policies/SecureMintPolicy.sol +271 -0
  87. package/packages/policy-management/src/policies/VolumePolicy.sol +133 -0
  88. package/packages/policy-management/src/policies/VolumeRatePolicy.sol +192 -0
  89. package/packages/policy-management/test/PolicyEngine.t.sol +368 -0
  90. package/packages/policy-management/test/PolicyFactory.t.sol +114 -0
  91. package/packages/policy-management/test/PolicyProtectedToken.t.sol +75 -0
  92. package/packages/policy-management/test/extractors/ComplianceTokenForceTransferExtractor.t.sol +59 -0
  93. package/packages/policy-management/test/extractors/ComplianceTokenFreezeUnfreezeExtractor.t.sol +74 -0
  94. package/packages/policy-management/test/extractors/ComplianceTokenMintBurnExtractor.t.sol +92 -0
  95. package/packages/policy-management/test/extractors/ERC20ApproveExtractor.t.sol +58 -0
  96. package/packages/policy-management/test/extractors/ERC3643ForcedTransferExtractor.t.sol +59 -0
  97. package/packages/policy-management/test/extractors/ERC3643FreezeUnfreezeExtractor.t.sol +74 -0
  98. package/packages/policy-management/test/extractors/ERC3643MintBurnExtractor.t.sol +73 -0
  99. package/packages/policy-management/test/extractors/ERC3643SetAddressFrozenExtractor.t.sol +56 -0
  100. package/packages/policy-management/test/helpers/BaseProxyTest.sol +75 -0
  101. package/packages/policy-management/test/helpers/CustomMapper.sol +26 -0
  102. package/packages/policy-management/test/helpers/DummyExtractor.sol +11 -0
  103. package/packages/policy-management/test/helpers/ExpectedParameterPolicy.sol +39 -0
  104. package/packages/policy-management/test/helpers/MockAggregatorV3.sol +51 -0
  105. package/packages/policy-management/test/helpers/MockToken.sol +66 -0
  106. package/packages/policy-management/test/helpers/MockTokenExtractor.sol +34 -0
  107. package/packages/policy-management/test/helpers/PolicyAlwaysAllowed.sol +45 -0
  108. package/packages/policy-management/test/helpers/PolicyAlwaysContinue.sol +23 -0
  109. package/packages/policy-management/test/helpers/PolicyAlwaysRejected.sol +23 -0
  110. package/packages/policy-management/test/helpers/PolicyFailingRun.sol +22 -0
  111. package/packages/policy-management/test/policies/AllowPolicy.t.sol +174 -0
  112. package/packages/policy-management/test/policies/BypassPolicy.t.sol +159 -0
  113. package/packages/policy-management/test/policies/IntervalPolicy.t.sol +307 -0
  114. package/packages/policy-management/test/policies/MaxPolicy.t.sol +54 -0
  115. package/packages/policy-management/test/policies/OnlyAuthorizedSenderPolicy.t.sol +95 -0
  116. package/packages/policy-management/test/policies/OnlyOwnerPolicy.t.sol +47 -0
  117. package/packages/policy-management/test/policies/PausePolicy.t.sol +75 -0
  118. package/packages/policy-management/test/policies/RejectPolicy.t.sol +182 -0
  119. package/packages/policy-management/test/policies/RoleBasedAccessControlPolicy.t.sol +223 -0
  120. package/packages/policy-management/test/policies/SecureMintPolicy.t.sol +442 -0
  121. package/packages/policy-management/test/policies/VolumePolicy.t.sol +158 -0
  122. package/packages/policy-management/test/policies/VolumeRatePolicy.t.sol +165 -0
  123. package/packages/tokens/erc-20/src/ComplianceTokenERC20.sol +345 -0
  124. package/packages/tokens/erc-20/src/ComplianceTokenStoreERC20.sol +29 -0
  125. package/packages/tokens/erc-20/test/ComplianceTokenERC20.t.sol +556 -0
  126. package/packages/tokens/erc-20/test/helpers/BaseProxyTest.sol +75 -0
  127. package/packages/tokens/erc-3643/README.md +24 -0
  128. package/packages/tokens/erc-3643/src/ComplianceTokenERC3643.sol +564 -0
  129. package/packages/tokens/erc-3643/src/ComplianceTokenStoreERC3643.sol +30 -0
  130. package/packages/tokens/erc-3643/test/ComplianceTokenERC3643.t.sol +815 -0
  131. package/packages/tokens/erc-3643/test/helpers/BaseProxyTest.sol +76 -0
  132. package/packages/tokens/erc-3643/test/helpers/ExpectedContextPolicy.sol +32 -0
  133. package/packages/vendor/erc-3643/compliance/modular/IModularCompliance.sol +220 -0
  134. package/packages/vendor/erc-3643/registry/interface/IClaimTopicsRegistry.sol +101 -0
  135. package/packages/vendor/erc-3643/registry/interface/IIdentityRegistry.sol +251 -0
  136. package/packages/vendor/erc-3643/registry/interface/IIdentityRegistryStorage.sol +191 -0
  137. package/packages/vendor/erc-3643/registry/interface/ITrustedIssuersRegistry.sol +161 -0
  138. package/packages/vendor/erc-3643/token/IToken.sol +457 -0
  139. package/packages/vendor/onchain-id/interface/IClaimIssuer.sol +53 -0
  140. package/packages/vendor/onchain-id/interface/IERC734.sol +110 -0
  141. package/packages/vendor/onchain-id/interface/IERC735.sol +105 -0
  142. package/packages/vendor/onchain-id/interface/IIdentity.sol +26 -0
  143. package/packages/vendor/onchain-id/interface/IImplementationAuthority.sol +21 -0
  144. package/remappings.txt +6 -0
  145. package/script/DeployComplianceTokenERC20.s.sol +191 -0
  146. package/script/DeployComplianceTokenERC3643.s.sol +208 -0
  147. package/script/DeploySimpleComplianceToken.s.sol +38 -0
  148. package/script/getting_started/DeployGettingStarted.s.sol +74 -0
  149. package/script/getting_started/advanced/DeployAdvancedGettingStarted.s.sol +332 -0
  150. package/script/getting_started/advanced/DeploySanctionsList.s.sol +26 -0
@@ -0,0 +1,159 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {IPolicyEngine, PolicyEngine} from "@chainlink/policy-management/core/PolicyEngine.sol";
5
+ import {ERC20TransferExtractor} from "@chainlink/policy-management/extractors/ERC20TransferExtractor.sol";
6
+ import {BypassPolicy} from "@chainlink/policy-management/policies/BypassPolicy.sol";
7
+ import {MockToken} from "../helpers/MockToken.sol";
8
+ import {ERC3643MintBurnExtractor} from "@chainlink/policy-management/extractors/ERC3643MintBurnExtractor.sol";
9
+ import {BaseProxyTest} from "../helpers/BaseProxyTest.sol";
10
+
11
+ contract BypassPolicyTest is BaseProxyTest {
12
+ PolicyEngine public policyEngine;
13
+ MockToken public token;
14
+ BypassPolicy public bypassPolicy;
15
+ address public deployer;
16
+ address public account;
17
+ address public recipient;
18
+
19
+ function setUp() public {
20
+ deployer = makeAddr("deployer");
21
+ account = makeAddr("account");
22
+ recipient = makeAddr("recipient");
23
+
24
+ vm.startPrank(deployer, deployer);
25
+
26
+ policyEngine = _deployPolicyEngine(false, deployer);
27
+
28
+ BypassPolicy bypassPolicyImpl = new BypassPolicy();
29
+ bypassPolicy = BypassPolicy(_deployPolicy(address(bypassPolicyImpl), address(policyEngine), deployer, ""));
30
+ // add account by default
31
+ bypassPolicy.allowAddress(account);
32
+
33
+ token = MockToken(_deployMockToken(address(policyEngine)));
34
+
35
+ // set up the bypassPolicy to check the recipient and origin address of token transfers
36
+ ERC20TransferExtractor transferExtractor = new ERC20TransferExtractor();
37
+ bytes32[] memory transferPolicyParams = new bytes32[](2);
38
+ transferPolicyParams[0] = transferExtractor.PARAM_TO();
39
+ transferPolicyParams[1] = transferExtractor.PARAM_FROM();
40
+ policyEngine.setExtractor(MockToken.transfer.selector, address(transferExtractor));
41
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(bypassPolicy), transferPolicyParams);
42
+ // set up the bypassPolicy to check the mint account (single account)
43
+ ERC3643MintBurnExtractor mintBurnExtractor = new ERC3643MintBurnExtractor();
44
+ bytes32[] memory mintPolicyParams = new bytes32[](1);
45
+ mintPolicyParams[0] = mintBurnExtractor.PARAM_ACCOUNT();
46
+ policyEngine.setExtractor(MockToken.mint.selector, address(mintBurnExtractor));
47
+ policyEngine.addPolicy(address(token), MockToken.mint.selector, address(bypassPolicy), mintPolicyParams);
48
+ }
49
+
50
+ function test_allowAddress_succeeds() public {
51
+ vm.startPrank(deployer, deployer);
52
+
53
+ // add the address to the allow list
54
+ bypassPolicy.allowAddress(recipient);
55
+ vm.assertEq(bypassPolicy.addressAllowed(recipient), true);
56
+ }
57
+
58
+ function test_allowAddress_alreadyInList_fails() public {
59
+ vm.startPrank(deployer, deployer);
60
+
61
+ // add the address to the bypass list (setup and sanity check)
62
+ bypassPolicy.allowAddress(recipient);
63
+ vm.assertEq(bypassPolicy.addressAllowed(recipient), true);
64
+
65
+ // add the address to the bypass list again (reverts)
66
+ vm.expectRevert("Account already in bypass list");
67
+ bypassPolicy.allowAddress(recipient);
68
+ }
69
+
70
+ function test_disallowAddress_succeeds() public {
71
+ vm.startPrank(deployer, deployer);
72
+
73
+ // add the address to the bypass list (setup and sanity check)
74
+ bypassPolicy.allowAddress(recipient);
75
+ vm.assertEq(bypassPolicy.addressAllowed(recipient), true);
76
+
77
+ // remove the address from the bypass list
78
+ bypassPolicy.disallowAddress(recipient);
79
+ vm.assertEq(bypassPolicy.addressAllowed(recipient), false);
80
+ }
81
+
82
+ function test_disallowAddress_notInList_fails() public {
83
+ vm.startPrank(deployer, deployer);
84
+
85
+ // remove the address from the bypass list (reverts)
86
+ vm.expectRevert("Account not in bypass list");
87
+ bypassPolicy.disallowAddress(recipient);
88
+ }
89
+
90
+ function test_transfer_inList_succeeds() public {
91
+ vm.startPrank(deployer, deployer);
92
+
93
+ // add the recipient to the bypass list
94
+ bypassPolicy.allowAddress(recipient);
95
+ vm.assertEq(bypassPolicy.addressAllowed(recipient), true);
96
+
97
+ vm.startPrank(account, account);
98
+
99
+ // transfer from address to recipient
100
+ token.transfer(recipient, 100);
101
+ vm.assertEq(token.balanceOf(recipient), 100);
102
+ }
103
+
104
+ function test_transfer_notInList_defaultReject_fails() public {
105
+ vm.startPrank(account, account);
106
+
107
+ // transfer from address to recipient (reverts)
108
+ vm.expectRevert(_encodeRejectedRevert(0, address(0), "no policy allowed the action and default is reject"));
109
+ token.transfer(recipient, 100);
110
+ }
111
+
112
+ function test_transfer_removedFromList_defaultReject_fails() public {
113
+ // add the address to the bypass list (setup)
114
+ vm.startPrank(deployer, deployer);
115
+ bypassPolicy.allowAddress(recipient);
116
+
117
+ // transfer from address to recipient (sanity check)
118
+ vm.startPrank(account, account);
119
+ token.transfer(recipient, 100);
120
+ vm.assertEq(token.balanceOf(recipient), 100);
121
+
122
+ // remove from the bypass list
123
+ vm.startPrank(deployer, deployer);
124
+ bypassPolicy.disallowAddress(recipient);
125
+
126
+ // transfer from address to recipient (should revert after removal)
127
+ vm.startPrank(account, account);
128
+ vm.expectRevert(_encodeRejectedRevert(0, address(0), "no policy allowed the action and default is reject"));
129
+ token.transfer(recipient, 100);
130
+ }
131
+
132
+ function test_mint_inList_success() public {
133
+ vm.startPrank(deployer, deployer);
134
+ token.mint(account, 100);
135
+ vm.assertEq(token.balanceOf(account), 100);
136
+ }
137
+
138
+ function test_mint_notInList_defaultReject_failure() public {
139
+ vm.startPrank(deployer, deployer);
140
+ vm.expectRevert(_encodeRejectedRevert(0, address(0), "no policy allowed the action and default is reject"));
141
+ token.mint(recipient, 100);
142
+ }
143
+
144
+ function test_misconfiguration_failure() public {
145
+ vm.startPrank(deployer);
146
+ // misconfigure the bypassPolicy to check burn operations (no accounts)
147
+ ERC3643MintBurnExtractor mintBurnExtractor = new ERC3643MintBurnExtractor();
148
+ policyEngine.setExtractor(MockToken.burn.selector, address(mintBurnExtractor));
149
+ policyEngine.addPolicy(address(token), MockToken.burn.selector, address(bypassPolicy), new bytes32[](0));
150
+
151
+ bytes memory error = abi.encodeWithSignature("Error(string)", "expected at least 1 parameter");
152
+ vm.expectRevert(
153
+ abi.encodeWithSelector(
154
+ IPolicyEngine.PolicyRunError.selector, MockToken.burn.selector, address(bypassPolicy), error
155
+ )
156
+ );
157
+ token.burn(account, 100);
158
+ }
159
+ }
@@ -0,0 +1,307 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {IPolicyEngine} from "@chainlink/policy-management/interfaces/IPolicyEngine.sol";
5
+ import {PolicyEngine} from "@chainlink/policy-management/core/PolicyEngine.sol";
6
+ import {IntervalPolicy} from "@chainlink/policy-management/policies/IntervalPolicy.sol";
7
+ import {MockToken} from "../helpers/MockToken.sol";
8
+ import {BaseProxyTest} from "../helpers/BaseProxyTest.sol";
9
+
10
+ contract IntervalPolicyTest is BaseProxyTest {
11
+ PolicyEngine public policyEngine;
12
+ IntervalPolicy public intervalPolicy;
13
+ MockToken public token;
14
+ address public deployer;
15
+ address public recipient;
16
+ uint256 public OFFSET_TIMESTAMP = 1737470407; // Tue Jan 21 2025 14:40:07
17
+
18
+ function setUp() public {
19
+ deployer = makeAddr("deployer");
20
+ recipient = makeAddr("recipient");
21
+
22
+ vm.startPrank(deployer);
23
+
24
+ policyEngine = _deployPolicyEngine(true, deployer);
25
+
26
+ token = MockToken(_deployMockToken(address(policyEngine)));
27
+
28
+ IntervalPolicy intervalPolicyImpl = new IntervalPolicy();
29
+ bytes memory configParamBytes = abi.encode(
30
+ 11, // start slot
31
+ 17, // end slot
32
+ 1 hours, // slot duration
33
+ 24, // cycle size (24 slots for 24 hours)
34
+ 0 // cycle offset
35
+ );
36
+ intervalPolicy =
37
+ IntervalPolicy(_deployPolicy(address(intervalPolicyImpl), address(policyEngine), deployer, configParamBytes));
38
+
39
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(intervalPolicy), new bytes32[](0));
40
+ vm.warp(OFFSET_TIMESTAMP);
41
+ }
42
+
43
+ function generateTimestampForTargetHour(uint256 targetHour) private view returns (uint256) {
44
+ uint256 currentTimestamp = block.timestamp;
45
+ uint256 currentHour = (currentTimestamp / 3600) % 24;
46
+ if (targetHour > currentHour) {
47
+ uint256 hoursToAdd = targetHour - currentHour;
48
+ return currentTimestamp + (hoursToAdd * 3600);
49
+ }
50
+
51
+ uint256 hoursToDecrease = currentHour - targetHour;
52
+ return currentTimestamp - (hoursToDecrease * 3600);
53
+ }
54
+
55
+ function generateTimestampForTargetDayAndHour(uint256 targetDay, uint256 targetHour) private view returns (uint256) {
56
+ uint256 currentTimestamp = block.timestamp;
57
+ uint256 timestampWithHour = generateTimestampForTargetHour(targetHour);
58
+ uint256 currentDay = (currentTimestamp / 86400 + 4) % 7;
59
+ uint256 daysToAdd = (targetDay + 7 - currentDay) % 7;
60
+ uint256 newTimestamp = timestampWithHour + (daysToAdd * 86400);
61
+ return newTimestamp;
62
+ }
63
+
64
+ function test_transfer_timeWithinInterval_succeeds() public {
65
+ uint256 timestamp = generateTimestampForTargetHour(16);
66
+ vm.warp(timestamp);
67
+ token.transfer(recipient, 100);
68
+
69
+ assert(token.balanceOf(recipient) == 100);
70
+ }
71
+
72
+ function test_transfer_timeExtractorExtractsTimeBelowLowerBoundInterval_reverts() public {
73
+ uint256 timestamp = generateTimestampForTargetHour(10);
74
+ vm.warp(timestamp);
75
+ vm.expectRevert(
76
+ _encodeRejectedRevert(
77
+ MockToken.transfer.selector, address(intervalPolicy), "execution outside allowed time interval"
78
+ )
79
+ );
80
+ token.transfer(recipient, 100);
81
+ }
82
+
83
+ function test_transfer_timeExtractorExtractsTimeAboveUpperBoundInterval_reverts() public {
84
+ uint256 timestamp = generateTimestampForTargetHour(18);
85
+ vm.warp(timestamp);
86
+ vm.expectRevert(
87
+ _encodeRejectedRevert(
88
+ MockToken.transfer.selector, address(intervalPolicy), "execution outside allowed time interval"
89
+ )
90
+ );
91
+ token.transfer(recipient, 100);
92
+ }
93
+
94
+ function test_transfer_timeExtractorWithTwoPoliciesExtractsTimeWithinInterval_succeeds() public {
95
+ vm.startPrank(deployer);
96
+ IntervalPolicy dayIntervalPolicyImpl = new IntervalPolicy();
97
+ bytes memory configParamBytes = abi.encode(1, 6, 1 days, 7, 4);
98
+ IntervalPolicy dayIntervalPolicy =
99
+ IntervalPolicy(_deployPolicy(address(dayIntervalPolicyImpl), address(policyEngine), deployer, configParamBytes));
100
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(dayIntervalPolicy), new bytes32[](0));
101
+
102
+ uint256 timestamp = generateTimestampForTargetDayAndHour(2, 13);
103
+ vm.warp(timestamp);
104
+ token.transfer(recipient, 100);
105
+
106
+ assert(token.balanceOf(recipient) == 100);
107
+ }
108
+
109
+ function test_transfer_timeExtractorWithTwoPoliciesHourBelowLowerBoundInterval_reverts() public {
110
+ vm.startPrank(deployer);
111
+ IntervalPolicy dayIntervalPolicyImpl = new IntervalPolicy();
112
+ bytes memory configParamBytes = abi.encode(1, 6, 1 days, 7, 4);
113
+ IntervalPolicy dayIntervalPolicy =
114
+ IntervalPolicy(_deployPolicy(address(dayIntervalPolicyImpl), address(policyEngine), deployer, configParamBytes));
115
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(dayIntervalPolicy), new bytes32[](0));
116
+
117
+ uint256 timestamp = generateTimestampForTargetDayAndHour(2, 10);
118
+ vm.warp(timestamp);
119
+ vm.expectRevert(
120
+ _encodeRejectedRevert(
121
+ MockToken.transfer.selector, address(intervalPolicy), "execution outside allowed time interval"
122
+ )
123
+ );
124
+ token.transfer(recipient, 100);
125
+ }
126
+
127
+ function test_transfer_timeExtractorWithTwoPoliciesHourAboveUpperBoundInterval_reverts() public {
128
+ vm.startPrank(deployer);
129
+ IntervalPolicy dayIntervalPolicyImpl = new IntervalPolicy();
130
+ bytes memory configParamBytes = abi.encode(1, 6, 1 days, 7, 4);
131
+ IntervalPolicy dayIntervalPolicy =
132
+ IntervalPolicy(_deployPolicy(address(dayIntervalPolicyImpl), address(policyEngine), deployer, configParamBytes));
133
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(dayIntervalPolicy), new bytes32[](0));
134
+
135
+ uint256 timestamp = generateTimestampForTargetDayAndHour(2, 18);
136
+ vm.warp(timestamp);
137
+ vm.expectRevert(
138
+ _encodeRejectedRevert(
139
+ MockToken.transfer.selector, address(intervalPolicy), "execution outside allowed time interval"
140
+ )
141
+ );
142
+ token.transfer(recipient, 100);
143
+ }
144
+
145
+ function test_transfer_timeExtractorWithTwoPoliciesDayBelowLowerBoundInterval_reverts() public {
146
+ vm.startPrank(deployer);
147
+ IntervalPolicy dayIntervalPolicyImpl = new IntervalPolicy();
148
+ bytes memory configParamBytes = abi.encode(1, 6, 1 days, 7, 4);
149
+ IntervalPolicy dayIntervalPolicy =
150
+ IntervalPolicy(_deployPolicy(address(dayIntervalPolicyImpl), address(policyEngine), deployer, configParamBytes));
151
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(dayIntervalPolicy), new bytes32[](0));
152
+
153
+ uint256 timestamp = generateTimestampForTargetDayAndHour(0, 14);
154
+ vm.warp(timestamp);
155
+ vm.expectRevert(
156
+ _encodeRejectedRevert(
157
+ MockToken.transfer.selector, address(dayIntervalPolicy), "execution outside allowed time interval"
158
+ )
159
+ );
160
+ token.transfer(recipient, 100);
161
+ }
162
+
163
+ function test_transfer_timeExtractorWithTwoPoliciesDayAboveUpperBoundInterval_reverts() public {
164
+ vm.startPrank(deployer);
165
+ IntervalPolicy dayIntervalPolicyImpl = new IntervalPolicy();
166
+ bytes memory configParamBytes = abi.encode(1, 6, 1 days, 7, 4);
167
+ IntervalPolicy dayIntervalPolicy =
168
+ IntervalPolicy(_deployPolicy(address(dayIntervalPolicyImpl), address(policyEngine), deployer, configParamBytes));
169
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(dayIntervalPolicy), new bytes32[](0));
170
+
171
+ uint256 timestamp = generateTimestampForTargetDayAndHour(6, 14);
172
+ vm.warp(timestamp);
173
+ vm.expectRevert(
174
+ _encodeRejectedRevert(
175
+ MockToken.transfer.selector, address(dayIntervalPolicy), "execution outside allowed time interval"
176
+ )
177
+ );
178
+ token.transfer(recipient, 100);
179
+ }
180
+
181
+ function test_setUpEndGreaterThanTimePeriodWindow_reverts() public {
182
+ vm.startPrank(deployer);
183
+ vm.expectRevert("End slot must be greater than start slot");
184
+ intervalPolicy.setEndSlot(2);
185
+ }
186
+
187
+ function test_configure_emitsAllEvents() public {
188
+ vm.startPrank(deployer);
189
+
190
+ IntervalPolicy intervalPolicyImpl = new IntervalPolicy();
191
+ bytes memory configParamBytes = abi.encode(
192
+ 5, // start slot
193
+ 15, // end slot
194
+ 2 hours, // slot duration
195
+ 24, // cycle size
196
+ 2 // cycle offset
197
+ );
198
+
199
+ vm.expectEmit(true, true, true, true);
200
+ emit IntervalPolicy.StartSlotSet(5);
201
+ vm.expectEmit(true, true, true, true);
202
+ emit IntervalPolicy.EndSlotSet(15);
203
+ vm.expectEmit(true, true, true, true);
204
+ emit IntervalPolicy.CycleParametersSet(2 hours, 24, 2);
205
+
206
+ IntervalPolicy(_deployPolicy(address(intervalPolicyImpl), address(policyEngine), deployer, configParamBytes));
207
+ }
208
+
209
+ function test_setStartSlot_emitsEvent() public {
210
+ vm.startPrank(deployer);
211
+
212
+ vm.expectEmit(true, true, true, true);
213
+ emit IntervalPolicy.StartSlotSet(5);
214
+ intervalPolicy.setStartSlot(5);
215
+
216
+ assertEq(intervalPolicy.getStartSlot(), 5);
217
+ }
218
+
219
+ function test_setEndSlot_emitsEvent() public {
220
+ vm.startPrank(deployer);
221
+
222
+ vm.expectEmit(true, true, true, true);
223
+ emit IntervalPolicy.EndSlotSet(20);
224
+ intervalPolicy.setEndSlot(20);
225
+
226
+ assertEq(intervalPolicy.getEndSlot(), 20);
227
+ }
228
+
229
+ function test_setCycleParameters_emitsEvent() public {
230
+ vm.startPrank(deployer);
231
+
232
+ intervalPolicy.setEndSlot(23);
233
+
234
+ vm.expectEmit(true, true, true, true);
235
+ emit IntervalPolicy.CycleParametersSet(30 minutes, 48, 5);
236
+ intervalPolicy.setCycleParameters(30 minutes, 48, 5);
237
+
238
+ (uint256 slotDuration, uint256 cycleSize, uint256 cycleOffset) = intervalPolicy.getCycleParameters();
239
+ assertEq(slotDuration, 30 minutes);
240
+ assertEq(cycleSize, 48);
241
+ assertEq(cycleOffset, 5);
242
+ }
243
+
244
+ function test_canSetEndSlotEqualToCycleSize_succeeds() public {
245
+ vm.startPrank(deployer);
246
+
247
+ IntervalPolicy lastSlotPolicyImpl = new IntervalPolicy();
248
+ bytes memory configParamBytes = abi.encode(5, 12, 1 hours, 12, 0);
249
+
250
+ IntervalPolicy lastSlotPolicy =
251
+ IntervalPolicy(_deployPolicy(address(lastSlotPolicyImpl), address(policyEngine), deployer, configParamBytes));
252
+
253
+ assertEq(lastSlotPolicy.getStartSlot(), 5);
254
+ assertEq(lastSlotPolicy.getEndSlot(), 12);
255
+ (, uint256 cycleSize,) = lastSlotPolicy.getCycleParameters();
256
+ assertEq(cycleSize, 12);
257
+ }
258
+
259
+ function test_firstSlotAndlastSlotExecution_succeds() public {
260
+ vm.startPrank(deployer);
261
+
262
+ policyEngine.removePolicy(address(token), MockToken.transfer.selector, address(intervalPolicy));
263
+
264
+ IntervalPolicy lastSlotPolicyImpl = new IntervalPolicy();
265
+ bytes memory configParamBytes = abi.encode(
266
+ 5, // start slot
267
+ 12, // end slot (equal to cycle size - now allowed)
268
+ 1 hours, // slot duration
269
+ 12, // cycle size
270
+ 0 // cycle offset
271
+ );
272
+
273
+ IntervalPolicy lastSlotPolicy =
274
+ IntervalPolicy(_deployPolicy(address(lastSlotPolicyImpl), address(policyEngine), deployer, configParamBytes));
275
+
276
+ assertEq(lastSlotPolicy.getStartSlot(), 5);
277
+ assertEq(lastSlotPolicy.getEndSlot(), 12);
278
+ (, uint256 cycleSize,) = lastSlotPolicy.getCycleParameters();
279
+ assertEq(cycleSize, 12);
280
+
281
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(lastSlotPolicy), new bytes32[](0));
282
+
283
+ uint256 baseTimestamp = (12 * 3600);
284
+
285
+ uint256 slot5Timestamp = baseTimestamp + (5 * 3600);
286
+ vm.warp(slot5Timestamp);
287
+
288
+ token.transfer(recipient, 50);
289
+ assertEq(token.balanceOf(recipient), 50);
290
+
291
+ uint256 slot4Timestamp = baseTimestamp + (4 * 3600);
292
+ vm.warp(slot4Timestamp);
293
+
294
+ vm.expectRevert(
295
+ _encodeRejectedRevert(
296
+ MockToken.transfer.selector, address(lastSlotPolicy), "execution outside allowed time interval"
297
+ )
298
+ );
299
+ token.transfer(recipient, 25);
300
+
301
+ uint256 slot11Timestamp = baseTimestamp + (11 * 3600);
302
+ vm.warp(slot11Timestamp);
303
+
304
+ token.transfer(recipient, 100);
305
+ assertEq(token.balanceOf(recipient), 150);
306
+ }
307
+ }
@@ -0,0 +1,54 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {IPolicyEngine, PolicyEngine} from "@chainlink/policy-management/core/PolicyEngine.sol";
5
+ import {MaxPolicy} from "@chainlink/policy-management/policies/MaxPolicy.sol";
6
+ import {ERC20TransferExtractor} from "@chainlink/policy-management/extractors/ERC20TransferExtractor.sol";
7
+ import {MockToken} from "../helpers/MockToken.sol";
8
+ import {BaseProxyTest} from "../helpers/BaseProxyTest.sol";
9
+
10
+ contract MaxPolicyTest is BaseProxyTest {
11
+ PolicyEngine public policyEngine;
12
+ MockToken public token;
13
+ MaxPolicy public policy;
14
+ address public deployer;
15
+ address public recipient;
16
+
17
+ function setUp() public {
18
+ deployer = makeAddr("deployer");
19
+ recipient = makeAddr("recipient");
20
+
21
+ vm.startPrank(deployer);
22
+
23
+ policyEngine = _deployPolicyEngine(true, deployer);
24
+
25
+ MaxPolicy policyImpl = new MaxPolicy();
26
+ policy = MaxPolicy(_deployPolicy(address(policyImpl), address(policyEngine), deployer, abi.encode(100)));
27
+ ERC20TransferExtractor extractor = new ERC20TransferExtractor();
28
+ bytes32[] memory parameterOutputFormat = new bytes32[](1);
29
+ parameterOutputFormat[0] = extractor.PARAM_AMOUNT();
30
+
31
+ token = MockToken(_deployMockToken(address(policyEngine)));
32
+
33
+ policyEngine.setExtractor(MockToken.transfer.selector, address(extractor));
34
+
35
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(policy), parameterOutputFormat);
36
+ }
37
+
38
+ function test_setMax_success() public {
39
+ policy.setMax(200);
40
+ vm.assertEq(policy.getMax(), 200);
41
+ token.transfer(recipient, 200);
42
+ vm.assertEq(token.balanceOf(recipient), 200);
43
+ }
44
+
45
+ function test_transfer_success() public {
46
+ token.transfer(recipient, 100);
47
+ vm.assertEq(token.balanceOf(recipient), 100);
48
+ }
49
+
50
+ function test_transfer_overMax_reverts() public {
51
+ vm.expectRevert(_encodeRejectedRevert(MockToken.transfer.selector, address(policy), "amount exceeds maximum limit"));
52
+ token.transfer(recipient, 200);
53
+ }
54
+ }
@@ -0,0 +1,95 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {IPolicyEngine, PolicyEngine} from "@chainlink/policy-management/core/PolicyEngine.sol";
5
+ import {OnlyAuthorizedSenderPolicy} from "@chainlink/policy-management/policies/OnlyAuthorizedSenderPolicy.sol";
6
+ import {MockToken} from "../helpers/MockToken.sol";
7
+ import {BaseProxyTest} from "../helpers/BaseProxyTest.sol";
8
+
9
+ contract OnlyAuthorizedSenderPolicyTest is BaseProxyTest {
10
+ PolicyEngine public policyEngine;
11
+ MockToken public token;
12
+ OnlyAuthorizedSenderPolicy public policy;
13
+ address public deployer;
14
+ address public sender;
15
+ address public recipient;
16
+
17
+ function setUp() public {
18
+ deployer = makeAddr("deployer");
19
+ sender = makeAddr("sender");
20
+ recipient = makeAddr("recipient");
21
+
22
+ vm.startPrank(deployer, deployer);
23
+
24
+ policyEngine = _deployPolicyEngine(true, deployer);
25
+
26
+ OnlyAuthorizedSenderPolicy policyImpl = new OnlyAuthorizedSenderPolicy();
27
+ policy = OnlyAuthorizedSenderPolicy(_deployPolicy(address(policyImpl), address(policyEngine), deployer, ""));
28
+
29
+ token = MockToken(_deployMockToken(address(policyEngine)));
30
+
31
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(policy), new bytes32[](0));
32
+ }
33
+
34
+ function test_authorizeSender_succeeds() public {
35
+ vm.startPrank(deployer, deployer);
36
+
37
+ // add the sender to the authorized list
38
+ policy.authorizeSender(sender);
39
+ vm.assertEq(policy.senderAuthorized(sender), true);
40
+ }
41
+
42
+ function test_authorizeSender_alreadyInList_fails() public {
43
+ vm.startPrank(deployer, deployer);
44
+
45
+ // add the sender to the authorized list (setup and sanity check)
46
+ policy.authorizeSender(sender);
47
+ vm.assertEq(policy.senderAuthorized(sender), true);
48
+
49
+ // add the sender to the authorized list again (reverts)
50
+ vm.expectRevert("Account already in authorized list");
51
+ policy.authorizeSender(sender);
52
+ }
53
+
54
+ function test_unauthorizeSender_succeeds() public {
55
+ vm.startPrank(deployer, deployer);
56
+
57
+ // add the sender to the authorized list (setup and sanity check)
58
+ policy.authorizeSender(sender);
59
+ vm.assertEq(policy.senderAuthorized(sender), true);
60
+
61
+ // remove the sender from the authorized list
62
+ policy.unauthorizeSender(sender);
63
+ vm.assertEq(policy.senderAuthorized(sender), false);
64
+ }
65
+
66
+ function test_unauthorizeSender_notInList_fails() public {
67
+ vm.startPrank(deployer, deployer);
68
+
69
+ // remove the sender from the authorized list (reverts)
70
+ vm.expectRevert("Account not in authorized list");
71
+ policy.unauthorizeSender(sender);
72
+ }
73
+
74
+ function test_transfer_inList_succeeds() public {
75
+ vm.startPrank(deployer, deployer);
76
+
77
+ // add the sender to the allow list
78
+ policy.authorizeSender(sender);
79
+ vm.assertEq(policy.senderAuthorized(sender), true);
80
+
81
+ vm.startPrank(sender, sender);
82
+
83
+ // transfer from sender to recipient
84
+ token.transfer(recipient, 100);
85
+ vm.assertEq(token.balanceOf(recipient), 100);
86
+ }
87
+
88
+ function test_transfer_notInList_fails() public {
89
+ vm.startPrank(sender, sender);
90
+
91
+ // transfer from sender to recipient (reverts)
92
+ vm.expectRevert(_encodeRejectedRevert(MockToken.transfer.selector, address(policy), "sender is not authorized"));
93
+ token.transfer(recipient, 100);
94
+ }
95
+ }
@@ -0,0 +1,47 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.26;
3
+
4
+ import {BaseProxyTest} from "../helpers/BaseProxyTest.sol";
5
+ import {IPolicyEngine, PolicyEngine} from "@chainlink/policy-management/core/PolicyEngine.sol";
6
+ import {OnlyOwnerPolicy} from "@chainlink/policy-management/policies/OnlyOwnerPolicy.sol";
7
+ import {MockToken} from "../helpers/MockToken.sol";
8
+
9
+ contract OnlyOwnerPolicyTest is BaseProxyTest {
10
+ PolicyEngine public policyEngine;
11
+ MockToken public token;
12
+ OnlyOwnerPolicy public policy;
13
+ address public deployer;
14
+ address public account;
15
+ address public recipient;
16
+
17
+ function setUp() public {
18
+ deployer = makeAddr("deployer");
19
+ account = makeAddr("account");
20
+ recipient = makeAddr("recipient");
21
+
22
+ vm.startPrank(deployer, deployer);
23
+
24
+ policyEngine = PolicyEngine(_deployPolicyEngine(true, deployer));
25
+
26
+ OnlyOwnerPolicy policyImpl = new OnlyOwnerPolicy();
27
+ policy = OnlyOwnerPolicy(_deployPolicy(address(policyImpl), address(policyEngine), deployer, new bytes(0)));
28
+
29
+ token = MockToken(_deployMockToken(address(policyEngine)));
30
+
31
+ policyEngine.addPolicy(address(token), MockToken.transfer.selector, address(policy), new bytes32[](0));
32
+ }
33
+
34
+ function test_transfer_owner_success() public {
35
+ vm.startPrank(deployer, deployer);
36
+ token.transfer(recipient, 100);
37
+ vm.assertEq(token.balanceOf(recipient), 100);
38
+ }
39
+
40
+ function test_transfer_notOwner_reverts() public {
41
+ vm.startPrank(account, account);
42
+ vm.expectRevert(
43
+ _encodeRejectedRevert(MockToken.transfer.selector, address(policy), "caller is not the policy owner")
44
+ );
45
+ token.transfer(recipient, 100);
46
+ }
47
+ }