@bananapus/suckers-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.
@@ -4,13 +4,17 @@ pragma solidity 0.8.28;
4
4
  import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
5
5
  import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
6
6
  import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
7
+ import {IJBPrices} from "@bananapus/core-v6/src/interfaces/IJBPrices.sol";
7
8
  import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
9
+ import {JBFixedPointNumber} from "@bananapus/core-v6/src/libraries/JBFixedPointNumber.sol";
8
10
  import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
9
11
  import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
10
12
  import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
11
13
  import {Context} from "@openzeppelin/contracts/utils/Context.sol";
12
14
  import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
15
+ import {mulDiv} from "@prb/math/src/Common.sol";
13
16
 
17
+ import {JBPeerChainContext} from "./structs/JBPeerChainContext.sol";
14
18
  import {JBPeerChainValue} from "./structs/JBPeerChainValue.sol";
15
19
  import {JBSuckerState} from "./enums/JBSuckerState.sol";
16
20
  import {IJBSucker} from "./interfaces/IJBSucker.sol";
@@ -48,6 +52,10 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
48
52
  // ------------------------- internal constants ----------------------- //
49
53
  //*********************************************************************//
50
54
 
55
+ /// @notice The fixed-point fidelity used when valuing remote contexts across currencies, matching the terminal
56
+ /// store's `_MAX_FIXED_POINT_FIDELITY`.
57
+ uint256 internal constant _PRICE_FIDELITY = 18;
58
+
51
59
  /// @notice A constant indicating that this sucker exists and belongs to a specific project.
52
60
  uint256 internal constant _SUCKER_EXISTS = 1;
53
61
 
@@ -62,6 +70,10 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
62
70
  /// @notice The Juicebox directory used to look up project terminals and controllers.
63
71
  IJBDirectory public immutable override DIRECTORY;
64
72
 
73
+ /// @notice The prices contract used to value remote per-context surplus and balance into a requested currency,
74
+ /// exactly as the terminal store values local surplus.
75
+ IJBPrices public immutable PRICES;
76
+
65
77
  /// @notice A contract which mints ERC-721s that represent project ownership and transfers.
66
78
  IJBProjects public immutable override PROJECTS;
67
79
 
@@ -89,10 +101,12 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
89
101
 
90
102
  /// @param directory The juicebox directory.
91
103
  /// @param permissions A contract storing permissions.
104
+ /// @param prices The prices contract used to value remote per-context surplus/balance into a requested currency.
92
105
  /// @param initialOwner The initial owner of this contract.
93
106
  constructor(
94
107
  IJBDirectory directory,
95
108
  IJBPermissions permissions,
109
+ IJBPrices prices,
96
110
  address initialOwner,
97
111
  address trustedForwarder
98
112
  )
@@ -101,6 +115,7 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
101
115
  Ownable(initialOwner)
102
116
  {
103
117
  DIRECTORY = directory;
118
+ PRICES = prices;
104
119
  PROJECTS = directory.PROJECTS();
105
120
  toRemoteFee = MAX_TO_REMOTE_FEE;
106
121
  }
@@ -131,6 +146,145 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
131
146
  return exists && (val == _SUCKER_EXISTS || val == _SUCKER_DEPRECATED);
132
147
  }
133
148
 
149
+ /// @notice Values one sucker's raw peer-chain balance into a currency, bundled with the peer chain ID and
150
+ /// freshness.
151
+ /// @dev Exposed as an external self-call boundary so `totalRemoteBalanceOf` can `try` it and drop a single sucker
152
+ /// whose price feed is missing. A context whose currency already matches `currency` folds in at par (no feed read);
153
+ /// a missing cross-currency feed reverts, and the aggregator catches it and skips just this sucker.
154
+ /// @param sucker The sucker to read.
155
+ /// @param projectId The project whose price feeds to use.
156
+ /// @param currency The currency to value into.
157
+ /// @param decimals The decimal precision for the returned value.
158
+ /// @return A `JBPeerChainValue` with the valued balance, the sucker's peer chain ID, and its snapshot freshness
159
+ /// key.
160
+ function remoteBalanceOf(
161
+ address sucker,
162
+ uint256 projectId,
163
+ uint256 currency,
164
+ uint256 decimals
165
+ )
166
+ external
167
+ view
168
+ returns (JBPeerChainValue memory)
169
+ {
170
+ // Read this sucker's raw snapshot: one context per distinct local currency the peer reported, plus the peer
171
+ // chain ID and the snapshot's freshness key.
172
+ (JBPeerChainContext[] memory contexts, uint256 chainId, uint256 snapshot) =
173
+ IJBSucker(sucker).peerChainContextsOf();
174
+
175
+ // Value each context's balance out of the currency and decimals it was recorded in, into the requested
176
+ // `currency` and `decimals`, and sum across every context. A context already denominated in `currency` folds
177
+ // in at par; a cross-currency context is converted through the project's price feed.
178
+ uint256 value;
179
+ uint256 numContexts = contexts.length;
180
+ for (uint256 i; i < numContexts;) {
181
+ value += _valued({
182
+ amount: contexts[i].balance,
183
+ fromCurrency: contexts[i].currency,
184
+ fromDecimals: contexts[i].decimals,
185
+ toCurrency: currency,
186
+ toDecimals: decimals,
187
+ projectId: projectId
188
+ });
189
+ unchecked {
190
+ ++i;
191
+ }
192
+ }
193
+
194
+ // Carry the peer chain ID and snapshot freshness alongside the summed value so the aggregator can deduplicate
195
+ // peers and keep only the freshest snapshot per chain.
196
+ return JBPeerChainValue({value: value, peerChainId: chainId, snapshotTimestamp: snapshot});
197
+ }
198
+
199
+ /// @notice Values one sucker's raw peer-chain surplus into a currency, bundled with the peer chain ID and
200
+ /// freshness.
201
+ /// @dev Exposed as an external self-call boundary so `totalRemoteSurplusOf` can `try` it and drop a single sucker
202
+ /// whose price feed is missing. A context whose currency already matches `currency` folds in at par (no feed read);
203
+ /// a missing cross-currency feed reverts, and the aggregator catches it and skips just this sucker.
204
+ /// @param sucker The sucker to read.
205
+ /// @param projectId The project whose price feeds to use.
206
+ /// @param currency The currency to value into.
207
+ /// @param decimals The decimal precision for the returned value.
208
+ /// @return A `JBPeerChainValue` with the valued surplus, the sucker's peer chain ID, and its snapshot freshness
209
+ /// key.
210
+ function remoteSurplusOf(
211
+ address sucker,
212
+ uint256 projectId,
213
+ uint256 currency,
214
+ uint256 decimals
215
+ )
216
+ external
217
+ view
218
+ returns (JBPeerChainValue memory)
219
+ {
220
+ // Read this sucker's raw snapshot: one context per distinct local currency the peer reported, plus the peer
221
+ // chain ID and the snapshot's freshness key.
222
+ (JBPeerChainContext[] memory contexts, uint256 chainId, uint256 snapshot) =
223
+ IJBSucker(sucker).peerChainContextsOf();
224
+
225
+ // Value each context's surplus out of the currency and decimals it was recorded in, into the requested
226
+ // `currency` and `decimals`, and sum across every context. A context already denominated in `currency` folds
227
+ // in at par; a cross-currency context is converted through the project's price feed.
228
+ uint256 value;
229
+ uint256 numContexts = contexts.length;
230
+ for (uint256 i; i < numContexts;) {
231
+ value += _valued({
232
+ amount: contexts[i].surplus,
233
+ fromCurrency: contexts[i].currency,
234
+ fromDecimals: contexts[i].decimals,
235
+ toCurrency: currency,
236
+ toDecimals: decimals,
237
+ projectId: projectId
238
+ });
239
+ unchecked {
240
+ ++i;
241
+ }
242
+ }
243
+
244
+ // Carry the peer chain ID and snapshot freshness alongside the summed value so the aggregator can deduplicate
245
+ // peers and keep only the freshest snapshot per chain.
246
+ return JBPeerChainValue({value: value, peerChainId: chainId, snapshotTimestamp: snapshot});
247
+ }
248
+
249
+ /// @notice The cumulative total supply across all remote peer chains for a project.
250
+ /// @dev Includes deprecated suckers only when no active sucker answers for the same peer chain, to prevent
251
+ /// undercounting during migration windows without letting stale deprecated snapshots dominate live routes.
252
+ /// Silently skips suckers that revert.
253
+ /// @param projectId The ID of the project.
254
+ /// @return totalSupply The combined peer chain total supply.
255
+ function remoteTotalSupplyOf(uint256 projectId) external view override returns (uint256 totalSupply) {
256
+ address[] memory allSuckers = _suckersOf[projectId].keys();
257
+ uint256 len = allSuckers.length;
258
+
259
+ // Per-chain dedup arrays. The number of suckers per project is small (typically 1-5),
260
+ // so a linear scan is cheaper than a mapping.
261
+ PeerValueScratch memory scratch = _peerValueScratch(len);
262
+
263
+ for (uint256 i; i < len;) {
264
+ (, uint256 val) = _suckersOf[projectId].tryGet(allSuckers[i]);
265
+ // Include both active and deprecated suckers in aggregate economic views.
266
+ if (val == _SUCKER_EXISTS || val == _SUCKER_DEPRECATED) {
267
+ // One call returns the value, peer chain ID, and snapshot freshness key together.
268
+ try IJBSucker(allSuckers[i]).peerChainTotalSupplyValue() returns (JBPeerChainValue memory read) {
269
+ scratch.chainCount = _recordPeerChainValue({
270
+ scratch: scratch, read: read, sucker: allSuckers[i], isActive: val == _SUCKER_EXISTS
271
+ });
272
+ } catch {}
273
+ }
274
+ unchecked {
275
+ ++i;
276
+ }
277
+ }
278
+
279
+ // Sum the per-chain selected values.
280
+ for (uint256 k; k < scratch.chainCount;) {
281
+ totalSupply += scratch.values[k];
282
+ unchecked {
283
+ ++k;
284
+ }
285
+ }
286
+ }
287
+
134
288
  /// @notice All active (non-deprecated) suckers for a project, with their remote peer address and chain ID.
135
289
  /// @param projectId The ID of the project to get the suckers of.
136
290
  /// @return pairs The pairs of suckers and their metadata.
@@ -202,18 +356,20 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
202
356
  }
203
357
  }
204
358
 
205
- /// @notice The cumulative balance across all remote peer chains for a project, denominated in a given currency.
206
- /// @dev Includes deprecated suckers only when no active sucker answers for the same peer chain, to prevent
207
- /// undercounting during migration windows without letting stale deprecated snapshots dominate live routes.
208
- /// Silently skips suckers that revert.
359
+ /// @notice The cumulative peer-chain balance across all remote peer chains for a project, valued into a currency.
360
+ /// @dev Dedups same-peer suckers by freshest snapshot, then sums each sucker's balance valued into `currency`.
361
+ /// Includes deprecated suckers only when no active sucker answers for the same peer chain, to prevent undercounting
362
+ /// during migration windows without letting stale deprecated snapshots dominate live routes. A context whose
363
+ /// currency already matches is taken at par (no feed); a missing cross-currency feed reverts and that sucker is
364
+ /// silently skipped (conservative, bias-low).
209
365
  /// @param projectId The ID of the project.
366
+ /// @param currency The currency to value the combined balance into.
210
367
  /// @param decimals The decimal precision for the returned value.
211
- /// @param currency The currency to normalize to.
212
368
  /// @return balance The combined peer chain balance.
213
- function remoteBalanceOf(
369
+ function totalRemoteBalanceOf(
214
370
  uint256 projectId,
215
- uint256 decimals,
216
- uint256 currency
371
+ uint256 currency,
372
+ uint256 decimals
217
373
  )
218
374
  external
219
375
  view
@@ -231,8 +387,11 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
231
387
  (, uint256 val) = _suckersOf[projectId].tryGet(allSuckers[i]);
232
388
  // Include both active and deprecated suckers in aggregate economic views.
233
389
  if (val == _SUCKER_EXISTS || val == _SUCKER_DEPRECATED) {
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 (
390
+ // A registry self-call values the sucker's raw contexts so a missing feed reverts only this sucker
391
+ // (caught here), and returns the value, peer chain ID, and snapshot freshness key together.
392
+ try this.remoteBalanceOf({
393
+ sucker: allSuckers[i], projectId: projectId, currency: currency, decimals: decimals
394
+ }) returns (
236
395
  JBPeerChainValue memory read
237
396
  ) {
238
397
  scratch.chainCount = _recordPeerChainValue({
@@ -254,18 +413,20 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
254
413
  }
255
414
  }
256
415
 
257
- /// @notice The cumulative surplus across all remote peer chains for a project, denominated in a given currency.
258
- /// @dev Includes deprecated suckers only when no active sucker answers for the same peer chain, to prevent
259
- /// undercounting during migration windows without letting stale deprecated snapshots dominate live routes.
260
- /// Silently skips suckers that revert.
416
+ /// @notice The cumulative peer-chain surplus across all remote peer chains for a project, valued into a currency.
417
+ /// @dev Dedups same-peer suckers by freshest snapshot, then sums each sucker's surplus valued into `currency`.
418
+ /// Includes deprecated suckers only when no active sucker answers for the same peer chain, to prevent undercounting
419
+ /// during migration windows without letting stale deprecated snapshots dominate live routes. A context whose
420
+ /// currency already matches is taken at par (no feed); a missing cross-currency feed reverts and that sucker is
421
+ /// silently skipped (conservative, bias-low).
261
422
  /// @param projectId The ID of the project.
423
+ /// @param currency The currency to value the combined surplus into.
262
424
  /// @param decimals The decimal precision for the returned value.
263
- /// @param currency The currency to normalize to.
264
425
  /// @return surplus The combined peer chain surplus.
265
- function remoteSurplusOf(
426
+ function totalRemoteSurplusOf(
266
427
  uint256 projectId,
267
- uint256 decimals,
268
- uint256 currency
428
+ uint256 currency,
429
+ uint256 decimals
269
430
  )
270
431
  external
271
432
  view
@@ -283,8 +444,11 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
283
444
  (, uint256 val) = _suckersOf[projectId].tryGet(allSuckers[i]);
284
445
  // Include both active and deprecated suckers in aggregate economic views.
285
446
  if (val == _SUCKER_EXISTS || val == _SUCKER_DEPRECATED) {
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 (
447
+ // A registry self-call values the sucker's raw contexts so a missing feed reverts only this sucker
448
+ // (caught here), and returns the value, peer chain ID, and snapshot freshness key together.
449
+ try this.remoteSurplusOf({
450
+ sucker: allSuckers[i], projectId: projectId, currency: currency, decimals: decimals
451
+ }) returns (
288
452
  JBPeerChainValue memory read
289
453
  ) {
290
454
  scratch.chainCount = _recordPeerChainValue({
@@ -306,45 +470,6 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
306
470
  }
307
471
  }
308
472
 
309
- /// @notice The cumulative total supply across all remote peer chains for a project.
310
- /// @dev Includes deprecated suckers only when no active sucker answers for the same peer chain, to prevent
311
- /// undercounting during migration windows without letting stale deprecated snapshots dominate live routes.
312
- /// Silently skips suckers that revert.
313
- /// @param projectId The ID of the project.
314
- /// @return totalSupply The combined peer chain total supply.
315
- function remoteTotalSupplyOf(uint256 projectId) external view override returns (uint256 totalSupply) {
316
- address[] memory allSuckers = _suckersOf[projectId].keys();
317
- uint256 len = allSuckers.length;
318
-
319
- // Per-chain dedup arrays. The number of suckers per project is small (typically 1-5),
320
- // so a linear scan is cheaper than a mapping.
321
- PeerValueScratch memory scratch = _peerValueScratch(len);
322
-
323
- for (uint256 i; i < len;) {
324
- (, uint256 val) = _suckersOf[projectId].tryGet(allSuckers[i]);
325
- // Include both active and deprecated suckers in aggregate economic views.
326
- if (val == _SUCKER_EXISTS || val == _SUCKER_DEPRECATED) {
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
331
- });
332
- } catch {}
333
- }
334
- unchecked {
335
- ++i;
336
- }
337
- }
338
-
339
- // Sum the per-chain selected values.
340
- for (uint256 k; k < scratch.chainCount;) {
341
- totalSupply += scratch.values[k];
342
- unchecked {
343
- ++k;
344
- }
345
- }
346
- }
347
-
348
473
  //*********************************************************************//
349
474
  // ------------------------ internal views --------------------------- //
350
475
  //*********************************************************************//
@@ -477,6 +602,46 @@ contract JBSuckerRegistry is ERC2771Context, Ownable, JBPermissioned, IJBSuckerR
477
602
  });
478
603
  }
479
604
 
605
+ /// @notice Values an amount held in one currency/decimals into another, mirroring the terminal store.
606
+ /// @dev Adjusts decimals, then converts currency via the prices contract. Both steps short-circuit on identity, and
607
+ /// the currency step also short-circuits on a zero amount, so a same-currency context consults no feed. A missing
608
+ /// feed reverts (fail-closed), and the caller catches it to drop just the affected sucker.
609
+ /// @param amount The raw amount in `fromCurrency`/`fromDecimals`.
610
+ /// @param fromCurrency The currency the amount is held in.
611
+ /// @param fromDecimals The decimals the amount is held in.
612
+ /// @param toCurrency The currency to value into.
613
+ /// @param toDecimals The decimals to value into.
614
+ /// @param projectId The project whose price feeds to use.
615
+ /// @return The amount valued into `toCurrency`/`toDecimals`.
616
+ function _valued(
617
+ uint256 amount,
618
+ uint256 fromCurrency,
619
+ uint256 fromDecimals,
620
+ uint256 toCurrency,
621
+ uint256 toDecimals,
622
+ uint256 projectId
623
+ )
624
+ internal
625
+ view
626
+ returns (uint256)
627
+ {
628
+ // Step 1: adjust decimals.
629
+ uint256 value = fromDecimals == toDecimals
630
+ ? amount
631
+ : JBFixedPointNumber.adjustDecimals({value: amount, decimals: fromDecimals, targetDecimals: toDecimals});
632
+
633
+ // Step 2: convert currency. The price is the denominator: pricePerUnitOf returns the `fromCurrency` price of
634
+ // one `toCurrency`, so dividing the amount by it yields the amount in `toCurrency`.
635
+ if (value == 0 || fromCurrency == toCurrency) return value;
636
+ return mulDiv({
637
+ x: value,
638
+ y: 10 ** _PRICE_FIDELITY,
639
+ denominator: PRICES.pricePerUnitOf({
640
+ projectId: projectId, pricingCurrency: fromCurrency, unitCurrency: toCurrency, decimals: _PRICE_FIDELITY
641
+ })
642
+ });
643
+ }
644
+
480
645
  //*********************************************************************//
481
646
  // ---------------------- public transactions ----------------------- //
482
647
  //*********************************************************************//
@@ -3,7 +3,6 @@ pragma solidity 0.8.28;
3
3
 
4
4
  import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
5
5
  import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
6
- import {IJBPrices} from "@bananapus/core-v6/src/interfaces/IJBPrices.sol";
7
6
  import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
8
7
  import {IJBTokens} from "@bananapus/core-v6/src/interfaces/IJBTokens.sol";
9
8
  import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
@@ -38,19 +37,17 @@ contract JBCeloSucker is JBOptimismSucker {
38
37
  /// @param deployer A contract that deploys clones of this contract.
39
38
  /// @param directory A contract storing directories of terminals and controllers for each project.
40
39
  /// @param permissions A contract storing permissions.
41
- /// @param prices The price oracle used to convert peer-chain balances and surplus.
42
40
  /// @param tokens A contract that manages token minting and burning.
43
41
  constructor(
44
42
  JBCeloSuckerDeployer deployer,
45
43
  IJBDirectory directory,
46
44
  IJBPermissions permissions,
47
- IJBPrices prices,
48
45
  IJBTokens tokens,
49
46
  uint256 feeProjectId,
50
47
  IJBSuckerRegistry registry,
51
48
  address trustedForwarder
52
49
  )
53
- JBOptimismSucker(deployer, directory, permissions, prices, tokens, feeProjectId, registry, trustedForwarder)
50
+ JBOptimismSucker(deployer, directory, permissions, tokens, feeProjectId, registry, trustedForwarder)
54
51
  {
55
52
  // Fetch the wrapped native token by doing a callback to the deployer contract.
56
53
  WRAPPED_NATIVE_TOKEN = JBCeloSuckerDeployer(deployer).wrappedNative();
@@ -4,7 +4,6 @@ pragma solidity 0.8.28;
4
4
  // Core JB imports.
5
5
  import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
6
6
  import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
7
- import {IJBPrices} from "@bananapus/core-v6/src/interfaces/IJBPrices.sol";
8
7
  import {IJBTokens} from "@bananapus/core-v6/src/interfaces/IJBTokens.sol";
9
8
  // CCIP imports.
10
9
  import {Client} from "@chainlink/contracts-ccip/contracts/libraries/Client.sol";
@@ -204,7 +203,6 @@ contract JBSwapCCIPSucker is JBCCIPSucker, IUnlockCallback, IUniswapV3SwapCallba
204
203
  /// @param deployer The deployer that stores chain-specific configuration.
205
204
  /// @param directory The directory of terminals and controllers for projects.
206
205
  /// @param permissions The permissions contract.
207
- /// @param prices The price oracle used to convert peer-chain balances and surplus.
208
206
  /// @param tokens The contract that manages token minting and burning.
209
207
  /// @param feeProjectId The project ID that receives bridge fees.
210
208
  /// @param registry The sucker registry.
@@ -213,13 +211,12 @@ contract JBSwapCCIPSucker is JBCCIPSucker, IUnlockCallback, IUniswapV3SwapCallba
213
211
  JBSwapCCIPSuckerDeployer deployer,
214
212
  IJBDirectory directory,
215
213
  IJBPermissions permissions,
216
- IJBPrices prices,
217
214
  IJBTokens tokens,
218
215
  uint256 feeProjectId,
219
216
  IJBSuckerRegistry registry,
220
217
  address trustedForwarder
221
218
  )
222
- JBCCIPSucker(deployer, directory, permissions, prices, tokens, feeProjectId, registry, trustedForwarder)
219
+ JBCCIPSucker(deployer, directory, permissions, tokens, feeProjectId, registry, trustedForwarder)
223
220
  {
224
221
  IJBSwapCCIPSuckerDeployer swapDeployer = IJBSwapCCIPSuckerDeployer(address(deployer));
225
222
  BRIDGE_TOKEN = swapDeployer.bridgeToken();
@@ -1,21 +1,19 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity ^0.8.0;
3
3
 
4
+ import {JBSourceContext} from "../structs/JBSourceContext.sol";
5
+
4
6
  /// @notice Optional data-hook interface for adding project-specific adjusted accounts to peer-chain snapshots.
5
7
  interface IJBPeerChainAdjustedAccounts {
6
- /// @notice Extra supply, surplus, and balance that should be included in cross-chain peer snapshots.
8
+ /// @notice Extra project token supply and per-context surplus/balance to include in cross-chain peer snapshots.
9
+ /// @dev The hook reports off-terminal surplus and balance per accounting context in each context's own currency,
10
+ /// un-valued — exactly like the terminal contexts. The receiving chain folds each one into its same-asset local
11
+ /// context at par, so no price oracle is consulted for the hook's contribution either.
7
12
  /// @param projectId The ID of the project to snapshot.
8
- /// @param decimals The decimals the returned surplus and balance should use.
9
- /// @param currency The currency the returned surplus and balance should be in terms of.
10
- /// @return supply The extra supply to include in `sourceTotalSupply`.
11
- /// @return surplus The extra surplus to include in `sourceSurplus`.
12
- /// @return balance The extra balance to include in `sourceBalance`.
13
- function peerChainAdjustedAccountsOf(
14
- uint256 projectId,
15
- uint256 decimals,
16
- uint256 currency
17
- )
13
+ /// @return supply The extra project token supply to include in `sourceTotalSupply`.
14
+ /// @return contexts The extra per-context surplus and balance to include in the snapshot, un-valued.
15
+ function peerChainAdjustedAccountsOf(uint256 projectId)
18
16
  external
19
17
  view
20
- returns (uint256 supply, uint256 surplus, uint256 balance);
18
+ returns (uint256 supply, JBSourceContext[] memory contexts);
21
19
  }
@@ -6,9 +6,9 @@ import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
6
6
  import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
7
7
  import {IJBTokens} from "@bananapus/core-v6/src/interfaces/IJBTokens.sol";
8
8
  import {JBClaim} from "../structs/JBClaim.sol";
9
- import {JBDenominatedAmount} from "../structs/JBDenominatedAmount.sol";
10
9
  import {JBInboxTreeRoot} from "../structs/JBInboxTreeRoot.sol";
11
10
  import {JBOutboxTree} from "../structs/JBOutboxTree.sol";
11
+ import {JBPeerChainContext} from "../structs/JBPeerChainContext.sol";
12
12
  import {JBPeerChainValue} from "../structs/JBPeerChainValue.sol";
13
13
  import {JBRemoteToken} from "../structs/JBRemoteToken.sol";
14
14
  import {JBSuckerState} from "../enums/JBSuckerState.sol";
@@ -153,37 +153,17 @@ interface IJBSucker is IERC165 {
153
153
  /// @return chainId The remote chain ID.
154
154
  function peerChainId() external view returns (uint256 chainId);
155
155
 
156
- /// @notice The aggregate peer chain balance, normalized to a desired currency and decimal precision using JBPrices.
157
- /// @dev The balance is stored as ETH-denominated (18 decimals) and converted to the requested currency/decimals
158
- /// using the local JBPrices oracle.
159
- /// @param decimals The decimal precision for the returned value.
160
- /// @param currency The currency to normalize to.
161
- /// @return A `JBDenominatedAmount` with the converted value.
162
- function peerChainBalanceOf(uint256 decimals, uint256 currency) external view returns (JBDenominatedAmount memory);
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
-
172
- /// @notice The aggregate peer chain surplus, normalized to a desired currency and decimal precision using JBPrices.
173
- /// @dev The surplus is stored as ETH-denominated (18 decimals) and converted to the requested currency/decimals
174
- /// using the local JBPrices oracle.
175
- /// @param decimals The decimal precision for the returned value.
176
- /// @param currency The currency to normalize to.
177
- /// @return A `JBDenominatedAmount` with the converted value.
178
- function peerChainSurplusOf(uint256 decimals, uint256 currency) external view returns (JBDenominatedAmount memory);
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);
156
+ /// @notice The peer chain's raw per-context surplus and balance from the latest snapshot, bundled with the peer
157
+ /// chain ID and snapshot freshness key.
158
+ /// @dev Un-valued each context is in its own currency and decimals. The registry dedups same-peer suckers by
159
+ /// freshness, then values each context into a requested currency. The sucker consults no price oracle.
160
+ /// @return contexts The per-currency surplus and balance from the latest snapshot.
161
+ /// @return chainId The peer chain these contexts belong to.
162
+ /// @return snapshot The source freshness key of the latest snapshot.
163
+ function peerChainContextsOf()
164
+ external
165
+ view
166
+ returns (JBPeerChainContext[] memory contexts, uint256 chainId, uint256 snapshot);
187
167
 
188
168
  /// @notice The last known total token supply on the peer chain, updated each time a bridge message is received.
189
169
  /// @dev Used by data hooks to compute `effectiveTotalSupply = localSupply + sum(peerChainTotalSupply)` across all
@@ -69,6 +69,13 @@ interface IJBSuckerRegistry {
69
69
  /// @return Whether the sucker belongs to the project.
70
70
  function isSuckerOf(uint256 projectId, address addr) external view returns (bool);
71
71
 
72
+ /// @notice The cumulative total supply across all remote peer chains for a project.
73
+ /// @dev Dedupes same-peer active suckers by freshest snapshot, then sums peer-chain values. Silently skips suckers
74
+ /// that revert.
75
+ /// @param projectId The ID of the project.
76
+ /// @return totalSupply The combined peer chain total supply.
77
+ function remoteTotalSupplyOf(uint256 projectId) external view returns (uint256 totalSupply);
78
+
72
79
  /// @notice Whether the specified sucker deployer is approved by this registry.
73
80
  /// @param deployer The address of the deployer to check.
74
81
  /// @return Whether the deployer is allowed.
@@ -84,49 +91,44 @@ interface IJBSuckerRegistry {
84
91
  /// @return The addresses of the suckers.
85
92
  function suckersOf(uint256 projectId) external view returns (address[] memory);
86
93
 
87
- /// @notice The cumulative total supply across all remote peer chains for a project.
88
- /// @dev Dedupes same-peer active suckers by freshest snapshot, then sums peer-chain values. Silently skips suckers
89
- /// that revert.
90
- /// @param projectId The ID of the project.
91
- /// @return totalSupply The combined peer chain total supply.
92
- function remoteTotalSupplyOf(uint256 projectId) external view returns (uint256 totalSupply);
94
+ /// @notice The ETH fee (in wei) paid into the fee project on each toRemote() call.
95
+ /// @return The current fee.
96
+ function toRemoteFee() external view returns (uint256);
93
97
 
94
- /// @notice The cumulative balance across all remote peer chains for a project, denominated in a given currency.
95
- /// @dev Dedupes same-peer active suckers by freshest snapshot, then sums peer-chain values. Silently skips suckers
96
- /// that revert.
98
+ /// @notice The cumulative peer-chain balance across all remote peer chains for a project, valued into a currency.
99
+ /// @dev Dedups same-peer active suckers by freshest snapshot, then sums each sucker's balance valued into
100
+ /// `currency`. A context whose currency already matches is taken at par (no feed); a missing cross-currency feed
101
+ /// reverts and that sucker is silently skipped (conservative, bias-low).
97
102
  /// @param projectId The ID of the project.
103
+ /// @param currency The currency to value the combined balance into.
98
104
  /// @param decimals The decimal precision for the returned value.
99
- /// @param currency The currency to normalize to.
100
105
  /// @return balance The combined peer chain balance.
101
- function remoteBalanceOf(
106
+ function totalRemoteBalanceOf(
102
107
  uint256 projectId,
103
- uint256 decimals,
104
- uint256 currency
108
+ uint256 currency,
109
+ uint256 decimals
105
110
  )
106
111
  external
107
112
  view
108
113
  returns (uint256 balance);
109
114
 
110
- /// @notice The cumulative surplus across all remote peer chains for a project, denominated in a given currency.
111
- /// @dev Dedupes same-peer active suckers by freshest snapshot, then sums peer-chain values. Silently skips suckers
112
- /// that revert.
115
+ /// @notice The cumulative peer-chain surplus across all remote peer chains for a project, valued into a currency.
116
+ /// @dev Dedups same-peer active suckers by freshest snapshot, then sums each sucker's surplus valued into
117
+ /// `currency`. A context whose currency already matches is taken at par (no feed); a missing cross-currency feed
118
+ /// reverts and that sucker is silently skipped (conservative, bias-low).
113
119
  /// @param projectId The ID of the project.
120
+ /// @param currency The currency to value the combined surplus into.
114
121
  /// @param decimals The decimal precision for the returned value.
115
- /// @param currency The currency to normalize to.
116
122
  /// @return surplus The combined peer chain surplus.
117
- function remoteSurplusOf(
123
+ function totalRemoteSurplusOf(
118
124
  uint256 projectId,
119
- uint256 decimals,
120
- uint256 currency
125
+ uint256 currency,
126
+ uint256 decimals
121
127
  )
122
128
  external
123
129
  view
124
130
  returns (uint256 surplus);
125
131
 
126
- /// @notice The ETH fee (in wei) paid into the fee project on each toRemote() call.
127
- /// @return The current fee.
128
- function toRemoteFee() external view returns (uint256);
129
-
130
132
  // State-changing functions
131
133
 
132
134
  /// @notice Add a sucker deployer to the allowlist.