@bananapus/core-v6 0.0.68 → 0.0.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/foundry.toml CHANGED
@@ -2,7 +2,7 @@
2
2
  solc = '0.8.28'
3
3
  bytecode_hash = "none"
4
4
  evm_version = 'cancun'
5
- optimizer_runs = 200
5
+ optimizer_runs = 150
6
6
  libs = ["node_modules", "lib"]
7
7
  fs_permissions = [{ access = "read-write", path = "./"}]
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/core-v6",
3
- "version": "0.0.68",
3
+ "version": "0.0.69",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -89,7 +89,7 @@ Use this file when you need deeper protocol reference material after the repo-lo
89
89
  - `JBERC20` is cloned via `Clones.clone()` -- its constructor sets invalid name/symbol; real values set in `initialize()`
90
90
  - Fee is 2.5% (`FEE = 25` out of `MAX_FEE = 1000`)
91
91
  - Project #1 is the fee beneficiary project (receives all protocol fees)
92
- - **Fee-free cashout exemption is scoped to fee-free intra-terminal payout amounts.** `_feeFreeSurplusOf[projectId][token]` accumulates the value of fee-free payouts. After any outflow (payouts, `useAllowanceOf`, non-zero-tax or feeless cashouts), the counter is capped at the remaining balance — non-fee-free funds leave first, preserving the fee-free counter. During cashout with `cashOutTaxRate=0`, the 2.5% fee applies only up to this surplus, then depletes. Once consumed, subsequent cashouts are fee-free again. Cleared on terminal migration. This prevents a round-trip fee bypass (intra-terminal payout → zero-tax cashout) while scoping fees precisely to the fee-free inflow.
92
+ - **Fee-free cashout exemption is scoped to fee-free intra-terminal payout amounts.** `feeFreeSurplusOf[projectId][token]` accumulates the value of fee-free payouts. After any outflow (payouts, `useAllowanceOf`, non-zero-tax or feeless cashouts), the counter is capped at the remaining balance — non-fee-free funds leave first, preserving the fee-free counter. During cashout with `cashOutTaxRate=0`, the 2.5% fee applies only up to this surplus, then depletes. Once consumed, subsequent cashouts are fee-free again. Cleared on terminal migration. This prevents a round-trip fee bypass (intra-terminal payout → zero-tax cashout) while scoping fees precisely to the fee-free inflow.
93
93
  - `JBProjects` constructor optionally mints project #1 to `feeProjectOwner` -- if `address(0)`, no fee project is created
94
94
  - `JBMultiTerminal` derives `DIRECTORY` from the provided `store` in its constructor -- not passed directly
95
95
  - `JBPrices.pricePerUnitOf()` checks project direct feeds, project inverse feeds, default direct feeds, then default inverse feeds. It skips feeds that revert or return zero.
@@ -111,7 +111,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
111
111
  IJBTokens public immutable override TOKENS;
112
112
 
113
113
  //*********************************************************************//
114
- // --------------------- internal stored properties ------------------ //
114
+ // --------------------- public stored properties -------------------- //
115
115
  //*********************************************************************//
116
116
 
117
117
  /// @notice The cumulative amount of fee-free intra-terminal payouts a project has received for a given token.
@@ -126,7 +126,11 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
126
126
  /// unconsumed balance. There is no admin function to reset it.
127
127
  /// @custom:param projectId The ID of the project that received the payout.
128
128
  /// @custom:param token The token that was received.
129
- mapping(uint256 projectId => mapping(address token => uint256)) internal _feeFreeSurplusOf;
129
+ mapping(uint256 projectId => mapping(address token => uint256)) public override feeFreeSurplusOf;
130
+
131
+ //*********************************************************************//
132
+ // --------------------- internal stored properties ------------------ //
133
+ //*********************************************************************//
130
134
 
131
135
  /// @notice Fees currently held for each project.
132
136
  /// @dev Projects can temporarily hold fees and unlock them later by adding funds to the project's balance.
@@ -447,7 +451,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
447
451
 
448
452
  // Same-terminal adds never invoke destination pay hooks, so the full amount remains in the
449
453
  // destination project's balance and must be fee-liable on its later zero-tax cashout.
450
- if (isThisTerminal) _feeFreeSurplusOf[split.projectId][token] += netPayoutAmount;
454
+ if (isThisTerminal) feeFreeSurplusOf[split.projectId][token] += netPayoutAmount;
451
455
  } else {
452
456
  // Keep a reference to the beneficiary of the payment.
453
457
  address beneficiary = split.beneficiary != address(0) ? split.beneficiary : originalMessageSender;
@@ -592,7 +596,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
592
596
  }
593
597
 
594
598
  // Clear fee-free surplus tracking — the fee-free liability is settled by the migration fee below.
595
- delete _feeFreeSurplusOf[projectId][token];
599
+ delete feeFreeSurplusOf[projectId][token];
596
600
 
597
601
  // Terminal migration intentionally does not transfer held fees. Held fees belong to the
598
602
  // fee beneficiary (project #1), not the migrating project. They unlock after 28 days regardless of terminal.
@@ -1213,7 +1217,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
1213
1217
  /// @param token The token whose fee-free surplus to cap.
1214
1218
  function _capFeeFreeSurplus(uint256 projectId, address token) internal {
1215
1219
  // Get the current fee-free surplus for this project/token pair.
1216
- uint256 feeFreeSurplus = _feeFreeSurplusOf[projectId][token];
1220
+ uint256 feeFreeSurplus = feeFreeSurplusOf[projectId][token];
1217
1221
 
1218
1222
  // Nothing to cap if there's no fee-free surplus tracked.
1219
1223
  if (feeFreeSurplus == 0) return;
@@ -1223,7 +1227,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
1223
1227
 
1224
1228
  // Cap fee-free surplus at the remaining balance.
1225
1229
  if (feeFreeSurplus > remainingBalance) {
1226
- _feeFreeSurplusOf[projectId][token] = remainingBalance;
1230
+ feeFreeSurplusOf[projectId][token] = remainingBalance;
1227
1231
  }
1228
1232
  }
1229
1233
 
@@ -1300,11 +1304,11 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
1300
1304
  }
1301
1305
  } else {
1302
1306
  // Zero tax: fees apply only up to the fee-free surplus (round-trip prevention).
1303
- uint256 feeFreeSurplus = _feeFreeSurplusOf[projectId][tokenToReclaim];
1307
+ uint256 feeFreeSurplus = feeFreeSurplusOf[projectId][tokenToReclaim];
1304
1308
  if (feeFreeSurplus != 0) {
1305
1309
  uint256 feeableAmount = reclaimAmount < feeFreeSurplus ? reclaimAmount : feeFreeSurplus;
1306
1310
  unchecked {
1307
- _feeFreeSurplusOf[projectId][tokenToReclaim] = feeFreeSurplus - feeableAmount;
1311
+ feeFreeSurplusOf[projectId][tokenToReclaim] = feeFreeSurplus - feeableAmount;
1308
1312
  }
1309
1313
  amountEligibleForFees += feeableAmount;
1310
1314
  unchecked {
@@ -1338,7 +1342,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
1338
1342
  });
1339
1343
  }
1340
1344
 
1341
- // Cap fee-free surplus after every cash-out path so stale `_feeFreeSurplusOf` cannot survive after
1345
+ // Cap fee-free surplus after every cash-out path so stale `feeFreeSurplusOf` cannot survive after
1342
1346
  // associated surplus leaves. Do this after hook fulfillment so hook-driven balance reductions are included.
1343
1347
  _capFeeFreeSurplus({projectId: projectId, token: tokenToReclaim});
1344
1348
 
@@ -1788,7 +1792,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
1788
1792
  ++i;
1789
1793
  }
1790
1794
  }
1791
- _feeFreeSurplusOf[projectId][token] += feeFreeAmount;
1795
+ feeFreeSurplusOf[projectId][token] += feeFreeAmount;
1792
1796
  }
1793
1797
 
1794
1798
  // Keep a reference to the number of tokens issued for the beneficiary.
@@ -1885,7 +1889,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
1885
1889
  _recordAddedBalanceFor({projectId: projectId, token: token, amount: amount});
1886
1890
  // The store balance was credited first; this mirrors that bounded increase for fee recovery.
1887
1891
  unchecked {
1888
- _feeFreeSurplusOf[projectId][token] += amount;
1892
+ feeFreeSurplusOf[projectId][token] += amount;
1889
1893
  }
1890
1894
  }
1891
1895
  }
@@ -43,4 +43,14 @@ interface IJBMultiTerminal is IJBTerminal, IJBFeeTerminal, IJBCashOutTerminal, I
43
43
  /// @dev Per-referrer cumulative fee payment amounts credited via this terminal are stored in
44
44
  /// `JBTerminalStore.feeVolumeByReferralOf(address terminal, uint256 referralChainId, uint256 referralProjectId)`.
45
45
  function currentReferralProjectId() external view returns (uint256);
46
+
47
+ /// @notice The cumulative amount of fee-free intra-terminal payouts a project has received for a given token.
48
+ /// @dev Incremented each time a fee-free payout lands (same terminal, no fee charged) and consumed during
49
+ /// zero-tax cash outs to prevent a round-trip fee bypass (intra-terminal payout -> zero-tax cash out). Exposed
50
+ /// for off-chain indexers and integrating contracts that need to reason about a project's outstanding fee-free
51
+ /// exposure before performing related operations.
52
+ /// @param projectId The ID of the project that received the payout.
53
+ /// @param token The token that was received.
54
+ /// @return The cumulative unconsumed fee-free surplus tracked for the (project, token) pair.
55
+ function feeFreeSurplusOf(uint256 projectId, address token) external view returns (uint256);
46
56
  }