@bananapus/suckers-v6 0.0.1

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 (149) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +422 -0
  3. package/SECURITY.md +55 -0
  4. package/SKILLS.md +163 -0
  5. package/deployments/nana-suckers-v5/arbitrum/JBArbitrumSucker.json +1425 -0
  6. package/deployments/nana-suckers-v5/arbitrum/JBArbitrumSuckerDeployer.json +391 -0
  7. package/deployments/nana-suckers-v5/arbitrum/JBCCIPSucker.json +1479 -0
  8. package/deployments/nana-suckers-v5/arbitrum/JBCCIPSuckerDeployer.json +433 -0
  9. package/deployments/nana-suckers-v5/arbitrum/JBCCIPSuckerDeployer_1.json +433 -0
  10. package/deployments/nana-suckers-v5/arbitrum/JBCCIPSuckerDeployer_2.json +433 -0
  11. package/deployments/nana-suckers-v5/arbitrum/JBCCIPSucker_1.json +1479 -0
  12. package/deployments/nana-suckers-v5/arbitrum/JBCCIPSucker_2.json +1479 -0
  13. package/deployments/nana-suckers-v5/arbitrum/JBSuckerRegistry.json +690 -0
  14. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBArbitrumSucker.json +1425 -0
  15. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBArbitrumSuckerDeployer.json +391 -0
  16. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSucker.json +1479 -0
  17. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSuckerDeployer.json +433 -0
  18. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSuckerDeployer_1.json +433 -0
  19. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSuckerDeployer_2.json +433 -0
  20. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSucker_1.json +1479 -0
  21. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBCCIPSucker_2.json +1479 -0
  22. package/deployments/nana-suckers-v5/arbitrum_sepolia/JBSuckerRegistry.json +690 -0
  23. package/deployments/nana-suckers-v5/base/JBBaseSucker.json +1389 -0
  24. package/deployments/nana-suckers-v5/base/JBBaseSuckerDeployer.json +376 -0
  25. package/deployments/nana-suckers-v5/base/JBCCIPSucker.json +1483 -0
  26. package/deployments/nana-suckers-v5/base/JBCCIPSuckerDeployer.json +436 -0
  27. package/deployments/nana-suckers-v5/base/JBCCIPSuckerDeployer_1.json +436 -0
  28. package/deployments/nana-suckers-v5/base/JBCCIPSuckerDeployer_2.json +436 -0
  29. package/deployments/nana-suckers-v5/base/JBCCIPSucker_1.json +1483 -0
  30. package/deployments/nana-suckers-v5/base/JBCCIPSucker_2.json +1483 -0
  31. package/deployments/nana-suckers-v5/base/JBSuckerRegistry.json +694 -0
  32. package/deployments/nana-suckers-v5/base_sepolia/JBBaseSucker.json +1389 -0
  33. package/deployments/nana-suckers-v5/base_sepolia/JBBaseSuckerDeployer.json +376 -0
  34. package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSucker.json +1483 -0
  35. package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSuckerDeployer.json +436 -0
  36. package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSuckerDeployer_1.json +436 -0
  37. package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSuckerDeployer_2.json +436 -0
  38. package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSucker_1.json +1483 -0
  39. package/deployments/nana-suckers-v5/base_sepolia/JBCCIPSucker_2.json +1483 -0
  40. package/deployments/nana-suckers-v5/base_sepolia/JBSuckerRegistry.json +694 -0
  41. package/deployments/nana-suckers-v5/ethereum/JBArbitrumSucker.json +1429 -0
  42. package/deployments/nana-suckers-v5/ethereum/JBArbitrumSuckerDeployer.json +394 -0
  43. package/deployments/nana-suckers-v5/ethereum/JBBaseSucker.json +1389 -0
  44. package/deployments/nana-suckers-v5/ethereum/JBBaseSuckerDeployer.json +376 -0
  45. package/deployments/nana-suckers-v5/ethereum/JBCCIPSucker.json +1483 -0
  46. package/deployments/nana-suckers-v5/ethereum/JBCCIPSuckerDeployer.json +436 -0
  47. package/deployments/nana-suckers-v5/ethereum/JBCCIPSuckerDeployer_1.json +436 -0
  48. package/deployments/nana-suckers-v5/ethereum/JBCCIPSuckerDeployer_2.json +436 -0
  49. package/deployments/nana-suckers-v5/ethereum/JBCCIPSucker_1.json +1483 -0
  50. package/deployments/nana-suckers-v5/ethereum/JBCCIPSucker_2.json +1483 -0
  51. package/deployments/nana-suckers-v5/ethereum/JBOptimismSucker.json +1389 -0
  52. package/deployments/nana-suckers-v5/ethereum/JBOptimismSuckerDeployer.json +376 -0
  53. package/deployments/nana-suckers-v5/ethereum/JBSuckerRegistry.json +694 -0
  54. package/deployments/nana-suckers-v5/optimism/JBCCIPSucker.json +1479 -0
  55. package/deployments/nana-suckers-v5/optimism/JBCCIPSuckerDeployer.json +433 -0
  56. package/deployments/nana-suckers-v5/optimism/JBCCIPSuckerDeployer_1.json +433 -0
  57. package/deployments/nana-suckers-v5/optimism/JBCCIPSuckerDeployer_2.json +433 -0
  58. package/deployments/nana-suckers-v5/optimism/JBCCIPSucker_1.json +1479 -0
  59. package/deployments/nana-suckers-v5/optimism/JBCCIPSucker_2.json +1479 -0
  60. package/deployments/nana-suckers-v5/optimism/JBOptimismSucker.json +1385 -0
  61. package/deployments/nana-suckers-v5/optimism/JBOptimismSuckerDeployer.json +373 -0
  62. package/deployments/nana-suckers-v5/optimism/JBSuckerRegistry.json +690 -0
  63. package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSucker.json +1483 -0
  64. package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSuckerDeployer.json +436 -0
  65. package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSuckerDeployer_1.json +436 -0
  66. package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSuckerDeployer_2.json +436 -0
  67. package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSucker_1.json +1483 -0
  68. package/deployments/nana-suckers-v5/optimism_sepolia/JBCCIPSucker_2.json +1483 -0
  69. package/deployments/nana-suckers-v5/optimism_sepolia/JBOptimismSucker.json +1389 -0
  70. package/deployments/nana-suckers-v5/optimism_sepolia/JBOptimismSuckerDeployer.json +376 -0
  71. package/deployments/nana-suckers-v5/optimism_sepolia/JBSuckerRegistry.json +694 -0
  72. package/deployments/nana-suckers-v5/sepolia/JBArbitrumSucker.json +1429 -0
  73. package/deployments/nana-suckers-v5/sepolia/JBArbitrumSuckerDeployer.json +394 -0
  74. package/deployments/nana-suckers-v5/sepolia/JBBaseSucker.json +1389 -0
  75. package/deployments/nana-suckers-v5/sepolia/JBBaseSuckerDeployer.json +376 -0
  76. package/deployments/nana-suckers-v5/sepolia/JBCCIPSucker.json +1483 -0
  77. package/deployments/nana-suckers-v5/sepolia/JBCCIPSuckerDeployer.json +436 -0
  78. package/deployments/nana-suckers-v5/sepolia/JBCCIPSuckerDeployer_1.json +436 -0
  79. package/deployments/nana-suckers-v5/sepolia/JBCCIPSuckerDeployer_2.json +436 -0
  80. package/deployments/nana-suckers-v5/sepolia/JBCCIPSucker_1.json +1483 -0
  81. package/deployments/nana-suckers-v5/sepolia/JBCCIPSucker_2.json +1483 -0
  82. package/deployments/nana-suckers-v5/sepolia/JBOptimismSucker.json +1389 -0
  83. package/deployments/nana-suckers-v5/sepolia/JBOptimismSuckerDeployer.json +376 -0
  84. package/deployments/nana-suckers-v5/sepolia/JBSuckerRegistry.json +694 -0
  85. package/foundry.lock +11 -0
  86. package/foundry.toml +22 -0
  87. package/package.json +33 -0
  88. package/remappings.txt +1 -0
  89. package/script/Deploy.s.sol +506 -0
  90. package/script/helpers/SuckerDeploymentLib.sol +97 -0
  91. package/slither-ci.config.json +10 -0
  92. package/sphinx.lock +476 -0
  93. package/src/JBArbitrumSucker.sol +311 -0
  94. package/src/JBBaseSucker.sol +41 -0
  95. package/src/JBCCIPSucker.sol +303 -0
  96. package/src/JBOptimismSucker.sol +143 -0
  97. package/src/JBSucker.sol +1159 -0
  98. package/src/JBSuckerRegistry.sol +262 -0
  99. package/src/deployers/JBArbitrumSuckerDeployer.sol +86 -0
  100. package/src/deployers/JBBaseSuckerDeployer.sol +26 -0
  101. package/src/deployers/JBCCIPSuckerDeployer.sol +88 -0
  102. package/src/deployers/JBOptimismSuckerDeployer.sol +82 -0
  103. package/src/deployers/JBSuckerDeployer.sol +147 -0
  104. package/src/enums/JBAddToBalanceMode.sol +11 -0
  105. package/src/enums/JBLayer.sol +8 -0
  106. package/src/enums/JBSuckerState.sol +14 -0
  107. package/src/interfaces/IArbGatewayRouter.sol +11 -0
  108. package/src/interfaces/IArbL1GatewayRouter.sol +17 -0
  109. package/src/interfaces/IArbL2GatewayRouter.sol +14 -0
  110. package/src/interfaces/ICCIPRouter.sol +11 -0
  111. package/src/interfaces/IJBArbitrumSucker.sol +13 -0
  112. package/src/interfaces/IJBArbitrumSuckerDeployer.sol +12 -0
  113. package/src/interfaces/IJBCCIPSuckerDeployer.sol +15 -0
  114. package/src/interfaces/IJBOpSuckerDeployer.sol +11 -0
  115. package/src/interfaces/IJBOptimismSucker.sol +10 -0
  116. package/src/interfaces/IJBSucker.sol +144 -0
  117. package/src/interfaces/IJBSuckerDeployer.sol +40 -0
  118. package/src/interfaces/IJBSuckerExtended.sol +22 -0
  119. package/src/interfaces/IJBSuckerRegistry.sol +75 -0
  120. package/src/interfaces/IOPMessenger.sol +18 -0
  121. package/src/interfaces/IOPStandardBridge.sol +29 -0
  122. package/src/interfaces/IWrappedNativeToken.sol +13 -0
  123. package/src/libraries/ARBAddresses.sol +17 -0
  124. package/src/libraries/ARBChains.sol +11 -0
  125. package/src/libraries/CCIPHelper.sol +136 -0
  126. package/src/structs/JBClaim.sol +13 -0
  127. package/src/structs/JBInboxTreeRoot.sol +12 -0
  128. package/src/structs/JBLeaf.sol +14 -0
  129. package/src/structs/JBMessageRoot.sol +16 -0
  130. package/src/structs/JBOutboxTree.sol +18 -0
  131. package/src/structs/JBRemoteToken.sol +17 -0
  132. package/src/structs/JBSuckerDeployerConfig.sol +12 -0
  133. package/src/structs/JBSuckersPair.sol +11 -0
  134. package/src/structs/JBTokenMapping.sol +13 -0
  135. package/src/utils/MerkleLib.sol +1020 -0
  136. package/test/Fork.t.sol +514 -0
  137. package/test/InteropCompat.t.sol +676 -0
  138. package/test/SuckerAttacks.t.sol +509 -0
  139. package/test/SuckerDeepAttacks.t.sol +1563 -0
  140. package/test/mocks/ERC20Mock.sol +36 -0
  141. package/test/mocks/MockMessenger.sol +42 -0
  142. package/test/unit/arb.t.sol +28 -0
  143. package/test/unit/ccip_native_interop.t.sol +719 -0
  144. package/test/unit/ccip_refund.t.sol +234 -0
  145. package/test/unit/deployer.t.sol +475 -0
  146. package/test/unit/emergency.t.sol +305 -0
  147. package/test/unit/merkle.t.sol +212 -0
  148. package/test/unit/multi_chain_evolution.t.sol +622 -0
  149. package/test/unit/registry.t.sol +26 -0
@@ -0,0 +1,514 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.23;
3
+
4
+ import /* {*} from */ "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
5
+ import {MockPriceFeed} from "@bananapus/core-v6/test/mock/MockPriceFeed.sol";
6
+ import {IJBSucker} from "../src/interfaces/IJBSucker.sol";
7
+ import {IJBSuckerDeployer} from "../src/interfaces/IJBSuckerDeployer.sol";
8
+ import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
9
+ import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
10
+ import {IJBTokens} from "@bananapus/core-v6/src/interfaces/IJBTokens.sol";
11
+ import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
12
+ import {IJBCashOutTerminal} from "@bananapus/core-v6/src/interfaces/IJBCashOutTerminal.sol";
13
+ import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
14
+ import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
15
+ import {JBPermissionsData} from "@bananapus/core-v6/src/structs/JBPermissionsData.sol";
16
+ import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
17
+ import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
18
+ import {ICCIPRouter} from "src/interfaces/ICCIPRouter.sol";
19
+ import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
20
+ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
21
+
22
+ import {JBTokenMapping} from "../src/structs/JBTokenMapping.sol";
23
+ import {JBRemoteToken} from "../src/structs/JBRemoteToken.sol";
24
+ import {JBOutboxTree} from "../src/structs/JBOutboxTree.sol";
25
+ import {JBInboxTreeRoot} from "../src/structs/JBInboxTreeRoot.sol";
26
+ import {JBMessageRoot} from "../src/structs/JBMessageRoot.sol";
27
+ import {JBClaim} from "../src/structs/JBClaim.sol";
28
+ import {JBAddToBalanceMode} from "../src/enums/JBAddToBalanceMode.sol";
29
+ import {MerkleLib} from "../src/utils/MerkleLib.sol";
30
+
31
+ import "forge-std/Test.sol";
32
+ import {JBCCIPSuckerDeployer} from "src/deployers/JBCCIPSuckerDeployer.sol";
33
+ import {JBCCIPSucker} from "../src/JBCCIPSucker.sol";
34
+ import {BurnMintERC677Helper} from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
35
+ import {CCIPLocalSimulatorFork, Register} from "@chainlink/local/src/ccip/CCIPLocalSimulatorFork.sol";
36
+
37
+ import {JBClaim} from "../src/structs/JBClaim.sol";
38
+ import {JBLeaf} from "../src/structs/JBClaim.sol";
39
+ import {MerkleLib} from "../src/utils/MerkleLib.sol";
40
+ import {CCIPHelper} from "../src/libraries/CCIPHelper.sol";
41
+
42
+ contract CCIPSuckerForkedTests is TestBaseWorkflow, JBTest {
43
+ // CCIP Local Simulator Contracts
44
+ CCIPLocalSimulatorFork ccipLocalSimulatorFork;
45
+ BurnMintERC677Helper ccipBnM;
46
+ BurnMintERC677Helper ccipBnMArbSepolia;
47
+
48
+ // Re-used parameters for project/ruleset/sucker setups
49
+ JBRulesetMetadata _metadata;
50
+ JBAddToBalanceMode atbMode = JBAddToBalanceMode.ON_CLAIM;
51
+
52
+ // Sucker and token
53
+ JBCCIPSuckerDeployer suckerDeployer;
54
+ JBCCIPSuckerDeployer suckerDeployer2;
55
+ IJBSucker suckerGlobal;
56
+ IJBToken projectOneToken;
57
+
58
+ // Chain ids and selectors
59
+ uint256 sepoliaFork;
60
+ uint256 arbSepoliaFork;
61
+ uint64 arbSepoliaChainSelector = 3_478_487_238_524_512_106;
62
+ uint64 ethSepoliaChainSelector = 16_015_286_601_757_825_753;
63
+
64
+ // RPCs
65
+ string ETHEREUM_SEPOLIA_RPC_URL = vm.envOr("RPC_ETHEREUM_SEPOLIA", string("https://1rpc.io/sepolia"));
66
+ string ARBITRUM_SEPOLIA_RPC_URL =
67
+ vm.envOr("RPC_ARBITRUM_SEPOLIA", string("https://arbitrum-sepolia.gateway.tenderly.co"));
68
+
69
+ //*********************************************************************//
70
+ // ---------------------------- Setup parts -------------------------- //
71
+ //*********************************************************************//
72
+
73
+ function initL1AndUtils() public {
74
+ // Setup starts on sepolia fork
75
+ sepoliaFork = vm.createSelectFork(ETHEREUM_SEPOLIA_RPC_URL);
76
+
77
+ ccipLocalSimulatorFork = new CCIPLocalSimulatorFork();
78
+ vm.makePersistent(address(ccipLocalSimulatorFork));
79
+ Register.NetworkDetails memory sepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
80
+
81
+ ccipBnM = BurnMintERC677Helper(sepoliaNetworkDetails.ccipBnMAddress);
82
+ vm.label(address(ccipBnM), "bnmEthSep");
83
+ vm.makePersistent(address(ccipBnM));
84
+ }
85
+
86
+ function initMetadata() public {
87
+ _metadata = JBRulesetMetadata({
88
+ reservedPercent: JBConstants.MAX_RESERVED_PERCENT / 2, //50%
89
+ cashOutTaxRate: 0,
90
+ baseCurrency: uint32(uint160(address(JBConstants.NATIVE_TOKEN))),
91
+ pausePay: false,
92
+ pauseCreditTransfers: false,
93
+ allowOwnerMinting: true,
94
+ allowSetCustomToken: false,
95
+ allowTerminalMigration: false,
96
+ allowSetTerminals: false,
97
+ allowSetController: false,
98
+ allowAddAccountingContext: true,
99
+ allowAddPriceFeed: true,
100
+ ownerMustSendPayouts: false,
101
+ holdFees: false,
102
+ useTotalSurplusForCashOuts: true,
103
+ useDataHookForPay: false,
104
+ useDataHookForCashOut: false,
105
+ dataHook: address(0),
106
+ metadata: 0
107
+ });
108
+ }
109
+
110
+ function launchAndConfigureL1Project() public {
111
+ // Setup: terminal / project
112
+ // Package up the limits for the given terminal.
113
+ JBFundAccessLimitGroup[] memory _fundAccessLimitGroup = new JBFundAccessLimitGroup[](1);
114
+ {
115
+ // Specify a payout limit.
116
+ JBCurrencyAmount[] memory _payoutLimits = new JBCurrencyAmount[](0);
117
+ // _payoutLimits[0] =
118
+ // JBCurrencyAmount({amount: 10 * 10 ** 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))});
119
+
120
+ // Specify a surplus allowance.
121
+ JBCurrencyAmount[] memory _surplusAllowances = new JBCurrencyAmount[](1);
122
+ _surplusAllowances[0] =
123
+ JBCurrencyAmount({amount: 5 * 10 ** 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))});
124
+
125
+ _fundAccessLimitGroup[0] = JBFundAccessLimitGroup({
126
+ terminal: address(jbMultiTerminal()),
127
+ token: JBConstants.NATIVE_TOKEN,
128
+ payoutLimits: _payoutLimits,
129
+ surplusAllowances: _surplusAllowances
130
+ });
131
+ }
132
+
133
+ {
134
+ // Package up the ruleset configuration.
135
+ JBRulesetConfig[] memory _rulesetConfigurations = new JBRulesetConfig[](1);
136
+ _rulesetConfigurations[0].mustStartAtOrAfter = 0;
137
+ _rulesetConfigurations[0].duration = 0;
138
+ _rulesetConfigurations[0].weight = 1000 * 10 ** 18;
139
+ _rulesetConfigurations[0].weightCutPercent = 0;
140
+ _rulesetConfigurations[0].approvalHook = IJBRulesetApprovalHook(address(0));
141
+ _rulesetConfigurations[0].metadata = _metadata;
142
+ _rulesetConfigurations[0].splitGroups = new JBSplitGroup[](0);
143
+ _rulesetConfigurations[0].fundAccessLimitGroups = _fundAccessLimitGroup;
144
+
145
+ JBTerminalConfig[] memory _terminalConfigurations = new JBTerminalConfig[](1);
146
+ JBAccountingContext[] memory _tokensToAccept = new JBAccountingContext[](2);
147
+
148
+ _tokensToAccept[0] = JBAccountingContext({
149
+ token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
150
+ });
151
+
152
+ _tokensToAccept[1] = JBAccountingContext({
153
+ token: address(ccipBnM), decimals: 18, currency: uint32(uint160(address(ccipBnM)))
154
+ });
155
+
156
+ _terminalConfigurations[0] =
157
+ JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: _tokensToAccept});
158
+
159
+ vm.expectCall(address(ccipBnM), abi.encodeWithSelector(IERC20Metadata.decimals.selector));
160
+
161
+ // Create a first project to collect fees.
162
+ jbController()
163
+ .launchProjectFor({
164
+ owner: multisig(),
165
+ projectUri: "whatever",
166
+ rulesetConfigurations: _rulesetConfigurations,
167
+ terminalConfigurations: _terminalConfigurations, // Set terminals to receive fees.
168
+ memo: ""
169
+ });
170
+
171
+ // Setup an erc20 for the project
172
+ projectOneToken = jbController().deployERC20For(1, "SuckerToken", "SOOK", bytes32(0));
173
+
174
+ // Add a price-feed to reconcile pays and cash outs with our test token
175
+ MockPriceFeed _priceFeedNativeTest = new MockPriceFeed(100 * 10 ** 18, 18); // 2000 test token == 1 native
176
+ // token
177
+ vm.label(address(_priceFeedNativeTest), "Mock Price Feed Native-ccipBnM");
178
+
179
+ vm.startPrank(address(jbController()));
180
+ IJBPrices(jbPrices())
181
+ .addPriceFeedFor({
182
+ projectId: 1,
183
+ pricingCurrency: uint32(uint160(address(ccipBnM))),
184
+ unitCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
185
+ feed: IJBPriceFeed(_priceFeedNativeTest)
186
+ });
187
+ }
188
+ }
189
+
190
+ function initL2AndUtils() public {
191
+ // Create and select our L2 fork- preparing to deploy our project and sucker
192
+ arbSepoliaFork = vm.createSelectFork(ARBITRUM_SEPOLIA_RPC_URL);
193
+
194
+ // Get the corresponding remote token and label it for convenience in reading any trace in console
195
+ Register.NetworkDetails memory arbSepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(421_614);
196
+
197
+ // This is a faux token helper provided to emulate token bridges of the burn and mint type via CCIP
198
+ ccipBnMArbSepolia = BurnMintERC677Helper(arbSepoliaNetworkDetails.ccipBnMAddress);
199
+ vm.label(address(ccipBnMArbSepolia), "bnmArbSep");
200
+ }
201
+
202
+ function launchAndConfigureL2Project() public {
203
+ JBFundAccessLimitGroup[] memory _fundAccessLimitGroup = new JBFundAccessLimitGroup[](1);
204
+ {
205
+ // Package up the ruleset configuration.
206
+ JBRulesetConfig[] memory _rulesetConfigurations = new JBRulesetConfig[](1);
207
+ _rulesetConfigurations[0].mustStartAtOrAfter = 0;
208
+ _rulesetConfigurations[0].duration = 0;
209
+ _rulesetConfigurations[0].weight = 1000 * 10 ** 18;
210
+ _rulesetConfigurations[0].weightCutPercent = 0;
211
+ _rulesetConfigurations[0].approvalHook = IJBRulesetApprovalHook(address(0));
212
+ _rulesetConfigurations[0].metadata = _metadata;
213
+ _rulesetConfigurations[0].splitGroups = new JBSplitGroup[](0);
214
+ _rulesetConfigurations[0].fundAccessLimitGroups = _fundAccessLimitGroup;
215
+
216
+ JBTerminalConfig[] memory _terminalConfigurations = new JBTerminalConfig[](1);
217
+ JBAccountingContext[] memory _tokensToAccept = new JBAccountingContext[](2);
218
+
219
+ _tokensToAccept[0] = JBAccountingContext({
220
+ token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
221
+ });
222
+
223
+ _tokensToAccept[1] = JBAccountingContext({
224
+ token: address(ccipBnMArbSepolia), decimals: 18, currency: uint32(uint160(address(ccipBnMArbSepolia)))
225
+ });
226
+
227
+ _terminalConfigurations[0] =
228
+ JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: _tokensToAccept});
229
+
230
+ vm.expectCall(address(ccipBnMArbSepolia), abi.encodeWithSelector(IERC20Metadata.decimals.selector));
231
+
232
+ // Create a first project to collect fees.
233
+ jbController()
234
+ .launchProjectFor({
235
+ owner: multisig(),
236
+ projectUri: "whatever",
237
+ rulesetConfigurations: _rulesetConfigurations,
238
+ terminalConfigurations: _terminalConfigurations, // Set terminals to receive fees.
239
+ memo: ""
240
+ });
241
+ }
242
+ }
243
+
244
+ //*********************************************************************//
245
+ // ------------------------------- Setup ----------------------------- //
246
+ //*********************************************************************//
247
+
248
+ function setUp() public override {
249
+ // Create (and select) Sepolia fork and make simulator helper contracts persistent.
250
+ initL1AndUtils();
251
+
252
+ // Set metadata for the test projects to use.
253
+ initMetadata();
254
+
255
+ // run setup on our first fork (sepolia) so we have a JBV4 setup (deploys v4 contracts).
256
+ super.setUp();
257
+
258
+ vm.stopPrank();
259
+ vm.startPrank(address(0x1112222));
260
+ suckerDeployer = new JBCCIPSuckerDeployer(jbDirectory(), jbPermissions(), jbTokens(), address(this), address(0));
261
+ vm.stopPrank();
262
+
263
+ // Set the remote chain as arb-sep, which also grabs the chain selector from CCIPHelper for deployer
264
+ suckerDeployer.setChainSpecificConstants(
265
+ 421_614, CCIPHelper.selectorOfChain(421_614), ICCIPRouter(CCIPHelper.routerOfChain(block.chainid))
266
+ );
267
+
268
+ // Deploy the singleton and configure it.
269
+ vm.startPrank(address(0x1112222));
270
+ JBCCIPSucker singleton = new JBCCIPSucker({
271
+ deployer: suckerDeployer,
272
+ directory: jbDirectory(),
273
+ permissions: jbPermissions(),
274
+ tokens: jbTokens(),
275
+ addToBalanceMode: JBAddToBalanceMode.MANUAL,
276
+ trustedForwarder: address(0)
277
+ });
278
+ vm.stopPrank();
279
+
280
+ suckerDeployer.configureSingleton(singleton);
281
+
282
+ // deploy our first sucker (on sepolia, the current fork, or "L1").
283
+ suckerGlobal = suckerDeployer.createForSender(1, "salty");
284
+ vm.label(address(suckerGlobal), "suckerGlobal");
285
+
286
+ // In-memory vars needed for setup
287
+ // Allow the sucker to mint- This permission array is also used in second project config toward the end of this
288
+ // setup.
289
+ uint8[] memory ids = new uint8[](1);
290
+ ids[0] = JBPermissionIds.MINT_TOKENS;
291
+
292
+ // Permissions data for setPermissionsFor().
293
+ JBPermissionsData memory perms =
294
+ JBPermissionsData({operator: address(suckerGlobal), projectId: 1, permissionIds: ids});
295
+
296
+ // Chain selectors of remote chains allowed by the suckers (bi-directional in this example).
297
+ uint64[] memory allowedChains = new uint64[](2);
298
+ allowedChains[0] = arbSepoliaChainSelector;
299
+ allowedChains[1] = ethSepoliaChainSelector;
300
+
301
+ // Allow our L1 sucker to mint.
302
+ vm.startPrank(multisig());
303
+ jbPermissions().setPermissionsFor(multisig(), perms);
304
+
305
+ // Launch and configure our project on L1 (selected fork is still sepolia).
306
+ launchAndConfigureL1Project();
307
+
308
+ // Sucker (on L1) now allows our intended chains and L1 setup is complete.
309
+ vm.stopPrank();
310
+
311
+ // Init our L2 fork and CCIP Local simulator utils for L2.
312
+ initL2AndUtils();
313
+
314
+ // Setup JBV4 on our forked L2 (arb-sep).
315
+ super.setUp();
316
+
317
+ vm.stopPrank();
318
+
319
+ vm.startPrank(address(0x1112222));
320
+ suckerDeployer2 =
321
+ new JBCCIPSuckerDeployer(jbDirectory(), jbPermissions(), jbTokens(), address(this), address(0));
322
+ vm.stopPrank();
323
+
324
+ suckerDeployer2.setChainSpecificConstants(
325
+ 11_155_111, CCIPHelper.selectorOfChain(11_155_111), ICCIPRouter(CCIPHelper.routerOfChain(block.chainid))
326
+ );
327
+
328
+ // Deploy the singleton and configure it.
329
+ vm.startPrank(address(0x1112222));
330
+ JBCCIPSucker singleton2 = new JBCCIPSucker({
331
+ deployer: suckerDeployer2,
332
+ directory: jbDirectory(),
333
+ permissions: jbPermissions(),
334
+ tokens: jbTokens(),
335
+ addToBalanceMode: JBAddToBalanceMode.MANUAL,
336
+ trustedForwarder: address(0)
337
+ });
338
+ vm.stopPrank();
339
+
340
+ suckerDeployer2.configureSingleton(singleton2);
341
+
342
+ // Deploy the sucker on L2.
343
+ suckerDeployer2.createForSender(1, "salty");
344
+
345
+ // Launch our project on L2.
346
+ vm.startPrank(multisig());
347
+ launchAndConfigureL2Project();
348
+
349
+ // Allow the L2 sucker to mint.
350
+ jbPermissions().setPermissionsFor(multisig(), perms);
351
+
352
+ // Enable intended chains for the L2 Sucker
353
+ vm.stopPrank();
354
+ }
355
+
356
+ //*********************************************************************//
357
+ // ------------------------------- Tests ----------------------------- //
358
+ //*********************************************************************//
359
+
360
+ function test_forkNativeTransfer() external {
361
+ // The pool is disabled for now, but functionality was confirmed in past runs.
362
+ // vm.skip(true);
363
+
364
+ // Declare test actors and parameters
365
+ address rootSender = makeAddr("rootSender");
366
+ address user = makeAddr("him");
367
+ uint256 amountToSend = 0.05 ether;
368
+ uint256 maxCashedOut = amountToSend / 2;
369
+
370
+ // Select our L1 fork to begin this test.
371
+ vm.selectFork(sepoliaFork);
372
+
373
+ // Give ourselves test tokens
374
+ vm.deal(user, amountToSend);
375
+
376
+ // Map the token
377
+ JBTokenMapping memory map = JBTokenMapping({
378
+ localToken: JBConstants.NATIVE_TOKEN,
379
+ minGas: 200_000,
380
+ remoteToken: bytes32(uint256(uint160(JBConstants.NATIVE_TOKEN))),
381
+ minBridgeAmount: 1
382
+ });
383
+
384
+ vm.prank(multisig());
385
+ suckerGlobal.mapToken(map);
386
+
387
+ // Let the terminal spend our test tokens so we can pay and receive project tokens
388
+ vm.startPrank(user);
389
+ // ccipBnM.approve(address(jbMultiTerminal()), amountToSend);
390
+
391
+ // receive 500 project tokens as a result
392
+ uint256 projectTokenAmount =
393
+ jbMultiTerminal().pay{value: amountToSend}(1, JBConstants.NATIVE_TOKEN, amountToSend, user, 0, "", "");
394
+
395
+ // Approve the sucker to use those project tokens received by the user (we are still pranked as user)
396
+ IERC20(address(projectOneToken)).approve(address(suckerGlobal), projectTokenAmount);
397
+
398
+ // Call prepare which uses our project tokens to retrieve (cash out) for our backing tokens (test token)
399
+ suckerGlobal.prepare(
400
+ projectTokenAmount, bytes32(uint256(uint160(user))), maxCashedOut, JBConstants.NATIVE_TOKEN
401
+ );
402
+ vm.stopPrank();
403
+
404
+ // Give the root sender some eth to pay the fees
405
+ vm.deal(rootSender, 1 ether);
406
+
407
+ // Initiates the bridging
408
+ vm.prank(rootSender);
409
+ suckerGlobal.toRemote{value: 1 ether}(JBConstants.NATIVE_TOKEN);
410
+
411
+ // Check outbox is cleared
412
+ uint256 outboxBalance = suckerGlobal.outboxOf(JBConstants.NATIVE_TOKEN).balance;
413
+ assertEq(outboxBalance, 0);
414
+
415
+ // Fees are paid but balance isn't zero (excess msg.value is returned)
416
+ assert(rootSender.balance < 1 ether);
417
+ assert(rootSender.balance > 0);
418
+
419
+ // Use CCIP local to initiate the transfer on the L2
420
+ ccipLocalSimulatorFork.switchChainAndRouteMessage(arbSepoliaFork);
421
+
422
+ // Check that the tokens were transferred
423
+ assertEq(address(suckerGlobal).balance, maxCashedOut);
424
+
425
+ // This is the most simple verification that messages are being sent and received though
426
+ // Meaning CCIP transferred the data to our sucker on L2's inbox
427
+ bytes32 inboxRoot = suckerGlobal.inboxOf(JBConstants.NATIVE_TOKEN).root;
428
+ assertNotEq(inboxRoot, bytes32(0));
429
+
430
+ // Ensure correct native value was sent.
431
+ assertEq(address(suckerGlobal).balance, maxCashedOut);
432
+ }
433
+
434
+ function test_forkTokenTransfer() external {
435
+ // Declare test actors and parameters
436
+ address rootSender = makeAddr("rootSender");
437
+ address user = makeAddr("him");
438
+ uint256 amountToSend = 100;
439
+ uint256 maxCashedOut = amountToSend / 2;
440
+
441
+ // Select our L1 fork to begin this test.
442
+ vm.selectFork(sepoliaFork);
443
+
444
+ // Give ourselves test tokens
445
+ ccipBnM.drip(address(user));
446
+
447
+ // Map the token
448
+ JBTokenMapping memory map = JBTokenMapping({
449
+ localToken: address(ccipBnM),
450
+ minGas: 200_000,
451
+ remoteToken: bytes32(uint256(uint160(address(ccipBnMArbSepolia)))),
452
+ minBridgeAmount: 1
453
+ });
454
+
455
+ vm.prank(multisig());
456
+ suckerGlobal.mapToken(map);
457
+
458
+ // Let the terminal spend our test tokens so we can pay and receive project tokens
459
+ vm.startPrank(user);
460
+ ccipBnM.approve(address(jbMultiTerminal()), amountToSend);
461
+
462
+ // receive 500 project tokens as a result
463
+ uint256 projectTokenAmount = jbMultiTerminal().pay(1, address(ccipBnM), amountToSend, user, 0, "", "");
464
+
465
+ // Approve the sucker to use those project tokens received by the user (we are still pranked as user)
466
+ IERC20(address(projectOneToken)).approve(address(suckerGlobal), projectTokenAmount);
467
+
468
+ // Call prepare which uses our project tokens to retrieve (cash out) for our backing tokens (test token)
469
+ suckerGlobal.prepare(projectTokenAmount, bytes32(uint256(uint160(user))), maxCashedOut, address(ccipBnM));
470
+ vm.stopPrank();
471
+
472
+ // Give the root sender some eth to pay the fees
473
+ vm.deal(rootSender, 1 ether);
474
+
475
+ // Initiates the bridging
476
+ vm.prank(rootSender);
477
+ suckerGlobal.toRemote{value: 1 ether}(address(ccipBnM));
478
+
479
+ // Fees are paid but balance isn't zero (excess msg.value is returned)
480
+ assert(rootSender.balance < 1 ether);
481
+ assert(rootSender.balance > 0);
482
+
483
+ // Check outbox is cleared
484
+ uint256 outboxBalance = suckerGlobal.outboxOf(address(ccipBnM)).balance;
485
+ assertEq(outboxBalance, 0);
486
+
487
+ // Use CCIP local to initiate the transfer on the L2
488
+ ccipLocalSimulatorFork.switchChainAndRouteMessage(arbSepoliaFork);
489
+
490
+ // Check that the tokens were transferred
491
+ assertEq(ccipBnMArbSepolia.balanceOf(address(suckerGlobal)), maxCashedOut);
492
+
493
+ // This is the most simple verification that messages are being sent and received though
494
+ // Meaning CCIP transferred the data to our sucker on L2's inbox
495
+ bytes32 inboxRoot = suckerGlobal.inboxOf(address(ccipBnMArbSepolia)).root;
496
+ assertNotEq(inboxRoot, bytes32(0));
497
+
498
+ // TODO: Maybe test claiming but it was working in previous version from another repo
499
+ // Setup claim data
500
+ /* JBLeaf memory _leaf = JBLeaf({
501
+ index: 1,
502
+ beneficiary: user,
503
+ projectTokenAmount: projectTokenAmount,
504
+ terminalTokenAmount: maxCashedOut
505
+ });
506
+
507
+ // faux proof data for test claim
508
+ bytes32[32] memory _proof;
509
+
510
+ JBClaim memory _claimData = JBClaim({token: address(ccipBnMArbSepolia), leaf: _leaf, proof: _proof});
511
+
512
+ suckerGlobal.testClaim(_claimData); */
513
+ }
514
+ }