@bananapus/suckers-v6 0.0.30 → 0.0.32

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.
Files changed (94) hide show
  1. package/README.md +2 -2
  2. package/foundry.lock +1 -7
  3. package/foundry.toml +1 -2
  4. package/package.json +24 -13
  5. package/references/operations.md +1 -1
  6. package/references/runtime.md +1 -1
  7. package/script/Deploy.s.sol +9 -0
  8. package/src/JBArbitrumSucker.sol +6 -5
  9. package/src/JBBaseSucker.sol +3 -1
  10. package/src/JBCCIPSucker.sol +16 -9
  11. package/src/JBCeloSucker.sol +6 -4
  12. package/src/JBOptimismSucker.sol +4 -2
  13. package/src/JBSucker.sol +186 -48
  14. package/src/JBSuckerRegistry.sol +94 -70
  15. package/src/JBSwapCCIPSucker.sol +106 -101
  16. package/src/deployers/JBSuckerDeployer.sol +5 -3
  17. package/src/interfaces/IJBPeerChainAdjustedAccounts.sol +21 -0
  18. package/src/interfaces/IJBSuckerDeployer.sol +3 -2
  19. package/src/interfaces/IJBSuckerExtended.sol +56 -0
  20. package/src/libraries/JBSuckerLib.sol +148 -73
  21. package/src/libraries/JBSwapLib.sol +4 -0
  22. package/src/libraries/JBSwapPoolLib.sol +261 -88
  23. package/src/structs/JBMessageRoot.sol +3 -2
  24. package/src/structs/JBSuckerDeployerConfig.sol +3 -0
  25. package/ADMINISTRATION.md +0 -26
  26. package/ARCHITECTURE.md +0 -34
  27. package/AUDIT_INSTRUCTIONS.md +0 -33
  28. package/RISKS.md +0 -153
  29. package/SKILLS.md +0 -25
  30. package/STYLE_GUIDE.md +0 -610
  31. package/USER_JOURNEYS.md +0 -50
  32. package/slither-ci.config.json +0 -10
  33. package/test/AdversarialSuckerFork.t.sol +0 -449
  34. package/test/ForkArbitrum.t.sol +0 -404
  35. package/test/ForkCelo.t.sol +0 -387
  36. package/test/ForkClaimMainnet.t.sol +0 -1039
  37. package/test/ForkMainnet.t.sol +0 -642
  38. package/test/ForkOPStack.t.sol +0 -423
  39. package/test/ForkSwap.t.sol +0 -445
  40. package/test/ForkSwapMainnet.t.sol +0 -525
  41. package/test/InteropCompat.t.sol +0 -705
  42. package/test/MultiSuckerFork.t.sol +0 -529
  43. package/test/SuckerAttacks.t.sol +0 -540
  44. package/test/SuckerCrossChainAdversarial.t.sol +0 -745
  45. package/test/SuckerDeepAttacks.t.sol +0 -1740
  46. package/test/SuckerRegressions.t.sol +0 -366
  47. package/test/TestAuditGaps.sol +0 -1073
  48. package/test/audit/2026-04-21-codex-nemesis-RegistryStaleDeprecatedMaxSurplus.t.sol +0 -127
  49. package/test/audit/2026-04-22-codex-nemesis-PeerTopologyAuthBreak.t.sol +0 -112
  50. package/test/audit/2026-04-22-codex-nemesis-RegistryPeerAuthBreak.t.sol +0 -198
  51. package/test/audit/2026-04-22-codex-nemesis-ZeroOutputRetryClaim.t.sol +0 -162
  52. package/test/audit/2026-04-24-codex-nemesis-FreshRound.t.sol +0 -383
  53. package/test/audit/2026-04-24-codex-nemesis-RegistryPeerMismatch.t.sol +0 -112
  54. package/test/audit/2026-04-24-codex-nemesis-RegistryStaleMaxAggregation.t.sol +0 -88
  55. package/test/audit/2026-04-25-codex-nemesis-TransientClaimContext.t.sol +0 -258
  56. package/test/audit/ArbitrumL2ToRemoteFeeDoS.t.sol +0 -137
  57. package/test/audit/CertikAIScan.t.sol +0 -352
  58. package/test/audit/DeprecatedSuckerAggregateViews.t.sol +0 -247
  59. package/test/audit/DeprecatedSuckerDestination.t.sol +0 -177
  60. package/test/audit/ToRemoteFeeFallback.t.sol +0 -140
  61. package/test/audit/TrustedForwarderSpoof.t.sol +0 -121
  62. package/test/audit/TrustedForwarderSpoofCCIP.t.sol +0 -121
  63. package/test/audit/ZeroOutputSwapPending.t.sol +0 -218
  64. package/test/audit/codex-CCIPLegacyFormatCompatibility.t.sol +0 -85
  65. package/test/audit/codex-CCIPWrappedNativeMisunwrap.t.sol +0 -196
  66. package/test/audit/codex-FeeLocking.t.sol +0 -328
  67. package/test/audit/codex-MapTokensEnableOnlyValueStuck.t.sol +0 -84
  68. package/test/audit/codex-NemesisSwapQueueOrder.t.sol +0 -186
  69. package/test/audit/codex-PeerDeterminism.t.sol +0 -174
  70. package/test/audit/codex-PeerSnapshotDesync.t.sol +0 -162
  71. package/test/audit/codex-SwapBatchRateMixing.t.sol +0 -139
  72. package/test/audit/codex-SwapZeroAmountBatchGap.t.sol +0 -203
  73. package/test/audit/codex-ToRemoteFeeIrrecoverable.t.sol +0 -253
  74. package/test/audit/codex-nemesis-DeprecatedRemovalUndercount.t.sol +0 -141
  75. package/test/audit/codex-nemesis-SwapZeroLocalTotalUnbackedClaim.t.sol +0 -179
  76. package/test/fork/OptimismSuckerFork.t.sol +0 -459
  77. package/test/mocks/ERC20Mock.sol +0 -37
  78. package/test/mocks/MockMessenger.sol +0 -43
  79. package/test/regression/MapTokensDust.t.sol +0 -237
  80. package/test/unit/arb.t.sol +0 -28
  81. package/test/unit/ccip_native_interop.t.sol +0 -737
  82. package/test/unit/ccip_refund.t.sol +0 -245
  83. package/test/unit/deployer.t.sol +0 -739
  84. package/test/unit/emergency.t.sol +0 -323
  85. package/test/unit/fee_fallback.t.sol +0 -259
  86. package/test/unit/invariants.t.sol +0 -480
  87. package/test/unit/merkle.t.sol +0 -232
  88. package/test/unit/merkle_equivalence.t.sol +0 -121
  89. package/test/unit/multi_chain_evolution.t.sol +0 -607
  90. package/test/unit/peer_chain_state.t.sol +0 -618
  91. package/test/unit/pool_discovery.t.sol +0 -366
  92. package/test/unit/registry.t.sol +0 -25
  93. package/test/unit/relay_beneficiary.t.sol +0 -141
  94. package/test/unit/swap_ccip.t.sol +0 -577
package/README.md CHANGED
@@ -72,8 +72,8 @@ That means every bridge path has two trust surfaces:
72
72
  1. `test/unit/registry.t.sol`
73
73
  2. `test/unit/multi_chain_evolution.t.sol`
74
74
  3. `test/ForkClaimMainnet.t.sol`
75
- 4. `test/audit/codex-PeerSnapshotDesync.t.sol`
76
- 5. `test/audit/codex-ToRemoteFeeIrrecoverable.t.sol`
75
+ 4. `test/audit/PeerSnapshotDesync.t.sol`
76
+ 5. `test/audit/ToRemoteFeeIrrecoverable.t.sol`
77
77
 
78
78
  ## Install
79
79
 
package/foundry.lock CHANGED
@@ -1,11 +1,5 @@
1
1
  {
2
2
  "lib/forge-std": {
3
3
  "rev": "83c5d212a01f8950727da4095cdfe2654baccb5b"
4
- },
5
- "lib/sphinx": {
6
- "branch": {
7
- "name": "v0.23.0",
8
- "rev": "5fb24a825f46bd6ae0b5359fe0da1d2346126b09"
9
- }
10
4
  }
11
- }
5
+ }
package/foundry.toml CHANGED
@@ -14,8 +14,7 @@ depth = 100
14
14
  fail_on_revert = false
15
15
 
16
16
  [lint]
17
- exclude_lints = ["pascal-case-struct", "mixed-case-variable"]
18
- lint_on_build = false
17
+ exclude_lints = ["mixed-case-variable", "pascal-case-struct"]
19
18
 
20
19
  [fmt]
21
20
  number_underscore = "thousands"
package/package.json CHANGED
@@ -1,11 +1,22 @@
1
1
  {
2
2
  "name": "@bananapus/suckers-v6",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/Bananapus/nana-suckers-v6"
8
8
  },
9
+ "files": [
10
+ "CHANGELOG.md",
11
+ "foundry.lock",
12
+ "foundry.toml",
13
+ "references/",
14
+ "remappings.txt",
15
+ "script/Deploy.s.sol",
16
+ "script/helpers/",
17
+ "sphinx.lock",
18
+ "src/"
19
+ ],
9
20
  "engines": {
10
21
  "node": ">=20.0.0"
11
22
  },
@@ -18,19 +29,19 @@
18
29
  "analyze": "slither . --config-file slither-ci.config.json"
19
30
  },
20
31
  "dependencies": {
21
- "@arbitrum/nitro-contracts": "^1.2.1",
22
- "@bananapus/core-v6": "^0.0.36",
23
- "@bananapus/permission-ids-v6": "^0.0.19",
24
- "@chainlink/contracts-ccip": "^1.6.0",
25
- "@chainlink/local": "github:smartcontractkit/chainlink-local#v0.2.7",
26
- "@openzeppelin/contracts": "^5.6.1",
27
- "@prb/math": "^4.1.0",
28
- "@uniswap/v3-core": "github:Uniswap/v3-core#0.8",
29
- "@uniswap/v3-periphery": "github:Uniswap/v3-periphery#0.8",
30
- "@uniswap/v4-core": "^1.0.2",
31
- "solady": "^0.1.26"
32
+ "@arbitrum/nitro-contracts": "3.2.0",
33
+ "@bananapus/core-v6": "0.0.39",
34
+ "@bananapus/permission-ids-v6": "0.0.22",
35
+ "@chainlink/contracts-ccip": "1.6.4",
36
+ "@chainlink/local": "0.2.7",
37
+ "@openzeppelin/contracts": "5.6.1",
38
+ "@prb/math": "4.1.1",
39
+ "@uniswap/v3-core": "github:Uniswap/v3-core#6562c52e8f75f0c10f9deaf44861847585fc8129",
40
+ "@uniswap/v3-periphery": "github:Uniswap/v3-periphery#b325bb0905d922ae61fcc7df85ee802e8df5e96c",
41
+ "@uniswap/v4-core": "1.0.2",
42
+ "solady": "0.1.26"
32
43
  },
33
44
  "devDependencies": {
34
- "@sphinx-labs/plugins": "^0.33.2"
45
+ "@sphinx-labs/plugins": "0.33.3"
35
46
  }
36
47
  }
@@ -25,4 +25,4 @@
25
25
 
26
26
  - [`test/SuckerAttacks.t.sol`](../test/SuckerAttacks.t.sol), [`test/SuckerDeepAttacks.t.sol`](../test/SuckerDeepAttacks.t.sol), and [`test/TestAuditGaps.sol`](../test/TestAuditGaps.sol) for security-sensitive assumptions.
27
27
  - [`test/InteropCompat.t.sol`](../test/InteropCompat.t.sol) when the problem is deployment wiring rather than runtime logic.
28
- - [`test/unit/invariants.t.sol`](../test/unit/invariants.t.sol), [`test/unit/peer_chain_state.t.sol`](../test/unit/peer_chain_state.t.sol), and [`test/audit/codex-PeerSnapshotDesync.t.sol`](../test/audit/codex-PeerSnapshotDesync.t.sol) when shared accounting or snapshot boundaries are in doubt.
28
+ - [`test/unit/invariants.t.sol`](../test/unit/invariants.t.sol), [`test/unit/peer_chain_state.t.sol`](../test/unit/peer_chain_state.t.sol), and [`test/audit/PeerSnapshotDesync.t.sol`](../test/audit/PeerSnapshotDesync.t.sol) when shared accounting or snapshot boundaries are in doubt.
@@ -27,4 +27,4 @@
27
27
  - [`test/ForkMainnet.t.sol`](../test/ForkMainnet.t.sol), [`test/ForkArbitrum.t.sol`](../test/ForkArbitrum.t.sol), [`test/ForkCelo.t.sol`](../test/ForkCelo.t.sol), and [`test/ForkOPStack.t.sol`](../test/ForkOPStack.t.sol) for real transport assumptions.
28
28
  - [`test/ForkSwap.t.sol`](../test/ForkSwap.t.sol), [`test/ForkClaimMainnet.t.sol`](../test/ForkClaimMainnet.t.sol), and [`test/SuckerRegressions.t.sol`](../test/SuckerRegressions.t.sol) for pinned cross-chain edge cases.
29
29
  - [`test/unit/invariants.t.sol`](../test/unit/invariants.t.sol), [`test/unit/peer_chain_state.t.sol`](../test/unit/peer_chain_state.t.sol), and [`test/unit/registry.t.sol`](../test/unit/registry.t.sol) for shared-accounting invariants.
30
- - [`test/SuckerAttacks.t.sol`](../test/SuckerAttacks.t.sol), [`test/SuckerDeepAttacks.t.sol`](../test/SuckerDeepAttacks.t.sol), [`test/audit/codex-PeerSnapshotDesync.t.sol`](../test/audit/codex-PeerSnapshotDesync.t.sol), and [`test/audit/codex-PeerDeterminism.t.sol`](../test/audit/codex-PeerDeterminism.t.sol) when the bug could involve base logic, registry behavior, or a specific bridge implementation.
30
+ - [`test/SuckerAttacks.t.sol`](../test/SuckerAttacks.t.sol), [`test/SuckerDeepAttacks.t.sol`](../test/SuckerDeepAttacks.t.sol), [`test/audit/PeerSnapshotDesync.t.sol`](../test/audit/PeerSnapshotDesync.t.sol), and [`test/audit/PeerDeterminism.t.sol`](../test/audit/PeerDeterminism.t.sol) when the bug could involve base logic, registry behavior, or a specific bridge implementation.
@@ -224,6 +224,7 @@ contract DeployScript is Script, Sphinx {
224
224
  deployer: _opDeployer,
225
225
  directory: core.directory,
226
226
  permissions: core.permissions,
227
+ prices: address(core.prices),
227
228
  tokens: core.tokens,
228
229
  feeProjectId: 1,
229
230
  registry: REGISTRY,
@@ -275,6 +276,7 @@ contract DeployScript is Script, Sphinx {
275
276
  deployer: _opDeployer,
276
277
  directory: core.directory,
277
278
  permissions: core.permissions,
279
+ prices: address(core.prices),
278
280
  tokens: core.tokens,
279
281
  feeProjectId: 1,
280
282
  registry: REGISTRY,
@@ -357,6 +359,7 @@ contract DeployScript is Script, Sphinx {
357
359
  deployer: _baseDeployer,
358
360
  directory: core.directory,
359
361
  permissions: core.permissions,
362
+ prices: address(core.prices),
360
363
  tokens: core.tokens,
361
364
  feeProjectId: 1,
362
365
  registry: REGISTRY,
@@ -408,6 +411,7 @@ contract DeployScript is Script, Sphinx {
408
411
  deployer: _baseDeployer,
409
412
  directory: core.directory,
410
413
  permissions: core.permissions,
414
+ prices: address(core.prices),
411
415
  tokens: core.tokens,
412
416
  feeProjectId: 1,
413
417
  registry: REGISTRY,
@@ -486,6 +490,7 @@ contract DeployScript is Script, Sphinx {
486
490
  deployer: _arbDeployer,
487
491
  directory: core.directory,
488
492
  permissions: core.permissions,
493
+ prices: address(core.prices),
489
494
  tokens: core.tokens,
490
495
  feeProjectId: 1,
491
496
  registry: REGISTRY,
@@ -540,6 +545,7 @@ contract DeployScript is Script, Sphinx {
540
545
  deployer: _arbDeployer,
541
546
  directory: core.directory,
542
547
  permissions: core.permissions,
548
+ prices: address(core.prices),
543
549
  tokens: core.tokens,
544
550
  feeProjectId: 1,
545
551
  registry: REGISTRY,
@@ -715,6 +721,7 @@ contract DeployScript is Script, Sphinx {
715
721
  salt: salt,
716
722
  directory: core.directory,
717
723
  permissions: core.permissions,
724
+ prices: address(core.prices),
718
725
  tokens: core.tokens,
719
726
  configurator: safeAddress(),
720
727
  trustedForwarder: TRUSTED_FORWARDER,
@@ -731,6 +738,7 @@ contract DeployScript is Script, Sphinx {
731
738
  bytes32 salt,
732
739
  IJBDirectory directory,
733
740
  IJBPermissions permissions,
741
+ address prices,
734
742
  IJBTokens tokens,
735
743
  address configurator,
736
744
  address trustedForwarder,
@@ -786,6 +794,7 @@ contract DeployScript is Script, Sphinx {
786
794
  directory: directory,
787
795
  tokens: tokens,
788
796
  permissions: permissions,
797
+ prices: prices,
789
798
  feeProjectId: 1,
790
799
  registry: REGISTRY,
791
800
  trustedForwarder: trustedForwarder
@@ -53,17 +53,19 @@ contract JBArbitrumSucker is JBSucker, IJBArbitrumSucker {
53
53
 
54
54
  /// @param directory A contract storing directories of terminals and controllers for each project.
55
55
  /// @param permissions A contract storing permissions.
56
+ /// @param prices The price oracle used to convert peer-chain balances and surplus.
56
57
  /// @param tokens A contract that manages token minting and burning.
57
58
  constructor(
58
59
  JBArbitrumSuckerDeployer deployer,
59
60
  IJBDirectory directory,
60
61
  IJBPermissions permissions,
62
+ address prices,
61
63
  IJBTokens tokens,
62
64
  uint256 feeProjectId,
63
65
  IJBSuckerRegistry registry,
64
66
  address trustedForwarder
65
67
  )
66
- JBSucker(directory, permissions, tokens, feeProjectId, registry, trustedForwarder)
68
+ JBSucker(directory, permissions, prices, tokens, feeProjectId, registry, trustedForwarder)
67
69
  {
68
70
  GATEWAYROUTER = JBArbitrumSuckerDeployer(deployer).arbGatewayRouter();
69
71
  ARBINBOX = JBArbitrumSuckerDeployer(deployer).arbInbox();
@@ -206,8 +208,8 @@ contract JBArbitrumSucker is JBSucker, IJBArbitrumSucker {
206
208
  // slither-disable-next-line calls-loop,unused-return
207
209
  IArbL2GatewayRouter(address(GATEWAYROUTER))
208
210
  .outboundTransfer({
209
- l1Token: _toAddress(remoteToken.addr), to: peerAddress, amount: amount, data: bytes("")
210
- });
211
+ l1Token: _toAddress(remoteToken.addr), to: peerAddress, amount: amount, data: bytes("")
212
+ });
211
213
  } else {
212
214
  // Otherwise, the token is the native token, and the amount will be sent as `msg.value`.
213
215
  nativeValue = amount;
@@ -269,8 +271,7 @@ contract JBArbitrumSucker is JBSucker, IJBArbitrumSucker {
269
271
  IL1ArbitrumGateway(gateway)
270
272
  .getOutboundCalldata({
271
273
  _token: token, _from: address(this), _to: _peerAddress(), _amount: amount, _data: bytes("")
272
- })
273
- .length;
274
+ }).length;
274
275
  // slither-disable-next-line calls-loop
275
276
  maxSubmissionCostERC20 = ARBINBOX.calculateRetryableSubmissionFee({
276
277
  dataLength: outboundCalldataLength, baseFee: maxFeePerGas
@@ -18,17 +18,19 @@ contract JBBaseSucker is JBOptimismSucker {
18
18
  /// @param deployer A contract that deploys the clones for this contracts.
19
19
  /// @param directory A contract storing directories of terminals and controllers for each project.
20
20
  /// @param permissions A contract storing permissions.
21
+ /// @param prices The price oracle used to convert peer-chain balances and surplus.
21
22
  /// @param tokens A contract that manages token minting and burning.
22
23
  constructor(
23
24
  JBOptimismSuckerDeployer deployer,
24
25
  IJBDirectory directory,
25
26
  IJBPermissions permissions,
27
+ address prices,
26
28
  IJBTokens tokens,
27
29
  uint256 feeProjectId,
28
30
  IJBSuckerRegistry registry,
29
31
  address trustedForwarder
30
32
  )
31
- JBOptimismSucker(deployer, directory, permissions, tokens, feeProjectId, registry, trustedForwarder)
33
+ JBOptimismSucker(deployer, directory, permissions, prices, tokens, feeProjectId, registry, trustedForwarder)
32
34
  {}
33
35
 
34
36
  //*********************************************************************//
@@ -42,10 +42,10 @@ contract JBCCIPSucker is JBSucker, IAny2EVMMessageReceiver {
42
42
  //*********************************************************************//
43
43
 
44
44
  /// @notice Emitted when a transport payment refund fails after a successful CCIP send.
45
- /// @dev The refunded ETH is permanently stuck in this contract there is no recovery function.
46
- /// This is an accepted tradeoff to avoid reverting after CCIP has committed the bridge message.
45
+ /// @dev The refunded ETH is retained as account-scoped credit so the CCIP send does not revert after
46
+ /// committing the bridge message.
47
47
  /// @param recipient The address that was supposed to receive the refund.
48
- /// @param amount The amount of the failed refund (permanently stuck in this contract).
48
+ /// @param amount The amount of the failed refund.
49
49
  event TransportPaymentRefundFailed(address indexed recipient, uint256 amount);
50
50
 
51
51
  //*********************************************************************//
@@ -74,21 +74,23 @@ contract JBCCIPSucker is JBSucker, IAny2EVMMessageReceiver {
74
74
 
75
75
  /// @param deployer A contract that deploys the clones for this contract.
76
76
  /// @param directory A contract storing directories of terminals and controllers for each project.
77
- /// @param tokens A contract that manages token minting and burning.
78
77
  /// @param permissions A contract storing permissions.
78
+ /// @param prices The price oracle used to convert peer-chain balances and surplus.
79
+ /// @param tokens A contract that manages token minting and burning.
79
80
  /// @param feeProjectId The ID of the project that receives fees.
80
81
  /// @param registry The sucker registry that tracks deployed suckers.
81
82
  /// @param trustedForwarder The trusted forwarder for ERC-2771 meta-transactions.
82
83
  constructor(
83
84
  JBCCIPSuckerDeployer deployer,
84
85
  IJBDirectory directory,
85
- IJBTokens tokens,
86
86
  IJBPermissions permissions,
87
+ address prices,
88
+ IJBTokens tokens,
87
89
  uint256 feeProjectId,
88
90
  IJBSuckerRegistry registry,
89
91
  address trustedForwarder
90
92
  )
91
- JBSucker(directory, permissions, tokens, feeProjectId, registry, trustedForwarder)
93
+ JBSucker(directory, permissions, prices, tokens, feeProjectId, registry, trustedForwarder)
92
94
  {
93
95
  // Read the remote chain ID from the deployer.
94
96
  REMOTE_CHAIN_ID = IJBCCIPSuckerDeployer(deployer).ccipRemoteChainId();
@@ -248,8 +250,13 @@ contract JBCCIPSucker is JBSucker, IAny2EVMMessageReceiver {
248
250
  refundRecipient: _msgSender()
249
251
  });
250
252
 
251
- // Emit an event if the excess transport payment refund failed.
252
- if (refundFailed) emit TransportPaymentRefundFailed(_msgSender(), refundAmount);
253
+ // Retain failed refunds as caller credit instead of leaving them project-addable or stranded.
254
+ if (refundFailed) {
255
+ // Refund accounting is isolated per caller; reentry cannot increase the retained credit.
256
+ // slither-disable-next-line reentrancy-benign
257
+ _retainTransportPaymentRefund({account: _msgSender(), amount: refundAmount});
258
+ emit TransportPaymentRefundFailed({recipient: _msgSender(), amount: refundAmount});
259
+ }
253
260
  }
254
261
 
255
262
  //*********************************************************************//
@@ -282,7 +289,7 @@ contract JBCCIPSucker is JBSucker, IAny2EVMMessageReceiver {
282
289
  // funds. CCIP wraps native tokens to WETH before bridging (see `_sendRootOverAMB`), so ALL tokens —
283
290
  // including native — need sufficient gas for an ERC-20 transfer on the remote chain.
284
291
  if (map.minGas < MESSENGER_ERC20_MIN_GAS_LIMIT) {
285
- revert JBSucker_BelowMinGas(map.minGas, MESSENGER_ERC20_MIN_GAS_LIMIT);
292
+ revert JBSucker_BelowMinGas({minGas: map.minGas, minGasLimit: MESSENGER_ERC20_MIN_GAS_LIMIT});
286
293
  }
287
294
  }
288
295
  }
@@ -37,17 +37,19 @@ contract JBCeloSucker is JBOptimismSucker {
37
37
  /// @param deployer A contract that deploys the clones for this contracts.
38
38
  /// @param directory A contract storing directories of terminals and controllers for each project.
39
39
  /// @param permissions A contract storing permissions.
40
+ /// @param prices The price oracle used to convert peer-chain balances and surplus.
40
41
  /// @param tokens A contract that manages token minting and burning.
41
42
  constructor(
42
43
  JBCeloSuckerDeployer deployer,
43
44
  IJBDirectory directory,
44
45
  IJBPermissions permissions,
46
+ address prices,
45
47
  IJBTokens tokens,
46
48
  uint256 feeProjectId,
47
49
  IJBSuckerRegistry registry,
48
50
  address trustedForwarder
49
51
  )
50
- JBOptimismSucker(deployer, directory, permissions, tokens, feeProjectId, registry, trustedForwarder)
52
+ JBOptimismSucker(deployer, directory, permissions, prices, tokens, feeProjectId, registry, trustedForwarder)
51
53
  {
52
54
  // Fetch the wrapped native token by doing a callback to the deployer contract.
53
55
  WRAPPED_NATIVE = JBCeloSuckerDeployer(deployer).wrappedNative();
@@ -82,7 +84,7 @@ contract JBCeloSucker is JBOptimismSucker {
82
84
  // Check addable amount against WETH balance before unwrapping.
83
85
  uint256 addableAmount = amountToAddToBalanceOf(token);
84
86
  if (amount > addableAmount) {
85
- revert JBSucker_InsufficientBalance(amount, addableAmount);
87
+ revert JBSucker_InsufficientBalance({amount: amount, balance: addableAmount});
86
88
  }
87
89
 
88
90
  // Unwrap WETH → native ETH.
@@ -95,7 +97,7 @@ contract JBCeloSucker is JBOptimismSucker {
95
97
  DIRECTORY.primaryTerminalOf({projectId: cachedProjectId, token: JBConstants.NATIVE_TOKEN});
96
98
 
97
99
  if (address(terminal) == address(0)) {
98
- revert JBSucker_NoTerminalForToken(cachedProjectId, JBConstants.NATIVE_TOKEN);
100
+ revert JBSucker_NoTerminalForToken({projectId: cachedProjectId, token: JBConstants.NATIVE_TOKEN});
99
101
  }
100
102
 
101
103
  // Add native ETH to the project's balance.
@@ -186,7 +188,7 @@ contract JBCeloSucker is JBOptimismSucker {
186
188
  // Enforce a reasonable minimum gas limit for bridging. Since we always bridge as ERC-20
187
189
  // (wrapping native ETH to WETH), all tokens need sufficient gas for an ERC-20 transfer.
188
190
  if (map.minGas < MESSENGER_ERC20_MIN_GAS_LIMIT) {
189
- revert JBSucker_BelowMinGas(map.minGas, MESSENGER_ERC20_MIN_GAS_LIMIT);
191
+ revert JBSucker_BelowMinGas({minGas: map.minGas, minGasLimit: MESSENGER_ERC20_MIN_GAS_LIMIT});
190
192
  }
191
193
  }
192
194
  }
@@ -35,18 +35,20 @@ contract JBOptimismSucker is JBSucker, IJBOptimismSucker {
35
35
 
36
36
  /// @param deployer A contract that deploys the clones for this contracts.
37
37
  /// @param directory A contract storing directories of terminals and controllers for each project.
38
- /// @param tokens A contract that manages token minting and burning.
39
38
  /// @param permissions A contract storing permissions.
39
+ /// @param prices The price oracle used to convert peer-chain balances and surplus.
40
+ /// @param tokens A contract that manages token minting and burning.
40
41
  constructor(
41
42
  JBOptimismSuckerDeployer deployer,
42
43
  IJBDirectory directory,
43
44
  IJBPermissions permissions,
45
+ address prices,
44
46
  IJBTokens tokens,
45
47
  uint256 feeProjectId,
46
48
  IJBSuckerRegistry registry,
47
49
  address trustedForwarder
48
50
  )
49
- JBSucker(directory, permissions, tokens, feeProjectId, registry, trustedForwarder)
51
+ JBSucker(directory, permissions, prices, tokens, feeProjectId, registry, trustedForwarder)
50
52
  {
51
53
  // Fetch the messenger and bridge by doing a callback to the deployer contract.
52
54
  OPBRIDGE = JBOptimismSuckerDeployer(deployer).opBridge();