@rev-net/core-v6 0.0.13 → 0.0.15
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/CHANGE_LOG.md +8 -3
- package/SKILLS.md +1 -1
- package/foundry.toml +7 -0
- package/package.json +8 -9
- package/src/REVDeployer.sol +34 -12
- package/src/REVLoans.sol +1 -5
- package/test/REVDeployerRegressions.t.sol +6 -3
- package/test/REVInvincibility.t.sol +6 -18
- package/test/REVLoansAttacks.t.sol +19 -11
- package/test/REVLoansFeeRecovery.t.sol +19 -11
- package/test/REVLoansFindings.t.sol +19 -11
- package/test/REVLoansRegressions.t.sol +19 -11
- package/test/REVLoansSourced.t.sol +0 -8
- package/test/TestCashOutCallerValidation.t.sol +74 -0
- package/test/TestLowFindings.t.sol +3 -1
- package/test/TestMixedFixes.t.sol +6 -4
- package/test/TestSplitWeightAdjustment.t.sol +10 -5
- package/test/fork/TestAutoIssuanceFork.t.sol +148 -0
- package/test/fork/TestCashOutFork.t.sol +22 -21
- package/test/fork/TestIssuanceDecayFork.t.sol +158 -0
- package/test/fork/TestLoanERC20Fork.t.sol +463 -0
- package/test/fork/TestLoanRepayFork.t.sol +2 -2
- package/test/fork/TestPermit2PaymentFork.t.sol +299 -0
- package/test/helpers/MaliciousContracts.sol +36 -22
- package/test/mock/MockBuybackDataHook.sol +50 -6
- package/deployments/revnet-core-v5/arbitrum/REVDeployer.json +0 -2821
- package/deployments/revnet-core-v5/arbitrum/REVLoans.json +0 -2260
- package/deployments/revnet-core-v5/arbitrum_sepolia/REVDeployer.json +0 -2821
- package/deployments/revnet-core-v5/arbitrum_sepolia/REVLoans.json +0 -2260
- package/deployments/revnet-core-v5/base/REVDeployer.json +0 -2825
- package/deployments/revnet-core-v5/base/REVLoans.json +0 -2264
- package/deployments/revnet-core-v5/base_sepolia/REVDeployer.json +0 -2825
- package/deployments/revnet-core-v5/base_sepolia/REVLoans.json +0 -2264
- package/deployments/revnet-core-v5/ethereum/REVDeployer.json +0 -2825
- package/deployments/revnet-core-v5/ethereum/REVLoans.json +0 -2264
- package/deployments/revnet-core-v5/optimism/REVDeployer.json +0 -2821
- package/deployments/revnet-core-v5/optimism/REVLoans.json +0 -2260
- package/deployments/revnet-core-v5/optimism_sepolia/REVDeployer.json +0 -2825
- package/deployments/revnet-core-v5/optimism_sepolia/REVLoans.json +0 -2264
- package/deployments/revnet-core-v5/sepolia/REVDeployer.json +0 -2825
- package/deployments/revnet-core-v5/sepolia/REVLoans.json +0 -2264
- package/docs/book.css +0 -13
- package/docs/book.toml +0 -13
- package/docs/solidity.min.js +0 -74
- package/docs/src/README.md +0 -185
- package/docs/src/SUMMARY.md +0 -18
- package/docs/src/src/README.md +0 -7
- package/docs/src/src/REVDeployer.sol/contract.REVDeployer.md +0 -999
- package/docs/src/src/REVLoans.sol/contract.REVLoans.md +0 -1108
- package/docs/src/src/interfaces/IREVDeployer.sol/interface.IREVDeployer.md +0 -525
- package/docs/src/src/interfaces/IREVLoans.sol/interface.IREVLoans.md +0 -598
- package/docs/src/src/interfaces/README.md +0 -5
- package/docs/src/src/structs/README.md +0 -12
- package/docs/src/src/structs/REVAutoIssuance.sol/struct.REVAutoIssuance.md +0 -19
- package/docs/src/src/structs/REVBuybackHookConfig.sol/struct.REVBuybackHookConfig.md +0 -19
- package/docs/src/src/structs/REVBuybackPoolConfig.sol/struct.REVBuybackPoolConfig.md +0 -21
- package/docs/src/src/structs/REVConfig.sol/struct.REVConfig.md +0 -23
- package/docs/src/src/structs/REVCroptopAllowedPost.sol/struct.REVCroptopAllowedPost.md +0 -32
- package/docs/src/src/structs/REVDeploy721TiersHookConfig.sol/struct.REVDeploy721TiersHookConfig.md +0 -34
- package/docs/src/src/structs/REVDescription.sol/struct.REVDescription.md +0 -23
- package/docs/src/src/structs/REVLoan.sol/struct.REVLoan.md +0 -28
- package/docs/src/src/structs/REVLoanSource.sol/struct.REVLoanSource.md +0 -16
- package/docs/src/src/structs/REVStageConfig.sol/struct.REVStageConfig.md +0 -44
- package/docs/src/src/structs/REVSuckerDeploymentConfig.sol/struct.REVSuckerDeploymentConfig.md +0 -16
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
5
|
+
import "./ForkTestBase.sol";
|
|
6
|
+
import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
|
|
7
|
+
import {MockERC20} from "@bananapus/core-v6/test/mock/MockERC20.sol";
|
|
8
|
+
import {MockPriceFeed} from "@bananapus/core-v6/test/mock/MockPriceFeed.sol";
|
|
9
|
+
import {JBSingleAllowance} from "@bananapus/core-v6/src/structs/JBSingleAllowance.sol";
|
|
10
|
+
import {IAllowanceTransfer} from "@uniswap/permit2/src/interfaces/IAllowanceTransfer.sol";
|
|
11
|
+
import {IPermit2} from "@uniswap/permit2/src/interfaces/IPermit2.sol";
|
|
12
|
+
|
|
13
|
+
/// @notice Fork tests for Permit2-based ERC-20 payments to a revnet terminal.
|
|
14
|
+
///
|
|
15
|
+
/// Verifies that JBMultiTerminal._acceptFundsFor() correctly processes Permit2 metadata,
|
|
16
|
+
/// including valid signatures, expired signatures, and replayed nonces.
|
|
17
|
+
///
|
|
18
|
+
/// Run with: FOUNDRY_PROFILE=fork forge test --match-contract TestPermit2PaymentFork -vvv
|
|
19
|
+
contract TestPermit2PaymentFork is ForkTestBase {
|
|
20
|
+
using JBMetadataResolver for bytes;
|
|
21
|
+
|
|
22
|
+
// Permit2 signing key.
|
|
23
|
+
uint256 constant PRIV_KEY = 0xBEEF1234;
|
|
24
|
+
address signer;
|
|
25
|
+
|
|
26
|
+
MockERC20 testToken;
|
|
27
|
+
uint256 revnetId;
|
|
28
|
+
|
|
29
|
+
// Permit2 EIP-712 typehashes (copied from nana-core-v6 test patterns).
|
|
30
|
+
bytes32 public constant _PERMIT_DETAILS_TYPEHASH =
|
|
31
|
+
keccak256("PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)");
|
|
32
|
+
|
|
33
|
+
bytes32 public constant _PERMIT_SINGLE_TYPEHASH = keccak256(
|
|
34
|
+
"PermitSingle(PermitDetails details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)"
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// Permit2 domain separator (fetched at runtime from the deployed Permit2 contract).
|
|
38
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
39
|
+
bytes32 DOMAIN_SEPARATOR;
|
|
40
|
+
|
|
41
|
+
function setUp() public override {
|
|
42
|
+
super.setUp();
|
|
43
|
+
|
|
44
|
+
signer = vm.addr(PRIV_KEY);
|
|
45
|
+
vm.deal(signer, 10 ether);
|
|
46
|
+
|
|
47
|
+
// Deploy fee project.
|
|
48
|
+
_deployFeeProject(5000);
|
|
49
|
+
|
|
50
|
+
// Deploy a mock ERC-20 for testing.
|
|
51
|
+
testToken = new MockERC20("Test Token", "TEST");
|
|
52
|
+
|
|
53
|
+
// Add a price feed: testToken → NATIVE_TOKEN so the buyback hook can quote.
|
|
54
|
+
// 1 testToken (6 dec) = 0.0005 ETH (i.e. 1 ETH = 2000 testTokens).
|
|
55
|
+
MockPriceFeed priceFeed = new MockPriceFeed(5e14, 18);
|
|
56
|
+
vm.prank(multisig());
|
|
57
|
+
jbPrices()
|
|
58
|
+
.addPriceFeedFor(
|
|
59
|
+
0, uint32(uint160(address(testToken))), uint32(uint160(JBConstants.NATIVE_TOKEN)), priceFeed
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Deploy a revnet that accepts both native token and the test ERC-20.
|
|
63
|
+
revnetId = _deployRevnetWithERC20(5000);
|
|
64
|
+
|
|
65
|
+
// Fetch the Permit2 domain separator from the on-chain Permit2 contract.
|
|
66
|
+
DOMAIN_SEPARATOR = permit2().DOMAIN_SEPARATOR();
|
|
67
|
+
|
|
68
|
+
// Mint tokens to the signer and approve Permit2 (NOT the terminal directly).
|
|
69
|
+
testToken.mint(signer, 1000e6);
|
|
70
|
+
vm.prank(signer);
|
|
71
|
+
IERC20(address(testToken)).approve(address(permit2()), type(uint256).max);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// @notice Deploy a revnet that accepts both native token and the test ERC-20.
|
|
75
|
+
function _deployRevnetWithERC20(uint16 cashOutTaxRate) internal returns (uint256 id) {
|
|
76
|
+
JBAccountingContext[] memory acc = new JBAccountingContext[](2);
|
|
77
|
+
acc[0] = JBAccountingContext({
|
|
78
|
+
token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
79
|
+
});
|
|
80
|
+
acc[1] = JBAccountingContext({
|
|
81
|
+
token: address(testToken), decimals: 6, currency: uint32(uint160(address(testToken)))
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
JBTerminalConfig[] memory tc = new JBTerminalConfig[](1);
|
|
85
|
+
tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: acc});
|
|
86
|
+
|
|
87
|
+
REVStageConfig[] memory stages = new REVStageConfig[](1);
|
|
88
|
+
JBSplit[] memory splits = new JBSplit[](1);
|
|
89
|
+
splits[0].beneficiary = payable(multisig());
|
|
90
|
+
splits[0].percent = 10_000;
|
|
91
|
+
stages[0] = REVStageConfig({
|
|
92
|
+
startsAtOrAfter: uint40(block.timestamp),
|
|
93
|
+
autoIssuances: new REVAutoIssuance[](0),
|
|
94
|
+
splitPercent: 0,
|
|
95
|
+
splits: splits,
|
|
96
|
+
initialIssuance: INITIAL_ISSUANCE,
|
|
97
|
+
issuanceCutFrequency: 0,
|
|
98
|
+
issuanceCutPercent: 0,
|
|
99
|
+
cashOutTaxRate: cashOutTaxRate,
|
|
100
|
+
extraMetadata: 0
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
REVConfig memory cfg = REVConfig({
|
|
104
|
+
description: REVDescription("Permit2 Test", "P2T", "ipfs://p2t", "PERMIT2_SALT"),
|
|
105
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
106
|
+
splitOperator: multisig(),
|
|
107
|
+
stageConfigurations: stages
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
REVSuckerDeploymentConfig memory sdc = REVSuckerDeploymentConfig({
|
|
111
|
+
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256(abi.encodePacked("PERMIT2_TEST"))
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
(id,) = REV_DEPLOYER.deployFor({
|
|
115
|
+
revnetId: 0,
|
|
116
|
+
configuration: cfg,
|
|
117
|
+
terminalConfigurations: tc,
|
|
118
|
+
suckerDeploymentConfiguration: sdc,
|
|
119
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
120
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/// @notice Build Permit2 metadata for a payment.
|
|
125
|
+
function _buildPermit2Metadata(
|
|
126
|
+
uint160 amount,
|
|
127
|
+
uint48 expiration,
|
|
128
|
+
uint48 nonce,
|
|
129
|
+
uint256 sigDeadline
|
|
130
|
+
)
|
|
131
|
+
internal
|
|
132
|
+
view
|
|
133
|
+
returns (bytes memory metadata)
|
|
134
|
+
{
|
|
135
|
+
// Build the permit struct for signing.
|
|
136
|
+
IAllowanceTransfer.PermitSingle memory permitSingle = IAllowanceTransfer.PermitSingle({
|
|
137
|
+
details: IAllowanceTransfer.PermitDetails({
|
|
138
|
+
token: address(testToken), amount: amount, expiration: expiration, nonce: nonce
|
|
139
|
+
}),
|
|
140
|
+
spender: address(jbMultiTerminal()),
|
|
141
|
+
sigDeadline: sigDeadline
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Sign it.
|
|
145
|
+
bytes memory sig = _getPermitSignature(permitSingle, PRIV_KEY, DOMAIN_SEPARATOR);
|
|
146
|
+
|
|
147
|
+
// Pack into JBSingleAllowance.
|
|
148
|
+
JBSingleAllowance memory allowance = JBSingleAllowance({
|
|
149
|
+
sigDeadline: sigDeadline, amount: amount, expiration: expiration, nonce: nonce, signature: sig
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Encode as metadata with the "permit2" key targeting the terminal, plus a zero "quote"
|
|
153
|
+
// so the buyback hook skips the TWAP pool lookup (no pool exists for testToken).
|
|
154
|
+
bytes4[] memory ids = new bytes4[](2);
|
|
155
|
+
bytes[] memory datas = new bytes[](2);
|
|
156
|
+
ids[0] = JBMetadataResolver.getId("permit2", address(jbMultiTerminal()));
|
|
157
|
+
datas[0] = abi.encode(allowance);
|
|
158
|
+
ids[1] = JBMetadataResolver.getId("quote", address(BUYBACK_HOOK));
|
|
159
|
+
datas[1] = abi.encode(uint256(0), uint256(0));
|
|
160
|
+
metadata = JBMetadataResolver.createMetadata(ids, datas);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/// @notice Pay a revnet with ERC-20 via Permit2 with zero direct terminal approval -- succeeds.
|
|
164
|
+
function testFork_Permit2PaymentSucceeds() public {
|
|
165
|
+
uint160 payAmount = 100e6; // 100 tokens (6 decimals)
|
|
166
|
+
uint48 expiration = uint48(block.timestamp + 1 days);
|
|
167
|
+
uint256 sigDeadline = block.timestamp + 1 days;
|
|
168
|
+
uint48 nonce = 0;
|
|
169
|
+
|
|
170
|
+
bytes memory metadata = _buildPermit2Metadata(payAmount, expiration, nonce, sigDeadline);
|
|
171
|
+
|
|
172
|
+
// Verify the signer has NOT approved the terminal directly.
|
|
173
|
+
assertEq(
|
|
174
|
+
IERC20(address(testToken)).allowance(signer, address(jbMultiTerminal())),
|
|
175
|
+
0,
|
|
176
|
+
"signer should have zero direct terminal approval"
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Pay using Permit2.
|
|
180
|
+
vm.prank(signer);
|
|
181
|
+
uint256 tokensReceived = jbMultiTerminal()
|
|
182
|
+
.pay({
|
|
183
|
+
projectId: revnetId,
|
|
184
|
+
token: address(testToken),
|
|
185
|
+
amount: payAmount,
|
|
186
|
+
beneficiary: signer,
|
|
187
|
+
minReturnedTokens: 0,
|
|
188
|
+
memo: "permit2 payment",
|
|
189
|
+
metadata: metadata
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Verify payment succeeded.
|
|
193
|
+
assertGt(tokensReceived, 0, "should receive project tokens from Permit2 payment");
|
|
194
|
+
|
|
195
|
+
// Verify tokens were transferred from signer to terminal.
|
|
196
|
+
assertEq(
|
|
197
|
+
testToken.balanceOf(address(jbMultiTerminal())), payAmount, "terminal should hold the paid ERC-20 tokens"
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
// Verify signer's token balance decreased.
|
|
201
|
+
assertEq(testToken.balanceOf(signer), 1000e6 - payAmount, "signer's token balance should decrease");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/// @notice Payment with an expired Permit2 signature deadline should revert.
|
|
205
|
+
function testFork_Permit2ExpiredSignatureReverts() public {
|
|
206
|
+
uint160 payAmount = 100e6;
|
|
207
|
+
uint48 expiration = uint48(block.timestamp + 1 days);
|
|
208
|
+
uint48 nonce = 0;
|
|
209
|
+
|
|
210
|
+
// Set sigDeadline in the past.
|
|
211
|
+
uint256 expiredDeadline = block.timestamp - 1;
|
|
212
|
+
|
|
213
|
+
bytes memory metadata = _buildPermit2Metadata(payAmount, expiration, nonce, expiredDeadline);
|
|
214
|
+
|
|
215
|
+
// The Permit2 contract should reject the expired signature.
|
|
216
|
+
// The terminal catches permit failures in try-catch and falls back to transferFrom,
|
|
217
|
+
// which will also fail due to zero approval. So we expect a generic revert.
|
|
218
|
+
vm.prank(signer);
|
|
219
|
+
vm.expectRevert();
|
|
220
|
+
jbMultiTerminal()
|
|
221
|
+
.pay({
|
|
222
|
+
projectId: revnetId,
|
|
223
|
+
token: address(testToken),
|
|
224
|
+
amount: payAmount,
|
|
225
|
+
beneficiary: signer,
|
|
226
|
+
minReturnedTokens: 0,
|
|
227
|
+
memo: "expired permit2",
|
|
228
|
+
metadata: metadata
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/// @notice Replaying the same Permit2 nonce should revert on the second payment.
|
|
233
|
+
function testFork_Permit2ReplayReverts() public {
|
|
234
|
+
uint160 payAmount = 50e6;
|
|
235
|
+
uint48 expiration = uint48(block.timestamp + 1 days);
|
|
236
|
+
uint256 sigDeadline = block.timestamp + 1 days;
|
|
237
|
+
uint48 nonce = 0;
|
|
238
|
+
|
|
239
|
+
bytes memory metadata = _buildPermit2Metadata(payAmount, expiration, nonce, sigDeadline);
|
|
240
|
+
|
|
241
|
+
// First payment succeeds.
|
|
242
|
+
vm.prank(signer);
|
|
243
|
+
jbMultiTerminal()
|
|
244
|
+
.pay({
|
|
245
|
+
projectId: revnetId,
|
|
246
|
+
token: address(testToken),
|
|
247
|
+
amount: payAmount,
|
|
248
|
+
beneficiary: signer,
|
|
249
|
+
minReturnedTokens: 0,
|
|
250
|
+
memo: "first permit2 payment",
|
|
251
|
+
metadata: metadata
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Second payment with the same nonce.
|
|
255
|
+
// The permit call will fail (nonce already used), terminal catches it via try-catch,
|
|
256
|
+
// then falls back to transferFrom which fails because there is no direct approval.
|
|
257
|
+
vm.prank(signer);
|
|
258
|
+
vm.expectRevert();
|
|
259
|
+
jbMultiTerminal()
|
|
260
|
+
.pay({
|
|
261
|
+
projectId: revnetId,
|
|
262
|
+
token: address(testToken),
|
|
263
|
+
amount: payAmount,
|
|
264
|
+
beneficiary: signer,
|
|
265
|
+
minReturnedTokens: 0,
|
|
266
|
+
memo: "replayed permit2 payment",
|
|
267
|
+
metadata: metadata
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ───────────────────────── Permit2 Signature Helpers
|
|
272
|
+
// ─────────────────────────
|
|
273
|
+
|
|
274
|
+
/// @notice Sign a PermitSingle struct and return the concatenated signature.
|
|
275
|
+
function _getPermitSignature(
|
|
276
|
+
IAllowanceTransfer.PermitSingle memory permitSingle,
|
|
277
|
+
uint256 privateKey,
|
|
278
|
+
bytes32 domainSeparator
|
|
279
|
+
)
|
|
280
|
+
internal
|
|
281
|
+
pure
|
|
282
|
+
returns (bytes memory sig)
|
|
283
|
+
{
|
|
284
|
+
bytes32 permitHash = keccak256(abi.encode(_PERMIT_DETAILS_TYPEHASH, permitSingle.details));
|
|
285
|
+
|
|
286
|
+
bytes32 msgHash = keccak256(
|
|
287
|
+
abi.encodePacked(
|
|
288
|
+
"\x19\x01",
|
|
289
|
+
domainSeparator,
|
|
290
|
+
keccak256(
|
|
291
|
+
abi.encode(_PERMIT_SINGLE_TYPEHASH, permitHash, permitSingle.spender, permitSingle.sigDeadline)
|
|
292
|
+
)
|
|
293
|
+
)
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, msgHash);
|
|
297
|
+
return bytes.concat(r, s, bytes1(v));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
@@ -7,6 +7,8 @@ import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
|
7
7
|
import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
8
8
|
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
9
9
|
import {IJBPayoutTerminal} from "@bananapus/core-v6/src/interfaces/IJBPayoutTerminal.sol";
|
|
10
|
+
import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
|
|
11
|
+
import {JBPayHookSpecification} from "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
|
|
10
12
|
import {IREVLoans} from "../../src/interfaces/IREVLoans.sol";
|
|
11
13
|
import {REVLoanSource} from "../../src/structs/REVLoanSource.sol";
|
|
12
14
|
|
|
@@ -70,17 +72,7 @@ contract BrokenFeeTerminal is ERC165, IJBPayoutTerminal {
|
|
|
70
72
|
|
|
71
73
|
function addAccountingContextsFor(uint256, JBAccountingContext[] calldata) external override {}
|
|
72
74
|
|
|
73
|
-
function currentSurplusOf(
|
|
74
|
-
uint256,
|
|
75
|
-
JBAccountingContext[] memory,
|
|
76
|
-
uint256,
|
|
77
|
-
uint256
|
|
78
|
-
)
|
|
79
|
-
external
|
|
80
|
-
pure
|
|
81
|
-
override
|
|
82
|
-
returns (uint256)
|
|
83
|
-
{
|
|
75
|
+
function currentSurplusOf(uint256, address[] calldata, uint256, uint256) external pure override returns (uint256) {
|
|
84
76
|
return 0;
|
|
85
77
|
}
|
|
86
78
|
|
|
@@ -110,6 +102,22 @@ contract BrokenFeeTerminal is ERC165, IJBPayoutTerminal {
|
|
|
110
102
|
return 0;
|
|
111
103
|
}
|
|
112
104
|
|
|
105
|
+
function previewPayFor(
|
|
106
|
+
uint256,
|
|
107
|
+
address,
|
|
108
|
+
uint256,
|
|
109
|
+
address,
|
|
110
|
+
bytes calldata
|
|
111
|
+
)
|
|
112
|
+
external
|
|
113
|
+
pure
|
|
114
|
+
override
|
|
115
|
+
returns (JBRuleset memory, uint256, uint256, JBPayHookSpecification[] memory)
|
|
116
|
+
{
|
|
117
|
+
JBRuleset memory ruleset;
|
|
118
|
+
return (ruleset, 0, 0, new JBPayHookSpecification[](0));
|
|
119
|
+
}
|
|
120
|
+
|
|
113
121
|
function supportsInterface(bytes4 interfaceId) public view override(ERC165, IERC165) returns (bool) {
|
|
114
122
|
return interfaceId == type(IJBTerminal).interfaceId || interfaceId == type(IJBPayoutTerminal).interfaceId
|
|
115
123
|
|| super.supportsInterface(interfaceId);
|
|
@@ -184,17 +192,7 @@ contract SurplusInflator is ERC165, IJBPayoutTerminal {
|
|
|
184
192
|
|
|
185
193
|
function addAccountingContextsFor(uint256, JBAccountingContext[] calldata) external override {}
|
|
186
194
|
|
|
187
|
-
function currentSurplusOf(
|
|
188
|
-
uint256,
|
|
189
|
-
JBAccountingContext[] memory,
|
|
190
|
-
uint256,
|
|
191
|
-
uint256
|
|
192
|
-
)
|
|
193
|
-
external
|
|
194
|
-
pure
|
|
195
|
-
override
|
|
196
|
-
returns (uint256)
|
|
197
|
-
{
|
|
195
|
+
function currentSurplusOf(uint256, address[] calldata, uint256, uint256) external pure override returns (uint256) {
|
|
198
196
|
return 0;
|
|
199
197
|
}
|
|
200
198
|
|
|
@@ -224,6 +222,22 @@ contract SurplusInflator is ERC165, IJBPayoutTerminal {
|
|
|
224
222
|
return 0;
|
|
225
223
|
}
|
|
226
224
|
|
|
225
|
+
function previewPayFor(
|
|
226
|
+
uint256,
|
|
227
|
+
address,
|
|
228
|
+
uint256,
|
|
229
|
+
address,
|
|
230
|
+
bytes calldata
|
|
231
|
+
)
|
|
232
|
+
external
|
|
233
|
+
pure
|
|
234
|
+
override
|
|
235
|
+
returns (JBRuleset memory, uint256, uint256, JBPayHookSpecification[] memory)
|
|
236
|
+
{
|
|
237
|
+
JBRuleset memory ruleset;
|
|
238
|
+
return (ruleset, 0, 0, new JBPayHookSpecification[](0));
|
|
239
|
+
}
|
|
240
|
+
|
|
227
241
|
function supportsInterface(bytes4 interfaceId) public view override(ERC165, IERC165) returns (bool) {
|
|
228
242
|
return interfaceId == type(IJBTerminal).interfaceId || interfaceId == type(IJBPayoutTerminal).interfaceId
|
|
229
243
|
|| super.supportsInterface(interfaceId);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
4
|
import {IJBRulesetDataHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetDataHook.sol";
|
|
5
|
+
import {IJBCashOutHook} from "@bananapus/core-v6/src/interfaces/IJBCashOutHook.sol";
|
|
5
6
|
import {IJBPayHook} from "@bananapus/core-v6/src/interfaces/IJBPayHook.sol";
|
|
6
7
|
// forge-lint: disable-next-line(unused-import)
|
|
7
8
|
import {IJBBuybackHook} from "@bananapus/buyback-hook-v6/src/interfaces/IJBBuybackHook.sol";
|
|
@@ -16,6 +17,13 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
|
|
|
16
17
|
|
|
17
18
|
/// @notice A minimal mock buyback data hook for tests. Returns the default weight and a no-op pay hook specification.
|
|
18
19
|
contract MockBuybackDataHook is IJBRulesetDataHook, IJBPayHook {
|
|
20
|
+
bool public shouldReturnCashOutHookSpec;
|
|
21
|
+
uint256 public cashOutCountToReturn;
|
|
22
|
+
bytes public cashOutHookMetadata;
|
|
23
|
+
uint256 public cashOutHookAmount;
|
|
24
|
+
uint256 public cashOutTaxRateToReturn;
|
|
25
|
+
uint256 public totalSupplyToReturn;
|
|
26
|
+
|
|
19
27
|
function beforePayRecordedWith(JBBeforePayRecordedContext calldata context)
|
|
20
28
|
external
|
|
21
29
|
view
|
|
@@ -24,12 +32,13 @@ contract MockBuybackDataHook is IJBRulesetDataHook, IJBPayHook {
|
|
|
24
32
|
{
|
|
25
33
|
weight = context.weight;
|
|
26
34
|
hookSpecifications = new JBPayHookSpecification[](1);
|
|
27
|
-
hookSpecifications[0] =
|
|
35
|
+
hookSpecifications[0] =
|
|
36
|
+
JBPayHookSpecification({hook: IJBPayHook(address(this)), noop: false, amount: 0, metadata: ""});
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
function beforeCashOutRecordedWith(JBBeforeCashOutRecordedContext calldata context)
|
|
31
40
|
external
|
|
32
|
-
|
|
41
|
+
view
|
|
33
42
|
override
|
|
34
43
|
returns (
|
|
35
44
|
uint256 cashOutTaxRate,
|
|
@@ -38,10 +47,45 @@ contract MockBuybackDataHook is IJBRulesetDataHook, IJBPayHook {
|
|
|
38
47
|
JBCashOutHookSpecification[] memory hookSpecifications
|
|
39
48
|
)
|
|
40
49
|
{
|
|
41
|
-
cashOutTaxRate = context.cashOutTaxRate;
|
|
42
|
-
cashOutCount = context.cashOutCount;
|
|
43
|
-
totalSupply = context.totalSupply;
|
|
44
|
-
|
|
50
|
+
cashOutTaxRate = cashOutTaxRateToReturn == 0 ? context.cashOutTaxRate : cashOutTaxRateToReturn;
|
|
51
|
+
cashOutCount = cashOutCountToReturn == 0 ? context.cashOutCount : cashOutCountToReturn;
|
|
52
|
+
totalSupply = totalSupplyToReturn == 0 ? context.totalSupply : totalSupplyToReturn;
|
|
53
|
+
|
|
54
|
+
if (!shouldReturnCashOutHookSpec) {
|
|
55
|
+
hookSpecifications = new JBCashOutHookSpecification[](0);
|
|
56
|
+
return (cashOutTaxRate, cashOutCount, totalSupply, hookSpecifications);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
hookSpecifications = new JBCashOutHookSpecification[](1);
|
|
60
|
+
hookSpecifications[0] = JBCashOutHookSpecification({
|
|
61
|
+
hook: IJBCashOutHook(address(this)), noop: false, amount: cashOutHookAmount, metadata: cashOutHookMetadata
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function configureCashOutResult(
|
|
66
|
+
uint256 cashOutTaxRate,
|
|
67
|
+
uint256 cashOutCount,
|
|
68
|
+
uint256 totalSupply,
|
|
69
|
+
uint256 hookAmount,
|
|
70
|
+
bytes calldata hookMetadata
|
|
71
|
+
)
|
|
72
|
+
external
|
|
73
|
+
{
|
|
74
|
+
shouldReturnCashOutHookSpec = true;
|
|
75
|
+
cashOutTaxRateToReturn = cashOutTaxRate;
|
|
76
|
+
cashOutCountToReturn = cashOutCount;
|
|
77
|
+
totalSupplyToReturn = totalSupply;
|
|
78
|
+
cashOutHookAmount = hookAmount;
|
|
79
|
+
cashOutHookMetadata = hookMetadata;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function resetCashOutResult() external {
|
|
83
|
+
shouldReturnCashOutHookSpec = false;
|
|
84
|
+
cashOutTaxRateToReturn = 0;
|
|
85
|
+
cashOutCountToReturn = 0;
|
|
86
|
+
totalSupplyToReturn = 0;
|
|
87
|
+
cashOutHookAmount = 0;
|
|
88
|
+
cashOutHookMetadata = "";
|
|
45
89
|
}
|
|
46
90
|
|
|
47
91
|
function hasMintPermissionFor(uint256, JBRuleset calldata, address) external pure override returns (bool) {
|