@bananapus/suckers-v6 0.0.66 → 0.0.67

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/suckers-v6",
3
- "version": "0.0.66",
3
+ "version": "0.0.67",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -81,7 +81,7 @@ contract JBArbitrumSucker is JBSucker, IJBArbitrumSucker {
81
81
 
82
82
  /// @notice Returns the chain on which the peer is located.
83
83
  /// @return chainId of the peer.
84
- function peerChainId() external view virtual override returns (uint256) {
84
+ function peerChainId() public view virtual override returns (uint256) {
85
85
  uint256 chainId = block.chainid;
86
86
  if (chainId == ARBChains.ETH_CHAINID) return ARBChains.ARB_CHAINID;
87
87
  if (chainId == ARBChains.ARB_CHAINID) return ARBChains.ETH_CHAINID;
@@ -40,7 +40,7 @@ contract JBBaseSucker is JBOptimismSucker {
40
40
 
41
41
  /// @notice Returns the chain on which the peer is located.
42
42
  /// @return chainId of the peer.
43
- function peerChainId() external view virtual override returns (uint256) {
43
+ function peerChainId() public view virtual override returns (uint256) {
44
44
  uint256 chainId = block.chainid;
45
45
  if (chainId == 1) return 8453;
46
46
  if (chainId == 8453) return 1;
@@ -119,7 +119,7 @@ contract JBCCIPSucker is JBSucker, IAny2EVMMessageReceiver {
119
119
 
120
120
  /// @notice Returns the chain on which the peer is located.
121
121
  /// @return chainId The chain ID of the peer.
122
- function peerChainId() external view virtual override returns (uint256 chainId) {
122
+ function peerChainId() public view virtual override returns (uint256 chainId) {
123
123
  return REMOTE_CHAIN_ID;
124
124
  }
125
125
 
@@ -64,7 +64,7 @@ contract JBOptimismSucker is JBSucker, IJBOptimismSucker {
64
64
 
65
65
  /// @notice Returns the chain on which the peer is located.
66
66
  /// @return chainId of the peer.
67
- function peerChainId() external view virtual override returns (uint256) {
67
+ function peerChainId() public view virtual override returns (uint256) {
68
68
  uint256 chainId = block.chainid;
69
69
  if (chainId == 1) return 10;
70
70
  if (chainId == 10) return 1;
package/src/JBSucker.sol CHANGED
@@ -40,6 +40,7 @@ import {JBDenominatedAmount} from "./structs/JBDenominatedAmount.sol";
40
40
  import {JBInboxTreeRoot} from "./structs/JBInboxTreeRoot.sol";
41
41
  import {JBMessageRoot} from "./structs/JBMessageRoot.sol";
42
42
  import {JBOutboxTree} from "./structs/JBOutboxTree.sol";
43
+ import {JBPeerChainValue} from "./structs/JBPeerChainValue.sol";
43
44
  import {JBRemoteToken} from "./structs/JBRemoteToken.sol";
44
45
  import {JBTokenMapping} from "./structs/JBTokenMapping.sol";
45
46
 
@@ -773,10 +774,6 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
773
774
  return _outboxOf[token];
774
775
  }
775
776
 
776
- /// @notice Returns the chain on which the peer is located.
777
- /// @return chain ID of the peer.
778
- function peerChainId() external view virtual returns (uint256);
779
-
780
777
  /// @notice The peer chain balance, converted from the source denomination to the requested currency and decimal
781
778
  /// precision using the local JBPrices oracle.
782
779
  /// @param decimals The decimal precision for the returned value.
@@ -792,6 +789,28 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
792
789
  });
793
790
  }
794
791
 
792
+ /// @notice The peer chain balance bundled with the peer chain ID and snapshot freshness key.
793
+ /// @dev Lets aggregators (e.g. `JBSuckerRegistry`) read the value, the peer chain it belongs to, and its
794
+ /// freshness in one call instead of three separate staticcalls. The `value` is identical to
795
+ /// `peerChainBalanceOf`.
796
+ /// @param decimals The decimal precision for the returned value.
797
+ /// @param currency The currency to normalize to (e.g. `uint256(uint160(JBConstants.NATIVE_TOKEN))` for ETH).
798
+ /// @return A `JBPeerChainValue` with the converted balance, peer chain ID, and snapshot freshness key.
799
+ function peerChainBalanceValueOf(
800
+ uint256 decimals,
801
+ uint256 currency
802
+ )
803
+ external
804
+ view
805
+ returns (JBPeerChainValue memory)
806
+ {
807
+ return JBPeerChainValue({
808
+ value: _convertPeerValue({source: _peerChainBalance, decimals: decimals, currency: currency}),
809
+ peerChainId: peerChainId(),
810
+ snapshotTimestamp: snapshotTimestamp
811
+ });
812
+ }
813
+
795
814
  /// @notice The peer chain surplus, converted from the source denomination to the requested currency and decimal
796
815
  /// precision using the local JBPrices oracle.
797
816
  /// @param decimals The decimal precision for the returned value.
@@ -807,6 +826,39 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
807
826
  });
808
827
  }
809
828
 
829
+ /// @notice The peer chain surplus bundled with the peer chain ID and snapshot freshness key.
830
+ /// @dev Lets aggregators (e.g. `JBSuckerRegistry`) read the value, the peer chain it belongs to, and its
831
+ /// freshness in one call instead of three separate staticcalls. The `value` is identical to
832
+ /// `peerChainSurplusOf`.
833
+ /// @param decimals The decimal precision for the returned value.
834
+ /// @param currency The currency to normalize to (e.g. `uint256(uint160(JBConstants.NATIVE_TOKEN))` for ETH).
835
+ /// @return A `JBPeerChainValue` with the converted surplus, peer chain ID, and snapshot freshness key.
836
+ function peerChainSurplusValueOf(
837
+ uint256 decimals,
838
+ uint256 currency
839
+ )
840
+ external
841
+ view
842
+ returns (JBPeerChainValue memory)
843
+ {
844
+ return JBPeerChainValue({
845
+ value: _convertPeerValue({source: _peerChainSurplus, decimals: decimals, currency: currency}),
846
+ peerChainId: peerChainId(),
847
+ snapshotTimestamp: snapshotTimestamp
848
+ });
849
+ }
850
+
851
+ /// @notice The peer chain total supply bundled with the peer chain ID and snapshot freshness key.
852
+ /// @dev Lets aggregators (e.g. `JBSuckerRegistry`) read the value, the peer chain it belongs to, and its
853
+ /// freshness in one call instead of three separate staticcalls. The `value` is identical to
854
+ /// `peerChainTotalSupply`.
855
+ /// @return A `JBPeerChainValue` with the total supply, peer chain ID, and snapshot freshness key.
856
+ function peerChainTotalSupplyValue() external view returns (JBPeerChainValue memory) {
857
+ return JBPeerChainValue({
858
+ value: peerChainTotalSupply, peerChainId: peerChainId(), snapshotTimestamp: snapshotTimestamp
859
+ });
860
+ }
861
+
810
862
  /// @notice Information about the token on the remote chain that the given token on the local chain is mapped to.
811
863
  /// @param token The local terminal token to get the remote token for.
812
864
  /// @return The remote token mapping for the given local token.
@@ -841,6 +893,12 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
841
893
  return amount;
842
894
  }
843
895
 
896
+ /// @notice Returns the chain on which the peer is located.
897
+ /// @dev `public` (not `external`) so the combined peer-chain views in this contract can read it internally
898
+ /// without a self-call; subclasses implement the bridge-specific chain ID.
899
+ /// @return chain ID of the peer.
900
+ function peerChainId() public view virtual returns (uint256);
901
+
844
902
  /// @notice The peer sucker on the remote chain, as a bytes32 for cross-VM compatibility.
845
903
  /// @dev Defaults to `_toBytes32(address(this))`, assuming deterministic cross-chain deployment via CREATE2. The
846
904
  /// deployer (`JBSuckerDeployer`) uses `salt = keccak256(abi.encodePacked(_msgSender(), salt))` to ensure
@@ -11,7 +11,7 @@ import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol"
11
11
  import {Context} from "@openzeppelin/contracts/utils/Context.sol";
12
12
  import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
13
13
 
14
- import {JBDenominatedAmount} from "./structs/JBDenominatedAmount.sol";
14
+ import {JBPeerChainValue} from "./structs/JBPeerChainValue.sol";
15
15
  import {JBSuckerState} from "./enums/JBSuckerState.sol";
16
16
  import {IJBSucker} from "./interfaces/IJBSucker.sol";
17
17
  import {IJBSuckerDeployer} from "./interfaces/IJBSuckerDeployer.sol";
@@ -231,16 +231,12 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
231
231
  (, uint256 val) = _suckersOf[projectId].tryGet(allSuckers[i]);
232
232
  // Include both active and deprecated suckers in aggregate economic views.
233
233
  if (val == _SUCKER_EXISTS || val == _SUCKER_DEPRECATED) {
234
- try IJBSucker(allSuckers[i]).peerChainBalanceOf({decimals: decimals, currency: currency}) returns (
235
- JBDenominatedAmount memory amt
234
+ // One call returns the value, peer chain ID, and snapshot freshness key together.
235
+ try IJBSucker(allSuckers[i]).peerChainBalanceValueOf({decimals: decimals, currency: currency}) returns (
236
+ JBPeerChainValue memory read
236
237
  ) {
237
- uint256 chainId = _peerChainIdOf(IJBSucker(allSuckers[i]));
238
- scratch.chainCount = _recordPeerValue({
239
- scratch: scratch,
240
- chainId: chainId,
241
- value: amt.value,
242
- snapshotTimestamp: _snapshotTimestampOf(allSuckers[i]),
243
- isActive: val == _SUCKER_EXISTS
238
+ scratch.chainCount = _recordPeerChainValue({
239
+ scratch: scratch, read: read, sucker: allSuckers[i], isActive: val == _SUCKER_EXISTS
244
240
  });
245
241
  } catch {}
246
242
  }
@@ -287,16 +283,12 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
287
283
  (, uint256 val) = _suckersOf[projectId].tryGet(allSuckers[i]);
288
284
  // Include both active and deprecated suckers in aggregate economic views.
289
285
  if (val == _SUCKER_EXISTS || val == _SUCKER_DEPRECATED) {
290
- try IJBSucker(allSuckers[i]).peerChainSurplusOf({decimals: decimals, currency: currency}) returns (
291
- JBDenominatedAmount memory amt
286
+ // One call returns the value, peer chain ID, and snapshot freshness key together.
287
+ try IJBSucker(allSuckers[i]).peerChainSurplusValueOf({decimals: decimals, currency: currency}) returns (
288
+ JBPeerChainValue memory read
292
289
  ) {
293
- uint256 chainId = _peerChainIdOf(IJBSucker(allSuckers[i]));
294
- scratch.chainCount = _recordPeerValue({
295
- scratch: scratch,
296
- chainId: chainId,
297
- value: amt.value,
298
- snapshotTimestamp: _snapshotTimestampOf(allSuckers[i]),
299
- isActive: val == _SUCKER_EXISTS
290
+ scratch.chainCount = _recordPeerChainValue({
291
+ scratch: scratch, read: read, sucker: allSuckers[i], isActive: val == _SUCKER_EXISTS
300
292
  });
301
293
  } catch {}
302
294
  }
@@ -332,14 +324,10 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
332
324
  (, uint256 val) = _suckersOf[projectId].tryGet(allSuckers[i]);
333
325
  // Include both active and deprecated suckers in aggregate economic views.
334
326
  if (val == _SUCKER_EXISTS || val == _SUCKER_DEPRECATED) {
335
- try IJBSucker(allSuckers[i]).peerChainTotalSupply() returns (uint256 supply) {
336
- uint256 chainId = _peerChainIdOf(IJBSucker(allSuckers[i]));
337
- scratch.chainCount = _recordPeerValue({
338
- scratch: scratch,
339
- chainId: chainId,
340
- value: supply,
341
- snapshotTimestamp: _snapshotTimestampOf(allSuckers[i]),
342
- isActive: val == _SUCKER_EXISTS
327
+ // One call returns the value, peer chain ID, and snapshot freshness key together.
328
+ try IJBSucker(allSuckers[i]).peerChainTotalSupplyValue() returns (JBPeerChainValue memory read) {
329
+ scratch.chainCount = _recordPeerChainValue({
330
+ scratch: scratch, read: read, sucker: allSuckers[i], isActive: val == _SUCKER_EXISTS
343
331
  });
344
332
  } catch {}
345
333
  }
@@ -459,16 +447,34 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
459
447
  }
460
448
  }
461
449
 
462
- /// @notice Reads a sucker's snapshot timestamp, returning zero if the sucker does not expose it.
463
- /// @dev Older or malformed suckers should not brick aggregate registry views. A zero timestamp makes their value
464
- /// lose to any successful fresh read for the same peer chain.
465
- /// @param sucker The sucker to query.
466
- /// @return timestamp The reported snapshot timestamp, or zero if the call fails.
467
- function _snapshotTimestampOf(address sucker) internal view returns (uint256 timestamp) {
468
- // Keep aggregate views available even if one registered sucker has a stale ABI or reverts unexpectedly.
469
- try IJBSucker(sucker).snapshotTimestamp() returns (uint256 result) {
470
- timestamp = result;
471
- } catch {}
450
+ /// @notice Records a combined peer-chain read (value, peer chain ID, snapshot freshness key) from one sucker.
451
+ /// @dev A wrapper over `_recordPeerValue` that unpacks the single-call `JBPeerChainValue` read and enforces the
452
+ /// same non-zero peer-chain requirement the registry applies everywhere else. The peer-chain check reverts here
453
+ /// (inside the caller's `try` success body, so the revert propagates) to preserve the prior behavior where a
454
+ /// sucker reporting a zero peer chain ID fails the whole aggregate view.
455
+ /// @param scratch The per-chain aggregate values and freshness keys recorded so far.
456
+ /// @param read The combined value, peer chain ID, and snapshot freshness key returned by the sucker.
457
+ /// @param sucker The sucker the read came from, used only for the zero-peer-chain error.
458
+ /// @param isActive Whether the value came from an active sucker.
459
+ /// @return The updated number of populated chain entries.
460
+ function _recordPeerChainValue(
461
+ PeerValueScratch memory scratch,
462
+ JBPeerChainValue memory read,
463
+ address sucker,
464
+ bool isActive
465
+ )
466
+ internal
467
+ pure
468
+ returns (uint256)
469
+ {
470
+ if (read.peerChainId == 0) revert JBSuckerRegistry_ZeroPeerChainId({sucker: sucker});
471
+ return _recordPeerValue({
472
+ scratch: scratch,
473
+ chainId: read.peerChainId,
474
+ value: read.value,
475
+ snapshotTimestamp: read.snapshotTimestamp,
476
+ isActive: isActive
477
+ });
472
478
  }
473
479
 
474
480
  //*********************************************************************//
@@ -9,6 +9,7 @@ import {JBClaim} from "../structs/JBClaim.sol";
9
9
  import {JBDenominatedAmount} from "../structs/JBDenominatedAmount.sol";
10
10
  import {JBInboxTreeRoot} from "../structs/JBInboxTreeRoot.sol";
11
11
  import {JBOutboxTree} from "../structs/JBOutboxTree.sol";
12
+ import {JBPeerChainValue} from "../structs/JBPeerChainValue.sol";
12
13
  import {JBRemoteToken} from "../structs/JBRemoteToken.sol";
13
14
  import {JBSuckerState} from "../enums/JBSuckerState.sol";
14
15
  import {JBTokenMapping} from "../structs/JBTokenMapping.sol";
@@ -160,6 +161,14 @@ interface IJBSucker is IERC165 {
160
161
  /// @return A `JBDenominatedAmount` with the converted value.
161
162
  function peerChainBalanceOf(uint256 decimals, uint256 currency) external view returns (JBDenominatedAmount memory);
162
163
 
164
+ /// @notice The peer chain balance bundled with the peer chain ID and snapshot freshness key.
165
+ /// @dev Lets aggregators read the value, the peer chain it belongs to, and its freshness in one call. The
166
+ /// `value` matches `peerChainBalanceOf`.
167
+ /// @param decimals The decimal precision for the returned value.
168
+ /// @param currency The currency to normalize to.
169
+ /// @return A `JBPeerChainValue` with the converted balance, peer chain ID, and snapshot freshness key.
170
+ function peerChainBalanceValueOf(uint256 decimals, uint256 currency) external view returns (JBPeerChainValue memory);
171
+
163
172
  /// @notice The aggregate peer chain surplus, normalized to a desired currency and decimal precision using JBPrices.
164
173
  /// @dev The surplus is stored as ETH-denominated (18 decimals) and converted to the requested currency/decimals
165
174
  /// using the local JBPrices oracle.
@@ -168,12 +177,26 @@ interface IJBSucker is IERC165 {
168
177
  /// @return A `JBDenominatedAmount` with the converted value.
169
178
  function peerChainSurplusOf(uint256 decimals, uint256 currency) external view returns (JBDenominatedAmount memory);
170
179
 
180
+ /// @notice The peer chain surplus bundled with the peer chain ID and snapshot freshness key.
181
+ /// @dev Lets aggregators read the value, the peer chain it belongs to, and its freshness in one call. The
182
+ /// `value` matches `peerChainSurplusOf`.
183
+ /// @param decimals The decimal precision for the returned value.
184
+ /// @param currency The currency to normalize to.
185
+ /// @return A `JBPeerChainValue` with the converted surplus, peer chain ID, and snapshot freshness key.
186
+ function peerChainSurplusValueOf(uint256 decimals, uint256 currency) external view returns (JBPeerChainValue memory);
187
+
171
188
  /// @notice The last known total token supply on the peer chain, updated each time a bridge message is received.
172
189
  /// @dev Used by data hooks to compute `effectiveTotalSupply = localSupply + sum(peerChainTotalSupply)` across all
173
190
  /// suckers, preventing cash out tax bypass on chains where a holder dominates the local supply.
174
191
  /// @return The peer chain's total supply.
175
192
  function peerChainTotalSupply() external view returns (uint256);
176
193
 
194
+ /// @notice The peer chain total supply bundled with the peer chain ID and snapshot freshness key.
195
+ /// @dev Lets aggregators read the value, the peer chain it belongs to, and its freshness in one call. The
196
+ /// `value` matches `peerChainTotalSupply`.
197
+ /// @return A `JBPeerChainValue` with the total supply, peer chain ID, and snapshot freshness key.
198
+ function peerChainTotalSupplyValue() external view returns (JBPeerChainValue memory);
199
+
177
200
  /// @notice The ID of the project on the local chain that this sucker is associated with.
178
201
  /// @return The project ID.
179
202
  function projectId() external view returns (uint256);
@@ -0,0 +1,15 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ /// @notice A single peer-chain aggregate read bundled with the keys the registry needs to dedupe and rank it.
5
+ /// @dev Returned by the sucker's combined peer-chain views so the registry can read the value, the peer chain it
6
+ /// belongs to, and its snapshot freshness in one call instead of three separate staticcalls.
7
+ /// @custom:member value The requested peer-chain amount (balance, surplus, or total supply), already converted to the
8
+ /// caller's currency and decimals where applicable.
9
+ /// @custom:member peerChainId The chain ID of the remote peer this snapshot describes.
10
+ /// @custom:member snapshotTimestamp The freshness key of the snapshot the value came from.
11
+ struct JBPeerChainValue {
12
+ uint256 value;
13
+ uint256 peerChainId;
14
+ uint256 snapshotTimestamp;
15
+ }