@bananapus/suckers-v6 0.0.17 → 0.0.18

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 (51) hide show
  1. package/CHANGE_LOG.md +23 -0
  2. package/SKILLS.md +2 -0
  3. package/package.json +3 -3
  4. package/script/Deploy.s.sol +396 -215
  5. package/script/helpers/SuckerDeploymentLib.sol +4 -1
  6. package/src/JBArbitrumSucker.sol +1 -0
  7. package/src/JBCCIPSucker.sol +8 -2
  8. package/src/JBCeloSucker.sol +1 -0
  9. package/src/JBOptimismSucker.sol +1 -0
  10. package/src/JBSucker.sol +20 -14
  11. package/src/interfaces/IJBCCIPSuckerDeployer.sol +0 -1
  12. package/src/interfaces/IJBSucker.sol +0 -2
  13. package/src/structs/JBClaim.sol +1 -0
  14. package/src/structs/JBInboxTreeRoot.sol +1 -0
  15. package/src/structs/JBLeaf.sol +1 -0
  16. package/src/structs/JBMessageRoot.sol +1 -0
  17. package/src/structs/JBOutboxTree.sol +1 -0
  18. package/src/structs/JBRemoteToken.sol +1 -0
  19. package/src/structs/JBSuckerDeployerConfig.sol +1 -0
  20. package/src/structs/JBSuckersPair.sol +1 -0
  21. package/src/structs/JBTokenMapping.sol +1 -0
  22. package/test/Fork.t.sol +5 -19
  23. package/test/ForkArbitrum.t.sol +2 -0
  24. package/test/ForkCelo.t.sol +5 -0
  25. package/test/ForkClaim.t.sol +5 -15
  26. package/test/ForkMainnet.t.sol +3 -1
  27. package/test/ForkOPStack.t.sol +2 -0
  28. package/test/InteropCompat.t.sol +12 -1
  29. package/test/SuckerAttacks.t.sol +4 -3
  30. package/test/SuckerDeepAttacks.t.sol +12 -1
  31. package/test/SuckerRegressions.t.sol +5 -3
  32. package/test/TestAuditGaps.sol +6 -5
  33. package/test/audit/ArbitrumL2ToRemoteFeeDoS.t.sol +8 -0
  34. package/test/audit/{CodexNemesisPoC.t.sol → DeprecatedSuckerDestination.t.sol} +11 -13
  35. package/test/audit/ToRemoteFeeFallback.t.sol +131 -0
  36. package/test/audit/TrustedForwarderSpoof.t.sol +109 -0
  37. package/test/audit/TrustedForwarderSpoofCCIP.t.sol +112 -0
  38. package/test/fork/OptimismSuckerFork.t.sol +3 -1
  39. package/test/mocks/ERC20Mock.sol +1 -0
  40. package/test/mocks/MockMessenger.sol +1 -0
  41. package/test/regression/MapTokensDust.t.sol +3 -1
  42. package/test/unit/arb.t.sol +1 -1
  43. package/test/unit/ccip_native_interop.t.sol +12 -2
  44. package/test/unit/ccip_refund.t.sol +3 -5
  45. package/test/unit/deployer.t.sol +17 -7
  46. package/test/unit/emergency.t.sol +6 -0
  47. package/test/unit/fee_fallback.t.sol +243 -0
  48. package/test/unit/invariants.t.sol +3 -1
  49. package/test/unit/merkle.t.sol +6 -0
  50. package/test/unit/multi_chain_evolution.t.sol +23 -2
  51. package/test/unit/registry.t.sol +3 -4
package/CHANGE_LOG.md CHANGED
@@ -188,6 +188,13 @@ In v5, `JBSucker.initialize()` used `msg.sender` to set the `deployer` field. In
188
188
 
189
189
  ## 3. Event Changes
190
190
 
191
+ ### 3.0 Indexer Notes
192
+
193
+ This repo requires the largest schema change for subgraphs:
194
+ - any entity keyed by beneficiary, remote address, or remote token must now support raw `bytes32`;
195
+ - do not assume every remote identifier can be rendered as an EVM checksum address;
196
+ - separate transport-fee accounting from bridged-token accounting, since `toRemoteFee` replaced `minBridgeAmount`.
197
+
191
198
  ### 3.1 New Events
192
199
 
193
200
  See section 2.3 above.
@@ -440,3 +447,19 @@ Throughout the codebase, function calls were updated to use named argument synta
440
447
  | `ARBChains` | `ARBChains` | Identical |
441
448
  | `CCIPHelper` | `CCIPHelper` | Bare `revert("Unsupported chain")` replaced with typed `CCIPHelper_UnsupportedChain` error. |
442
449
  | `MerkleLib` | `MerkleLib` | Identical |
450
+
451
+ ---
452
+
453
+ ## 9. Post-Audit Fixes
454
+
455
+ ### 9.1 Deploy Script Resumability (NEW-L-1)
456
+
457
+ The `Deploy.s.sol` script's `_optimismSucker()`, `_baseSucker()`, `_arbitrumSucker()`, and `_deployCCIPSuckerWith()` functions previously returned early when the deployer contract existed at the CREATE2 address, without checking whether singleton configuration or registry allowlisting had completed. If a non-Sphinx deployment was interrupted after deployer creation but before `setChainSpecificConstants()`, `configureSingleton()`, or `allowSuckerDeployers()`, re-running the script would skip the incomplete deployer, leaving it unusable.
458
+
459
+ **Fix**: The early return now requires both `singleton != address(0)` and `REGISTRY.suckerDeployerIsAllowed(deployer)` to be true. If the deployer exists but is not fully configured, the script resumes from where it left off — skipping already-completed steps (deployer creation, chain-specific constants, singleton deployment) and executing only the missing ones. A new `_computeAddress()` helper was added to reuse the CREATE2 address computation logic.
460
+
461
+ ### 9.2 Fee Fallback DoS on Zero-Cost Bridges
462
+
463
+ The `JBSucker.toRemote()` fee payment has a best-effort pattern: if the fee terminal is missing or the `pay()` call reverts, the transaction should proceed without the fee. Previously, the catch block and no-terminal branch reset `transportPayment = msg.value`, restoring the fee amount into the transport payment. This caused zero-cost bridges (OP, Base, Celo, Arbitrum L2->L1) to revert with `JBSucker_UnexpectedMsgValue` because they check `if (transportPayment != 0) revert`.
464
+
465
+ **Fix**: On fee payment failure, `transportPayment` is no longer overwritten. It stays at `msg.value - _toRemoteFee` (which is 0 when the caller sends exactly the fee amount). The fee ETH is retained by the sucker contract rather than being added back to the transport payment. This preserves bridge compatibility for all zero-cost bridge implementations.
package/SKILLS.md CHANGED
@@ -158,6 +158,8 @@ Cross-chain token and fund bridging for Juicebox V6 projects, using merkle trees
158
158
  - `toRemote` fee payment is best-effort: if the fee project has no native token terminal or `terminal.pay()` reverts, `toRemote` proceeds without collecting the fee. The caller still receives project tokens from the fee payment when it succeeds.
159
159
  - `JBCCIPSucker` transport payment refund uses a low-level `call` that does NOT revert on failure. If the refund fails (e.g., caller is a non-payable contract), the excess ETH is permanently stuck. The `TransportPaymentRefundFailed` event provides observability.
160
160
  - The sucker has an unrestricted `receive()` function -- it must accept ETH from bridges, WETH unwrapping, and terminal cash-outs. Excess ETH increases `amountToAddToBalanceOf` for the project (not a double-spend risk).
161
+ - `fromRemote()` validates the peer using `msg.sender`, not `_msgSender()`. Using `_msgSender()` would allow a trusted ERC-2771 forwarder to spoof the bridge peer address. Never use a meta-tx forwarder as a relay for `fromRemote` calls.
162
+ - `ccipReceive()` validates the CCIP router using `msg.sender`, not `_msgSender()`, for the same reason. A trusted forwarder could append the router address via the ERC-2771 calldata suffix and fully control the `Any2EVMMessage` struct.
161
163
 
162
164
  ### CRITICAL: NATIVE_TOKEN Mismatch on Non-ETH Chains
163
165
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananapus/suckers-v6",
3
- "version": "0.0.17",
3
+ "version": "0.0.18",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,8 +19,8 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@arbitrum/nitro-contracts": "^1.2.1",
22
- "@bananapus/address-registry-v6": "^0.0.15",
23
- "@bananapus/core-v6": "^0.0.27",
22
+ "@bananapus/address-registry-v6": "^0.0.16",
23
+ "@bananapus/core-v6": "^0.0.28",
24
24
  "@bananapus/permission-ids-v6": "^0.0.14",
25
25
  "@chainlink/contracts-ccip": "^1.5.0",
26
26
  "@chainlink/local": "github:smartcontractkit/chainlink-local",