@bananapus/core-v6 0.0.51 → 0.0.52

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/CHANGELOG.md CHANGED
@@ -57,6 +57,9 @@ This file describes the verified change from `nana-core-v5` to the current `nana
57
57
  - `IJBCashOutTerminal.previewCashOutFrom(...)` is new.
58
58
  - `IJBCashOutTerminal.payAfterCashOutTokensOf(...)` is new.
59
59
  - `IJBCashOutTerminal.addToBalanceAfterCashOutTokensOf(...)` is new.
60
+ - `IJBFeeTerminal.FEE()` is REMOVED. The terminal no longer re-exports the protocol fee constant; read
61
+ `JBConstants.FEE` directly. Off-chain integrators that previously called `terminal.FEE()` must switch to
62
+ reading the constant from `JBConstants`.
60
63
  - `IJBTerminalStore.previewPayFrom(...)` and `previewCashOutFrom(...)` are new.
61
64
  - `IJBTerminal.currentSurplusOf(...)` changed parameter shape.
62
65
  - `JBRulesetMetadata` adds `pauseCrossProjectFeeFreeInflows` and narrows `metadata` from 14 to 13 bits.
@@ -96,6 +99,8 @@ This file describes the verified change from `nana-core-v5` to the current `nana
96
99
  `HookAfterRecordCashOut` from the terminal address)
97
100
  - Renamed functions
98
101
  - `IJBController.addPriceFeed(...)` -> `addPriceFeedFor(...)`
102
+ - Removed functions
103
+ - `IJBFeeTerminal.FEE()` (read `JBConstants.FEE` directly)
99
104
  - Changed function shapes
100
105
  - `IJBTerminal.currentSurplusOf(...)`
101
106
  - Added events
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/core-v6",
3
- "version": "0.0.51",
3
+ "version": "0.0.52",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -81,16 +81,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
81
81
  error JBMultiTerminal_TokenNotAccepted(address token);
82
82
  error JBMultiTerminal_UnderMin(uint256 value, uint256 min);
83
83
 
84
- //*********************************************************************//
85
- // ------------------------- public constants ------------------------ //
86
- //*********************************************************************//
87
-
88
- /// @notice This terminal's fee (as a fraction out of `JBConstants.MAX_FEE`).
89
- /// @dev Fees are charged on payouts to addresses and surplus allowance usage, as well as cash outs while the
90
- /// cash out tax rate is less than 100%. Re-exports `JBConstants.FEE` so external callers can read it through
91
- /// the `IJBFeeTerminal` interface.
92
- uint256 public constant override FEE = JBConstants.FEE;
93
-
94
84
  //*********************************************************************//
95
85
  // ------------------------ internal constants ----------------------- //
96
86
  //*********************************************************************//
@@ -260,7 +250,14 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
260
250
  override
261
251
  returns (uint256 reclaimAmount)
262
252
  {
253
+ // Caller must hold (or be operator with) `CASH_OUT_TOKENS` permission on the source `holder`/`projectId`.
254
+ // Burning A's tokens is the load-bearing side effect — gating it stays at the same authority bar as the
255
+ // direct `cashOutTokensOf` entrypoint.
263
256
  _requireCashOutPermissionFrom({holder: holder, projectId: projectId});
257
+
258
+ // Destination opt-out check: B's current ruleset can set `pauseCrossProjectFeeFreeInflows = true` to
259
+ // refuse cross-project fee-free credits. Without this gate, anyone holding A's tokens could push a
260
+ // deferred-fee credit onto `_feeFreeSurplusOf[B]` without B consenting.
264
261
  _requireBeneficiaryAcceptsFeeFreeInflows(beneficiaryProjectId);
265
262
 
266
263
  // Burn source-project tokens, run cashout-side hooks, take any hook fees, and cap source fee-free.
@@ -278,13 +275,19 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
278
275
  // Nothing to route if the data hook returned zero reclaim.
279
276
  if (reclaimAmount == 0) return 0;
280
277
 
281
- // Route the reclaim to B's primary terminal as an `addToBalanceOf`, then credit B's fee-free surplus
282
- // by the delivery delta. `_efficientAddToBalance` handles same-terminal vs cross-terminal and
283
- // hardcodes `shouldReturnHeldFees: false` — this entrypoint cannot unlock B's held fees.
278
+ // Resolve B's primary terminal for the reclaim token. Could be this terminal (same-terminal short-
279
+ // circuit) or a router that swaps before depositing. Reverts if no primary terminal is registered.
284
280
  IJBTerminal destinationTerminal = _resolveBeneficiaryTerminal(beneficiaryProjectId, tokenToReclaim);
281
+
282
+ // Snapshot B's per-context balances on this terminal BEFORE routing. The post-routing comparison
283
+ // identifies the bucket the reclaim actually landed in (matters for cross-token routes where a router
284
+ // swaps to a different token in B's accounting-context list).
285
285
  (JBAccountingContext[] memory contexts, uint256[] memory balancesBefore) =
286
286
  _snapshotBeneficiaryContextBalances(beneficiaryProjectId);
287
287
 
288
+ // Route via `_efficientAddToBalance` — handles same-terminal vs cross-terminal (with the standard
289
+ // `_beforeTransferTo`/`_afterTransferTo` allowance dance) and hardcodes `shouldReturnHeldFees: false`,
290
+ // so this entrypoint cannot be used to unlock B's held fees on top of the source-side fee skip.
288
291
  _efficientAddToBalance({
289
292
  terminal: destinationTerminal,
290
293
  projectId: beneficiaryProjectId,
@@ -293,6 +296,9 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
293
296
  metadata: addToBalanceMetadata
294
297
  });
295
298
 
299
+ // Credit `_feeFreeSurplusOf[B]` on the first of B's contexts whose balance grew. This binds the
300
+ // skipped source-side fee on the destination side: B's next non-feeless cashout pays it. Reverts if
301
+ // no context grew (no delivery landed) — without delivery, the fee skip would leak.
296
302
  _creditFirstGrowingBeneficiaryContext(beneficiaryProjectId, contexts, balancesBefore);
297
303
  }
298
304
 
@@ -737,7 +743,14 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
737
743
  override
738
744
  returns (uint256 reclaimAmount, uint256 beneficiaryTokenCount)
739
745
  {
746
+ // Caller must hold (or be operator with) `CASH_OUT_TOKENS` permission on the source `holder`/`projectId`.
747
+ // Burning A's tokens is the load-bearing side effect — gating it stays at the same authority bar as the
748
+ // direct `cashOutTokensOf` entrypoint.
740
749
  _requireCashOutPermissionFrom({holder: holder, projectId: projectId});
750
+
751
+ // Destination opt-out check: B's current ruleset can set `pauseCrossProjectFeeFreeInflows = true` to
752
+ // refuse cross-project fee-free credits. Without this gate, anyone holding A's tokens could push a
753
+ // deferred-fee credit onto `_feeFreeSurplusOf[B]` without B consenting.
741
754
  _requireBeneficiaryAcceptsFeeFreeInflows(beneficiaryProjectId);
742
755
 
743
756
  // Burn source-project tokens, run cashout-side hooks, take any hook fees, and cap source fee-free.
@@ -1377,14 +1390,30 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
1377
1390
  )
1378
1391
  internal
1379
1392
  {
1393
+ // Walk B's accounting contexts in declared order. The first context whose balance grew is treated as
1394
+ // the bucket the router chose to deposit into (typically the post-swap token in a cross-token route).
1395
+ // We bind the deferred source-side fee to that bucket and stop — subsequent grown contexts are ignored
1396
+ // by design, capping the credit at one delivery delta even if a misbehaving router somehow split the
1397
+ // deposit across multiple buckets.
1380
1398
  for (uint256 i; i < contexts.length;) {
1399
+ // Read B's post-routing balance for this context's token. Compared against the pre-routing snapshot
1400
+ // captured by `_snapshotBeneficiaryContextBalances` to detect the delivery delta.
1381
1401
  uint256 balanceAfter =
1382
1402
  STORE.balanceOf({terminal: address(this), projectId: beneficiaryProjectId, token: contexts[i].token});
1383
1403
 
1384
1404
  if (balanceAfter > balancesBefore[i]) {
1405
+ // Credit the delivery delta into B's fee-free counter for this token. `unchecked` is safe:
1406
+ // `balanceAfter > balancesBefore[i]` is the loop condition, so the subtraction can't underflow,
1407
+ // and the addition can't overflow before the underlying balance does (terminal balance is the
1408
+ // upper bound on any cumulative credit — see the cap call below).
1385
1409
  unchecked {
1386
1410
  _feeFreeSurplusOf[beneficiaryProjectId][contexts[i].token] += balanceAfter - balancesBefore[i];
1387
1411
  }
1412
+
1413
+ // Cap the credit at B's current balance for this token. If pay/addToBalance hooks pulled funds
1414
+ // back out during routing, the post-balance read inside `_capFeeFreeSurplus` clamps the
1415
+ // counter so it never exceeds what's actually sitting in B's bucket. Without this, B's later
1416
+ // zero-tax cashouts would over-fee phantom amounts.
1388
1417
  _capFeeFreeSurplus({projectId: beneficiaryProjectId, token: contexts[i].token});
1389
1418
  return;
1390
1419
  }
@@ -1394,6 +1423,9 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
1394
1423
  }
1395
1424
  }
1396
1425
 
1426
+ // No context grew. Either the destination terminal silently dropped the funds or routed them
1427
+ // elsewhere (e.g. to a different terminal not registered as B's primary). Revert the entire
1428
+ // cross-project flow so the source-side fee skip never becomes a leak — A's burn is undone too.
1397
1429
  revert JBMultiTerminal_BeneficiaryProjectNotPaid(beneficiaryProjectId);
1398
1430
  }
1399
1431
 
@@ -2112,7 +2144,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
2112
2144
  projectId: projectId,
2113
2145
  token: token,
2114
2146
  amount: amount,
2115
- fee: FEE,
2147
+ fee: JBConstants.FEE,
2116
2148
  beneficiary: beneficiary,
2117
2149
  caller: _msgSender()
2118
2150
  });
@@ -71,9 +71,6 @@ interface IJBFeeTerminal is IJBTerminal {
71
71
  address caller
72
72
  );
73
73
 
74
- /// @notice The terminal's fee as a fraction of `JBConstants.MAX_FEE`.
75
- function FEE() external view returns (uint256);
76
-
77
74
  /// @notice The contract that tracks feeless addresses.
78
75
  function FEELESS_ADDRESSES() external view returns (IJBFeelessAddresses);
79
76