@bananapus/router-terminal-v6 0.0.21 → 0.0.22
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/ARCHITECTURE.md +1 -1
- package/AUDIT_INSTRUCTIONS.md +4 -4
- package/CHANGE_LOG.md +2 -2
- package/README.md +2 -2
- package/RISKS.md +7 -7
- package/SKILLS.md +2 -2
- package/package.json +1 -1
- package/script/Deploy.s.sol +0 -1
- package/src/JBRouterTerminal.sol +154 -58
- package/src/JBRouterTerminalRegistry.sol +3 -7
- package/src/interfaces/IGeomeanOracle.sol +21 -0
- package/test/RouterTerminal.t.sol +56 -36
- package/test/RouterTerminalCashOutFork.t.sol +0 -1
- package/test/RouterTerminalCreditCashout.t.sol +0 -5
- package/test/RouterTerminalERC2771.t.sol +0 -5
- package/test/RouterTerminalFeeCashOutFork.t.sol +0 -1
- package/test/RouterTerminalFork.t.sol +0 -1
- package/test/RouterTerminalMultihopFork.t.sol +0 -1
- package/test/RouterTerminalPreviewFork.t.sol +7 -5
- package/test/RouterTerminalReentrancy.t.sol +0 -5
- package/test/RouterTerminalRegistry.t.sol +3 -1
- package/test/RouterTerminalSandwichFork.t.sol +0 -1
- package/test/TestAuditGaps.sol +5 -7
- package/test/audit/LeftoverRefund.t.sol +45 -25
- package/test/audit/PayerTrackerRefund.t.sol +12 -8
- package/test/audit/RefundToBeneficiary.t.sol +0 -2
- package/test/audit/RegistryAddToBalancePartialFill.t.sol +48 -37
- package/test/fork/V4QuoteAndSettlementFork.t.sol +0 -1
- package/test/invariant/RouterTerminalInvariant.t.sol +1 -5
- package/test/regression/CashOutLoopLimit.t.sol +1 -4
- package/test/regression/RouterTerminalEdgeCases.t.sol +49 -27
|
@@ -6,7 +6,6 @@ import {Test} from "forge-std/Test.sol";
|
|
|
6
6
|
import {IJBCashOutTerminal} from "@bananapus/core-v6/src/interfaces/IJBCashOutTerminal.sol";
|
|
7
7
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
8
8
|
import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
9
|
-
import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
|
|
10
9
|
import {IJBRulesetApprovalHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetApprovalHook.sol";
|
|
11
10
|
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
12
11
|
import {IJBToken} from "@bananapus/core-v6/src/interfaces/IJBToken.sol";
|
|
@@ -65,14 +64,14 @@ contract MockERC20 {
|
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
contract MockERC20WithDecimals is MockERC20 {
|
|
68
|
-
uint8 internal immutable
|
|
67
|
+
uint8 internal immutable _DECIMALS;
|
|
69
68
|
|
|
70
69
|
constructor(uint8 decimals_) {
|
|
71
|
-
|
|
70
|
+
_DECIMALS = decimals_;
|
|
72
71
|
}
|
|
73
72
|
|
|
74
73
|
function decimals() external view returns (uint8) {
|
|
75
|
-
return
|
|
74
|
+
return _DECIMALS;
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
77
|
|
|
@@ -140,13 +139,13 @@ contract MockPoolManagerForSettle {
|
|
|
140
139
|
}
|
|
141
140
|
|
|
142
141
|
contract MockPreviewDestTerminal {
|
|
143
|
-
address public immutable
|
|
144
|
-
uint256 public immutable
|
|
142
|
+
address public immutable ACCEPTED_TOKEN;
|
|
143
|
+
uint256 public immutable PREVIEWED_TOKEN_COUNT;
|
|
145
144
|
uint256 public totalReceived;
|
|
146
145
|
|
|
147
146
|
constructor(address acceptedToken_, uint256 previewedTokenCount_) {
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
ACCEPTED_TOKEN = acceptedToken_;
|
|
148
|
+
PREVIEWED_TOKEN_COUNT = previewedTokenCount_;
|
|
150
149
|
}
|
|
151
150
|
|
|
152
151
|
function pay(
|
|
@@ -163,10 +162,11 @@ contract MockPreviewDestTerminal {
|
|
|
163
162
|
returns (uint256)
|
|
164
163
|
{
|
|
165
164
|
if (token == JBConstants.NATIVE_TOKEN) require(msg.value == amount, "MockPreviewDestTerminal: ETH mismatch");
|
|
165
|
+
// forge-lint: disable-next-line(erc20-unchecked-transfer)
|
|
166
166
|
else IERC20(token).transferFrom(msg.sender, address(this), amount);
|
|
167
167
|
|
|
168
168
|
totalReceived += amount;
|
|
169
|
-
return
|
|
169
|
+
return PREVIEWED_TOKEN_COUNT;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
function previewPayFor(
|
|
@@ -192,13 +192,14 @@ contract MockPreviewDestTerminal {
|
|
|
192
192
|
metadata: 0
|
|
193
193
|
});
|
|
194
194
|
hookSpecifications = new JBPayHookSpecification[](0);
|
|
195
|
-
return (ruleset,
|
|
195
|
+
return (ruleset, PREVIEWED_TOKEN_COUNT, 0, hookSpecifications);
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
function accountingContextsOf(uint256) external view returns (JBAccountingContext[] memory contexts) {
|
|
199
199
|
contexts = new JBAccountingContext[](1);
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
201
|
+
uint32 currency = uint32(uint160(ACCEPTED_TOKEN));
|
|
202
|
+
contexts[0] = JBAccountingContext({token: ACCEPTED_TOKEN, decimals: 18, currency: currency});
|
|
202
203
|
}
|
|
203
204
|
|
|
204
205
|
function supportsInterface(bytes4) external pure returns (bool) {
|
|
@@ -209,12 +210,12 @@ contract MockPreviewDestTerminal {
|
|
|
209
210
|
}
|
|
210
211
|
|
|
211
212
|
contract MockPreviewCashOutTerminal {
|
|
212
|
-
uint256 public immutable
|
|
213
|
-
MockERC20 public immutable
|
|
213
|
+
uint256 public immutable RECLAIM_AMOUNT;
|
|
214
|
+
MockERC20 public immutable TOKEN;
|
|
214
215
|
|
|
215
216
|
constructor(MockERC20 token_, uint256 reclaimAmount_) payable {
|
|
216
|
-
|
|
217
|
-
|
|
217
|
+
TOKEN = token_;
|
|
218
|
+
RECLAIM_AMOUNT = reclaimAmount_;
|
|
218
219
|
}
|
|
219
220
|
|
|
220
221
|
function cashOutTokensOf(
|
|
@@ -229,13 +230,13 @@ contract MockPreviewCashOutTerminal {
|
|
|
229
230
|
external
|
|
230
231
|
returns (uint256)
|
|
231
232
|
{
|
|
232
|
-
|
|
233
|
+
TOKEN.burn(holder, cashOutCount);
|
|
233
234
|
|
|
234
235
|
if (tokenToReclaim == JBConstants.NATIVE_TOKEN) {
|
|
235
|
-
(bool success,) = beneficiary.call{value:
|
|
236
|
+
(bool success,) = beneficiary.call{value: RECLAIM_AMOUNT}("");
|
|
236
237
|
require(success, "MockPreviewCashOutTerminal: ETH send failed");
|
|
237
238
|
}
|
|
238
|
-
return
|
|
239
|
+
return RECLAIM_AMOUNT;
|
|
239
240
|
}
|
|
240
241
|
|
|
241
242
|
function previewCashOutFrom(
|
|
@@ -262,7 +263,7 @@ contract MockPreviewCashOutTerminal {
|
|
|
262
263
|
metadata: 0
|
|
263
264
|
});
|
|
264
265
|
hookSpecifications = new JBCashOutHookSpecification[](0);
|
|
265
|
-
return (ruleset,
|
|
266
|
+
return (ruleset, RECLAIM_AMOUNT, 0, hookSpecifications);
|
|
266
267
|
}
|
|
267
268
|
|
|
268
269
|
function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
|
|
@@ -285,7 +286,6 @@ contract RouterTerminalHarness is JBRouterTerminal {
|
|
|
285
286
|
constructor(
|
|
286
287
|
IJBDirectory directory,
|
|
287
288
|
IJBPermissions permissions,
|
|
288
|
-
IJBProjects projects,
|
|
289
289
|
IJBTokens tokens,
|
|
290
290
|
IPermit2 permit2,
|
|
291
291
|
address owner,
|
|
@@ -294,9 +294,7 @@ contract RouterTerminalHarness is JBRouterTerminal {
|
|
|
294
294
|
IPoolManager poolManager,
|
|
295
295
|
address trustedForwarder
|
|
296
296
|
)
|
|
297
|
-
JBRouterTerminal(
|
|
298
|
-
directory, permissions, projects, tokens, permit2, owner, weth, factory, poolManager, trustedForwarder
|
|
299
|
-
)
|
|
297
|
+
JBRouterTerminal(directory, permissions, tokens, permit2, owner, weth, factory, poolManager, trustedForwarder)
|
|
300
298
|
{}
|
|
301
299
|
|
|
302
300
|
function exposedResolveTokenOut(
|
|
@@ -338,7 +336,6 @@ contract RouterTerminalTest is Test {
|
|
|
338
336
|
// Mocked dependencies
|
|
339
337
|
IJBDirectory mockDirectory;
|
|
340
338
|
IJBPermissions mockPermissions;
|
|
341
|
-
IJBProjects mockProjects;
|
|
342
339
|
IJBTokens mockTokens;
|
|
343
340
|
IPermit2 mockPermit2;
|
|
344
341
|
IWETH9 mockWeth;
|
|
@@ -352,8 +349,6 @@ contract RouterTerminalTest is Test {
|
|
|
352
349
|
vm.etch(address(mockDirectory), hex"00");
|
|
353
350
|
mockPermissions = IJBPermissions(makeAddr("mockPermissions"));
|
|
354
351
|
vm.etch(address(mockPermissions), hex"00");
|
|
355
|
-
mockProjects = IJBProjects(makeAddr("mockProjects"));
|
|
356
|
-
vm.etch(address(mockProjects), hex"00");
|
|
357
352
|
mockTokens = IJBTokens(makeAddr("mockTokens"));
|
|
358
353
|
vm.etch(address(mockTokens), hex"00");
|
|
359
354
|
mockPermit2 = IPermit2(makeAddr("mockPermit2"));
|
|
@@ -370,7 +365,6 @@ contract RouterTerminalTest is Test {
|
|
|
370
365
|
routerTerminal = new RouterTerminalHarness(
|
|
371
366
|
mockDirectory,
|
|
372
367
|
mockPermissions,
|
|
373
|
-
mockProjects,
|
|
374
368
|
mockTokens,
|
|
375
369
|
mockPermit2,
|
|
376
370
|
terminalOwner,
|
|
@@ -836,6 +830,7 @@ contract RouterTerminalTest is Test {
|
|
|
836
830
|
);
|
|
837
831
|
|
|
838
832
|
JBAccountingContext[] memory contexts = new JBAccountingContext[](1);
|
|
833
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
839
834
|
contexts[0] = JBAccountingContext({token: tokenIn, decimals: 18, currency: uint32(uint160(tokenIn))});
|
|
840
835
|
vm.mockCall(destTerminal, abi.encodeCall(IJBTerminal.accountingContextsOf, (projectId)), abi.encode(contexts));
|
|
841
836
|
|
|
@@ -980,7 +975,17 @@ contract RouterTerminalTest is Test {
|
|
|
980
975
|
)
|
|
981
976
|
),
|
|
982
977
|
abi.encode(
|
|
983
|
-
JBRuleset(
|
|
978
|
+
JBRuleset({
|
|
979
|
+
cycleNumber: 0,
|
|
980
|
+
id: 0,
|
|
981
|
+
basedOnId: 0,
|
|
982
|
+
start: 0,
|
|
983
|
+
duration: 0,
|
|
984
|
+
weight: 0,
|
|
985
|
+
weightCutPercent: 0,
|
|
986
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
987
|
+
metadata: 0
|
|
988
|
+
}),
|
|
984
989
|
uint256(60),
|
|
985
990
|
uint256(0),
|
|
986
991
|
new JBCashOutHookSpecification[](0)
|
|
@@ -1166,7 +1171,17 @@ contract RouterTerminalTest is Test {
|
|
|
1166
1171
|
)
|
|
1167
1172
|
),
|
|
1168
1173
|
abi.encode(
|
|
1169
|
-
JBRuleset(
|
|
1174
|
+
JBRuleset({
|
|
1175
|
+
cycleNumber: 0,
|
|
1176
|
+
id: 0,
|
|
1177
|
+
basedOnId: 0,
|
|
1178
|
+
start: 0,
|
|
1179
|
+
duration: 0,
|
|
1180
|
+
weight: 0,
|
|
1181
|
+
weightCutPercent: 0,
|
|
1182
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
1183
|
+
metadata: 0
|
|
1184
|
+
}),
|
|
1170
1185
|
uint256(60),
|
|
1171
1186
|
uint256(0),
|
|
1172
1187
|
new JBCashOutHookSpecification[](0)
|
|
@@ -1277,7 +1292,17 @@ contract RouterTerminalTest is Test {
|
|
|
1277
1292
|
destTerminal,
|
|
1278
1293
|
abi.encodeCall(IJBTerminal.previewPayFor, (projectId, tokenOut, quotedAmountOut, beneficiary, metadata)),
|
|
1279
1294
|
abi.encode(
|
|
1280
|
-
JBRuleset(
|
|
1295
|
+
JBRuleset({
|
|
1296
|
+
cycleNumber: 0,
|
|
1297
|
+
id: 1,
|
|
1298
|
+
basedOnId: 0,
|
|
1299
|
+
start: 0,
|
|
1300
|
+
duration: 0,
|
|
1301
|
+
weight: 0,
|
|
1302
|
+
weightCutPercent: 0,
|
|
1303
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
1304
|
+
metadata: 0
|
|
1305
|
+
}),
|
|
1281
1306
|
quotedAmountOut,
|
|
1282
1307
|
uint256(0),
|
|
1283
1308
|
expectedSpecs
|
|
@@ -1513,7 +1538,6 @@ contract RouterTerminalTest is Test {
|
|
|
1513
1538
|
RouterTerminalHarness noV4Router = new RouterTerminalHarness(
|
|
1514
1539
|
mockDirectory,
|
|
1515
1540
|
mockPermissions,
|
|
1516
|
-
mockProjects,
|
|
1517
1541
|
mockTokens,
|
|
1518
1542
|
mockPermit2,
|
|
1519
1543
|
terminalOwner,
|
|
@@ -1858,7 +1882,6 @@ contract SettleV4DeficitTest is Test {
|
|
|
1858
1882
|
// Mocked JB dependencies (unused by _settleV4 but required for constructor).
|
|
1859
1883
|
IJBDirectory mockDirectory;
|
|
1860
1884
|
IJBPermissions mockPermissions;
|
|
1861
|
-
IJBProjects mockProjects;
|
|
1862
1885
|
IJBTokens mockTokens;
|
|
1863
1886
|
IPermit2 mockPermit2;
|
|
1864
1887
|
IUniswapV3Factory mockFactory;
|
|
@@ -1868,8 +1891,6 @@ contract SettleV4DeficitTest is Test {
|
|
|
1868
1891
|
vm.etch(address(mockDirectory), hex"00");
|
|
1869
1892
|
mockPermissions = IJBPermissions(makeAddr("mockPermissions"));
|
|
1870
1893
|
vm.etch(address(mockPermissions), hex"00");
|
|
1871
|
-
mockProjects = IJBProjects(makeAddr("mockProjects"));
|
|
1872
|
-
vm.etch(address(mockProjects), hex"00");
|
|
1873
1894
|
mockTokens = IJBTokens(makeAddr("mockTokens"));
|
|
1874
1895
|
vm.etch(address(mockTokens), hex"00");
|
|
1875
1896
|
mockPermit2 = IPermit2(makeAddr("mockPermit2"));
|
|
@@ -1884,7 +1905,6 @@ contract SettleV4DeficitTest is Test {
|
|
|
1884
1905
|
routerTerminal = new RouterTerminalHarness(
|
|
1885
1906
|
mockDirectory,
|
|
1886
1907
|
mockPermissions,
|
|
1887
|
-
mockProjects,
|
|
1888
1908
|
mockTokens,
|
|
1889
1909
|
mockPermit2,
|
|
1890
1910
|
makeAddr("owner"),
|
|
@@ -6,7 +6,6 @@ import {Test} from "forge-std/Test.sol";
|
|
|
6
6
|
import {IJBCashOutTerminal} from "@bananapus/core-v6/src/interfaces/IJBCashOutTerminal.sol";
|
|
7
7
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
8
8
|
import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
9
|
-
import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
|
|
10
9
|
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
11
10
|
import {IJBTokens} from "@bananapus/core-v6/src/interfaces/IJBTokens.sol";
|
|
12
11
|
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
@@ -30,7 +29,6 @@ contract RouterTerminalCreditCashoutTest is Test {
|
|
|
30
29
|
// Mocked dependencies.
|
|
31
30
|
IJBDirectory mockDirectory;
|
|
32
31
|
IJBPermissions mockPermissions;
|
|
33
|
-
IJBProjects mockProjects;
|
|
34
32
|
IJBTokens mockTokens;
|
|
35
33
|
IPermit2 mockPermit2;
|
|
36
34
|
IWETH9 mockWeth;
|
|
@@ -44,8 +42,6 @@ contract RouterTerminalCreditCashoutTest is Test {
|
|
|
44
42
|
vm.etch(address(mockDirectory), hex"00");
|
|
45
43
|
mockPermissions = IJBPermissions(makeAddr("mockPermissions"));
|
|
46
44
|
vm.etch(address(mockPermissions), hex"00");
|
|
47
|
-
mockProjects = IJBProjects(makeAddr("mockProjects"));
|
|
48
|
-
vm.etch(address(mockProjects), hex"00");
|
|
49
45
|
mockTokens = IJBTokens(makeAddr("mockTokens"));
|
|
50
46
|
vm.etch(address(mockTokens), hex"00");
|
|
51
47
|
mockPermit2 = IPermit2(makeAddr("mockPermit2"));
|
|
@@ -62,7 +58,6 @@ contract RouterTerminalCreditCashoutTest is Test {
|
|
|
62
58
|
routerTerminal = new JBRouterTerminal(
|
|
63
59
|
mockDirectory,
|
|
64
60
|
mockPermissions,
|
|
65
|
-
mockProjects,
|
|
66
61
|
mockTokens,
|
|
67
62
|
mockPermit2,
|
|
68
63
|
terminalOwner,
|
|
@@ -5,7 +5,6 @@ import {Test} from "forge-std/Test.sol";
|
|
|
5
5
|
|
|
6
6
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
7
7
|
import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
8
|
-
import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
|
|
9
8
|
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
10
9
|
import {IJBTokens} from "@bananapus/core-v6/src/interfaces/IJBTokens.sol";
|
|
11
10
|
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
@@ -85,7 +84,6 @@ contract RouterTerminalERC2771Test is Test {
|
|
|
85
84
|
// Mocked dependencies.
|
|
86
85
|
IJBDirectory mockDirectory;
|
|
87
86
|
IJBPermissions mockPermissions;
|
|
88
|
-
IJBProjects mockProjects;
|
|
89
87
|
IJBTokens mockTokens;
|
|
90
88
|
IPermit2 mockPermit2;
|
|
91
89
|
IUniswapV3Factory mockFactory;
|
|
@@ -101,8 +99,6 @@ contract RouterTerminalERC2771Test is Test {
|
|
|
101
99
|
vm.etch(address(mockDirectory), hex"00");
|
|
102
100
|
mockPermissions = IJBPermissions(makeAddr("mockPermissions"));
|
|
103
101
|
vm.etch(address(mockPermissions), hex"00");
|
|
104
|
-
mockProjects = IJBProjects(makeAddr("mockProjects"));
|
|
105
|
-
vm.etch(address(mockProjects), hex"00");
|
|
106
102
|
mockTokens = IJBTokens(makeAddr("mockTokens"));
|
|
107
103
|
vm.etch(address(mockTokens), hex"00");
|
|
108
104
|
mockPermit2 = IPermit2(makeAddr("mockPermit2"));
|
|
@@ -118,7 +114,6 @@ contract RouterTerminalERC2771Test is Test {
|
|
|
118
114
|
routerTerminal = new JBRouterTerminal(
|
|
119
115
|
mockDirectory,
|
|
120
116
|
mockPermissions,
|
|
121
|
-
mockProjects,
|
|
122
117
|
mockTokens,
|
|
123
118
|
mockPermit2,
|
|
124
119
|
terminalOwner,
|
|
@@ -18,7 +18,7 @@ import {JBTerminalStore} from "@bananapus/core-v6/src/JBTerminalStore.sol";
|
|
|
18
18
|
import {JBTokens} from "@bananapus/core-v6/src/JBTokens.sol";
|
|
19
19
|
import {JBERC20} from "@bananapus/core-v6/src/JBERC20.sol";
|
|
20
20
|
import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
import {IJBRulesetApprovalHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetApprovalHook.sol";
|
|
23
23
|
import {IJBTokens} from "@bananapus/core-v6/src/interfaces/IJBTokens.sol";
|
|
24
24
|
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
@@ -78,7 +78,6 @@ contract RouterTerminalPreviewForkTest is Test {
|
|
|
78
78
|
routerTerminal = new JBRouterTerminal({
|
|
79
79
|
directory: jbDirectory,
|
|
80
80
|
permissions: IJBPermissions(address(jbPermissions)),
|
|
81
|
-
projects: IJBProjects(address(jbProjects)),
|
|
82
81
|
tokens: IJBTokens(address(jbTokens)),
|
|
83
82
|
permit2: PERMIT2,
|
|
84
83
|
owner: multisig,
|
|
@@ -286,10 +285,12 @@ contract RouterTerminalPreviewForkTest is Test {
|
|
|
286
285
|
internal
|
|
287
286
|
returns (uint256 projectId)
|
|
288
287
|
{
|
|
288
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
289
|
+
uint32 baseCurrency = uint32(uint160(acceptedToken));
|
|
289
290
|
JBRulesetMetadata memory metadata = JBRulesetMetadata({
|
|
290
291
|
reservedPercent: 0,
|
|
291
292
|
cashOutTaxRate: cashOutTaxRate,
|
|
292
|
-
baseCurrency:
|
|
293
|
+
baseCurrency: baseCurrency,
|
|
293
294
|
pausePay: false,
|
|
294
295
|
pauseCreditTransfers: false,
|
|
295
296
|
allowOwnerMinting: false,
|
|
@@ -319,8 +320,9 @@ contract RouterTerminalPreviewForkTest is Test {
|
|
|
319
320
|
rulesetConfigs[0].fundAccessLimitGroups = new JBFundAccessLimitGroup[](0);
|
|
320
321
|
|
|
321
322
|
JBAccountingContext[] memory tokensToAccept = new JBAccountingContext[](1);
|
|
322
|
-
|
|
323
|
-
|
|
323
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
324
|
+
uint32 tokenCurrency = uint32(uint160(acceptedToken));
|
|
325
|
+
tokensToAccept[0] = JBAccountingContext({token: acceptedToken, decimals: decimals, currency: tokenCurrency});
|
|
324
326
|
|
|
325
327
|
JBTerminalConfig[] memory terminalConfigs = new JBTerminalConfig[](1);
|
|
326
328
|
terminalConfigs[0] = JBTerminalConfig({terminal: jbMultiTerminal, accountingContextsToAccept: tokensToAccept});
|
|
@@ -6,7 +6,6 @@ import {Test} from "forge-std/Test.sol";
|
|
|
6
6
|
import {IJBCashOutTerminal} from "@bananapus/core-v6/src/interfaces/IJBCashOutTerminal.sol";
|
|
7
7
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
8
8
|
import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
9
|
-
import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
|
|
10
9
|
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
11
10
|
import {IJBToken} from "@bananapus/core-v6/src/interfaces/IJBToken.sol";
|
|
12
11
|
import {IJBTokens} from "@bananapus/core-v6/src/interfaces/IJBTokens.sol";
|
|
@@ -242,7 +241,6 @@ contract RouterTerminalReentrancyTest is Test {
|
|
|
242
241
|
// Mocked dependencies.
|
|
243
242
|
IJBDirectory mockDirectory;
|
|
244
243
|
IJBPermissions mockPermissions;
|
|
245
|
-
IJBProjects mockProjects;
|
|
246
244
|
IJBTokens mockTokens;
|
|
247
245
|
IPermit2 mockPermit2;
|
|
248
246
|
IWETH9 mockWeth;
|
|
@@ -256,8 +254,6 @@ contract RouterTerminalReentrancyTest is Test {
|
|
|
256
254
|
vm.etch(address(mockDirectory), hex"00");
|
|
257
255
|
mockPermissions = IJBPermissions(makeAddr("mockPermissions"));
|
|
258
256
|
vm.etch(address(mockPermissions), hex"00");
|
|
259
|
-
mockProjects = IJBProjects(makeAddr("mockProjects"));
|
|
260
|
-
vm.etch(address(mockProjects), hex"00");
|
|
261
257
|
mockTokens = IJBTokens(makeAddr("mockTokens"));
|
|
262
258
|
vm.etch(address(mockTokens), hex"00");
|
|
263
259
|
mockPermit2 = IPermit2(makeAddr("mockPermit2"));
|
|
@@ -274,7 +270,6 @@ contract RouterTerminalReentrancyTest is Test {
|
|
|
274
270
|
routerTerminal = new JBRouterTerminal(
|
|
275
271
|
mockDirectory,
|
|
276
272
|
mockPermissions,
|
|
277
|
-
mockProjects,
|
|
278
273
|
mockTokens,
|
|
279
274
|
mockPermit2,
|
|
280
275
|
terminalOwner,
|
|
@@ -150,8 +150,10 @@ contract RouterTerminalRegistryTest is Test {
|
|
|
150
150
|
|
|
151
151
|
function test_accountingContext_passthroughDoesNotCorrectTerminalDecimals() public {
|
|
152
152
|
address usdcLike = makeAddr("usdcLike");
|
|
153
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
154
|
+
uint32 usdcCurrency = uint32(uint160(usdcLike));
|
|
153
155
|
JBAccountingContext memory expected =
|
|
154
|
-
JBAccountingContext({token: usdcLike, decimals: 18, currency:
|
|
156
|
+
JBAccountingContext({token: usdcLike, decimals: 18, currency: usdcCurrency});
|
|
155
157
|
|
|
156
158
|
vm.mockCall(
|
|
157
159
|
address(terminalA),
|
package/test/TestAuditGaps.sol
CHANGED
|
@@ -102,7 +102,6 @@ contract AuditHarness is JBRouterTerminal {
|
|
|
102
102
|
constructor(
|
|
103
103
|
IJBDirectory d,
|
|
104
104
|
IJBPermissions p,
|
|
105
|
-
IJBProjects pr,
|
|
106
105
|
IJBTokens t,
|
|
107
106
|
IPermit2 pm,
|
|
108
107
|
address o,
|
|
@@ -111,7 +110,7 @@ contract AuditHarness is JBRouterTerminal {
|
|
|
111
110
|
IPoolManager pm4,
|
|
112
111
|
address tf
|
|
113
112
|
)
|
|
114
|
-
JBRouterTerminal(d, p,
|
|
113
|
+
JBRouterTerminal(d, p, t, pm, o, w, f, pm4, tf)
|
|
115
114
|
{}
|
|
116
115
|
|
|
117
116
|
function exposedAcceptFundsFor(
|
|
@@ -169,7 +168,7 @@ contract TestAuditGaps is Test {
|
|
|
169
168
|
vm.etch(address(pm), hex"00");
|
|
170
169
|
owner = makeAddr("owner");
|
|
171
170
|
|
|
172
|
-
router = new AuditHarness(dir, perms,
|
|
171
|
+
router = new AuditHarness(dir, perms, toks, permit2, owner, weth, factory, pm, address(0));
|
|
173
172
|
}
|
|
174
173
|
|
|
175
174
|
// ═══════════════════════════════════════════════════════════════════════
|
|
@@ -761,14 +760,13 @@ contract TestAuditGaps is Test {
|
|
|
761
760
|
// GAP 6 (M-7): Registry receive() Accepts Native Token Refunds
|
|
762
761
|
// ═══════════════════════════════════════════════════════════════════════
|
|
763
762
|
|
|
764
|
-
/// @notice Registry
|
|
765
|
-
function
|
|
763
|
+
/// @notice Registry reverts on bare native token transfers (no receive() function).
|
|
764
|
+
function test_registryReceive_revertsOnBareTransfer() public {
|
|
766
765
|
JBRouterTerminalRegistry reg = new JBRouterTerminalRegistry(perms, proj, permit2, owner, address(0));
|
|
767
766
|
|
|
768
767
|
vm.deal(address(this), 1 ether);
|
|
769
768
|
(bool success,) = address(reg).call{value: 1 ether}("");
|
|
770
|
-
|
|
771
|
-
assertEq(address(reg).balance, 1 ether, "Registry should hold the received ETH");
|
|
769
|
+
assertFalse(success, "Registry should reject bare native token transfers");
|
|
772
770
|
}
|
|
773
771
|
|
|
774
772
|
/// @notice ETH that is directly deposited to the registry (not via partial-fill refund) is still stuck.
|
|
@@ -5,7 +5,6 @@ import {Test} from "forge-std/Test.sol";
|
|
|
5
5
|
|
|
6
6
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
7
7
|
import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
8
|
-
import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
|
|
9
8
|
import {IJBRulesetApprovalHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetApprovalHook.sol";
|
|
10
9
|
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
11
10
|
import {IJBTokens} from "@bananapus/core-v6/src/interfaces/IJBTokens.sol";
|
|
@@ -116,6 +115,7 @@ contract AuditLeftoverDestTerminal is IJBTerminal {
|
|
|
116
115
|
override
|
|
117
116
|
returns (JBAccountingContext memory)
|
|
118
117
|
{
|
|
118
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
119
119
|
return JBAccountingContext({token: token, decimals: 18, currency: uint32(uint160(token))});
|
|
120
120
|
}
|
|
121
121
|
|
|
@@ -159,7 +159,22 @@ contract AuditLeftoverDestTerminal is IJBTerminal {
|
|
|
159
159
|
returns (JBRuleset memory, uint256, uint256, JBPayHookSpecification[] memory hookSpecifications)
|
|
160
160
|
{
|
|
161
161
|
hookSpecifications = new JBPayHookSpecification[](0);
|
|
162
|
-
return (
|
|
162
|
+
return (
|
|
163
|
+
JBRuleset({
|
|
164
|
+
cycleNumber: 0,
|
|
165
|
+
id: 0,
|
|
166
|
+
basedOnId: 0,
|
|
167
|
+
start: 0,
|
|
168
|
+
duration: 0,
|
|
169
|
+
weight: 0,
|
|
170
|
+
weightCutPercent: 0,
|
|
171
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
172
|
+
metadata: 0
|
|
173
|
+
}),
|
|
174
|
+
amount,
|
|
175
|
+
0,
|
|
176
|
+
hookSpecifications
|
|
177
|
+
);
|
|
163
178
|
}
|
|
164
179
|
|
|
165
180
|
function supportsInterface(bytes4) external pure override returns (bool) {
|
|
@@ -168,14 +183,16 @@ contract AuditLeftoverDestTerminal is IJBTerminal {
|
|
|
168
183
|
}
|
|
169
184
|
|
|
170
185
|
contract AuditLeftoverPartialFillPool is IUniswapV3Pool {
|
|
171
|
-
address internal immutable
|
|
172
|
-
address internal immutable
|
|
173
|
-
AuditLeftoverMockERC20 internal immutable
|
|
186
|
+
address internal immutable _TOKEN0;
|
|
187
|
+
address internal immutable _TOKEN1;
|
|
188
|
+
AuditLeftoverMockERC20 internal immutable _OUTPUT_TOKEN;
|
|
189
|
+
// forge-lint: disable-next-line(screaming-snake-case-immutable)
|
|
174
190
|
uint24 public immutable override fee = 3000;
|
|
191
|
+
// forge-lint: disable-next-line(screaming-snake-case-immutable)
|
|
175
192
|
uint128 public immutable override liquidity = 1_000_000;
|
|
176
193
|
|
|
177
|
-
uint256 public immutable
|
|
178
|
-
uint256 public immutable
|
|
194
|
+
uint256 public immutable AMOUNT_IN_USED;
|
|
195
|
+
uint256 public immutable AMOUNT_OUT_GIVEN;
|
|
179
196
|
|
|
180
197
|
constructor(
|
|
181
198
|
address token0_,
|
|
@@ -184,11 +201,11 @@ contract AuditLeftoverPartialFillPool is IUniswapV3Pool {
|
|
|
184
201
|
uint256 amountInUsed_,
|
|
185
202
|
uint256 amountOutGiven_
|
|
186
203
|
) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
204
|
+
_TOKEN0 = token0_;
|
|
205
|
+
_TOKEN1 = token1_;
|
|
206
|
+
_OUTPUT_TOKEN = outputToken_;
|
|
207
|
+
AMOUNT_IN_USED = amountInUsed_;
|
|
208
|
+
AMOUNT_OUT_GIVEN = amountOutGiven_;
|
|
192
209
|
}
|
|
193
210
|
|
|
194
211
|
function swap(
|
|
@@ -202,24 +219,28 @@ contract AuditLeftoverPartialFillPool is IUniswapV3Pool {
|
|
|
202
219
|
override
|
|
203
220
|
returns (int256 amount0, int256 amount1)
|
|
204
221
|
{
|
|
222
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
223
|
+
int256 signedIn = int256(AMOUNT_IN_USED);
|
|
224
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
225
|
+
int256 signedOut = int256(AMOUNT_OUT_GIVEN);
|
|
226
|
+
|
|
205
227
|
if (zeroForOne) {
|
|
206
|
-
JBRouterTerminal(payable(msg.sender))
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
return (int256(amountInUsed), -int256(amountOutGiven));
|
|
228
|
+
JBRouterTerminal(payable(msg.sender)).uniswapV3SwapCallback(signedIn, -signedOut, data);
|
|
229
|
+
_OUTPUT_TOKEN.mint(recipient, AMOUNT_OUT_GIVEN);
|
|
230
|
+
return (signedIn, -signedOut);
|
|
210
231
|
}
|
|
211
232
|
|
|
212
|
-
JBRouterTerminal(payable(msg.sender)).uniswapV3SwapCallback(-
|
|
213
|
-
|
|
214
|
-
return (-
|
|
233
|
+
JBRouterTerminal(payable(msg.sender)).uniswapV3SwapCallback(-signedOut, signedIn, data);
|
|
234
|
+
_OUTPUT_TOKEN.mint(recipient, AMOUNT_OUT_GIVEN);
|
|
235
|
+
return (-signedOut, signedIn);
|
|
215
236
|
}
|
|
216
237
|
|
|
217
238
|
function token0() external view override returns (address) {
|
|
218
|
-
return
|
|
239
|
+
return _TOKEN0;
|
|
219
240
|
}
|
|
220
241
|
|
|
221
242
|
function token1() external view override returns (address) {
|
|
222
|
-
return
|
|
243
|
+
return _TOKEN1;
|
|
223
244
|
}
|
|
224
245
|
|
|
225
246
|
function tickSpacing() external pure override returns (int24) {
|
|
@@ -266,7 +287,7 @@ contract LeftoverRefundTest is Test {
|
|
|
266
287
|
JBRouterTerminal internal router;
|
|
267
288
|
IJBDirectory internal directory;
|
|
268
289
|
IJBPermissions internal permissions;
|
|
269
|
-
|
|
290
|
+
|
|
270
291
|
IJBTokens internal tokens;
|
|
271
292
|
IPermit2 internal permit2;
|
|
272
293
|
IUniswapV3Factory internal factory;
|
|
@@ -277,7 +298,7 @@ contract LeftoverRefundTest is Test {
|
|
|
277
298
|
function setUp() public {
|
|
278
299
|
directory = IJBDirectory(makeAddr("directory"));
|
|
279
300
|
permissions = IJBPermissions(makeAddr("permissions"));
|
|
280
|
-
|
|
301
|
+
|
|
281
302
|
tokens = IJBTokens(makeAddr("tokens"));
|
|
282
303
|
permit2 = IPermit2(makeAddr("permit2"));
|
|
283
304
|
factory = IUniswapV3Factory(makeAddr("factory"));
|
|
@@ -285,7 +306,6 @@ contract LeftoverRefundTest is Test {
|
|
|
285
306
|
|
|
286
307
|
vm.etch(address(directory), hex"00");
|
|
287
308
|
vm.etch(address(permissions), hex"00");
|
|
288
|
-
vm.etch(address(projects), hex"00");
|
|
289
309
|
vm.etch(address(tokens), hex"00");
|
|
290
310
|
vm.etch(address(permit2), hex"00");
|
|
291
311
|
vm.etch(address(factory), hex"00");
|
|
@@ -293,7 +313,6 @@ contract LeftoverRefundTest is Test {
|
|
|
293
313
|
router = new JBRouterTerminal(
|
|
294
314
|
directory,
|
|
295
315
|
permissions,
|
|
296
|
-
projects,
|
|
297
316
|
tokens,
|
|
298
317
|
permit2,
|
|
299
318
|
makeAddr("owner"),
|
|
@@ -341,6 +360,7 @@ contract LeftoverRefundTest is Test {
|
|
|
341
360
|
|
|
342
361
|
tokenIn.mint(donor, 50 ether);
|
|
343
362
|
vm.prank(donor);
|
|
363
|
+
// forge-lint: disable-next-line(erc20-unchecked-transfer)
|
|
344
364
|
tokenIn.transfer(address(router), 50 ether);
|
|
345
365
|
|
|
346
366
|
tokenIn.mint(attacker, 1000 ether);
|